From c29b042f5e458e4dcef5dd12372e9d96aa9b1b6e Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Tue, 3 Dec 2019 00:51:11 -0800 Subject: [PATCH] avm1: Get child clip instances in StageObject Add the logic to get children display objects as properties in `StageObject`. --- core/src/avm1/object.rs | 43 ++++++++++++++++++------------ core/src/avm1/stage_object.rs | 49 ++++++++++++++++++++++++++++++----- 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index 36c6688e6..0c5002ce2 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -48,23 +48,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy if self.has_own_property(name) { self.get_local(name, avm, context, (*self).into()) } else { - let mut depth = 0; - let mut proto = self.proto(); - - while proto.is_some() { - if depth == 255 { - return Err("Encountered an excessively deep prototype chain.".into()); - } - - if proto.unwrap().has_own_property(name) { - return proto.unwrap().get_local(name, avm, context, (*self).into()); - } - - proto = proto.unwrap().proto(); - depth += 1; - } - - Ok(Value::Undefined.into()) + search_prototype(self.proto(), name, avm, context, (*self).into()) } } @@ -200,3 +184,28 @@ impl<'gc> Object<'gc> { a.as_ptr() == b.as_ptr() } } + +pub fn search_prototype<'gc>( + mut proto: Option>, + name: &str, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, +) -> Result, Error> { + let mut depth = 0; + + while proto.is_some() { + if depth == 255 { + return Err("Encountered an excessively deep prototype chain.".into()); + } + + if proto.unwrap().has_own_property(name) { + return proto.unwrap().get_local(name, avm, context, this); + } + + proto = proto.unwrap().proto(); + depth += 1; + } + + Ok(Value::Undefined.into()) +} diff --git a/core/src/avm1/stage_object.rs b/core/src/avm1/stage_object.rs index e975f9c96..d290859e4 100644 --- a/core/src/avm1/stage_object.rs +++ b/core/src/avm1/stage_object.rs @@ -3,7 +3,7 @@ use crate::avm1::function::Executable; use crate::avm1::property::Attribute; use crate::avm1::return_value::ReturnValue; -use crate::avm1::{Avm1, Error, Object, ObjectPtr, ScriptObject, TObject, Value}; +use crate::avm1::{Avm1, Error, Object, ObjectPtr, ScriptObject, TDisplayObject, TObject, Value}; use crate::context::UpdateContext; use crate::display_object::DisplayObject; use enumset::EnumSet; @@ -57,6 +57,26 @@ impl fmt::Debug for StageObject<'_> { } impl<'gc> TObject<'gc> for StageObject<'gc> { + fn get( + &self, + name: &str, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + ) -> Result, Error> { + // Property search order for DisplayObjects: + if self.has_own_property(name) { + // 1) Actual properties on the underlying object + self.get_local(name, avm, context, (*self).into()) + } else if let Some(child) = self.display_object.get_child_by_name(name) { + // 2) Child display objects with the given instance name + Ok(child.object().into()) + } else { + // 3) Prototype + crate::avm1::object::search_prototype(self.proto(), name, avm, context, (*self).into()) + } + // 4) TODO: __resolve? + } + fn get_local( &self, name: &str, @@ -66,15 +86,15 @@ impl<'gc> TObject<'gc> for StageObject<'gc> { ) -> Result, Error> { self.base.get_local(name, avm, context, this) } + fn set( &self, name: &str, value: Value<'gc>, avm: &mut Avm1<'gc>, context: &mut UpdateContext<'_, 'gc, '_>, - this: Object<'gc>, ) -> Result<(), Error> { - self.base.set(name, value, avm, context, this) + self.base.set(name, value, avm, context) } fn call( @@ -127,10 +147,19 @@ impl<'gc> TObject<'gc> for StageObject<'gc> { } fn has_property(&self, name: &str) -> bool { - self.base.has_property(name) + if self.base.has_property(name) { + return true; + } + + if self.display_object.get_child_by_name(name).is_some() { + return true; + } + + false } fn has_own_property(&self, name: &str) -> bool { + // Note that `hasOwnProperty` does NOT return true for child display objects. self.base.has_own_property(name) } @@ -143,11 +172,19 @@ impl<'gc> TObject<'gc> for StageObject<'gc> { } fn get_keys(&self) -> HashSet { - self.base.get_keys() + // Keys from the underlying object are listed first, followed by + // child display objects in order from highest depth to lowest depth. + // TODO: It's possible to have multiple instances with the same name, + // and that name will be returned multiple times in the key list for a `for..in` loop. + let mut keys = self.base.get_keys(); + for child in self.display_object.children() { + keys.insert(child.name().to_string()); + } + keys } fn as_string(&self) -> String { - self.base.as_string() + self.display_object.path() } fn type_of(&self) -> &'static str {