The constructor check should return false for arrow and generator functions (#4328)

The previous `ecma_is_constructor` implementation did not checked if the
target function was an arrow or generator function. This resulted in
an incorrect execution for these function types.

JerryScript-DCO-1.0-Signed-off-by: Peter Gal pgal.usz@partner.samsung.com
This commit is contained in:
Péter Gál 2020-11-24 12:46:44 +01:00 committed by GitHub
parent cc52282f34
commit 3af3597f2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 130 additions and 105 deletions

View File

@ -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))

View File

@ -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 *

View File

@ -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");
}

View File

@ -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
{

View File

@ -318,7 +318,6 @@
<test id="built-ins/TypedArrayConstructors/ctors-bigint/object-arg/custom-proto-access-throws.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/ctors-bigint/object-arg/use-custom-proto-if-object.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/ctors-bigint/typedarray-arg/custom-proto-access-throws.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/ctors-bigint/typedarray-arg/other-ctor-buffer-ctor-species-not-ctor-throws.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/ctors/buffer-arg/byteoffset-is-negative-zero.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/ctors/buffer-arg/custom-proto-access-throws.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/ctors/buffer-arg/defined-negative-length.js"><reason></reason></test>
@ -333,7 +332,6 @@
<test id="built-ins/TypedArrayConstructors/ctors/object-arg/returns.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/ctors/object-arg/use-custom-proto-if-object.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/ctors/typedarray-arg/custom-proto-access-throws.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/ctors/typedarray-arg/other-ctor-buffer-ctor-species-not-ctor-throws.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/ctors/typedarray-arg/use-custom-proto-if-object.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/from/BigInt/custom-ctor-returns-other-instance.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/from/BigInt/custom-ctor.js"><reason></reason></test>
@ -368,7 +366,6 @@
<test id="built-ins/TypedArrayConstructors/of/custom-ctor-returns-other-instance.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/of/custom-ctor.js"><reason></reason></test>
<test id="built-ins/TypedArrayConstructors/of/new-instance-using-custom-ctor.js"><reason></reason></test>
<test id="harness/isConstructor.js"><reason></reason></test>
<test id="language/block-scope/syntax/redeclaration/async-function-name-redeclaration-attempt-with-async-function.js"><reason></reason></test>
<test id="language/block-scope/syntax/redeclaration/async-function-name-redeclaration-attempt-with-function.js"><reason></reason></test>
<test id="language/block-scope/syntax/redeclaration/async-function-name-redeclaration-attempt-with-generator.js"><reason></reason></test>
@ -730,9 +727,7 @@
<test id="language/statements/class/dstr/meth-static-ary-init-iter-no-close.js"><reason></reason></test>
<test id="language/statements/class/dstr/meth-static-dflt-ary-init-iter-no-close.js"><reason></reason></test>
<test id="language/statements/class/subclass/default-constructor-spread-override.js"><reason></reason></test>
<test id="language/statements/class/subclass/superclass-arrow-function.js"><reason></reason></test>
<test id="language/statements/class/subclass/superclass-async-function.js"><reason></reason></test>
<test id="language/statements/class/subclass/superclass-generator-function.js"><reason></reason></test>
<test id="language/statements/class/super/in-constructor-superproperty-evaluation.js"><reason></reason></test>
<test id="language/statements/const/dstr/ary-init-iter-no-close.js"><reason></reason></test>
<test id="language/statements/do-while/cptn-abrupt-empty.js"><reason></reason></test>
@ -9176,7 +9171,6 @@
<test id="language/statements/class/elements/same-line-async-gen-static-private-methods-with-fields.js"><reason></reason></test>
<test id="language/statements/class/elements/same-line-async-gen-static-private-methods.js"><reason></reason></test>
<test id="language/statements/class/elements/syntax/valid/grammar-static-private-async-gen-meth-prototype.js"><reason></reason></test>
<test id="language/statements/class/subclass/superclass-async-generator-function.js"><reason></reason></test>
<test id="language/statements/for-await-of/async-from-sync-iterator-continuation-abrupt-completion-get-constructor.js"><reason></reason></test>
<test id="language/statements/for-await-of/async-func-decl-dstr-array-elem-init-simple-no-strict.js"><reason></reason></test>
<test id="language/statements/for-await-of/async-func-decl-dstr-array-elem-init-yield-ident-invalid.js"><reason></reason></test>