From aaac3e9a62133e5022b2836d5b02fdb9f1890daf Mon Sep 17 00:00:00 2001 From: Kevin Putera Date: Thu, 7 Oct 2021 10:40:39 +0800 Subject: [PATCH] Implement Object.hasOwn and improve Object.prototype.hasOwnProperty (#1639) * Implement Object.hasOwn and fix/add docs to Object.prototype.hasOwnProperty * Tests for Object.hasOwn * Improve tests for Object.prototype.hasOwnProperty * Fix 'length' property descriptor bug * Simplify Object.prototype.hasOwnProperty --- boa/src/builtins/object/mod.rs | 33 ++++++++++++--- boa/src/builtins/object/tests.rs | 71 +++++++++++++++++++++++--------- 2 files changed, 78 insertions(+), 26 deletions(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 255a260e11..773ef74b59 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(None) - .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,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", 2) .build(); object.into() @@ -495,7 +496,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). @@ -511,12 +512,13 @@ impl Object { args: &[JsValue], context: &mut Context, ) -> JsResult { - let key = args - .get(0) - .unwrap_or(&JsValue::undefined()) - .to_property_key(context)?; + // 1. Let P be ? ToPropertyKey(V). + let key = args.get_or_undefined(0).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()) } @@ -858,6 +860,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 diff --git a/boa/src/builtins/object/tests.rs b/boa/src/builtins/object/tests.rs index 01e68f0628..27cd864f21 100644 --- a/boa/src/builtins/object/tests.rs +++ b/boa/src/builtins/object/tests.rs @@ -93,30 +93,61 @@ 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(); - 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 symA = Symbol('a'); + let symB = Symbol('b'); + + let x = { + undefinedProp: undefined, + nullProp: null, + someProp: 1, + [symA]: 2, + 100: 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, symB)", "false"), + TestAction::TestEq("Object.hasOwn(x, symA)", "true"), + TestAction::TestEq("Object.hasOwn(x, 1000)", "false"), + TestAction::TestEq("Object.hasOwn(x, 100)", "true"), + ]); } #[test]