From ee4b47d0622302d0d879ddd79d07bcb3eba290d0 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Mon, 28 Oct 2019 18:13:29 -0400 Subject: [PATCH] Add interface support, and add interface checking to `ActionInstanceOf`. --- core/src/avm1.rs | 35 ++++++++++++++-------------------- core/src/avm1/object.rs | 10 ++++++++++ core/src/avm1/script_object.rs | 15 +++++++++++++++ core/src/avm1/stage_object.rs | 8 ++++++++ 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 9d65e11ce..0071a82f9 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -1041,28 +1041,13 @@ impl<'gc> Avm1<'gc> { //TODO: What happens if we try to extend an object which has no `__proto__`? //e.g. `class Whatever extends Object.prototype` - let super_proto = superclass.read().proto().unwrap_or(self.prototypes.object); + let super_proto = superclass.proto().unwrap_or(self.prototypes.object); - let sub_prototype = GcCell::allocate( - context.gc_context, - Box::new(ScriptObject::object(context.gc_context, Some(super_proto))) - as Box>, - ); + let sub_prototype: Object<'gc> = + ScriptObject::object(context.gc_context, Some(super_proto)).into(); - sub_prototype.write(context.gc_context).set( - "constructor", - superclass.into(), - self, - context, - sub_prototype, - )?; - subclass.write(context.gc_context).set( - "prototype", - sub_prototype.into(), - self, - context, - subclass, - )?; + sub_prototype.set("constructor", superclass.into(), self, context)?; + subclass.set("prototype", sub_prototype.into(), self, context)?; Ok(()) } @@ -1364,7 +1349,6 @@ impl<'gc> Avm1<'gc> { let constr = self.pop()?.as_object()?; let obj = self.pop()?.as_object()?; - //TODO: Interface detection on SWF7 let prototype = constr .get("prototype", self, context)? .resolve(self, context)? @@ -1377,6 +1361,15 @@ impl<'gc> Avm1<'gc> { return Ok(()); } + if self.current_swf_version() >= 7 { + for interface in constr.interfaces() { + if Object::ptr_eq(interface, constr) { + self.push(true); + return Ok(()); + } + } + } + proto = this_proto.proto(); } diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index ed8562d5f..490a78a25 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -165,6 +165,16 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// Get the object's type string. fn type_of(&self) -> &'static str; + /// Enumerate all interfaces implemented by this object. + fn interfaces(&self) -> Vec>; + + /// Set the interface list for this object. (Only useful for prototypes.) + fn set_interfaces( + &mut self, + gc_context: MutationContext<'gc, '_>, + iface_list: Vec>, + ); + /// Get the underlying script object, if it exists. fn as_script_object(&self) -> Option>; diff --git a/core/src/avm1/script_object.rs b/core/src/avm1/script_object.rs index 131694430..46490be6d 100644 --- a/core/src/avm1/script_object.rs +++ b/core/src/avm1/script_object.rs @@ -27,6 +27,7 @@ pub struct ScriptObjectData<'gc> { prototype: Option>, values: HashMap>, function: Option>, + interfaces: Vec>, type_of: &'static str, array: ArrayStorage<'gc>, } @@ -37,6 +38,7 @@ unsafe impl<'gc> Collect for ScriptObjectData<'gc> { self.values.trace(cc); self.function.trace(cc); self.array.trace(cc); + self.interfaces.trace(cc); } } @@ -64,6 +66,7 @@ impl<'gc> ScriptObject<'gc> { values: HashMap::new(), function: None, array: ArrayStorage::Properties { length: 0 }, + interfaces: vec![], }, )) } @@ -80,6 +83,7 @@ impl<'gc> ScriptObject<'gc> { values: HashMap::new(), function: None, array: ArrayStorage::Vector(Vec::new()), + interfaces: vec![], }, )); object.sync_native_property("length", gc_context, Some(0.into())); @@ -99,6 +103,7 @@ impl<'gc> ScriptObject<'gc> { values: HashMap::new(), function: None, array: ArrayStorage::Properties { length: 0 }, + interfaces: vec![], }, )) .into() @@ -118,6 +123,7 @@ impl<'gc> ScriptObject<'gc> { values: HashMap::new(), function: None, array: ArrayStorage::Properties { length: 0 }, + interfaces: vec![], }, )) } @@ -136,6 +142,7 @@ impl<'gc> ScriptObject<'gc> { function: Some(function.into()), values: HashMap::new(), array: ArrayStorage::Properties { length: 0 }, + interfaces: vec![], }, )) } @@ -465,6 +472,14 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> { self.0.read().type_of } + fn interfaces(&self) -> Vec> { + self.0.read().interfaces.clone() + } + + fn set_interfaces(&mut self, context: MutationContext<'gc, '_>, iface_list: Vec>) { + self.0.write(context).interfaces = iface_list; + } + fn as_script_object(&self) -> Option> { Some(*self) } diff --git a/core/src/avm1/stage_object.rs b/core/src/avm1/stage_object.rs index c27eb5351..2aad83704 100644 --- a/core/src/avm1/stage_object.rs +++ b/core/src/avm1/stage_object.rs @@ -228,6 +228,14 @@ impl<'gc> TObject<'gc> for StageObject<'gc> { self.base.delete_array_element(index, gc_context) } + fn interfaces(&self) -> Vec> { + self.base.interfaces() + } + + fn set_interfaces(&mut self, context: MutationContext<'gc, '_>, iface_list: Vec>) { + self.base.set_interfaces(context, iface_list) + } + fn as_string(&self) -> String { self.display_object.path() }