From d85988af964519da7a191d18004195b8dadc8cd6 Mon Sep 17 00:00:00 2001 From: Daniel Vince Date: Mon, 21 Oct 2019 11:04:12 +0200 Subject: [PATCH] Implement ES2015 Function.prototype[@@hasInstance] (#3225) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added hasInstance well known symbol implementation for Function.prototype. This change however does not implement the ES2015 `instanceof` operator changes. JerryScript-DCO-1.0-Signed-off-by: Peter Gal pgal.u-szeged@partner.samsung.com JerryScript-DCO-1.0-Signed-off-by: Dániel Vince vinced@inf.u-szeged.hu --- .../ecma-builtin-function-prototype.c | 9 +++ .../ecma-builtin-function-prototype.inc.h | 8 ++ .../ecma-builtin-helpers-macro-defines.inc.h | 4 + .../ecma-builtin-helpers-macro-undefs.inc.h | 1 + ...a-builtin-internal-routines-template.inc.h | 23 ++++++ .../function-prototype-hasinstance-class.js | 53 ++++++++++++ .../es2015/function-prototype-hasinstance.js | 81 +++++++++++++++++++ 7 files changed, 179 insertions(+) create mode 100644 tests/jerry/es2015/function-prototype-hasinstance-class.js create mode 100644 tests/jerry/es2015/function-prototype-hasinstance.js diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c index bfca2bcfb..fb22e59be 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c @@ -44,6 +44,9 @@ enum ECMA_FUNCTION_PROTOTYPE_CALL, ECMA_FUNCTION_PROTOTYPE_APPLY, ECMA_FUNCTION_PROTOTYPE_BIND, +#if ENABLED (JERRY_ES2015_BUILTIN) + ECMA_FUNCTION_PROTOTYPE_SYMBOL_HAS_INSTANCE, +#endif /* ENABLED (JERRY_ES2015_BUILTIN) */ }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-function-prototype.inc.h" @@ -350,6 +353,12 @@ ecma_builtin_function_prototype_dispatch_routine (uint16_t builtin_routine_id, / { return ecma_builtin_function_prototype_object_bind (func_obj_p, arguments_list_p, arguments_number); } +#if ENABLED (JERRY_ES2015_BUILTIN) + case ECMA_FUNCTION_PROTOTYPE_SYMBOL_HAS_INSTANCE: + { + return ecma_op_object_has_instance (func_obj_p, arguments_list_p[0]); + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN) */ default: { JERRY_UNREACHABLE (); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.inc.h index 945a14fc8..b9ffdb60f 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.inc.h @@ -42,4 +42,12 @@ ROUTINE (LIT_MAGIC_STRING_APPLY, ECMA_FUNCTION_PROTOTYPE_APPLY, 2, 2) ROUTINE (LIT_MAGIC_STRING_CALL, ECMA_FUNCTION_PROTOTYPE_CALL, NON_FIXED, 1) ROUTINE (LIT_MAGIC_STRING_BIND, ECMA_FUNCTION_PROTOTYPE_BIND, NON_FIXED, 1) +#if ENABLED (JERRY_ES2015_BUILTIN) +/** + * ECMA-262 v6.0 19.2.3.6 @@hasInstance + * the property attributes are: { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }. + */ +ROUTINE_WITH_FLAGS (LIT_GLOBAL_SYMBOL_HAS_INSTANCE, ECMA_FUNCTION_PROTOTYPE_SYMBOL_HAS_INSTANCE, 1, 1, 0 /* flags */) +#endif /* ENABLED (JERRY_ES2015_BUILTIN) */ + #include "ecma-builtin-helpers-macro-undefs.inc.h" diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-macro-defines.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-macro-defines.inc.h index 53ecd64e2..ce51a3805 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-macro-defines.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-macro-defines.inc.h @@ -43,6 +43,10 @@ #define ROUTINE_CONFIGURABLE_ONLY(name, c_function_name, args_number, length_prop_value) #endif /* !ROUTINE_CONFIGURABLE_ONLY */ +#ifndef ROUTINE_WITH_FLAGS +#define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, flags) +#endif /* !ROUTINE_WITH_FLAGS */ + #ifndef ACCESSOR_READ_WRITE #define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) #endif /* !ACCESSOR_READ_WRITE */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-macro-undefs.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-macro-undefs.inc.h index 8f7252513..7ba34e36f 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-macro-undefs.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-macro-undefs.inc.h @@ -22,5 +22,6 @@ #undef OBJECT_VALUE #undef ROUTINE #undef ROUTINE_CONFIGURABLE_ONLY +#undef ROUTINE_WITH_FLAGS #undef ACCESSOR_READ_WRITE #undef ACCESSOR_READ_ONLY diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-internal-routines-template.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-internal-routines-template.inc.h index 06a10fb67..ab8ec8029 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-internal-routines-template.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-internal-routines-template.inc.h @@ -45,6 +45,8 @@ static ecma_value_t c_function_name (ROUTINE_ARG_LIST_ ## args_number); #define ROUTINE_CONFIGURABLE_ONLY(name, c_function_name, args_number, length_prop_value) \ static ecma_value_t c_function_name (ROUTINE_ARG_LIST_ ## args_number); +#define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, flags) \ + static ecma_value_t c_function_name (ROUTINE_ARG_LIST_ ## args_number); #define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) \ static ecma_value_t c_getter_func_name (ROUTINE_ARG_LIST_0); \ static ecma_value_t c_setter_func_name (ROUTINE_ARG_LIST_1); @@ -68,6 +70,8 @@ enum ECMA_ROUTINE_ ## name ## c_function_name, #define ROUTINE_CONFIGURABLE_ONLY(name, c_function_name, args_number, length_prop_value) \ ECMA_ROUTINE_ ## name ## c_function_name, +#define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, flags) \ + ECMA_ROUTINE_ ## name ## c_function_name, #define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) \ ECMA_ACCESSOR_ ## name ## c_getter_func_name, \ ECMA_ACCESSOR_ ## name ## c_setter_func_name, @@ -98,6 +102,13 @@ const ecma_builtin_property_descriptor_t PROPERTY_DESCRIPTOR_LIST_NAME[] = ECMA_PROPERTY_FLAG_CONFIGURABLE, \ ECMA_ROUTINE_VALUE (ECMA_ROUTINE_ ## name ## c_function_name, length_prop_value) \ }, +#define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, flags) \ + { \ + name, \ + ECMA_BUILTIN_PROPERTY_ROUTINE, \ + flags, \ + ECMA_ROUTINE_VALUE (ECMA_ROUTINE_ ## name ## c_function_name, length_prop_value) \ + }, #define ACCESSOR_READ_ONLY(name, c_getter_func_name, prop_attributes) \ { \ name, \ @@ -120,6 +131,13 @@ const ecma_builtin_property_descriptor_t PROPERTY_DESCRIPTOR_LIST_NAME[] = ECMA_PROPERTY_FLAG_CONFIGURABLE, \ ECMA_ROUTINE_VALUE (c_function_name, length_prop_value) \ }, +#define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, flags) \ + { \ + name, \ + ECMA_BUILTIN_PROPERTY_ROUTINE, \ + flags, \ + ECMA_ROUTINE_VALUE (c_function_name, length_prop_value) \ + }, #define ACCESSOR_READ_ONLY(name, c_getter_func_name, prop_attributes) \ { \ name, \ @@ -222,6 +240,11 @@ DISPATCH_ROUTINE_ROUTINE_NAME (uint16_t builtin_routine_id, /**< built-in wide r { \ return c_function_name (this_arg_value ROUTINE_ARG_LIST_ ## args_number); \ } +#define ROUTINE_WITH_FLAGS(name, c_function_name, args_number, length_prop_value, flags) \ + case ECMA_ROUTINE_ ## name ## c_function_name: \ + { \ + return c_function_name (this_arg_value ROUTINE_ARG_LIST_ ## args_number); \ + } #define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) \ case ECMA_ACCESSOR_ ## name ## c_getter_func_name: \ { \ diff --git a/tests/jerry/es2015/function-prototype-hasinstance-class.js b/tests/jerry/es2015/function-prototype-hasinstance-class.js new file mode 100644 index 000000000..d497744f9 --- /dev/null +++ b/tests/jerry/es2015/function-prototype-hasinstance-class.js @@ -0,0 +1,53 @@ +// 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. + +class base { + constructor (value) { + this.member = value; + } + + method () { + return this.member; + } +} + +class sub { + constructor (value) { + this.member = value; + } +} + +var obj_base = new base (3); +var obj_sub = new sub (4); + +assert (base[Symbol.hasInstance](obj_base) === true); +assert (base[Symbol.hasInstance](obj_sub) === false); + +assert (sub[Symbol.hasInstance](obj_base) === false); +assert (sub[Symbol.hasInstance](obj_sub) === true); + + +class sub_c extends base { + constructor (value) { + super(value); + this.member = value; + } +} + +var obj_sub_c = new sub_c (5); + +assert (base[Symbol.hasInstance](obj_sub_c) === true); + +assert (sub_c[Symbol.hasInstance](obj_base) === false); +assert (sub_c[Symbol.hasInstance](obj_sub_c) === true); diff --git a/tests/jerry/es2015/function-prototype-hasinstance.js b/tests/jerry/es2015/function-prototype-hasinstance.js new file mode 100644 index 000000000..d29d9cf36 --- /dev/null +++ b/tests/jerry/es2015/function-prototype-hasinstance.js @@ -0,0 +1,81 @@ +// 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. + +function base (value) { + this.member = value; +} + +base.prototype.method = function () { return this.member; } + +function sub (value) { + this.member = value; +} + +sub.prototype = base; + + +var obj_base = new base (3); +var obj_sub = new sub (4); + +assert (base[Symbol.hasInstance] (obj_base) === true); +assert (base[Symbol.hasInstance] (obj_sub) === false); +assert (Object[Symbol.hasInstance] (obj_base) === true); +assert (Object[Symbol.hasInstance] (obj_sub) === true); +assert (obj_base.method () === 3); + +assert (sub[Symbol.hasInstance] (obj_base) === false); +assert (sub[Symbol.hasInstance] (obj_sub) === true); +assert (obj_sub.method === undefined); + +function sub_c (value) { + this.member = value; +} + +sub_c.prototype = Object.create (base.prototype) +sub_c.prototype.constructor = sub_c + +var obj_sub_c = new sub_c (5); + +assert (base[Symbol.hasInstance] (obj_sub_c) === true); + +assert (sub_c[Symbol.hasInstance] (obj_base) === false); +assert (sub_c[Symbol.hasInstance] (obj_sub_c) === true); +assert (Object[Symbol.hasInstance] (obj_sub_c) === true); +assert (Function.prototype[Symbol.hasInstance].call (sub_c, obj_sub_c) === true); + +assert (obj_sub_c.method () === 5); + +assert (base[Symbol.hasInstance] (3) === false); +assert (Number[Symbol.hasInstance] (33) === false); +assert (Number[Symbol.hasInstance] (new Number (33)) === true); +assert (Object[Symbol.hasInstance] (44) === false); +assert (Object[Symbol.hasInstance] (new Number (22)) === true); + +assert (base[Symbol.hasInstance] ('demo') === false); +assert (String[Symbol.hasInstance] ('demo') === false); +assert (String[Symbol.hasInstance] (new String ('demo')) === true); +assert (Object[Symbol.hasInstance] ('demo') === false); +assert (Object[Symbol.hasInstance] (new String ('demo')) === true); + +assert (base[Symbol.hasInstance] ([]) === false); +assert (base[Symbol.hasInstance] ([1, 2]) === false); +assert (Array[Symbol.hasInstance] ([1, 2]) === true); +assert (Array[Symbol.hasInstance] (new Array(1, 2)) === true); +assert (Object[Symbol.hasInstance] ([]) === true); +assert (Object[Symbol.hasInstance] (new Array()) === true); + +assert (base[Symbol.hasInstance] (new RegExp('abc')) === false); +assert (RegExp[Symbol.hasInstance] (/abc/) === true); +assert (RegExp[Symbol.hasInstance] (new RegExp('abc')) === true); +assert (Object[Symbol.hasInstance] (/abc/) === true);