From 3d09ec81e22dc91308a7a3b5e4bf655959ddfa0a Mon Sep 17 00:00:00 2001 From: Nathan Adams Date: Tue, 8 Oct 2019 16:34:08 +0200 Subject: [PATCH] Add Attribute::DontEnum --- core/src/avm1.rs | 31 +++++++----- core/src/avm1/function.rs | 4 +- core/src/avm1/globals.rs | 2 +- core/src/avm1/globals/math.rs | 32 ++++++++---- core/src/avm1/movie_clip.rs | 4 +- core/src/avm1/object.rs | 95 ++++++++++++++++++++++++++++++----- core/src/avm1/scope.rs | 5 ++ 7 files changed, 132 insertions(+), 41 deletions(-) diff --git a/core/src/avm1.rs b/core/src/avm1.rs index e42aa4151..0c734e531 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -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 { + pub fn locals_into_form_values( + &mut self, + context: &mut ActionContext<'_, 'gc, '_>, + ) -> HashMap { 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, }; diff --git a/core/src/avm1/function.rs b/core/src/avm1/function.rs index 1028ef3be..7043e4f76 100644 --- a/core/src/avm1/function.rs +++ b/core/src/avm1/function.rs @@ -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, ); } diff --git a/core/src/avm1/globals.rs b/core/src/avm1/globals.rs index ea03096d1..f0f48417c 100644 --- a/core/src/avm1/globals.rs +++ b/core/src/avm1/globals.rs @@ -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); } diff --git a/core/src/avm1/globals/math.rs b/core/src/avm1/globals/math.rs index 5e759d752..a353fc12f 100644 --- a/core/src/avm1/globals/math.rs +++ b/core/src/avm1/globals/math.rs @@ -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) } diff --git a/core/src/avm1/movie_clip.rs b/core/src/avm1/movie_clip.rs index 70ad58eda..dbfe91917 100644 --- a/core/src/avm1/movie_clip.rs +++ b/core/src/avm1/movie_clip.rs @@ -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, ); )* }}; diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index dd6c270f8..dad6ee071 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -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, 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)> { - self.values.iter().filter_map(|(k, p)| match p { - Property::Virtual { .. } => None, - Property::Stored { value, .. } => Some((k, value)), - }) + pub fn get_keys(&self) -> Vec { + 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); + }) + } } diff --git a/core/src/avm1/scope.rs b/core/src/avm1/scope.rs index f814f9de6..fa948326d 100644 --- a/core/src/avm1/scope.rs +++ b/core/src/avm1/scope.rs @@ -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> { self.values.write(mc)