Add Attribute::DontEnum
This commit is contained in:
parent
f782aaee18
commit
3d09ec81e2
|
@ -115,17 +115,22 @@ impl<'gc> Avm1<'gc> {
|
|||
///
|
||||
/// This is necessary to support form submission from Flash via a couple of
|
||||
/// legacy methods, such as the `ActionGetURL2` opcode or `getURL` function.
|
||||
pub fn locals_into_form_values(&self) -> HashMap<String, String> {
|
||||
pub fn locals_into_form_values(
|
||||
&mut self,
|
||||
context: &mut ActionContext<'_, 'gc, '_>,
|
||||
) -> HashMap<String, String> {
|
||||
let mut form_values = HashMap::new();
|
||||
|
||||
for (k, v) in self
|
||||
.current_stack_frame()
|
||||
let locals = self
|
||||
.current_stack_frame_mut()
|
||||
.unwrap()
|
||||
.scope()
|
||||
.locals()
|
||||
.iter_values()
|
||||
{
|
||||
form_values.insert(k.clone(), v.clone().into_string());
|
||||
.scope_mut(context.gc_context)
|
||||
.locals_cell();
|
||||
|
||||
for k in locals.read().get_keys() {
|
||||
let v = locals
|
||||
.write(context.gc_context)
|
||||
.get(&k, self, context, locals);
|
||||
form_values.insert(k, v.clone().into_string());
|
||||
}
|
||||
|
||||
form_values
|
||||
|
@ -877,8 +882,8 @@ impl<'gc> Avm1<'gc> {
|
|||
}
|
||||
};
|
||||
|
||||
for (k, _) in ob.read().iter_values() {
|
||||
self.push(Value::String(k.to_owned()));
|
||||
for k in ob.read().get_keys() {
|
||||
self.push(Value::String(k));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1061,7 +1066,7 @@ impl<'gc> Avm1<'gc> {
|
|||
|
||||
fn action_get_url_2(
|
||||
&mut self,
|
||||
context: &mut ActionContext,
|
||||
context: &mut ActionContext<'_, 'gc, '_>,
|
||||
swf_method: swf::avm1::types::SendVarsMethod,
|
||||
is_target_sprite: bool,
|
||||
is_load_vars: bool,
|
||||
|
@ -1086,7 +1091,7 @@ impl<'gc> Avm1<'gc> {
|
|||
}
|
||||
|
||||
let vars = match NavigationMethod::from_send_vars_method(swf_method) {
|
||||
Some(method) => Some((method, self.locals_into_form_values())),
|
||||
Some(method) => Some((method, self.locals_into_form_values(context))),
|
||||
None => None,
|
||||
};
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@ impl<'gc> Executable<'gc> {
|
|||
arguments.force_set(
|
||||
"length",
|
||||
Value::Number(args.len() as f64),
|
||||
Attribute::DontDelete,
|
||||
Attribute::DontDelete | Attribute::DontEnum,
|
||||
);
|
||||
let argcell = GcCell::allocate(ac.gc_context, arguments);
|
||||
let child_scope = GcCell::allocate(
|
||||
|
@ -258,7 +258,7 @@ impl<'gc> Executable<'gc> {
|
|||
arguments.force_set(
|
||||
"length",
|
||||
Value::Number(args.len() as f64),
|
||||
Attribute::DontDelete,
|
||||
Attribute::DontDelete | Attribute::DontEnum,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ pub fn getURL<'a, 'gc>(
|
|||
Some(Value::String(s)) if s == "POST" => Some(NavigationMethod::POST),
|
||||
_ => None,
|
||||
};
|
||||
let vars_method = method.map(|m| (m, avm.locals_into_form_values()));
|
||||
let vars_method = method.map(|m| (m, avm.locals_into_form_values(context)));
|
||||
|
||||
context.navigator.navigate_to_url(url, window, vars_method);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ macro_rules! wrap_std {
|
|||
}
|
||||
},
|
||||
$gc_context,
|
||||
Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
)*
|
||||
}};
|
||||
|
@ -54,42 +54,42 @@ pub fn create<'gc>(gc_context: MutationContext<'gc, '_>) -> GcCell<'gc, Object<'
|
|||
math.force_set(
|
||||
"E",
|
||||
Value::Number(std::f64::consts::E),
|
||||
Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
math.force_set(
|
||||
"LN10",
|
||||
Value::Number(std::f64::consts::LN_10),
|
||||
Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
math.force_set(
|
||||
"LN2",
|
||||
Value::Number(std::f64::consts::LN_2),
|
||||
Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
math.force_set(
|
||||
"LOG10E",
|
||||
Value::Number(std::f64::consts::LOG10_E),
|
||||
Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
math.force_set(
|
||||
"LOG2E",
|
||||
Value::Number(std::f64::consts::LOG2_E),
|
||||
Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
math.force_set(
|
||||
"PI",
|
||||
Value::Number(std::f64::consts::PI),
|
||||
Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
math.force_set(
|
||||
"SQRT1_2",
|
||||
Value::Number(std::f64::consts::FRAC_1_SQRT_2),
|
||||
Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
math.force_set(
|
||||
"SQRT2",
|
||||
Value::Number(std::f64::consts::SQRT_2),
|
||||
Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
|
||||
wrap_std!(math, gc_context,
|
||||
|
@ -107,8 +107,18 @@ pub fn create<'gc>(gc_context: MutationContext<'gc, '_>) -> GcCell<'gc, Object<'
|
|||
"tan" => f64::tan
|
||||
);
|
||||
|
||||
math.force_set_function("atan2", atan2, gc_context, Attribute::DontDelete | Attribute::ReadOnly);
|
||||
math.force_set_function("random", random, gc_context, Attribute::DontDelete | Attribute::ReadOnly);
|
||||
math.force_set_function(
|
||||
"atan2",
|
||||
atan2,
|
||||
gc_context,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
math.force_set_function(
|
||||
"random",
|
||||
random,
|
||||
gc_context,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
|
||||
GcCell::allocate(gc_context, math)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ macro_rules! with_movie_clip {
|
|||
Value::Undefined
|
||||
},
|
||||
$gc_context,
|
||||
Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
)*
|
||||
}};
|
||||
|
@ -37,7 +37,7 @@ macro_rules! with_movie_clip_mut {
|
|||
Value::Undefined
|
||||
},
|
||||
$gc_context,
|
||||
Attribute::DontDelete | Attribute::ReadOnly,
|
||||
Attribute::DontDelete | Attribute::ReadOnly | Attribute::DontEnum,
|
||||
);
|
||||
)*
|
||||
}};
|
||||
|
|
|
@ -26,7 +26,7 @@ fn default_to_string<'gc>(
|
|||
#[derive(EnumSetType, Debug)]
|
||||
pub enum Attribute {
|
||||
DontDelete,
|
||||
// DontEnum,
|
||||
DontEnum,
|
||||
ReadOnly,
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,9 @@ impl<'gc> Property<'gc> {
|
|||
function(avm, context, this, &[new_value]);
|
||||
}
|
||||
}
|
||||
Property::Stored { value, attributes, .. } => {
|
||||
Property::Stored {
|
||||
value, attributes, ..
|
||||
} => {
|
||||
if !attributes.contains(Attribute::ReadOnly) {
|
||||
replace::<Value<'gc>>(value, new_value);
|
||||
}
|
||||
|
@ -83,6 +85,13 @@ impl<'gc> Property<'gc> {
|
|||
Property::Stored { attributes, .. } => !attributes.contains(Attribute::DontDelete),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_enumerable(&self) -> bool {
|
||||
match self {
|
||||
Property::Virtual { attributes, .. } => !attributes.contains(Attribute::DontEnum),
|
||||
Property::Stored { attributes, .. } => !attributes.contains(Attribute::DontEnum),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'gc> gc_arena::Collect for Property<'gc> {
|
||||
|
@ -157,7 +166,7 @@ impl<'gc> Object<'gc> {
|
|||
"toString",
|
||||
default_to_string,
|
||||
gc_context,
|
||||
Attribute::DontDelete,
|
||||
Attribute::DontDelete | Attribute::DontEnum,
|
||||
);
|
||||
|
||||
result
|
||||
|
@ -338,11 +347,17 @@ impl<'gc> Object<'gc> {
|
|||
self.values.contains_key(name)
|
||||
}
|
||||
|
||||
pub fn iter_values(&self) -> impl Iterator<Item = (&String, &Value<'gc>)> {
|
||||
self.values.iter().filter_map(|(k, p)| match p {
|
||||
Property::Virtual { .. } => None,
|
||||
Property::Stored { value, .. } => Some((k, value)),
|
||||
})
|
||||
pub fn get_keys(&self) -> Vec<String> {
|
||||
self.values
|
||||
.iter()
|
||||
.filter_map(|(k, p)| {
|
||||
if p.is_enumerable() {
|
||||
Some(k.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn call(
|
||||
|
@ -472,8 +487,20 @@ mod tests {
|
|||
Attribute::ReadOnly,
|
||||
);
|
||||
|
||||
object.write(context.gc_context).set("normal", Value::String("replaced".to_string()), avm, context, object);
|
||||
object.write(context.gc_context).set("readonly", Value::String("replaced".to_string()), avm, context, object);
|
||||
object.write(context.gc_context).set(
|
||||
"normal",
|
||||
Value::String("replaced".to_string()),
|
||||
avm,
|
||||
context,
|
||||
object,
|
||||
);
|
||||
object.write(context.gc_context).set(
|
||||
"readonly",
|
||||
Value::String("replaced".to_string()),
|
||||
avm,
|
||||
context,
|
||||
object,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
object.read().get("normal", avm, context, object),
|
||||
|
@ -501,7 +528,13 @@ mod tests {
|
|||
Value::String("initial".to_string())
|
||||
);
|
||||
|
||||
object.write(context.gc_context).set("test", Value::String("replaced".to_string()), avm, context, object);
|
||||
object.write(context.gc_context).set(
|
||||
"test",
|
||||
Value::String("replaced".to_string()),
|
||||
avm,
|
||||
context,
|
||||
object,
|
||||
);
|
||||
|
||||
assert_eq!(object.write(context.gc_context).delete("test"), false);
|
||||
assert_eq!(
|
||||
|
@ -576,7 +609,10 @@ mod tests {
|
|||
assert_eq!(object.write(context.gc_context).delete("virtual_un"), false);
|
||||
assert_eq!(object.write(context.gc_context).delete("stored"), true);
|
||||
assert_eq!(object.write(context.gc_context).delete("stored_un"), false);
|
||||
assert_eq!(object.write(context.gc_context).delete("non_existent"), false);
|
||||
assert_eq!(
|
||||
object.write(context.gc_context).delete("non_existent"),
|
||||
false
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
object.read().get("virtual", avm, context, object),
|
||||
|
@ -596,4 +632,39 @@ mod tests {
|
|||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iter_values() {
|
||||
with_object(0, |_avm, context, object| {
|
||||
let getter: NativeFunction = |_avm, _context, _this, _args| Value::Null;
|
||||
|
||||
object
|
||||
.write(context.gc_context)
|
||||
.force_set("stored", Value::Null, EnumSet::empty());
|
||||
object.write(context.gc_context).force_set(
|
||||
"stored_hidden",
|
||||
Value::Null,
|
||||
Attribute::DontEnum,
|
||||
);
|
||||
object.write(context.gc_context).force_set_virtual(
|
||||
"virtual",
|
||||
getter,
|
||||
None,
|
||||
EnumSet::empty(),
|
||||
);
|
||||
object.write(context.gc_context).force_set_virtual(
|
||||
"virtual_hidden",
|
||||
getter,
|
||||
None,
|
||||
Attribute::DontEnum,
|
||||
);
|
||||
|
||||
let keys = object.read().get_keys();
|
||||
assert_eq!(keys.len(), 2);
|
||||
assert_eq!(keys.contains(&"stored".to_string()), true);
|
||||
assert_eq!(keys.contains(&"stored_hidden".to_string()), false);
|
||||
assert_eq!(keys.contains(&"virtual".to_string()), true);
|
||||
assert_eq!(keys.contains(&"virtual_hidden".to_string()), false);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,6 +210,11 @@ impl<'gc> Scope<'gc> {
|
|||
self.values.read()
|
||||
}
|
||||
|
||||
/// Returns a gc cell of the current local scope object.
|
||||
pub fn locals_cell(&self) -> GcCell<'gc, Object<'gc>> {
|
||||
self.values.to_owned()
|
||||
}
|
||||
|
||||
/// Returns a reference to the current local scope object for mutation.
|
||||
pub fn locals_mut(&self, mc: MutationContext<'gc, '_>) -> RefMut<Object<'gc>> {
|
||||
self.values.write(mc)
|
||||
|
|
Loading…
Reference in New Issue