From 2c0ccf7b0a196cee64b4b10308e54eaf1cec167c Mon Sep 17 00:00:00 2001 From: Kevin Putera Date: Wed, 6 Oct 2021 13:53:28 +0800 Subject: [PATCH 1/5] Implement Object.hasOwn and fix/add docs to Object.prototype.hasOwnProperty --- boa/src/builtins/object/mod.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index fd901975c4d..9709682fa8b 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -87,6 +87,7 @@ impl BuiltIn for Object { ) .static_method(Self::get_own_property_names, "getOwnPropertyNames", 1) .static_method(Self::get_own_property_symbols, "getOwnPropertySymbols", 1) + .static_method(Self::has_own, "hasOwn", 1) .build(); object.into() @@ -493,7 +494,7 @@ impl Object { Ok(format!("[object {}]", tag_str).into()) } - /// `Object.prototype.hasOwnPrototype( property )` + /// `Object.prototype.hasOwnProperty( property )` /// /// The method returns a boolean indicating whether the object has the specified property /// as its own property (as opposed to inheriting it). @@ -509,12 +510,16 @@ impl Object { args: &[JsValue], context: &mut Context, ) -> JsResult { + // 1. Let P be ? ToPropertyKey(V). let key = args .get(0) .unwrap_or(&JsValue::undefined()) .to_property_key(context)?; + + // 2. Let O be ? ToObject(this value). let object = this.to_object(context)?; + // 3. Return ? HasOwnProperty(O, P). Ok(object.has_own_property(key, context)?.into()) } @@ -856,6 +861,25 @@ impl Object { let o = args.get_or_undefined(0); get_own_property_keys(o, PropertyKeyType::Symbol, context) } + + /// `Object.hasOwn( object, property )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-object.hasown + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn + pub fn has_own(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // 1. Let obj be ? ToObject(O). + let obj = args.get_or_undefined(0).to_object(context)?; + + // 2. Let key be ? ToPropertyKey(P). + let key = args.get_or_undefined(1).to_property_key(context)?; + + // 3. Return ? HasOwnProperty(obj, key). + Ok(obj.has_own_property(key, context)?.into()) + } } /// The abstract operation ObjectDefineProperties From 051f9f5ee08648df626b75e9aa1678d2b8d67b11 Mon Sep 17 00:00:00 2001 From: Kevin Putera Date: Wed, 6 Oct 2021 14:17:59 +0800 Subject: [PATCH 2/5] Tests for Object.hasOwn --- boa/src/builtins/object/tests.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/boa/src/builtins/object/tests.rs b/boa/src/builtins/object/tests.rs index 01e68f0628e..95e9e56747d 100644 --- a/boa/src/builtins/object/tests.rs +++ b/boa/src/builtins/object/tests.rs @@ -93,6 +93,7 @@ fn object_is() { assert_eq!(forward(&mut context, "Object.is(undefined)"), "true"); assert!(context.global_object().is_global()); } + #[test] fn object_has_own_property() { let mut context = Context::new(); @@ -119,6 +120,33 @@ fn object_has_own_property() { ); } +#[test] +fn object_has_own() { + let scenario = r#" + let sym = Symbol('a'); + + let x = { + undefinedProp: undefined, + nullProp: null, + someProp: 1, + [sym]: 2, + }; + + let y = [1, 2, 3]; + "#; + + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("Object.hasOwn(x, 'hasOwnProperty')", "false"), + TestAction::TestEq("Object.hasOwn(x, 'undefinedProp')", "true"), + TestAction::TestEq("Object.hasOwn(x, 'nullProp')", "true"), + TestAction::TestEq("Object.hasOwn(x, 'someProp')", "true"), + TestAction::TestEq("Object.hasOwn(x, sym)", "true"), + TestAction::TestEq("Object.hasOwn(y, 3)", "false"), + TestAction::TestEq("Object.hasOwn(y, 1)", "true"), + ]); +} + #[test] fn object_property_is_enumerable() { let mut context = Context::new(); From 998018b6685ea8db029f976be828411a9cb33b9a Mon Sep 17 00:00:00 2001 From: Kevin Putera Date: Wed, 6 Oct 2021 14:22:14 +0800 Subject: [PATCH 3/5] Improve tests for Object.prototype.hasOwnProperty --- boa/src/builtins/object/tests.rs | 57 +++++++++++++++++--------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/boa/src/builtins/object/tests.rs b/boa/src/builtins/object/tests.rs index 95e9e56747d..27cd864f21f 100644 --- a/boa/src/builtins/object/tests.rs +++ b/boa/src/builtins/object/tests.rs @@ -96,43 +96,45 @@ fn object_is() { #[test] fn object_has_own_property() { - let mut context = Context::new(); - let init = r#" - let x = { someProp: 1, undefinedProp: undefined, nullProp: null }; + let scenario = r#" + let symA = Symbol('a'); + let symB = Symbol('b'); + + let x = { + undefinedProp: undefined, + nullProp: null, + someProp: 1, + [symA]: 2, + 100: 3, + }; "#; - eprintln!("{}", forward(&mut context, init)); - assert_eq!( - forward(&mut context, "x.hasOwnProperty('someProp')"), - "true" - ); - assert_eq!( - forward(&mut context, "x.hasOwnProperty('undefinedProp')"), - "true" - ); - assert_eq!( - forward(&mut context, "x.hasOwnProperty('nullProp')"), - "true" - ); - assert_eq!( - forward(&mut context, "x.hasOwnProperty('hasOwnProperty')"), - "false" - ); + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("x.hasOwnProperty('hasOwnProperty')", "false"), + TestAction::TestEq("x.hasOwnProperty('undefinedProp')", "true"), + TestAction::TestEq("x.hasOwnProperty('nullProp')", "true"), + TestAction::TestEq("x.hasOwnProperty('someProp')", "true"), + TestAction::TestEq("x.hasOwnProperty(symB)", "false"), + TestAction::TestEq("x.hasOwnProperty(symA)", "true"), + TestAction::TestEq("x.hasOwnProperty(1000)", "false"), + TestAction::TestEq("x.hasOwnProperty(100)", "true"), + ]); } #[test] fn object_has_own() { let scenario = r#" - let sym = Symbol('a'); + let symA = Symbol('a'); + let symB = Symbol('b'); let x = { undefinedProp: undefined, nullProp: null, someProp: 1, - [sym]: 2, + [symA]: 2, + 100: 3, }; - - let y = [1, 2, 3]; "#; check_output(&[ @@ -141,9 +143,10 @@ fn object_has_own() { TestAction::TestEq("Object.hasOwn(x, 'undefinedProp')", "true"), TestAction::TestEq("Object.hasOwn(x, 'nullProp')", "true"), TestAction::TestEq("Object.hasOwn(x, 'someProp')", "true"), - TestAction::TestEq("Object.hasOwn(x, sym)", "true"), - TestAction::TestEq("Object.hasOwn(y, 3)", "false"), - TestAction::TestEq("Object.hasOwn(y, 1)", "true"), + TestAction::TestEq("Object.hasOwn(x, symB)", "false"), + TestAction::TestEq("Object.hasOwn(x, symA)", "true"), + TestAction::TestEq("Object.hasOwn(x, 1000)", "false"), + TestAction::TestEq("Object.hasOwn(x, 100)", "true"), ]); } From c8fe3a6d493543e43a19e8ab9fc2f76b0643051c Mon Sep 17 00:00:00 2001 From: Kevin Putera Date: Wed, 6 Oct 2021 15:11:19 +0800 Subject: [PATCH 4/5] Fix 'length' property descriptor bug --- boa/src/builtins/object/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 9709682fa8b..4ad17491d54 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -54,7 +54,7 @@ impl BuiltIn for Object { .name(Self::NAME) .length(Self::LENGTH) .inherit(JsValue::null()) - .method(Self::has_own_property, "hasOwnProperty", 0) + .method(Self::has_own_property, "hasOwnProperty", 1) .method(Self::property_is_enumerable, "propertyIsEnumerable", 0) .method(Self::to_string, "toString", 0) .method(Self::value_of, "valueOf", 0) @@ -87,7 +87,7 @@ impl BuiltIn for Object { ) .static_method(Self::get_own_property_names, "getOwnPropertyNames", 1) .static_method(Self::get_own_property_symbols, "getOwnPropertySymbols", 1) - .static_method(Self::has_own, "hasOwn", 1) + .static_method(Self::has_own, "hasOwn", 2) .build(); object.into() From 397746f45d0fb681c3b7458868a3280479fab484 Mon Sep 17 00:00:00 2001 From: Kevin Putera Date: Thu, 7 Oct 2021 10:26:28 +0800 Subject: [PATCH 5/5] Simplify Object.prototype.hasOwnProperty --- boa/src/builtins/object/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 4ad17491d54..71cebbc0f13 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -511,10 +511,7 @@ impl Object { context: &mut Context, ) -> JsResult { // 1. Let P be ? ToPropertyKey(V). - let key = args - .get(0) - .unwrap_or(&JsValue::undefined()) - .to_property_key(context)?; + let key = args.get_or_undefined(0).to_property_key(context)?; // 2. Let O be ? ToObject(this value). let object = this.to_object(context)?;