avm2: Slot lookups don't need the whole trait list, just the first one we find.

This commit is contained in:
David Wendt 2021-04-26 18:43:29 -04:00 committed by Mike Welsh
parent fd08a6ebf6
commit 041003f256
5 changed files with 26 additions and 81 deletions

View File

@ -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<Trait<'gc>>,
all_traits: &[Trait<'gc>],
) -> Result<(), Error> {
) -> Result<Option<Trait<'gc>>, 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());
return Ok(Some(trait_entry.clone()));
}
}
known_traits.push(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<Trait<'gc>>,
) -> Result<(), Error> {
do_trait_lookup_by_slot(id, known_traits, &self.class_traits)
pub fn lookup_class_traits_by_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, 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<Trait<'gc>>,
) -> Result<(), Error> {
do_trait_lookup_by_slot(id, known_traits, &self.instance_traits)
pub fn lookup_instance_traits_by_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, Error> {
do_trait_lookup_by_slot(id, &self.instance_traits)
}
/// Determines if this class provides a given trait on its instances.

View File

@ -317,11 +317,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + 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<Trait<'gc>>,
) -> Result<(), Error>;
fn get_provided_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, Error>;
/// Retrieves the scope chain of the object at time of its creation.
///

View File

@ -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<Trait<'gc>>,
) -> Result<(), Error> {
self.0
.read()
.$field
.get_provided_trait_slot(id, known_traits)
fn get_provided_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, Error> {
self.0.read().$field.get_provided_trait_slot(id)
}
fn get_scope(self) -> Option<GcCell<'gc, Scope<'gc>>> {

View File

@ -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<Trait<'gc>>,
) -> Result<(), Error> {
self.0.read().get_provided_trait_slot(id, known_traits)
fn get_provided_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, Error> {
self.0.read().get_provided_trait_slot(id)
}
fn get_scope(self) -> Option<GcCell<'gc, Scope<'gc>>> {
@ -623,12 +619,7 @@ impl<'gc> ScriptObjectData<'gc> {
pub fn get_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, 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<Trait<'gc>>,
) -> Result<(), Error> {
pub fn get_provided_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, 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),
}
}

View File

@ -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<Trait<'gc>>,
) -> Result<(), Error> {
self.0.read().base.get_provided_trait_slot(id, known_traits)
fn get_provided_trait_slot(&self, id: u32) -> Result<Option<Trait<'gc>>, Error> {
self.0.read().base.get_provided_trait_slot(id)
}
fn get_scope(self) -> Option<GcCell<'gc, Scope<'gc>>> {