Add interface support, and add interface checking to `ActionInstanceOf`.

This commit is contained in:
David Wendt 2019-10-28 18:13:29 -04:00 committed by Mike Welsh
parent ca93bba5c1
commit ee4b47d062
4 changed files with 47 additions and 21 deletions

View File

@ -1041,28 +1041,13 @@ impl<'gc> Avm1<'gc> {
//TODO: What happens if we try to extend an object which has no `__proto__`? //TODO: What happens if we try to extend an object which has no `__proto__`?
//e.g. `class Whatever extends Object.prototype` //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( let sub_prototype: Object<'gc> =
context.gc_context, ScriptObject::object(context.gc_context, Some(super_proto)).into();
Box::new(ScriptObject::object(context.gc_context, Some(super_proto)))
as Box<dyn Object<'gc>>,
);
sub_prototype.write(context.gc_context).set( sub_prototype.set("constructor", superclass.into(), self, context)?;
"constructor", subclass.set("prototype", sub_prototype.into(), self, context)?;
superclass.into(),
self,
context,
sub_prototype,
)?;
subclass.write(context.gc_context).set(
"prototype",
sub_prototype.into(),
self,
context,
subclass,
)?;
Ok(()) Ok(())
} }
@ -1364,7 +1349,6 @@ impl<'gc> Avm1<'gc> {
let constr = self.pop()?.as_object()?; let constr = self.pop()?.as_object()?;
let obj = self.pop()?.as_object()?; let obj = self.pop()?.as_object()?;
//TODO: Interface detection on SWF7
let prototype = constr let prototype = constr
.get("prototype", self, context)? .get("prototype", self, context)?
.resolve(self, context)? .resolve(self, context)?
@ -1377,6 +1361,15 @@ impl<'gc> Avm1<'gc> {
return Ok(()); 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(); proto = this_proto.proto();
} }

View File

@ -165,6 +165,16 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// Get the object's type string. /// Get the object's type string.
fn type_of(&self) -> &'static str; fn type_of(&self) -> &'static str;
/// Enumerate all interfaces implemented by this object.
fn interfaces(&self) -> Vec<Object<'gc>>;
/// Set the interface list for this object. (Only useful for prototypes.)
fn set_interfaces(
&mut self,
gc_context: MutationContext<'gc, '_>,
iface_list: Vec<Object<'gc>>,
);
/// Get the underlying script object, if it exists. /// Get the underlying script object, if it exists.
fn as_script_object(&self) -> Option<ScriptObject<'gc>>; fn as_script_object(&self) -> Option<ScriptObject<'gc>>;

View File

@ -27,6 +27,7 @@ pub struct ScriptObjectData<'gc> {
prototype: Option<Object<'gc>>, prototype: Option<Object<'gc>>,
values: HashMap<String, Property<'gc>>, values: HashMap<String, Property<'gc>>,
function: Option<Executable<'gc>>, function: Option<Executable<'gc>>,
interfaces: Vec<Object<'gc>>,
type_of: &'static str, type_of: &'static str,
array: ArrayStorage<'gc>, array: ArrayStorage<'gc>,
} }
@ -37,6 +38,7 @@ unsafe impl<'gc> Collect for ScriptObjectData<'gc> {
self.values.trace(cc); self.values.trace(cc);
self.function.trace(cc); self.function.trace(cc);
self.array.trace(cc); self.array.trace(cc);
self.interfaces.trace(cc);
} }
} }
@ -64,6 +66,7 @@ impl<'gc> ScriptObject<'gc> {
values: HashMap::new(), values: HashMap::new(),
function: None, function: None,
array: ArrayStorage::Properties { length: 0 }, array: ArrayStorage::Properties { length: 0 },
interfaces: vec![],
}, },
)) ))
} }
@ -80,6 +83,7 @@ impl<'gc> ScriptObject<'gc> {
values: HashMap::new(), values: HashMap::new(),
function: None, function: None,
array: ArrayStorage::Vector(Vec::new()), array: ArrayStorage::Vector(Vec::new()),
interfaces: vec![],
}, },
)); ));
object.sync_native_property("length", gc_context, Some(0.into())); object.sync_native_property("length", gc_context, Some(0.into()));
@ -99,6 +103,7 @@ impl<'gc> ScriptObject<'gc> {
values: HashMap::new(), values: HashMap::new(),
function: None, function: None,
array: ArrayStorage::Properties { length: 0 }, array: ArrayStorage::Properties { length: 0 },
interfaces: vec![],
}, },
)) ))
.into() .into()
@ -118,6 +123,7 @@ impl<'gc> ScriptObject<'gc> {
values: HashMap::new(), values: HashMap::new(),
function: None, function: None,
array: ArrayStorage::Properties { length: 0 }, array: ArrayStorage::Properties { length: 0 },
interfaces: vec![],
}, },
)) ))
} }
@ -136,6 +142,7 @@ impl<'gc> ScriptObject<'gc> {
function: Some(function.into()), function: Some(function.into()),
values: HashMap::new(), values: HashMap::new(),
array: ArrayStorage::Properties { length: 0 }, array: ArrayStorage::Properties { length: 0 },
interfaces: vec![],
}, },
)) ))
} }
@ -465,6 +472,14 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
self.0.read().type_of self.0.read().type_of
} }
fn interfaces(&self) -> Vec<Object<'gc>> {
self.0.read().interfaces.clone()
}
fn set_interfaces(&mut self, context: MutationContext<'gc, '_>, iface_list: Vec<Object<'gc>>) {
self.0.write(context).interfaces = iface_list;
}
fn as_script_object(&self) -> Option<ScriptObject<'gc>> { fn as_script_object(&self) -> Option<ScriptObject<'gc>> {
Some(*self) Some(*self)
} }

View File

@ -228,6 +228,14 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
self.base.delete_array_element(index, gc_context) self.base.delete_array_element(index, gc_context)
} }
fn interfaces(&self) -> Vec<Object<'gc>> {
self.base.interfaces()
}
fn set_interfaces(&mut self, context: MutationContext<'gc, '_>, iface_list: Vec<Object<'gc>>) {
self.base.set_interfaces(context, iface_list)
}
fn as_string(&self) -> String { fn as_string(&self) -> String {
self.display_object.path() self.display_object.path()
} }