From ae17c1184c0efaf966e6dc1b0babf76d3cfe58d6 Mon Sep 17 00:00:00 2001 From: Roland Takacs <1487864+rtakacs@users.noreply.github.com> Date: Thu, 25 Jun 2020 12:08:46 +0200 Subject: [PATCH] Implement Object.getOwnPropertyDescriptors built-in method (#3921) JerryScript-DCO-1.0-Signed-off-by: Roland Takacs rtakacs@inf.u-szeged.hu --- .../builtin-objects/ecma-builtin-object.c | 78 ++++++++++++ .../builtin-objects/ecma-builtin-object.inc.h | 3 + jerry-core/ecma/operations/ecma-objects.c | 4 +- jerry-core/lit/lit-magic-strings.inc.h | 7 ++ jerry-core/lit/lit-magic-strings.ini | 1 + .../object-get-own-property-descriptors.js | 119 ++++++++++++++++++ 6 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 tests/jerry/es.next/object-get-own-property-descriptors.js diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-object.c b/jerry-core/ecma/builtin-objects/ecma-builtin-object.c index 8fdf8dd78..23f6588a7 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-object.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-object.c @@ -53,6 +53,7 @@ enum /* These should be in this order. */ ECMA_OBJECT_ROUTINE_ASSIGN, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTOR, + ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTORS, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_NAMES, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_SYMBOLS, ECMA_OBJECT_ROUTINE_GET_PROTOTYPE_OF, @@ -746,6 +747,76 @@ ecma_builtin_object_object_get_own_property_descriptor (ecma_object_t *obj_p, /* return ECMA_VALUE_UNDEFINED; } /* ecma_builtin_object_object_get_own_property_descriptor */ +#if ENABLED (JERRY_ESNEXT) +/** + * The Object object's 'getOwnPropertyDescriptors' routine + * + * See also: + * ECMA-262 v11, 19.1.2.9 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_object_object_get_own_property_descriptors (ecma_object_t *obj_p) /**< routine's first argument */ +{ + /* 2 */ + ecma_collection_t *prop_names_p = ecma_op_object_get_property_names (obj_p, ECMA_LIST_SYMBOLS); + +#if ENABLED (JERRY_BUILTIN_PROXY) + if (prop_names_p == NULL) + { + return ECMA_VALUE_ERROR; + } +#endif /* ENABLED (JERRY_BUILTIN_PROXY) */ + + ecma_value_t *names_buffer_p = prop_names_p->buffer_p; + + /* 3 */ + ecma_object_t *object_prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); + ecma_object_t *descriptors_p = ecma_create_object (object_prototype_p, 0, ECMA_OBJECT_TYPE_GENERAL); + + /* 4 */ + for (uint32_t i = 0; i < prop_names_p->item_count; i++) + { + ecma_string_t *property_name_p = ecma_get_prop_name_from_value (names_buffer_p[i]); + + /* 4.a */ + ecma_property_descriptor_t prop_desc; + ecma_value_t status = ecma_op_object_get_own_property_descriptor (obj_p, property_name_p, &prop_desc); + +#if ENABLED (JERRY_BUILTIN_PROXY) + if (ECMA_IS_VALUE_ERROR (status)) + { + ecma_deref_object (descriptors_p); + ecma_collection_free (prop_names_p); + + return status; + } +#endif /* ENABLED (JERRY_BUILTIN_PROXY) */ + + if (ecma_is_value_true (status)) + { + /* 4.b */ + ecma_object_t *desc_obj_p = ecma_op_from_property_descriptor (&prop_desc); + /* 4.c */ + ecma_property_value_t *value_p = ecma_create_named_data_property (descriptors_p, + property_name_p, + ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, + NULL); + value_p->value = ecma_make_object_value (desc_obj_p); + + ecma_deref_object (desc_obj_p); + ecma_free_property_descriptor (&prop_desc); + } + } + + ecma_collection_free (prop_names_p); + + return ecma_make_object_value (descriptors_p); +} /* ecma_builtin_object_object_get_own_property_descriptors */ +#endif /* ENABLED (JERRY_ESNEXT) */ + /** * The Object object's 'defineProperties' routine * @@ -1213,6 +1284,13 @@ ecma_builtin_object_dispatch_routine (uint16_t builtin_routine_id, /**< built-in ecma_deref_ecma_string (prop_name_p); break; } +#if ENABLED (JERRY_ESNEXT) + case ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTORS: + { + result = ecma_builtin_object_object_get_own_property_descriptors (obj_p); + break; + } +#endif /* ENABLED (JERRY_ESNEXT) */ default: { JERRY_UNREACHABLE (); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-object.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-object.inc.h index e5aeb0e54..17380c098 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-object.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-object.inc.h @@ -55,6 +55,9 @@ ROUTINE (LIT_MAGIC_STRING_IS_FROZEN_UL, ECMA_OBJECT_ROUTINE_IS_FROZEN, 1, 1) ROUTINE (LIT_MAGIC_STRING_IS_EXTENSIBLE, ECMA_OBJECT_ROUTINE_IS_EXTENSIBLE, 1, 1) ROUTINE (LIT_MAGIC_STRING_KEYS, ECMA_OBJECT_ROUTINE_KEYS, 1, 1) ROUTINE (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTOR, 2, 2) +#if ENABLED (JERRY_ESNEXT) +ROUTINE (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTORS_UL, ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTORS, 1, 1) +#endif /* ENABLED (JERRY_ESNEXT) */ ROUTINE (LIT_MAGIC_STRING_CREATE, ECMA_OBJECT_ROUTINE_CREATE, 2, 2) ROUTINE (LIT_MAGIC_STRING_DEFINE_PROPERTIES_UL, ECMA_OBJECT_ROUTINE_DEFINE_PROPERTIES, 2, 2) ROUTINE (LIT_MAGIC_STRING_DEFINE_PROPERTY_UL, ECMA_OBJECT_ROUTINE_DEFINE_PROPERTY, 3, 3) diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index 394637ee4..4e8331788 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -1860,8 +1860,8 @@ ecma_op_object_define_own_property (ecma_object_t *obj_p, /**< the object */ * * The output property descriptor will always be initialized to an empty descriptor. * - * @return true - if property found - * false - otherwise + * @return ECMA_VALUE_ERROR - if the Proxy.[[GetOwnProperty]] operation raises error + * ECMA_VALUE_{TRUE, FALSE} - if property found or not */ ecma_value_t ecma_op_object_get_own_property_descriptor (ecma_object_t *object_p, /**< the object */ diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index bee41a0c3..7c48754cd 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -889,6 +889,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL, "getOwnPrope LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL, "AsyncGeneratorFunction") #endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL, "getOwnPropertyDescriptor") +#if ENABLED (JERRY_ESNEXT) +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTORS_UL, "getOwnPropertyDescriptors") +#endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING__FUNCTION_TO_STRING, "function(){/* ecmascript */}") LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (0, LIT_MAGIC_STRING__EMPTY) @@ -1050,7 +1053,11 @@ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (22, LIT_MAGIC_STRING_GET_OWN_PROPERTY_D #endif LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (23, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (24, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL) +#if ENABLED (JERRY_ESNEXT) +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (25, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTORS_UL) +#else LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (25, LIT_MAGIC_STRING__FUNCTION_TO_STRING) +#endif LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (26, LIT_MAGIC_STRING__FUNCTION_TO_STRING) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (27, LIT_MAGIC_STRING__FUNCTION_TO_STRING) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (28, LIT_MAGIC_STRING__FUNCTION_TO_STRING) diff --git a/jerry-core/lit/lit-magic-strings.ini b/jerry-core/lit/lit-magic-strings.ini index d656aeb2b..312f5a804 100644 --- a/jerry-core/lit/lit-magic-strings.ini +++ b/jerry-core/lit/lit-magic-strings.ini @@ -365,4 +365,5 @@ LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL = "propertyIsEnumerable" LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL = "getOwnPropertySymbols" LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL = "AsyncGeneratorFunction" LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL = "getOwnPropertyDescriptor" +LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTORS_UL = "getOwnPropertyDescriptors" LIT_MAGIC_STRING__FUNCTION_TO_STRING = "function(){/* ecmascript */}" diff --git a/tests/jerry/es.next/object-get-own-property-descriptors.js b/tests/jerry/es.next/object-get-own-property-descriptors.js new file mode 100644 index 000000000..ad28c4b3d --- /dev/null +++ b/tests/jerry/es.next/object-get-own-property-descriptors.js @@ -0,0 +1,119 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +var object = {}; +var symbol = Symbol("symbol"); + +function getter() { return 2; } +function setter(value) {} + +// Append a symbol property. +object[symbol] = "symbol"; + +Object.defineProperties(object, { + "foo" : { + get: getter, + set: setter, + enumerable: true, + configurable: true, + }, + "bar": { + value: "bar", + writable: true, + enumerable: false, + configurable: true, + }, + "baz": { + value: undefined, + writable: false, + enumerable: true, + configurable: false, + }, +}); + +var descriptors = Object.getOwnPropertyDescriptors(object); + +// All the descriptor keys should be enumerable. +var keys = Object.keys(descriptors); +var names = Object.getOwnPropertyNames(descriptors); +var symbols = Object.getOwnPropertySymbols(descriptors); + +assert(keys.length === names.length); +assert(symbols.length === 1); + +for (var idx = 0; idx < keys.length; idx++) { + assert(keys[idx] === names[idx]); +} + +assert(descriptors[symbol].value === "symbol"); + +assert(descriptors["foo"].get === getter); +assert(descriptors["foo"].set === setter); +assert(descriptors["foo"].enumerable === true); +assert(descriptors["foo"].configurable === true); + +assert(descriptors["bar"].value === "bar"); +assert(descriptors["bar"].writable === true); +assert(descriptors["bar"].enumerable === false); +assert(descriptors["bar"].configurable === true); + +assert(descriptors["baz"].value === undefined); +assert(descriptors["baz"].writable === false); +assert(descriptors["baz"].enumerable === true); +assert(descriptors["baz"].configurable === false); + +// Compare getOwnPropertyDescriptor and getOwnPropertyDescriptors. +for (let i of Object.getOwnPropertyNames(object)) { + let lhs = JSON.stringify(Object.getOwnPropertyDescriptor(object, i)); + let rhs = JSON.stringify(descriptors[i]); + + assert(lhs === rhs); +} + +var array_desc = Object.getOwnPropertyDescriptors(Array); +assert(array_desc.prototype.value === Array.prototype); +assert(array_desc.prototype.writable === false); +assert(array_desc.prototype.configurable === false); +assert(array_desc.prototype.enumerable === false); + +try { + Object.getOwnPropertyDescriptors(undefined); + assert(false); +} catch(e) { + assert(e instanceof TypeError); +} + +var proxy_get_desc_handler = new Proxy(object, { + getOwnPropertyDescriptor(target, prop) { + throw new Error("Error"); + } +}); + +var proxy_own_keys_handler = new Proxy(object, { + ownKeys: 42, +}); + +try { + Object.getOwnPropertyDescriptors(proxy_get_desc_handler); + assert(false); +} catch(e) { + assert(e instanceof Error); +} + +try { + Object.getOwnPropertyDescriptors(proxy_own_keys_handler); + assert(false); +} catch(e) { + assert(e instanceof TypeError); +}