diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index c0e4f68a4..af04b3516 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -129,24 +129,25 @@ ecma_op_is_callable (ecma_value_t value) /**< ecma value */ } /* ecma_op_is_callable */ /** - * Checks whether the given object implements [[Construct]]. + * Implement IsConstructor abstract operation. * - * @return true - if the given object is constructor; - * false - otherwise + * + * @return ECMA_IS_VALID_CONSTRUCTOR - if object is a valid for constructor call + * any other value - if object is not a valid constructor, the pointer contains the error message. */ -inline bool JERRY_ATTR_ALWAYS_INLINE -ecma_object_is_constructor (ecma_object_t *obj_p) /**< ecma object */ +char * +ecma_object_check_constructor (ecma_object_t *obj_p) /**< ecma object */ { JERRY_ASSERT (!ecma_is_lexical_environment (obj_p)); ecma_object_type_t type = ecma_get_object_type (obj_p); - if (type < ECMA_OBJECT_TYPE_PROXY) + if (JERRY_UNLIKELY (type < ECMA_OBJECT_TYPE_PROXY)) { - return false; + return ECMA_ERR_MSG ("Invalid type for constructor call."); } - while (type == ECMA_OBJECT_TYPE_BOUND_FUNCTION) + while (JERRY_UNLIKELY (type == ECMA_OBJECT_TYPE_BOUND_FUNCTION)) { ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) obj_p; @@ -156,20 +157,115 @@ ecma_object_is_constructor (ecma_object_t *obj_p) /**< ecma object */ type = ecma_get_object_type (obj_p); } + if (JERRY_LIKELY (type == ECMA_OBJECT_TYPE_FUNCTION)) + { + bool is_builtin = ecma_get_object_is_builtin (obj_p); + + if (is_builtin) + { + if (ecma_builtin_function_is_routine (obj_p)) + { + return ECMA_ERR_MSG ("Built-in routines are not constructors."); + } + + return ECMA_IS_VALID_CONSTRUCTOR; + } + +#if ENABLED (JERRY_ESNEXT) + const ecma_compiled_code_t *byte_code_p = ecma_op_function_get_compiled_code ((ecma_extended_object_t *) obj_p); + + if (!CBC_FUNCTION_IS_CONSTRUCTABLE (byte_code_p->status_flags)) + { +#if ENABLED (JERRY_ERROR_MESSAGES) + switch (CBC_FUNCTION_GET_TYPE (byte_code_p->status_flags)) + { + case CBC_FUNCTION_GENERATOR: + { + return "Generator functions cannot be invoked with 'new'."; + } + case CBC_FUNCTION_ASYNC: + { + return "Async functions cannot be invoked with 'new'."; + } + case CBC_FUNCTION_ASYNC_GENERATOR: + { + return "Async generator functions cannot be invoked with 'new'."; + } + case CBC_FUNCTION_ACCESSOR: + { + return "Accessor functions cannot be invoked with 'new'."; + } + case CBC_FUNCTION_METHOD: + { + return "Methods cannot be invoked with 'new'."; + } + case CBC_FUNCTION_ARROW: + { + return "Arrow functions cannot be invoked with 'new'."; + } + default: + { + JERRY_ASSERT (CBC_FUNCTION_GET_TYPE (byte_code_p->status_flags) == CBC_FUNCTION_ASYNC_ARROW); + return "Async arrow functions cannot be invoked with 'new'."; + } + } +#else /* !ENABLED (JERRY_ERROR_MESSAGES) */ + return NULL; +#endif /* ENABLED (JERRY_ERROR_MESSAGES) */ + } +#endif /* ENABLED (JERRY_NEXT) */ + + return ECMA_IS_VALID_CONSTRUCTOR; + } + #if ENABLED (JERRY_BUILTIN_PROXY) if (ECMA_OBJECT_TYPE_IS_PROXY (type)) { - return ECMA_GET_SECOND_BIT_FROM_POINTER_TAG (obj_p->u1.property_list_cp) != 0; + if (ECMA_GET_SECOND_BIT_FROM_POINTER_TAG (obj_p->u1.property_list_cp) == 0) + { + return ECMA_ERR_MSG ("Proxy target is not a constructor."); + } + + return ECMA_IS_VALID_CONSTRUCTOR; } #endif /* ENABLED (JERRY_BUILTIN_PROXY) */ - if (type == ECMA_OBJECT_TYPE_FUNCTION) + JERRY_ASSERT (type == ECMA_OBJECT_TYPE_NATIVE_FUNCTION); + if (ecma_get_object_is_builtin (obj_p)) { - return (!ecma_get_object_is_builtin (obj_p) || !ecma_builtin_function_is_routine (obj_p)); + return ECMA_ERR_MSG ("Built-ins are not constructors."); } - JERRY_ASSERT (type == ECMA_OBJECT_TYPE_NATIVE_FUNCTION); - return !ecma_get_object_is_builtin (obj_p); + return ECMA_IS_VALID_CONSTRUCTOR; +} /* ecma_object_check_constructor */ + +/** + * Implement IsConstructor abstract operation. + * + * @return ECMA_IS_VALID_CONSTRUCTOR - if the input value is a constructor. + * any other value - if the input value is not a valid constructor, the pointer contains the error message. + */ +inline char *JERRY_ATTR_ALWAYS_INLINE +ecma_check_constructor (ecma_value_t value) /**< ecma object */ +{ + if (!ecma_is_value_object (value)) + { + return ECMA_ERR_MSG ("Invalid type for constructor call."); + } + + return ecma_object_check_constructor (ecma_get_object_from_value (value)); +} /* ecma_check_constructor */ + +/** + * Checks whether the given object implements [[Construct]]. + * + * @return true - if the given object is constructor; + * false - otherwise + */ +inline bool JERRY_ATTR_ALWAYS_INLINE +ecma_object_is_constructor (ecma_object_t *obj_p) /**< ecma object */ +{ + return ecma_object_check_constructor (obj_p) == ECMA_IS_VALID_CONSTRUCTOR; } /* ecma_object_is_constructor */ /** @@ -634,30 +730,6 @@ ecma_op_function_get_compiled_code (ecma_extended_object_t *function_p) /**< fun #endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */ } /* ecma_op_function_get_compiled_code */ -#if ENABLED (JERRY_ESNEXT) -/** - * Check whether the given object [[FunctionKind]] internal slot value is "generator". - * - * @return true - if the given object is a generator function - * false - otherwise - */ -bool -ecma_op_function_is_generator (ecma_object_t *obj_p) /**< object */ -{ - if (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_FUNCTION - && !ecma_get_object_is_builtin (obj_p)) - { - ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) obj_p; - const ecma_compiled_code_t *byte_code_p = ecma_op_function_get_compiled_code (ext_func_obj_p); - - return CBC_FUNCTION_GET_TYPE (byte_code_p->status_flags) == CBC_FUNCTION_GENERATOR; - } - - return false; -} /* ecma_op_function_is_generator */ - -#endif /* ENABLED (JERRY_ESNEXT) */ - /** * 15.3.5.3 implementation of [[HasInstance]] for Function objects * @@ -821,7 +893,9 @@ ecma_op_function_get_super_constructor (ecma_object_t *func_obj_p) /**< function /** * Ordinary internal method: GetPrototypeFromConstructor (constructor, intrinsicDefaultProto) * - * See also: ECMAScript v6, 9.1.15 + * See also: + * - ECMAScript v6, 9.1.15 + * - ECMAScript v10, 9.1.14 * * @return NULL - if the operation fail (exception on the global context is raised) * pointer to the prototype object - otherwise @@ -830,7 +904,7 @@ ecma_object_t * ecma_op_get_prototype_from_constructor (ecma_object_t *ctor_obj_p, /**< constructor to get prototype from */ ecma_builtin_id_t default_proto_id) /**< intrinsicDefaultProto */ { - JERRY_ASSERT (ecma_object_is_constructor (ctor_obj_p)); + JERRY_ASSERT (ecma_op_object_is_callable (ctor_obj_p)); JERRY_ASSERT (default_proto_id < ECMA_BUILTIN_ID__COUNT); ecma_value_t proto = ecma_op_object_get_by_magic_id (ctor_obj_p, LIT_MAGIC_STRING_PROTOTYPE); @@ -1322,55 +1396,6 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ #if ENABLED (JERRY_ESNEXT) ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) func_obj_p; - const ecma_compiled_code_t *byte_code_p = ecma_op_function_get_compiled_code (ext_func_obj_p); - - if (!CBC_FUNCTION_IS_CONSTRUCTABLE (byte_code_p->status_flags)) - { - const char *message_p; - - switch (CBC_FUNCTION_GET_TYPE (byte_code_p->status_flags)) - { - case CBC_FUNCTION_GENERATOR: - { - message_p = ECMA_ERR_MSG ("Generator functions cannot be invoked with 'new'."); - break; - } - case CBC_FUNCTION_ASYNC: - { - message_p = ECMA_ERR_MSG ("Async functions cannot be invoked with 'new'."); - break; - } - case CBC_FUNCTION_ASYNC_GENERATOR: - { - message_p = ECMA_ERR_MSG ("Async generator functions cannot be invoked with 'new'."); - break; - } - case CBC_FUNCTION_ARROW: - { - message_p = ECMA_ERR_MSG ("Arrow functions cannot be invoked with 'new'."); - break; - } - case CBC_FUNCTION_ASYNC_ARROW: - { - message_p = ECMA_ERR_MSG ("Async arrow functions cannot be invoked with 'new'."); - break; - } - case CBC_FUNCTION_METHOD: - { - message_p = ECMA_ERR_MSG ("Methods cannot be invoked with 'new'."); - break; - } - default: - { - JERRY_ASSERT (CBC_FUNCTION_GET_TYPE (byte_code_p->status_flags) == CBC_FUNCTION_ACCESSOR); - - message_p = ECMA_ERR_MSG ("Accessor functions cannot be invoked with 'new'."); - break; - } - } - - return ecma_raise_type_error (message_p); - } /* 5. */ if (!ECMA_GET_THIRD_BIT_FROM_POINTER_TAG (ext_func_obj_p->u.function.scope_cp)) diff --git a/jerry-core/ecma/operations/ecma-function-object.h b/jerry-core/ecma/operations/ecma-function-object.h index bbc4fb9ea..dc1777815 100644 --- a/jerry-core/ecma/operations/ecma-function-object.h +++ b/jerry-core/ecma/operations/ecma-function-object.h @@ -37,6 +37,16 @@ bool ecma_op_object_is_callable (ecma_object_t *obj_p); bool ecma_is_constructor (ecma_value_t value); bool ecma_object_is_constructor (ecma_object_t *obj_p); +/** + * Special constant indicating that the value is a valid constructor + * + * Use after the ecma_*_check_constructor calls. + */ +#define ECMA_IS_VALID_CONSTRUCTOR ((char *) 0x1) + +char *ecma_object_check_constructor (ecma_object_t *obj_p); +char *ecma_check_constructor (ecma_value_t value); + ecma_object_t * ecma_op_create_simple_function_object (ecma_object_t *scope_p, const ecma_compiled_code_t *bytecode_data_p); @@ -72,9 +82,6 @@ void ecma_op_native_handler_list_lazy_property_names (ecma_object_t *object_p, ecma_collection_t *prop_names_p, ecma_property_counter_t *prop_counter_p); - -bool -ecma_op_function_is_generator (ecma_object_t *func_obj_p); #endif /* ENABLED (JERRY_ESNEXT) */ ecma_object_t * diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index 4613571ec..dc6289c05 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -1314,8 +1314,7 @@ opfunc_init_class (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ else if (!ecma_is_value_null (super_class)) { /* 6.f, 6.g.i */ - if (!ecma_is_constructor (super_class) - || ecma_op_function_is_generator (ecma_get_object_from_value (super_class))) + if (!ecma_is_constructor (super_class)) { return ecma_raise_type_error ("Class extends value is not a constructor or null"); } diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 96a98fba9..ffa294b18 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -691,10 +691,10 @@ vm_spread_operation (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ if (frame_ctx_p->byte_code_p[1] == CBC_EXT_SPREAD_NEW) { - if (!ecma_is_value_object (func_value) - || !ecma_object_is_constructor (ecma_get_object_from_value (func_value))) + const char *constructor_message_p = ecma_check_constructor (func_value); + if (constructor_message_p != ECMA_IS_VALID_CONSTRUCTOR) { - completion_value = ecma_raise_type_error (ECMA_ERR_MSG ("Expected a constructor.")); + completion_value = ecma_raise_type_error (constructor_message_p); } else { @@ -879,10 +879,10 @@ opfunc_construct (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ ecma_value_t constructor_value = stack_top_p[-1]; ecma_value_t completion_value; - if (!ecma_is_value_object (constructor_value) - || !ecma_object_is_constructor (ecma_get_object_from_value (constructor_value))) + const char *constructor_message_p = ecma_check_constructor (constructor_value); + if (constructor_message_p != ECMA_IS_VALID_CONSTRUCTOR) { - completion_value = ecma_raise_type_error (ECMA_ERR_MSG ("Expected a constructor.")); + completion_value = ecma_raise_type_error (constructor_message_p); } else { diff --git a/tests/test262-esnext-excludelist.xml b/tests/test262-esnext-excludelist.xml index 764aa808a..e6cae0ba2 100644 --- a/tests/test262-esnext-excludelist.xml +++ b/tests/test262-esnext-excludelist.xml @@ -318,7 +318,6 @@ - @@ -333,7 +332,6 @@ - @@ -368,7 +366,6 @@ - @@ -730,9 +727,7 @@ - - @@ -9176,7 +9171,6 @@ -