Browse Source

Implement `Object.keys` and `Object.entries` (#1471)

Co-authored-by: jedel1043 <jedel0124@gmail.com>
pull/1498/head
Oleksii Halahan 3 years ago committed by GitHub
parent
commit
5ee865f745
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 66
      boa/src/builtins/object/mod.rs
  2. 62
      boa/src/object/internal_methods.rs
  3. 7
      boa/src/property/mod.rs

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

@ -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;
@ -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",
@ -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)
@ -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
boa/src/object/internal_methods.rs

@ -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,
};
@ -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"))).

7
boa/src/property/mod.rs

@ -662,3 +662,10 @@ impl PartialEq<&str> for PropertyKey {
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum PropertyNameKind {
Key,
Value,
KeyAndValue,
}

Loading…
Cancel
Save