Browse Source

Implement Object.getOwnPropertyNames and Object.getOwnPropertySymbols (#1606)

* Implement Object.getOwnPropertyNames and Object.getOwnPropertySymbols

* Add documentation

* Simplify implementation + add more docs following spec

* Tests for Object.getOwnPropertyNames

* Reduce duplication in test

* Use new TestAction API for tests

* Tests for Object.getOwnPropertySymbols

* Simplify tests

* Prevent unnecessary copies in get_own_property_keys
pull/1618/head
Kevin Putera 3 years ago committed by GitHub
parent
commit
bda00b0640
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 79
      boa/src/builtins/object/mod.rs
  2. 70
      boa/src/builtins/object/tests.rs

79
boa/src/builtins/object/mod.rs

@ -85,6 +85,8 @@ impl BuiltIn for Object {
"getOwnPropertyDescriptors",
1,
)
.static_method(Self::get_own_property_names, "getOwnPropertyNames", 1)
.static_method(Self::get_own_property_symbols, "getOwnPropertySymbols", 1)
.build();
(Self::NAME, object.into(), Self::attribute())
@ -824,6 +826,42 @@ impl Object {
Ok(JsValue::new(false))
}
}
/// `Object.getOwnPropertyNames( object )`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-object.getownpropertynames
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
pub fn get_own_property_names(
_: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Return ? GetOwnPropertyKeys(O, string).
let o = args.get_or_undefined(0);
get_own_property_keys(o, PropertyKeyType::String, context)
}
/// `Object.getOwnPropertySymbols( object )`
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-object.getownpropertysymbols
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols
pub fn get_own_property_symbols(
_: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Return ? GetOwnPropertyKeys(O, symbol).
let o = args.get_or_undefined(0);
get_own_property_keys(o, PropertyKeyType::Symbol, context)
}
}
/// The abstract operation ObjectDefineProperties
@ -877,3 +915,44 @@ fn object_define_properties(
// 7. Return O.
Ok(())
}
/// Type enum used in the abstract operation GetOwnPropertyKeys
#[derive(Debug, Copy, Clone)]
enum PropertyKeyType {
String,
Symbol,
}
/// The abstract operation GetOwnPropertyKeys
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-getownpropertykeys
fn get_own_property_keys(
o: &JsValue,
r#type: PropertyKeyType,
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Let obj be ? ToObject(o).
let obj = o.to_object(context)?;
// 2. Let keys be ? obj.[[OwnPropertyKeys]]().
let keys = obj.__own_property_keys__(context)?;
// 3. Let nameList be a new empty List.
// 4. For each element nextKey of keys, do
let name_list = keys.iter().filter_map(|next_key| {
// a. If Type(nextKey) is Symbol and type is symbol or Type(nextKey) is String and type is string, then
// i. Append nextKey as the last element of nameList.
match (r#type, &next_key) {
(PropertyKeyType::String, PropertyKey::String(_)) => Some(next_key.into()),
(PropertyKeyType::String, PropertyKey::Index(index)) => Some(index.to_string().into()),
(PropertyKeyType::Symbol, PropertyKey::Symbol(_)) => Some(next_key.into()),
_ => None,
}
});
// 5. Return CreateArrayFromList(nameList).
Ok(Array::create_array_from_list(name_list, context).into())
}

70
boa/src/builtins/object/tests.rs

@ -1,4 +1,4 @@
use crate::{forward, Context, JsValue};
use crate::{check_output, forward, Context, JsValue, TestAction};
#[test]
fn object_create_with_regular_object() {
@ -289,3 +289,71 @@ fn object_is_prototype_of() {
assert_eq!(context.eval(init).unwrap(), JsValue::new(true));
}
#[test]
fn object_get_own_property_names_invalid_args() {
let error_message = r#"Uncaught "TypeError": "cannot convert 'null' or 'undefined' to object""#;
check_output(&[
TestAction::TestEq("Object.getOwnPropertyNames()", error_message),
TestAction::TestEq("Object.getOwnPropertyNames(null)", error_message),
TestAction::TestEq("Object.getOwnPropertyNames(undefined)", error_message),
]);
}
#[test]
fn object_get_own_property_names() {
check_output(&[
TestAction::TestEq("Object.getOwnPropertyNames(0)", "[]"),
TestAction::TestEq("Object.getOwnPropertyNames(false)", "[]"),
TestAction::TestEq(r#"Object.getOwnPropertyNames(Symbol("a"))"#, "[]"),
TestAction::TestEq("Object.getOwnPropertyNames({})", "[]"),
TestAction::TestEq("Object.getOwnPropertyNames(NaN)", "[]"),
TestAction::TestEq(
"Object.getOwnPropertyNames([1, 2, 3])",
r#"[ "0", "1", "2", "length" ]"#,
),
TestAction::TestEq(
r#"Object.getOwnPropertyNames({
"a": 1,
"b": 2,
[ Symbol("c") ]: 3,
[ Symbol("d") ]: 4,
})"#,
r#"[ "a", "b" ]"#,
),
]);
}
#[test]
fn object_get_own_property_symbols_invalid_args() {
let error_message = r#"Uncaught "TypeError": "cannot convert 'null' or 'undefined' to object""#;
check_output(&[
TestAction::TestEq("Object.getOwnPropertySymbols()", error_message),
TestAction::TestEq("Object.getOwnPropertySymbols(null)", error_message),
TestAction::TestEq("Object.getOwnPropertySymbols(undefined)", error_message),
]);
}
#[test]
fn object_get_own_property_symbols() {
check_output(&[
TestAction::TestEq("Object.getOwnPropertySymbols(0)", "[]"),
TestAction::TestEq("Object.getOwnPropertySymbols(false)", "[]"),
TestAction::TestEq(r#"Object.getOwnPropertySymbols(Symbol("a"))"#, "[]"),
TestAction::TestEq("Object.getOwnPropertySymbols({})", "[]"),
TestAction::TestEq("Object.getOwnPropertySymbols(NaN)", "[]"),
TestAction::TestEq("Object.getOwnPropertySymbols([1, 2, 3])", "[]"),
TestAction::TestEq(
r#"
Object.getOwnPropertySymbols({
"a": 1,
"b": 2,
[ Symbol("c") ]: 3,
[ Symbol("d") ]: 4,
})"#,
"[ Symbol(c), Symbol(d) ]",
),
]);
}

Loading…
Cancel
Save