Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Object.keys and Object.entries #1471

Merged
merged 11 commits into from
Aug 22, 2021
66 changes: 63 additions & 3 deletions boa/src/builtins/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ use crate::{
object::{
ConstructorBuilder, Object as BuiltinObject, ObjectData, ObjectInitializer, PROTOTYPE,
},
property::{Attribute, DescriptorKind, PropertyDescriptor},
property::{Attribute, DescriptorKind, PropertyDescriptor, PropertyNameKind},
symbol::WellKnownSymbols,
value::{JsValue, Type},
BoaProfiler, Context, Result,
};

use super::Array;

pub mod for_in_iterator;
#[cfg(test)]
mod tests;
Expand Down Expand Up @@ -61,6 +63,8 @@ impl BuiltIn for Object {
.static_method(Self::define_properties, "defineProperties", 2)
.static_method(Self::assign, "assign", 2)
.static_method(Self::is, "is", 2)
.static_method(Self::keys, "keys", 1)
.static_method(Self::entries, "entries", 1)
.static_method(
Self::get_own_property_descriptor,
"getOwnPropertyDescriptor",
Expand Down Expand Up @@ -541,8 +545,6 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-object.assign
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
pub fn assign(_: &JsValue, args: &[JsValue], context: &mut Context) -> Result<JsValue> {
//
//
// 1. Let to be ? ToObject(target).
let to = args
.get(0)
Expand Down Expand Up @@ -582,4 +584,62 @@ impl Object {
// 4. Return to.
Ok(to.into())
}

/// `Object.keys( target )`
///
/// This method returns an array of a given object's own enumerable
/// property names, iterated in the same order that a normal loop would.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-object.keys
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
pub fn keys(_: &JsValue, args: &[JsValue], context: &mut Context) -> Result<JsValue> {
// 1. Let obj be ? ToObject(target).
let obj = args
.get(0)
.cloned()
.unwrap_or_default()
.to_object(context)?;

// 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key).
let name_list = obj.enumerable_own_property_names(PropertyNameKind::Key, context)?;

// 3. Return CreateArrayFromList(nameList).
let result = Array::create_array_from_list(name_list, context);

Ok(result.into())
}

/// `Object.entries( target )`
///
/// This method returns an array of a given object's own enumerable string-keyed property [key, value] pairs.
/// This is the same as iterating with a for...in loop,
/// except that a for...in loop enumerates properties in the prototype chain as well).
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-object.entries
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
pub fn entries(_: &JsValue, args: &[JsValue], context: &mut Context) -> Result<JsValue> {
// 1. Let obj be ? ToObject(target).
let obj = args
.get(0)
.cloned()
.unwrap_or_default()
.to_object(context)?;

// 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key+value).
let name_list =
obj.enumerable_own_property_names(PropertyNameKind::KeyAndValue, context)?;

// 3. Return CreateArrayFromList(nameList).
let result = Array::create_array_from_list(name_list, context);

Ok(result.into())
}
}
62 changes: 61 additions & 1 deletion boa/src/object/internal_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
//! [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots

use crate::{
builtins::Array,
object::{GcObject, Object, ObjectData},
property::{DescriptorKind, PropertyDescriptor, PropertyKey},
property::{DescriptorKind, PropertyDescriptor, PropertyKey, PropertyNameKind},
value::{JsValue, Type},
BoaProfiler, Context, Result,
};
Expand Down Expand Up @@ -881,6 +882,65 @@ impl GcObject {
Ok(list)
}

/// It is used to iterate over names of object's keys.
///
/// More information:
/// - [EcmaScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-enumerableownpropertynames
pub(crate) fn enumerable_own_property_names(
&self,
kind: PropertyNameKind,
context: &mut Context,
) -> Result<Vec<JsValue>> {
// 1. Assert: Type(O) is Object.
// 2. Let ownKeys be ? O.[[OwnPropertyKeys]]().
let own_keys = self.own_property_keys();
// 3. Let properties be a new empty List.
let mut properties = vec![];

// 4. For each element key of ownKeys, do
for key in own_keys {
// a. If Type(key) is String, then
if let PropertyKey::String(key_str) = &key {
// i. Let desc be ? O.[[GetOwnProperty]](key).
let desc = self.__get_own_property__(&key);
// ii. If desc is not undefined and desc.[[Enumerable]] is true, then
if let Some(desc) = desc {
if desc.expect_enumerable() {
// 1. If kind is key, append key to properties.
if let PropertyNameKind::Key = kind {
properties.push(key_str.clone().into())
}
// 2. Else,
else {
// a. Let value be ? Get(O, key).
let value = self.get(key.clone(), context)?;
// b. If kind is value, append value to properties.
if let PropertyNameKind::Value = kind {
properties.push(value)
}
// c. Else,
else {
// i. Assert: kind is key+value.
// ii. Let entry be ! CreateArrayFromList(« key, value »).
let key_val = key_str.clone().into();
let entry =
Array::create_array_from_list([key_val, value], context);

// iii. Append entry to properties.
properties.push(entry.into());
}
}
}
}
}
}

// 5. Return properties.
Ok(properties)
}

pub(crate) fn length_of_array_like(&self, context: &mut Context) -> Result<usize> {
// 1. Assert: Type(obj) is Object.
// 2. Return ℝ(? ToLength(? Get(obj, "length"))).
Expand Down
7 changes: 7 additions & 0 deletions boa/src/property/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,3 +662,10 @@ impl PartialEq<&str> for PropertyKey {
}
}
}

#[derive(Debug, Clone, Copy)]
pub(crate) enum PropertyNameKind {
Key,
Value,
KeyAndValue,
}