From b2dba535a62131f1261855609da4806e7e29f5aa Mon Sep 17 00:00:00 2001 From: tofpie <75836434+tofpie@users.noreply.github.com> Date: Mon, 21 Dec 2020 22:53:14 +0100 Subject: [PATCH] Implement Object.prototype.isPrototypeOf (#983) * Implement Object.prototype.isPrototypeOf * Fix formatting Co-authored-by: tofpie --- boa/src/builtins/object/mod.rs | 29 +++++++++++++++++++++++++++++ boa/src/builtins/object/tests.rs | 11 +++++++++++ boa/src/object/gcobject.rs | 16 +++++++++++++++- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 026973bfdf..f150257b21 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -51,6 +51,7 @@ impl BuiltIn for Object { .method(Self::has_own_property, "hasOwnProperty", 0) .method(Self::property_is_enumerable, "propertyIsEnumerable", 0) .method(Self::to_string, "toString", 0) + .method(Self::is_prototype_of, "isPrototypeOf", 0) .static_method(Self::create, "create", 2) .static_method(Self::set_prototype_of, "setPrototypeOf", 2) .static_method(Self::get_prototype_of, "getPrototypeOf", 1) @@ -258,6 +259,34 @@ impl Object { Ok(obj) } + /// `Object.prototype.isPrototypeOf( proto )` + /// + /// Check whether or not an object exists within another object's prototype chain. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.isprototypeof + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf + pub fn is_prototype_of(this: &Value, args: &[Value], context: &mut Context) -> Result { + let undefined = Value::undefined(); + let mut v = args.get(0).unwrap_or(&undefined).clone(); + if !v.is_object() { + return Ok(Value::Boolean(false)); + } + let o = Value::from(this.to_object(context)?); + loop { + v = Self::get_prototype_of(this, &[v], context)?; + if v.is_null() { + return Ok(Value::Boolean(false)); + } + if same_value(&o, &v) { + return Ok(Value::Boolean(true)); + } + } + } + /// Define a property in an object pub fn define_property(_: &Value, args: &[Value], context: &mut Context) -> Result { let obj = args.get(0).expect("Cannot get object"); diff --git a/boa/src/builtins/object/tests.rs b/boa/src/builtins/object/tests.rs index 5ab9b313a9..e59f739833 100644 --- a/boa/src/builtins/object/tests.rs +++ b/boa/src/builtins/object/tests.rs @@ -279,3 +279,14 @@ fn object_define_properties() { assert_eq!(forward(&mut context, "obj.p"), "42"); } + +#[test] +fn object_is_prototype_of() { + let mut context = Context::new(); + + let init = r#" + Object.prototype.isPrototypeOf(String.prototype) + "#; + + assert_eq!(context.eval(init).unwrap(), Value::boolean(true)); +} diff --git a/boa/src/object/gcobject.rs b/boa/src/object/gcobject.rs index 6405174fb3..a0d74cb6ab 100644 --- a/boa/src/object/gcobject.rs +++ b/boa/src/object/gcobject.rs @@ -198,7 +198,21 @@ impl GcObject { // #[track_caller] pub fn construct(&self, args: &[Value], context: &mut Context) -> Result { - let this: Value = Object::create(self.get(&PROTOTYPE.into())).into(); + // If the prototype of the constructor is not an object, then use the default object + // prototype as prototype for the new object + // see + // see + let proto = self.get(&PROTOTYPE.into()); + let proto = if proto.is_object() { + proto + } else { + context + .standard_objects() + .object_object() + .prototype() + .into() + }; + let this: Value = Object::create(proto).into(); let this_function_object = self.clone(); let body = if let Some(function) = self.borrow().as_function() {