From 041003f256b089dbb6b818ac814378cb5575883d Mon Sep 17 00:00:00 2001 From: David Wendt Date: Mon, 26 Apr 2021 18:43:29 -0400 Subject: [PATCH] avm2: Slot lookups don't need the whole trait list, just the first one we find. --- core/src/avm2/class.rs | 38 +++++------------------ core/src/avm2/object.rs | 6 +--- core/src/avm2/object/custom_object.rs | 11 ++----- core/src/avm2/object/script_object.rs | 44 +++++++++------------------ core/src/avm2/object/stage_object.rs | 8 ++--- 5 files changed, 26 insertions(+), 81 deletions(-) diff --git a/core/src/avm2/class.rs b/core/src/avm2/class.rs index 2392cf9a6..84d593f59 100644 --- a/core/src/avm2/class.rs +++ b/core/src/avm2/class.rs @@ -111,16 +111,10 @@ fn do_trait_lookup<'gc>( } /// Find traits in a list of traits matching a slot ID. -/// -/// This function also enforces final/override bits on the traits, and will -/// raise `VerifyError`s as needed. -/// -/// TODO: This is an O(n^2) algorithm, it sucks. fn do_trait_lookup_by_slot<'gc>( id: u32, - known_traits: &mut Vec>, all_traits: &[Trait<'gc>], -) -> Result<(), Error> { +) -> Result>, Error> { for trait_entry in all_traits { let trait_id = match trait_entry.kind() { TraitKind::Slot { slot_id, .. } => slot_id, @@ -131,21 +125,11 @@ fn do_trait_lookup_by_slot<'gc>( }; if id == *trait_id { - for known_trait in known_traits.iter() { - if known_trait.is_final() { - return Err("Attempting to override a final definition".into()); - } - - if !trait_entry.is_override() { - return Err("Definition override is not marked as override".into()); - } - } - - known_traits.push(trait_entry.clone()); + return Ok(Some(trait_entry.clone())); } } - Ok(()) + Ok(None) } impl<'gc> Class<'gc> { @@ -394,12 +378,8 @@ impl<'gc> Class<'gc> { /// If a given trait has an invalid name, attempts to override a final trait, /// or overlaps an existing trait without being an override, then this function /// returns an error. - pub fn lookup_class_traits_by_slot( - &self, - id: u32, - known_traits: &mut Vec>, - ) -> Result<(), Error> { - do_trait_lookup_by_slot(id, known_traits, &self.class_traits) + pub fn lookup_class_traits_by_slot(&self, id: u32) -> Result>, Error> { + do_trait_lookup_by_slot(id, &self.class_traits) } /// Determines if this class provides a given trait on itself. @@ -465,12 +445,8 @@ impl<'gc> Class<'gc> { /// If a given trait has an invalid name, attempts to override a final trait, /// or overlaps an existing trait without being an override, then this function /// returns an error. - pub fn lookup_instance_traits_by_slot( - &self, - id: u32, - known_traits: &mut Vec>, - ) -> Result<(), Error> { - do_trait_lookup_by_slot(id, known_traits, &self.instance_traits) + pub fn lookup_instance_traits_by_slot(&self, id: u32) -> Result>, Error> { + do_trait_lookup_by_slot(id, &self.instance_traits) } /// Determines if this class provides a given trait on its instances. diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index 7c9856033..88e7d5539 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -317,11 +317,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// not instances. For resolving traits for normal `TObject` methods, use /// `get_trait` and `has_trait` as it will tell you if the current object /// has a given trait. - fn get_provided_trait_slot( - &self, - id: u32, - known_traits: &mut Vec>, - ) -> Result<(), Error>; + fn get_provided_trait_slot(&self, id: u32) -> Result>, Error>; /// Retrieves the scope chain of the object at time of its creation. /// diff --git a/core/src/avm2/object/custom_object.rs b/core/src/avm2/object/custom_object.rs index 07e2aedd6..5e20482af 100644 --- a/core/src/avm2/object/custom_object.rs +++ b/core/src/avm2/object/custom_object.rs @@ -140,15 +140,8 @@ macro_rules! impl_avm2_custom_object { self.0.read().$field.get_provided_trait(name, known_traits) } - fn get_provided_trait_slot( - &self, - id: u32, - known_traits: &mut Vec>, - ) -> Result<(), Error> { - self.0 - .read() - .$field - .get_provided_trait_slot(id, known_traits) + fn get_provided_trait_slot(&self, id: u32) -> Result>, Error> { + self.0.read().$field.get_provided_trait_slot(id) } fn get_scope(self) -> Option>> { diff --git a/core/src/avm2/object/script_object.rs b/core/src/avm2/object/script_object.rs index 279de26d7..6f602446f 100644 --- a/core/src/avm2/object/script_object.rs +++ b/core/src/avm2/object/script_object.rs @@ -180,12 +180,8 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { self.0.read().get_provided_trait(name, known_traits) } - fn get_provided_trait_slot( - &self, - id: u32, - known_traits: &mut Vec>, - ) -> Result<(), Error> { - self.0.read().get_provided_trait_slot(id, known_traits) + fn get_provided_trait_slot(&self, id: u32) -> Result>, Error> { + self.0.read().get_provided_trait_slot(id) } fn get_scope(self) -> Option>> { @@ -623,12 +619,7 @@ impl<'gc> ScriptObjectData<'gc> { pub fn get_trait_slot(&self, id: u32) -> Result>, Error> { match &self.class { //Class constructors have local slot traits only. - ScriptObjectClass::ClassConstructor(..) => { - let mut known_traits = Vec::new(); - self.get_provided_trait_slot(id, &mut known_traits)?; - - Ok(known_traits.first().cloned()) - } + ScriptObjectClass::ClassConstructor(..) => self.get_provided_trait_slot(id), //Prototypes do not have traits available locally, but they provide //traits instead. @@ -637,20 +628,17 @@ impl<'gc> ScriptObjectData<'gc> { //Instances walk the prototype chain to build a list of known //traits provided by the classes attached to those prototypes. ScriptObjectClass::NoClass => { - let mut known_traits = Vec::new(); - let mut chain = Vec::new(); let mut proto = self.proto(); while let Some(p) = proto { - chain.push(p); + if let Some(trait_val) = p.get_provided_trait_slot(id)? { + return Ok(Some(trait_val)); + } + proto = p.proto(); } - for proto in chain.iter().rev() { - proto.get_provided_trait_slot(id, &mut known_traits)?; - } - - Ok(known_traits.first().cloned()) + Ok(None) } } } @@ -671,19 +659,15 @@ impl<'gc> ScriptObjectData<'gc> { } } - pub fn get_provided_trait_slot( - &self, - id: u32, - known_traits: &mut Vec>, - ) -> Result<(), Error> { + pub fn get_provided_trait_slot(&self, id: u32) -> Result>, Error> { match &self.class { ScriptObjectClass::ClassConstructor(class, ..) => { - class.read().lookup_class_traits_by_slot(id, known_traits) + class.read().lookup_class_traits_by_slot(id) } - ScriptObjectClass::InstancePrototype(class, ..) => class - .read() - .lookup_instance_traits_by_slot(id, known_traits), - ScriptObjectClass::NoClass => Ok(()), + ScriptObjectClass::InstancePrototype(class, ..) => { + class.read().lookup_instance_traits_by_slot(id) + } + ScriptObjectClass::NoClass => Ok(None), } } diff --git a/core/src/avm2/object/stage_object.rs b/core/src/avm2/object/stage_object.rs index 520331708..40495587f 100644 --- a/core/src/avm2/object/stage_object.rs +++ b/core/src/avm2/object/stage_object.rs @@ -181,12 +181,8 @@ impl<'gc> TObject<'gc> for StageObject<'gc> { self.0.read().base.get_provided_trait(name, known_traits) } - fn get_provided_trait_slot( - &self, - id: u32, - known_traits: &mut Vec>, - ) -> Result<(), Error> { - self.0.read().base.get_provided_trait_slot(id, known_traits) + fn get_provided_trait_slot(&self, id: u32) -> Result>, Error> { + self.0.read().base.get_provided_trait_slot(id) } fn get_scope(self) -> Option>> {