From ed3551d52f4819b6bd1d455ff74add26cf82974b Mon Sep 17 00:00:00 2001 From: skyne98 Date: Sun, 15 Aug 2021 21:22:21 +0200 Subject: [PATCH 1/9] Implement Object.keys --- boa/src/builtins/object/mod.rs | 37 ++++++++++++++++++ boa/src/object/internal_methods.rs | 62 ++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 61165faa412..ad6013e4057 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -24,6 +24,8 @@ use crate::{ BoaProfiler, Context, Result, }; +use super::Array; + pub mod for_in_iterator; #[cfg(test)] mod tests; @@ -61,6 +63,7 @@ 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::get_own_property_descriptor, "getOwnPropertyDescriptor", @@ -582,4 +585,38 @@ 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 { + // + // + // 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(true, false, context)?; + + // 3. Return CreateArrayFromList(nameList). + // TODO: Implement https://tc39.es/ecma262/#sec-createarrayfromlist + let result = Array::array_create(name_list.len(), None, context)?; + for (index, name) in name_list.iter().enumerate() { + result.set(index, name, false, context)?; + } + + Ok(JsValue::Object(result)) + } } diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index 4d8a66072d1..3cc9986cd97 100644 --- a/boa/src/object/internal_methods.rs +++ b/boa/src/object/internal_methods.rs @@ -881,6 +881,68 @@ 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_key: bool, + kind_value: bool, + context: &mut Context, + ) -> Result> { + // 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 kind_key && kind_value == false { + properties.push(JsValue::String(key_str.clone())) + } + // 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 kind_value && kind_key == false { + properties.push(value) + } + // c. Else, + else { + // i. Assert: kind is key+value. + // ii. Let entry be ! CreateArrayFromList(« key, value »). + // TODO: Implement https://tc39.es/ecma262/#sec-createarrayfromlist + let entry = JsValue::new_object(context); + let key_val = JsValue::String(key_str.clone()); + entry.add(&key_val, context)?; + entry.add(&value, context)?; + + // iii. Append entry to properties. + properties.push(entry); + } + } + } + } + } + } + + // 5. Return properties. + Ok(properties) + } + pub(crate) fn length_of_array_like(&self, context: &mut Context) -> Result { // 1. Assert: Type(obj) is Object. // 2. Return ℝ(? ToLength(? Get(obj, "length"))). From 0d2882af7fe8a69e696153834f599e61c1a54822 Mon Sep 17 00:00:00 2001 From: skyne98 Date: Thu, 19 Aug 2021 20:18:31 +0200 Subject: [PATCH 2/9] Add PropertyNameKind enum, use it in the internal function --- boa/src/builtins/object/mod.rs | 4 ++-- boa/src/object/internal_methods.rs | 9 ++++----- boa/src/property/mod.rs | 7 +++++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index ad6013e4057..919878eaf41 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -18,7 +18,7 @@ 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, @@ -608,7 +608,7 @@ impl Object { .to_object(context)?; // 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key). - let name_list = obj.enumerable_own_property_names(true, false, context)?; + let name_list = obj.enumerable_own_property_names(PropertyNameKind::Key, context)?; // 3. Return CreateArrayFromList(nameList). // TODO: Implement https://tc39.es/ecma262/#sec-createarrayfromlist diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index 3cc9986cd97..baf32f7d730 100644 --- a/boa/src/object/internal_methods.rs +++ b/boa/src/object/internal_methods.rs @@ -7,7 +7,7 @@ use crate::{ object::{GcObject, Object, ObjectData}, - property::{DescriptorKind, PropertyDescriptor, PropertyKey}, + property::{DescriptorKind, PropertyDescriptor, PropertyKey, PropertyNameKind}, value::{JsValue, Type}, BoaProfiler, Context, Result, }; @@ -889,8 +889,7 @@ impl GcObject { /// [spec]: https://tc39.es/ecma262/#sec-enumerableownpropertynames pub(crate) fn enumerable_own_property_names( &self, - kind_key: bool, - kind_value: bool, + kind: PropertyNameKind, context: &mut Context, ) -> Result> { // 1. Assert: Type(O) is Object. @@ -909,7 +908,7 @@ impl GcObject { if let Some(desc) = desc { if desc.expect_enumerable() { // 1. If kind is key, append key to properties. - if kind_key && kind_value == false { + if let PropertyNameKind::Key = kind { properties.push(JsValue::String(key_str.clone())) } // 2. Else, @@ -917,7 +916,7 @@ impl GcObject { // a. Let value be ? Get(O, key). let value = self.get(key.clone(), context)?; // b. If kind is value, append value to properties. - if kind_value && kind_key == false { + if let PropertyNameKind::Value = kind { properties.push(value) } // c. Else, diff --git a/boa/src/property/mod.rs b/boa/src/property/mod.rs index 74fc5990841..3ca52247d62 100644 --- a/boa/src/property/mod.rs +++ b/boa/src/property/mod.rs @@ -662,3 +662,10 @@ impl PartialEq<&str> for PropertyKey { } } } + +#[derive(Debug, Clone, Copy)] +pub enum PropertyNameKind { + Key, + Value, + KeyValue, +} From ae216e5550b083ab415533f98b028aec818fa447 Mon Sep 17 00:00:00 2001 From: skyne98 Date: Sat, 21 Aug 2021 23:58:42 +0200 Subject: [PATCH 3/9] Some minor clean-ups --- boa/src/object/internal_methods.rs | 3 ++- boa/src/property/mod.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index baf32f7d730..c9acb493c28 100644 --- a/boa/src/object/internal_methods.rs +++ b/boa/src/object/internal_methods.rs @@ -6,6 +6,7 @@ //! [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, PropertyNameKind}, value::{JsValue, Type}, @@ -909,7 +910,7 @@ impl GcObject { if desc.expect_enumerable() { // 1. If kind is key, append key to properties. if let PropertyNameKind::Key = kind { - properties.push(JsValue::String(key_str.clone())) + properties.push(key_str.clone().into()) } // 2. Else, else { diff --git a/boa/src/property/mod.rs b/boa/src/property/mod.rs index 3ca52247d62..c72171b393d 100644 --- a/boa/src/property/mod.rs +++ b/boa/src/property/mod.rs @@ -664,7 +664,7 @@ impl PartialEq<&str> for PropertyKey { } #[derive(Debug, Clone, Copy)] -pub enum PropertyNameKind { +pub(crate) enum PropertyNameKind { Key, Value, KeyValue, From 1109d62d892438dc474fbba3a12c6b48d02e1b78 Mon Sep 17 00:00:00 2001 From: skyne98 Date: Sun, 22 Aug 2021 00:06:15 +0200 Subject: [PATCH 4/9] More clean-ups --- boa/src/builtins/object/mod.rs | 12 ++---------- boa/src/object/internal_methods.rs | 8 +++----- boa/src/property/mod.rs | 2 +- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 919878eaf41..bca79e892ea 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -544,8 +544,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 { - // - // // 1. Let to be ? ToObject(target). let to = args .get(0) @@ -598,8 +596,6 @@ impl Object { /// [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 { - // - // // 1. Let obj be ? ToObject(target). let obj = args .get(0) @@ -611,12 +607,8 @@ impl Object { let name_list = obj.enumerable_own_property_names(PropertyNameKind::Key, context)?; // 3. Return CreateArrayFromList(nameList). - // TODO: Implement https://tc39.es/ecma262/#sec-createarrayfromlist - let result = Array::array_create(name_list.len(), None, context)?; - for (index, name) in name_list.iter().enumerate() { - result.set(index, name, false, context)?; - } + let result = Array::create_array_from_list(name_list.into_iter(), context); - Ok(JsValue::Object(result)) + Ok(result.into()) } } diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index c9acb493c28..f78d61f461e 100644 --- a/boa/src/object/internal_methods.rs +++ b/boa/src/object/internal_methods.rs @@ -924,14 +924,12 @@ impl GcObject { else { // i. Assert: kind is key+value. // ii. Let entry be ! CreateArrayFromList(« key, value »). - // TODO: Implement https://tc39.es/ecma262/#sec-createarrayfromlist - let entry = JsValue::new_object(context); let key_val = JsValue::String(key_str.clone()); - entry.add(&key_val, context)?; - entry.add(&value, context)?; + let entry = + Array::create_array_from_list([key_val, value], context); // iii. Append entry to properties. - properties.push(entry); + properties.push(entry.into()); } } } diff --git a/boa/src/property/mod.rs b/boa/src/property/mod.rs index 2fd58fa4781..48a92763ee9 100644 --- a/boa/src/property/mod.rs +++ b/boa/src/property/mod.rs @@ -667,5 +667,5 @@ impl PartialEq<&str> for PropertyKey { pub(crate) enum PropertyNameKind { Key, Value, - KeyValue, + KeyAndValue, } From 235078ab514cf865c668e442ccf46b284fa6b03e Mon Sep 17 00:00:00 2001 From: skyne98 Date: Sun, 22 Aug 2021 00:08:21 +0200 Subject: [PATCH 5/9] Add `Object.entries(...)` --- boa/src/builtins/object/mod.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index bca79e892ea..14297362551 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -611,4 +611,34 @@ impl Object { 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 { + // 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.into_iter(), context); + + Ok(result.into()) + } } From f74e25f3595c4cbc8defffdf44c472257b41cd9e Mon Sep 17 00:00:00 2001 From: skyne98 Date: Sun, 22 Aug 2021 00:12:16 +0200 Subject: [PATCH 6/9] Fix: Register method --- boa/src/builtins/object/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 14297362551..539159087b5 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -64,6 +64,7 @@ impl BuiltIn for Object { .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", From f1cfe22c6757ec57cd8e9d1f9ede67a3bd504baa Mon Sep 17 00:00:00 2001 From: Oleksii Halahan Date: Sun, 22 Aug 2021 01:25:45 +0200 Subject: [PATCH 7/9] Update boa/src/object/internal_methods.rs Co-authored-by: jedel1043 --- boa/src/object/internal_methods.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index f78d61f461e..f5474ae3598 100644 --- a/boa/src/object/internal_methods.rs +++ b/boa/src/object/internal_methods.rs @@ -924,7 +924,7 @@ impl GcObject { else { // i. Assert: kind is key+value. // ii. Let entry be ! CreateArrayFromList(« key, value »). - let key_val = JsValue::String(key_str.clone()); + let key_val = key_str.clone().into(); let entry = Array::create_array_from_list([key_val, value], context); From 50f9ee6d3fa17e505514245fd1af361e5af3ca4b Mon Sep 17 00:00:00 2001 From: Oleksii Halahan Date: Sun, 22 Aug 2021 01:26:22 +0200 Subject: [PATCH 8/9] Update boa/src/builtins/object/mod.rs Co-authored-by: jedel1043 --- boa/src/builtins/object/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 539159087b5..02099acc27b 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -608,7 +608,7 @@ impl Object { let name_list = obj.enumerable_own_property_names(PropertyNameKind::Key, context)?; // 3. Return CreateArrayFromList(nameList). - let result = Array::create_array_from_list(name_list.into_iter(), context); + let result = Array::create_array_from_list(name_list, context); Ok(result.into()) } From 34f44a82924bd858ba2a4d0ec3368521f990795b Mon Sep 17 00:00:00 2001 From: Oleksii Halahan Date: Sun, 22 Aug 2021 01:26:29 +0200 Subject: [PATCH 9/9] Update boa/src/builtins/object/mod.rs Co-authored-by: jedel1043 --- boa/src/builtins/object/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 02099acc27b..3986c85fa40 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -638,7 +638,7 @@ impl Object { obj.enumerable_own_property_names(PropertyNameKind::KeyAndValue, context)?; // 3. Return CreateArrayFromList(nameList). - let result = Array::create_array_from_list(name_list.into_iter(), context); + let result = Array::create_array_from_list(name_list, context); Ok(result.into()) }