Allow `is_instance_of` to inspect the prototype chains of implemented interfaces.

This makes the `extends_chain` test pass.
This commit is contained in:
David Wendt 2019-11-30 00:30:10 -05:00 committed by Mike Welsh
parent 854526923e
commit 33c66571f5
2 changed files with 31 additions and 11 deletions

View File

@ -837,7 +837,7 @@ impl<'gc> Avm1<'gc> {
.resolve(self, context)? .resolve(self, context)?
.as_object()?; .as_object()?;
if obj.is_instance_of(self, constr, prototype) { if obj.is_instance_of(self, context, constr, prototype)? {
self.push(obj); self.push(obj);
} else { } else {
self.push(Value::Null); self.push(Value::Null);
@ -1430,8 +1430,9 @@ impl<'gc> Avm1<'gc> {
.get("prototype", self, context)? .get("prototype", self, context)?
.resolve(self, context)? .resolve(self, context)?
.as_object()?; .as_object()?;
let is_instance_of = obj.is_instance_of(self, context, constr, prototype)?;
self.push(obj.is_instance_of(self, constr, prototype)); self.push(is_instance_of);
Ok(()) Ok(())
} }

View File

@ -197,31 +197,50 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
/// The class is provided in the form of it's constructor function and the /// The class is provided in the form of it's constructor function and the
/// explicit prototype of that constructor function. It is assumed that /// explicit prototype of that constructor function. It is assumed that
/// they are already linked. /// they are already linked.
///
/// Because ActionScript 2.0 added interfaces, this function cannot simply
/// check the prototype chain and call it a day. Each interface represents
/// a new, parallel prototype chain which also needs to be checked. You
/// can't implement interfaces within interfaces (fortunately), but if you
/// somehow could this would support that, too.
fn is_instance_of( fn is_instance_of(
&self, &self,
avm: &Avm1<'gc>, avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
constructor: Object<'gc>, constructor: Object<'gc>,
prototype: Object<'gc>, prototype: Object<'gc>,
) -> bool { ) -> Result<bool, Error> {
let mut proto = self.proto(); let mut proto_stack = vec![];
if let Some(p) = self.proto() {
proto_stack.push(p);
}
while let Some(this_proto) = proto { while let Some(this_proto) = proto_stack.pop() {
if Object::ptr_eq(this_proto, prototype) { if Object::ptr_eq(this_proto, prototype) {
return true; return Ok(true);
}
if let Some(p) = this_proto.proto() {
proto_stack.push(p);
} }
if avm.current_swf_version() >= 7 { if avm.current_swf_version() >= 7 {
for interface in this_proto.interfaces() { for interface in this_proto.interfaces() {
if Object::ptr_eq(interface, constructor) { if Object::ptr_eq(interface, constructor) {
return true; return Ok(true);
}
if let Value::Object(o) = interface
.get("prototype", avm, context)?
.resolve(avm, context)?
{
proto_stack.push(o);
}
} }
} }
} }
proto = this_proto.proto(); Ok(false)
}
false
} }
/// Get the underlying script object, if it exists. /// Get the underlying script object, if it exists.