diff --git a/core/src/avm1.rs b/core/src/avm1.rs index 2b24fce73..cc03e1e66 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -837,7 +837,7 @@ impl<'gc> Avm1<'gc> { .resolve(self, context)? .as_object()?; - if obj.is_instance_of(self, constr, prototype) { + if obj.is_instance_of(self, context, constr, prototype)? { self.push(obj); } else { self.push(Value::Null); @@ -1430,8 +1430,9 @@ impl<'gc> Avm1<'gc> { .get("prototype", self, context)? .resolve(self, context)? .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(()) } diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index 3b95bb61a..e2a91e9e2 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -197,31 +197,50 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// The class is provided in the form of it's constructor function and the /// explicit prototype of that constructor function. It is assumed that /// 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( &self, - avm: &Avm1<'gc>, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, constructor: Object<'gc>, prototype: Object<'gc>, - ) -> bool { - let mut proto = self.proto(); + ) -> Result { + 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) { - return true; + return Ok(true); + } + + if let Some(p) = this_proto.proto() { + proto_stack.push(p); } if avm.current_swf_version() >= 7 { for interface in this_proto.interfaces() { 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(); } - false + Ok(false) } /// Get the underlying script object, if it exists.