From 945b22976a6d0d0f8e5926cb9fcef0094919b07c Mon Sep 17 00:00:00 2001 From: Szilagyi Adam Date: Tue, 4 Aug 2020 16:41:05 +0200 Subject: [PATCH] Implement Object.prototype.{__lookupGetter__, __lookupSetter__} (#4039) __lookupGetter__ is based on ECMA-262 v11, B.2.2.4 __lookupSetter__ is based on ECMA-262 v11, B.2.2.5 JerryScript-DCO-1.0-Signed-off-by: Adam Szilagyi aszilagy@inf.u-szeged.hu --- .../ecma-builtin-object-prototype.c | 125 +++++++++++++++++- .../ecma-builtin-object-prototype.inc.h | 4 +- jerry-core/lit/lit-magic-strings.inc.h | 2 + jerry-core/lit/lit-magic-strings.ini | 2 + .../es.next/object-prototype-lookup-getter.js | 92 +++++++++++++ .../es.next/object-prototype-lookup-setter.js | 91 +++++++++++++ tests/test262-esnext-excludelist.xml | 32 ----- 7 files changed, 313 insertions(+), 35 deletions(-) create mode 100644 tests/jerry/es.next/object-prototype-lookup-getter.js create mode 100644 tests/jerry/es.next/object-prototype-lookup-setter.js diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.c index 9044e9b95..4346e0b71 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.c @@ -55,6 +55,8 @@ enum #if ENABLED (JERRY_ESNEXT) && ENABLED (JERRY_BUILTIN_ANNEXB) ECMA_OBJECT_PROTOTYPE_DEFINE_GETTER, ECMA_OBJECT_PROTOTYPE_DEFINE_SETTER, + ECMA_OBJECT_PROTOTYPE_LOOKUP_GETTER, + ECMA_OBJECT_PROTOTYPE_LOOKUP_SETTER, #endif /* ENABLED (JERRY_ESNEXT) && ENABLED (JERRY_BUILTIN_ANNEXB) */ }; @@ -262,6 +264,115 @@ ecma_builtin_object_prototype_define_getter_setter (ecma_object_t *obj_p, /**< t /* 6. */ return ECMA_VALUE_UNDEFINED; } /* ecma_builtin_object_prototype_define_getter_setter */ + +/** + * The Object.prototype object's '__lookupGetter__' and '__lookupSetter__' routine + * + * See also: + * ECMA-262 v11, B.2.2.4 + * ECMA-262 v11, B.2.2.5 + * + * @return ECMA_VALUE_ERROR - if the operation fails, + * ECMA_VALUE_UNDEFINED - if the property was not found + * Accessor property - otherwise + */ +static ecma_value_t +ecma_builtin_object_prototype_lookup_getter_setter (ecma_value_t this_arg, /**< this argument */ + ecma_value_t prop, /**< property */ + bool lookup_getter) /**< true - lookupGetter method + false - lookupSetter method */ +{ + /* 1. */ + ecma_value_t to_obj = ecma_op_to_object (this_arg); + + if (ECMA_IS_VALUE_ERROR (to_obj)) + { + return to_obj; + } + + ecma_object_t *obj_p = ecma_get_object_from_value (to_obj); + + /* 2. */ + ecma_string_t *prop_name_p = ecma_op_to_property_key (prop); + + if (JERRY_UNLIKELY (prop_name_p == NULL)) + { + ecma_deref_object (obj_p); + return ECMA_VALUE_ERROR; + } + + jmem_cpointer_t obj_cp; + ECMA_SET_NON_NULL_POINTER (obj_cp, obj_p); + + ecma_value_t ret_value = ECMA_VALUE_UNDEFINED; + + /* 3. */ + while (true) + { + /* 3.a */ + ecma_property_descriptor_t desc; + ecma_value_t get_desc = ecma_op_object_get_own_property_descriptor (obj_p, prop_name_p, &desc); + + if (ECMA_IS_VALUE_ERROR (get_desc)) + { + ret_value = get_desc; + break; + } + + /* 3.b */ + if (ecma_is_value_true (get_desc)) + { + if ((desc.flags & ECMA_PROP_IS_SET_DEFINED) || (desc.flags & ECMA_PROP_IS_GET_DEFINED)) + { + if (lookup_getter && desc.get_p != NULL) + { + ecma_ref_object (desc.get_p); + ret_value = ecma_make_object_value (desc.get_p); + } + else if (!lookup_getter && desc.set_p != NULL) + { + ecma_ref_object (desc.set_p); + ret_value = ecma_make_object_value (desc.set_p); + } + } + + ecma_free_property_descriptor (&desc); + break; + } + + /* 3.c */ +#if ENABLED (JERRY_BUILTIN_PROXY) + if (ECMA_OBJECT_IS_PROXY (obj_p)) + { + ecma_value_t parent = ecma_proxy_object_get_prototype_of (obj_p); + + if (ECMA_IS_VALUE_ERROR (parent)) + { + ret_value = parent; + break; + } + + obj_cp = ecma_proxy_object_prototype_to_cp (parent); + } + else +#endif /* ENABLED (JERRY_BUILTIN_PROXY) */ + { + obj_cp = ecma_op_ordinary_object_get_prototype_of (obj_p); + } + + if (obj_cp == JMEM_CP_NULL) + { + break; + } + + obj_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, obj_cp); + } + + ecma_free_value (to_obj); + ecma_deref_ecma_string (prop_name_p); + + return ret_value; +} /* ecma_builtin_object_prototype_lookup_getter_setter */ #endif /* ENABLED (JERRY_ESNEXT) && ENABLED (JERRY_BUILTIN_ANNEXB) */ /** @@ -326,7 +437,7 @@ ecma_builtin_object_prototype_dispatch_routine (uint16_t builtin_routine_id, /** ret_value = ecma_builtin_object_object_get_prototype_of (obj_p); } else -#endif /* ENABLED (JERRY_ESNEXT)*/ +#endif /* ENABLED (JERRY_ESNEXT) */ { ret_value = ecma_builtin_object_prototype_object_is_prototype_of (obj_p, arguments_list_p[0]); } @@ -343,6 +454,16 @@ ecma_builtin_object_prototype_dispatch_routine (uint16_t builtin_routine_id, /** { return ecma_builtin_object_object_set_proto (this_arg, arguments_list_p[0]); } +#if ENABLED (JERRY_BUILTIN_ANNEXB) + else if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_LOOKUP_GETTER) + { + return ecma_builtin_object_prototype_lookup_getter_setter (this_arg, arguments_list_p[0], true); + } + else if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_LOOKUP_SETTER) + { + return ecma_builtin_object_prototype_lookup_getter_setter (this_arg, arguments_list_p[0], false); + } +#endif /* ENABLED (JERRY_BUILTIN_ANNEXB) */ #endif /* ENABLED (JERRY_ESNEXT)*/ ecma_string_t *prop_name_p = ecma_op_to_property_key (arguments_list_p[0]); @@ -377,7 +498,7 @@ ecma_builtin_object_prototype_dispatch_routine (uint16_t builtin_routine_id, /** { ret_value = ecma_builtin_object_prototype_define_getter_setter (obj_p, prop_name_p, arguments_list_p[1], false); } -#endif /* ENABLED (JERRY_ESNEXT) && ENABLED (JERRY_BUILTIN_ANNEXB) */ +#endif /* ENABLED (JERRY_ESNEXT) && ENABLED (JERRY_BUILTIN_ANNEXB) */ else { ret_value = ecma_builtin_object_prototype_object_property_is_enumerable (obj_p, prop_name_p); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.inc.h index 86e38c327..2517e0acf 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-object-prototype.inc.h @@ -44,6 +44,8 @@ ROUTINE (LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL, ECMA_OBJECT_PROTOTYPE_PROPE #if ENABLED (JERRY_ESNEXT) && ENABLED (JERRY_BUILTIN_ANNEXB) ROUTINE (LIT_MAGIC_STRING_DEFINE_GETTER, ECMA_OBJECT_PROTOTYPE_DEFINE_GETTER, 2, 2) ROUTINE (LIT_MAGIC_STRING_DEFINE_SETTER, ECMA_OBJECT_PROTOTYPE_DEFINE_SETTER, 2, 2) -#endif /* ENABLED (JERRY_ESNEXT) && ENABLED (JERRY_BUILTIN_ANNEXB) */ +ROUTINE (LIT_MAGIC_STRING_LOOKUP_GETTER, ECMA_OBJECT_PROTOTYPE_LOOKUP_GETTER, 1, 1) +ROUTINE (LIT_MAGIC_STRING_LOOKUP_SETTER, ECMA_OBJECT_PROTOTYPE_LOOKUP_SETTER, 1, 1) +#endif /* ENABLED (JERRY_ESNEXT) && ENABLED (JERRY_BUILTIN_ANNEXB)*/ #include "ecma-builtin-helpers-macro-undefs.inc.h" diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 9d53e7c9c..0f2a66bc2 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -882,6 +882,8 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MIN_SAFE_INTEGER_U, "MIN_SAFE_INTEGER") #if ENABLED (JERRY_ESNEXT) && ENABLED (JERRY_BUILTIN_ANNEXB) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DEFINE_GETTER, "__defineGetter__") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DEFINE_SETTER, "__defineSetter__") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOOKUP_GETTER, "__lookupGetter__") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOOKUP_SETTER, "__lookupSetter__") #endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DEFINE_PROPERTIES_UL, "defineProperties") #if ENABLED (JERRY_BUILTIN_TYPEDARRAY) diff --git a/jerry-core/lit/lit-magic-strings.ini b/jerry-core/lit/lit-magic-strings.ini index 26e379372..d3247d76c 100644 --- a/jerry-core/lit/lit-magic-strings.ini +++ b/jerry-core/lit/lit-magic-strings.ini @@ -355,6 +355,8 @@ LIT_MAGIC_STRING_MAX_SAFE_INTEGER_U = "MAX_SAFE_INTEGER" LIT_MAGIC_STRING_MIN_SAFE_INTEGER_U = "MIN_SAFE_INTEGER" LIT_MAGIC_STRING_DEFINE_GETTER = "__defineGetter__" LIT_MAGIC_STRING_DEFINE_SETTER = "__defineSetter__" +LIT_MAGIC_STRING_LOOKUP_GETTER = "__lookupGetter__" +LIT_MAGIC_STRING_LOOKUP_SETTER = "__lookupSetter__" LIT_MAGIC_STRING_BYTES_PER_ELEMENT_U = "BYTES_PER_ELEMENT" LIT_MAGIC_STRING_NEGATIVE_INFINITY_U = "NEGATIVE_INFINITY" LIT_MAGIC_STRING_POSITIVE_INFINITY_U = "POSITIVE_INFINITY" diff --git a/tests/jerry/es.next/object-prototype-lookup-getter.js b/tests/jerry/es.next/object-prototype-lookup-getter.js new file mode 100644 index 000000000..108274abb --- /dev/null +++ b/tests/jerry/es.next/object-prototype-lookup-getter.js @@ -0,0 +1,92 @@ +// 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 obj = { + get foo() { return "bar" }, + qux: 1, + set bar(value) { this.x = value } +}; + +var foo = Object.prototype.__lookupGetter__.call(obj, "foo"); +assert(foo() === "bar"); +assert(Object.prototype.__lookupGetter__.call(obj, "qux") === undefined); +assert(Object.prototype.__lookupGetter__.call(obj, "baz") === undefined); +assert(Object.prototype.__lookupGetter__.call(obj, "bar") === undefined); + +var foo = Object.prototype.__lookupGetter__.call(Object.create(obj), "foo"); +assert(foo() === "bar"); +assert(Object.prototype.__lookupGetter__.call(obj, "qux") === undefined); +assert(Object.prototype.__lookupGetter__.call(obj, "baz") === undefined); +assert(Object.prototype.__lookupGetter__.call(obj, "bar") === undefined); + +var sym = Symbol(); +var sym2 = Symbol(); +var obj = {}; +Object.defineProperty(obj, sym, { get: function() { return "bar"; }}); +Object.defineProperty(obj, sym2, { value: 1 }); +var foo = Object.prototype.__lookupGetter__.call(obj, sym); + +assert(foo() === "bar"); +assert(Object.prototype.__lookupGetter__.call(obj, sym2) === undefined); +assert(Object.prototype.__lookupGetter__.call(obj, Symbol()) === undefined); + +Object.prototype.__lookupGetter__.call(1, 'key'); + +try { + Object.prototype.__lookupGetter__.call(null, 'key'); + assert(false); +} catch(e){ + assert(e instanceof TypeError); +} + +var a = {}; +var b = Object.create(a); +b.foo = 1; +Object.defineProperty(a, "foo", {function () {}}) +assert(b.__lookupGetter__("foo") === undefined); + +var gopd = []; +var gpo = false; +var p = new Proxy({}, { + getPrototypeOf: function(o) { gpo = true; return Object.getPrototypeOf(o); }, + getOwnPropertyDescriptor: function(o, v) { gopd.push(v); return Object.getOwnPropertyDescriptor(o, v); } +}); + +Object.prototype.__lookupGetter__.call(p, "foo"); +assert(gopd + '' === "foo"); +assert(gpo === true); + +var __lookupGetter__ = Object.prototype.__lookupGetter__; +var counter = 0; +var key = { + toString: function() { + counter += 1; + } +}; + +try { + __lookupGetter__.call(undefined, key); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + __lookupGetter__.call(null, key); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +assert(counter === 0); diff --git a/tests/jerry/es.next/object-prototype-lookup-setter.js b/tests/jerry/es.next/object-prototype-lookup-setter.js new file mode 100644 index 000000000..aea8077e3 --- /dev/null +++ b/tests/jerry/es.next/object-prototype-lookup-setter.js @@ -0,0 +1,91 @@ +// 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 obj = { + set foo(value) { return "bar" }, + qux: 1, + get bar() {} +}; + +var foo = Object.prototype.__lookupSetter__.call(obj, "foo"); +assert(foo() === "bar"); +assert(Object.prototype.__lookupSetter__.call(obj, "qux") === undefined); +assert(Object.prototype.__lookupSetter__.call(obj, "baz") === undefined); +assert(Object.prototype.__lookupSetter__.call(obj, "bar") === undefined); + +var foo = Object.prototype.__lookupSetter__.call(Object.create(obj), "foo"); +assert(foo() === "bar"); +assert(Object.prototype.__lookupSetter__.call(obj, "qux") === undefined); +assert(Object.prototype.__lookupSetter__.call(obj, "baz") === undefined); +assert(Object.prototype.__lookupSetter__.call(obj, "bar") === undefined); + +var sym = Symbol(); +var sym2 = Symbol(); +var obj = {}; +Object.defineProperty(obj, sym, { set: function(value) { return "bar"; }}); +Object.defineProperty(obj, sym2, { value: 1 }); +var foo = Object.prototype.__lookupSetter__.call(obj, sym); + +assert(foo() === "bar"); +assert(Object.prototype.__lookupSetter__.call(obj, sym2) === undefined); +assert(Object.prototype.__lookupSetter__.call(obj, Symbol()) === undefined); + +Object.prototype.__lookupSetter__.call(1, 'key'); + +try { + Object.prototype.__lookupSetter__.call(null, 'key'); + assert(false); +} catch(e){ + assert(e instanceof TypeError); +} + +var a = {}; +var b = Object.create(a); +b.foo = 1; +Object.defineProperty(a, "foo", {function () {}}) +assert(b.__lookupSetter__("foo") === undefined); + +var gopd = []; +var gpo = false; +var p = new Proxy({}, { + getPrototypeOf: function(o) { gpo = true; return Object.getPrototypeOf(o); }, + getOwnPropertyDescriptor: function(o, v) { gopd.push(v); return Object.getOwnPropertyDescriptor(o, v); } +}); + +Object.prototype.__lookupSetter__.call(p, "foo"); +assert(gopd + '' === "foo"); +assert(gpo); + +var __lookupSetter__ = Object.prototype.__lookupSetter__; +var counter = 0; +var key = { + toString: function() { + counter += 1; + } +}; + +try { + __lookupSetter__.call(undefined, key); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + __lookupSetter__.call(null, key); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +assert(counter === 0); diff --git a/tests/test262-esnext-excludelist.xml b/tests/test262-esnext-excludelist.xml index b2d639b57..c5000a4e9 100644 --- a/tests/test262-esnext-excludelist.xml +++ b/tests/test262-esnext-excludelist.xml @@ -18,38 +18,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -