diff --git a/jerry-core/vm/opcodes-ecma-relational-equality.c b/jerry-core/vm/opcodes-ecma-relational-equality.c index 93768f40a..f8680d07b 100644 --- a/jerry-core/vm/opcodes-ecma-relational-equality.c +++ b/jerry-core/vm/opcodes-ecma-relational-equality.c @@ -16,6 +16,7 @@ #include "ecma-comparison.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" +#include "ecma-function-object.h" #include "ecma-helpers.h" #include "ecma-objects.h" #include "ecma-try-catch-macro.h" @@ -109,6 +110,32 @@ opfunc_instanceof (ecma_value_t left_value, /**< left value */ return ecma_raise_type_error (ECMA_ERR_MSG ("Expected an object in 'instanceof' check.")); } +#if ENABLED (JERRY_ES2015_BUILTIN_SYMBOL) + ecma_value_t has_instance_method = ecma_op_get_method_by_symbol_id (right_value, LIT_MAGIC_STRING_HAS_INSTANCE); + if (ECMA_IS_VALUE_ERROR (has_instance_method)) + { + return has_instance_method; + } + + if (JERRY_UNLIKELY (!ecma_is_value_undefined (has_instance_method))) + { + ecma_object_t *method_obj_p = ecma_get_object_from_value (has_instance_method); + ecma_value_t has_instance_result = ecma_op_function_call (method_obj_p, right_value, &left_value, 1); + + ecma_free_value (has_instance_method); + + if (ECMA_IS_VALUE_ERROR (has_instance_result)) + { + return has_instance_result; + } + + bool has_instance = ecma_op_to_boolean (has_instance_result); + ecma_free_value (has_instance_result); + + return ecma_make_boolean_value (has_instance); + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_SYMBOL) */ + ecma_object_t *right_value_obj_p = ecma_get_object_from_value (right_value); return ecma_op_object_has_instance (right_value_obj_p, left_value); } /* opfunc_instanceof */ diff --git a/tests/jerry/es2015/instanceof-symbol-hasinstance-class.js b/tests/jerry/es2015/instanceof-symbol-hasinstance-class.js new file mode 100644 index 000000000..de39b57ea --- /dev/null +++ b/tests/jerry/es2015/instanceof-symbol-hasinstance-class.js @@ -0,0 +1,50 @@ +/* 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 NoParent { + static [Symbol.hasInstance] (arg) { + return false; + } +} + +var obj = new NoParent (); + +assert ((obj instanceof NoParent) === false); + +class PositiveNumber { + static [Symbol.hasInstance] (arg) { + return (arg instanceof Number) && (arg >= 0); + } +} + +var num_a = new Number (33); +var num_b = new Number (-50); + +assert ((num_a instanceof PositiveNumber) === true); +assert ((num_b instanceof PositiveNumber) === false); + + +class ErrorAlways { + static [Symbol.hasInstance] (arg) { + throw new URIError("ErrorAlways"); + } +} + +try { + (new Object ()) instanceof ErrorAlways; + assert (false); +} catch (ex) { + assert (ex instanceof URIError); +} diff --git a/tests/jerry/es2015/instanceof-symbol-hasinstance.js b/tests/jerry/es2015/instanceof-symbol-hasinstance.js new file mode 100644 index 000000000..aa3aba893 --- /dev/null +++ b/tests/jerry/es2015/instanceof-symbol-hasinstance.js @@ -0,0 +1,86 @@ +/* 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 NoParent () { } + +Object.defineProperty (NoParent, Symbol.hasInstance, { + value: function (arg) { return false; } +}); + +var obj = new NoParent (); + +assert ((obj instanceof NoParent) === false); + +try { + Object.defineProperty (NoParent, Symbol.hasInstance, { + value: function (arg) { return true; } + }); + assert (false) +} catch (ex) { + assert (ex instanceof TypeError); +} + + +function PositiveNumber () { } + +Object.defineProperty (PositiveNumber, Symbol.hasInstance, { + value: function (arg) { return (arg instanceof Number) && (arg >= 0); } +}) + +var num_a = new Number (33); +var num_b = new Number (-50); + +assert ((num_a instanceof PositiveNumber) === true); +assert ((num_b instanceof PositiveNumber) === false); + + +function ErrorAlways () { } + +Object.defineProperty (ErrorAlways, Symbol.hasInstance, { + value: function (arg) { throw new URIError ("ErrorAlways"); } +}) + +try { + (new Object ()) instanceof ErrorAlways; + assert (false); +} catch (ex) { + assert (ex instanceof URIError); +} + + +function NonCallable () { } + +Object.defineProperty (NonCallable, Symbol.hasInstance, { value: 11 }); + +try { + (new Object ()) instanceof NonCallable; + assert (false); +} catch (ex) { + assert (ex instanceof TypeError); +} + + +function ErrorGenerator () { } + +Object.defineProperty (ErrorGenerator, Symbol.hasInstance, { + get: function () { throw new URIError ("ErrorGenerator"); } +}); + +try { + (new Object ()) instanceof ErrorGenerator; + assert (false); +} catch (ex) { + assert (ex instanceof URIError); +}