diff --git a/core/src/avm2/globals/object.rs b/core/src/avm2/globals/object.rs index cdf1e04da..16597ecf5 100644 --- a/core/src/avm2/globals/object.rs +++ b/core/src/avm2/globals/object.rs @@ -12,7 +12,7 @@ use gc_arena::MutationContext; /// Implements `Object` pub fn constructor<'gc>( _avm: &mut Avm2<'gc>, - _action_context: &mut UpdateContext<'_, 'gc, '_>, + _context: &mut UpdateContext<'_, 'gc, '_>, _this: Option>, _args: &[Value<'gc>], ) -> Result, Error> { @@ -22,7 +22,7 @@ pub fn constructor<'gc>( /// `Object.prototype.hasOwnProperty` pub fn has_own_property<'gc>( _avm: &mut Avm2<'gc>, - _action_context: &mut UpdateContext<'_, 'gc, '_>, + _context: &mut UpdateContext<'_, 'gc, '_>, this: Option>, args: &[Value<'gc>], ) -> Result, Error> { @@ -39,6 +39,29 @@ pub fn has_own_property<'gc>( Ok(false.into()) } +/// `Object.prototype.isPrototypeOf` +pub fn is_prototype_of<'gc>( + _avm: &mut Avm2<'gc>, + _context: &mut UpdateContext<'_, 'gc, '_>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + let search_proto: Result, Error> = + this.ok_or_else(|| "No valid this parameter".into()); + let search_proto = search_proto?; + let mut target_proto = args.get(0).cloned().unwrap_or(Value::Undefined); + + while let Value::Object(proto) = target_proto { + if Object::ptr_eq(search_proto, proto) { + return Ok(true.into()); + } + + target_proto = proto.proto().map(|o| o.into()).unwrap_or(Value::Undefined); + } + + Ok(false.into()) +} + /// Partially construct `Object.prototype`. /// /// `__proto__` and other cross-linked properties of this object will *not* @@ -59,4 +82,10 @@ pub fn fill_proto<'gc>( 0, FunctionObject::from_builtin(gc_context, has_own_property, fn_proto), ); + object_proto.install_method( + gc_context, + QName::new(Namespace::public_namespace(), "isPrototypeOf"), + 0, + FunctionObject::from_builtin(gc_context, is_prototype_of, fn_proto), + ); } diff --git a/core/tests/swfs/avm2/is_prototype_of/Test.as b/core/tests/swfs/avm2/is_prototype_of/Test.as new file mode 100644 index 000000000..c23680bb7 --- /dev/null +++ b/core/tests/swfs/avm2/is_prototype_of/Test.as @@ -0,0 +1,28 @@ +package { + public class Test {} +} + +class ES4Class extends Object { + +} + +function ES3Class() { + +} + +var es4inst = new ES4Class(); +var es3inst = new ES3Class(); + +trace("//ES4Class.prototype.isPrototypeOf(es4inst);"); +trace(ES4Class.prototype.isPrototypeOf(es4inst)); +trace("//Object.prototype.isPrototypeOf(es4inst);"); +trace(Object.prototype.isPrototypeOf(es4inst)); +trace("//ES3Class.prototype.isPrototypeOf(es4inst);"); +trace(ES3Class.prototype.isPrototypeOf(es4inst)); + +trace("//ES4Class.prototype.isPrototypeOf(es3inst);"); +trace(ES4Class.prototype.isPrototypeOf(es3inst)); +trace("//Object.prototype.isPrototypeOf(es3inst);"); +trace(Object.prototype.isPrototypeOf(es3inst)); +trace("//ES3Class.prototype.isPrototypeOf(es3inst);"); +trace(ES3Class.prototype.isPrototypeOf(es3inst)); \ No newline at end of file diff --git a/core/tests/swfs/avm2/is_prototype_of/output.txt b/core/tests/swfs/avm2/is_prototype_of/output.txt new file mode 100644 index 000000000..3d3f2dc80 --- /dev/null +++ b/core/tests/swfs/avm2/is_prototype_of/output.txt @@ -0,0 +1,13 @@ + +//ES4Class.prototype.isPrototypeOf(es4inst); +true +//Object.prototype.isPrototypeOf(es4inst); +true +//ES3Class.prototype.isPrototypeOf(es4inst); +false +//ES4Class.prototype.isPrototypeOf(es3inst); +false +//Object.prototype.isPrototypeOf(es3inst); +true +//ES3Class.prototype.isPrototypeOf(es3inst); +true diff --git a/core/tests/swfs/avm2/is_prototype_of/test.fla b/core/tests/swfs/avm2/is_prototype_of/test.fla new file mode 100644 index 000000000..e84226824 Binary files /dev/null and b/core/tests/swfs/avm2/is_prototype_of/test.fla differ diff --git a/core/tests/swfs/avm2/is_prototype_of/test.swf b/core/tests/swfs/avm2/is_prototype_of/test.swf new file mode 100644 index 000000000..ddbacb6f4 Binary files /dev/null and b/core/tests/swfs/avm2/is_prototype_of/test.swf differ