Skip to content

Commit

Permalink
Implement Object.hasOwn and improve Object.prototype.hasOwnProperty (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
kevinputera committed Oct 7, 2021
1 parent 510623b commit aaac3e9
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 26 deletions.
33 changes: 27 additions & 6 deletions boa/src/builtins/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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).
Expand All @@ -511,12 +512,13 @@ impl Object {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
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())
}

Expand Down Expand Up @@ -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<JsValue> {
// 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
Expand Down
71 changes: 51 additions & 20 deletions boa/src/builtins/object/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down

0 comments on commit aaac3e9

Please sign in to comment.