avm1: Get child clip instances in StageObject

Add the logic to get children display objects as properties in
`StageObject`.
This commit is contained in:
Mike Welsh 2019-12-03 00:51:11 -08:00
parent 783ede6f79
commit c29b042f5e
2 changed files with 69 additions and 23 deletions

View File

@ -48,23 +48,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + 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<Object<'gc>>,
name: &str,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
this: Object<'gc>,
) -> Result<ReturnValue<'gc>, 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())
}

View File

@ -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<ReturnValue<'gc>, 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<ReturnValue<'gc>, 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<String> {
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 {