From 5e846c9efa8141f210137aa8bd3e25191d1b1b23 Mon Sep 17 00:00:00 2001 From: Robert Fancsik Date: Wed, 6 Feb 2019 15:06:43 +0100 Subject: [PATCH] Fix instanceof operator for implicit class constructors (#2748) Implicit class constructor functions should not be handled as bound functions during [[HasInstance]] check. JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu --- .../ecma/operations/ecma-function-object.c | 71 +++++++++++++++++++ .../es2015/class-inheritance-has-instance.js | 47 ++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 tests/jerry/es2015/class-inheritance-has-instance.js diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index 71119971d..13f944fdc 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -324,6 +324,70 @@ ecma_op_arrow_function_get_compiled_code (ecma_arrow_function_t *arrow_function_ #endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */ +#ifndef CONFIG_DISABLE_ES2015_CLASS +/** + * Helper function for implicit class constructors [[HasInstance]] check. + * + * @return ecma value + * Returned value must be freed with ecma_free_value + */ +static ecma_value_t +ecma_op_implicit_class_constructor_has_instance (ecma_object_t *func_obj_p, /**< Function object */ + ecma_value_t value) /**< argument 'V' */ +{ + JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION); + + /* Since bound functions represents individual class constructor functions, we should check + that the given value is instance of either of the bound function chain elements. */ + do + { + ecma_object_t *v_obj_p = ecma_get_object_from_value (value); + + ecma_value_t prototype_obj_value = ecma_op_object_get_by_magic_id (func_obj_p, + LIT_MAGIC_STRING_PROTOTYPE); + + if (ECMA_IS_VALUE_ERROR (prototype_obj_value)) + { + return prototype_obj_value; + } + + if (!ecma_is_value_object (prototype_obj_value)) + { + ecma_free_value (prototype_obj_value); + return ecma_raise_type_error (ECMA_ERR_MSG ("Object expected.")); + } + + ecma_object_t *prototype_obj_p = ecma_get_object_from_value (prototype_obj_value); + + while (true) + { + v_obj_p = ecma_get_object_prototype (v_obj_p); + + if (v_obj_p == NULL) + { + break; + } + + if (v_obj_p == prototype_obj_p) + { + ecma_deref_object (prototype_obj_p); + return ECMA_VALUE_TRUE; + } + } + + ecma_deref_object (prototype_obj_p); + + ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) func_obj_p; + + func_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, + ext_function_p->u.bound_function.target_function); + } + while (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION); + + return ECMA_VALUE_FALSE; +} /* ecma_op_implicit_class_constructor_has_instance */ +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + /** * 15.3.5.3 implementation of [[HasInstance]] for Function objects * @@ -344,6 +408,13 @@ ecma_op_function_has_instance (ecma_object_t *func_obj_p, /**< Function object * /* 1. 3. */ ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) func_obj_p; +#ifndef CONFIG_DISABLE_ES2015_CLASS + if (JERRY_UNLIKELY (ext_function_p->u.bound_function.args_len_or_this == ECMA_VALUE_IMPLICIT_CONSTRUCTOR)) + { + return ecma_op_implicit_class_constructor_has_instance (func_obj_p, value); + } +#endif /* !CONFIG_DISABLE_ES2015_CLASS */ + func_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, ext_function_p->u.bound_function.target_function); } diff --git a/tests/jerry/es2015/class-inheritance-has-instance.js b/tests/jerry/es2015/class-inheritance-has-instance.js new file mode 100644 index 000000000..7152db9ab --- /dev/null +++ b/tests/jerry/es2015/class-inheritance-has-instance.js @@ -0,0 +1,47 @@ +/* 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. + */ + +/* Create bound implicit class constructor */ +class myArray extends Array { }; + +var array = new myArray (1); +array.push (2); +assert (array.length === 2); +assert (array instanceof myArray); +assert (array instanceof Array); +assert (!([] instanceof myArray)); + +/* Add a new element to the bound function chain */ +class mySecretArray extends myArray { }; + +var secretArray = new mySecretArray (1, 2); +secretArray.push (3); +assert (secretArray.length === 3); +assert (secretArray instanceof mySecretArray); +assert (secretArray instanceof myArray); +assert (secretArray instanceof Array); +assert (!([] instanceof mySecretArray)); + +/* Add a new element to the bound function chain */ +class myEpicSecretArray extends myArray { }; + +var epicSecretArray = new myEpicSecretArray (1, 2, 3); +epicSecretArray.push (4); +assert (epicSecretArray.length === 4); +assert (epicSecretArray instanceof myEpicSecretArray); +assert (epicSecretArray instanceof mySecretArray); +assert (epicSecretArray instanceof myArray); +assert (epicSecretArray instanceof Array); +assert (!([] instanceof myEpicSecretArray));