mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Fix attributes of length property for bound function (#3659)
fix length property of bound function objects to be configurable (ECMA-262 v6, 19.2.4.1) JerryScript-DCO-1.0-Signed-off-by: HyukWoo Park hyukwoo.park@samsung.com
This commit is contained in:
parent
e470b13096
commit
73a78bd223
@ -217,15 +217,15 @@ ecma_gc_mark_bound_function_object (ecma_object_t *object_p) /**< bound function
|
||||
{
|
||||
JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION);
|
||||
|
||||
ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) object_p;
|
||||
ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) object_p;
|
||||
|
||||
ecma_object_t *target_func_obj_p;
|
||||
target_func_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
|
||||
ext_function_p->u.bound_function.target_function);
|
||||
ecma_object_t *target_func_p;
|
||||
target_func_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t,
|
||||
bound_func_p->header.u.bound_function.target_function);
|
||||
|
||||
ecma_gc_set_object_visited (target_func_obj_p);
|
||||
ecma_gc_set_object_visited (target_func_p);
|
||||
|
||||
ecma_value_t args_len_or_this = ext_function_p->u.bound_function.args_len_or_this;
|
||||
ecma_value_t args_len_or_this = bound_func_p->header.u.bound_function.args_len_or_this;
|
||||
|
||||
if (!ecma_is_value_integer_number (args_len_or_this))
|
||||
{
|
||||
@ -238,7 +238,7 @@ ecma_gc_mark_bound_function_object (ecma_object_t *object_p) /**< bound function
|
||||
}
|
||||
|
||||
ecma_integer_value_t args_length = ecma_get_integer_from_value (args_len_or_this);
|
||||
ecma_value_t *args_p = (ecma_value_t *) (ext_function_p + 1);
|
||||
ecma_value_t *args_p = (ecma_value_t *) (bound_func_p + 1);
|
||||
|
||||
JERRY_ASSERT (args_length > 0);
|
||||
|
||||
@ -1311,9 +1311,10 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
|
||||
}
|
||||
case ECMA_OBJECT_TYPE_BOUND_FUNCTION:
|
||||
{
|
||||
ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) object_p;
|
||||
ext_object_size = sizeof (ecma_bound_function_t);
|
||||
ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) object_p;
|
||||
|
||||
ecma_value_t args_len_or_this = ext_function_p->u.bound_function.args_len_or_this;
|
||||
ecma_value_t args_len_or_this = bound_func_p->header.u.bound_function.args_len_or_this;
|
||||
|
||||
if (!ecma_is_value_integer_number (args_len_or_this))
|
||||
{
|
||||
@ -1322,7 +1323,7 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
|
||||
}
|
||||
|
||||
ecma_integer_value_t args_length = ecma_get_integer_from_value (args_len_or_this);
|
||||
ecma_value_t *args_p = (ecma_value_t *) (ext_function_p + 1);
|
||||
ecma_value_t *args_p = (ecma_value_t *) (bound_func_p + 1);
|
||||
|
||||
for (ecma_integer_value_t i = 0; i < args_length; i++)
|
||||
{
|
||||
|
||||
@ -897,7 +897,7 @@ typedef struct
|
||||
*/
|
||||
struct
|
||||
{
|
||||
ecma_value_t target_function; /**< target function */
|
||||
jmem_cpointer_tag_t target_function; /**< target function */
|
||||
ecma_value_t args_len_or_this; /**< length of arguments or this value */
|
||||
} bound_function;
|
||||
|
||||
@ -947,6 +947,17 @@ typedef struct
|
||||
#define ECMA_REGEXP_PROTO_COMPILED_CODE_SIZE \
|
||||
(JERRY_ALIGNUP (sizeof (ecma_compiled_code_t), JMEM_ALIGNMENT) + sizeof (ecma_value_t))
|
||||
|
||||
/**
|
||||
* Description of bound function objects.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ecma_extended_object_t header; /**< extended object header */
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
ecma_integer_value_t target_length; /**< length of target function */
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
} ecma_bound_function_t;
|
||||
|
||||
#if ENABLED (JERRY_SNAPSHOT_EXEC)
|
||||
|
||||
/**
|
||||
|
||||
@ -244,46 +244,48 @@ ecma_builtin_function_prototype_object_bind (ecma_object_t *this_arg_obj_p , /**
|
||||
#endif /* !ENABLED (JERRY_ES2015) */
|
||||
|
||||
ecma_object_t *function_p;
|
||||
ecma_extended_object_t *ext_function_p;
|
||||
ecma_bound_function_t *bound_func_p;
|
||||
|
||||
if (arguments_number == 0
|
||||
|| (arguments_number == 1 && !ecma_is_value_integer_number (arguments_list_p[0])))
|
||||
{
|
||||
function_p = ecma_create_object (prototype_obj_p,
|
||||
sizeof (ecma_extended_object_t),
|
||||
sizeof (ecma_bound_function_t),
|
||||
ECMA_OBJECT_TYPE_BOUND_FUNCTION);
|
||||
|
||||
/* 8. */
|
||||
ext_function_p = (ecma_extended_object_t *) function_p;
|
||||
ECMA_SET_INTERNAL_VALUE_POINTER (ext_function_p->u.bound_function.target_function,
|
||||
this_arg_obj_p);
|
||||
bound_func_p = (ecma_bound_function_t *) function_p;
|
||||
ECMA_SET_NON_NULL_POINTER_TAG (bound_func_p->header.u.bound_function.target_function,
|
||||
this_arg_obj_p,
|
||||
0);
|
||||
|
||||
ext_function_p->u.bound_function.args_len_or_this = ECMA_VALUE_UNDEFINED;
|
||||
bound_func_p->header.u.bound_function.args_len_or_this = ECMA_VALUE_UNDEFINED;
|
||||
|
||||
if (arguments_number != 0)
|
||||
{
|
||||
ext_function_p->u.bound_function.args_len_or_this = ecma_copy_value_if_not_object (arguments_list_p[0]);
|
||||
bound_func_p->header.u.bound_function.args_len_or_this = ecma_copy_value_if_not_object (arguments_list_p[0]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JERRY_ASSERT (arguments_number > 0);
|
||||
|
||||
size_t obj_size = sizeof (ecma_extended_object_t) + (arguments_number * sizeof (ecma_value_t));
|
||||
size_t obj_size = sizeof (ecma_bound_function_t) + (arguments_number * sizeof (ecma_value_t));
|
||||
|
||||
function_p = ecma_create_object (prototype_obj_p,
|
||||
obj_size,
|
||||
ECMA_OBJECT_TYPE_BOUND_FUNCTION);
|
||||
|
||||
/* 8. */
|
||||
ext_function_p = (ecma_extended_object_t *) function_p;
|
||||
ECMA_SET_INTERNAL_VALUE_POINTER (ext_function_p->u.bound_function.target_function,
|
||||
this_arg_obj_p);
|
||||
bound_func_p = (ecma_bound_function_t *) function_p;
|
||||
ECMA_SET_NON_NULL_POINTER_TAG (bound_func_p->header.u.bound_function.target_function,
|
||||
this_arg_obj_p,
|
||||
0);
|
||||
|
||||
/* NOTE: This solution provides temporary false data about the object's size
|
||||
but prevents GC from freeing it until it's not fully initialized. */
|
||||
ext_function_p->u.bound_function.args_len_or_this = ECMA_VALUE_UNDEFINED;
|
||||
ecma_value_t *args_p = (ecma_value_t *) (ext_function_p + 1);
|
||||
bound_func_p->header.u.bound_function.args_len_or_this = ECMA_VALUE_UNDEFINED;
|
||||
ecma_value_t *args_p = (ecma_value_t *) (bound_func_p + 1);
|
||||
|
||||
for (ecma_length_t i = 0; i < arguments_number; i++)
|
||||
{
|
||||
@ -291,10 +293,45 @@ ecma_builtin_function_prototype_object_bind (ecma_object_t *this_arg_obj_p , /**
|
||||
}
|
||||
|
||||
ecma_value_t args_len_or_this = ecma_make_integer_value ((ecma_integer_value_t) arguments_number);
|
||||
ext_function_p->u.bound_function.args_len_or_this = args_len_or_this;
|
||||
bound_func_p->header.u.bound_function.args_len_or_this = args_len_or_this;
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
ecma_integer_value_t len = 0;
|
||||
ecma_string_t *len_string = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH);
|
||||
ecma_property_descriptor_t prop_desc;
|
||||
ecma_value_t status = ecma_op_object_get_own_property_descriptor (this_arg_obj_p,
|
||||
len_string,
|
||||
&prop_desc);
|
||||
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
|
||||
if (ECMA_IS_VALUE_ERROR (status))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
|
||||
|
||||
if (ecma_is_value_true (status))
|
||||
{
|
||||
ecma_free_property_descriptor (&prop_desc);
|
||||
ecma_value_t len_value = ecma_op_object_get (this_arg_obj_p,
|
||||
len_string);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (len_value))
|
||||
{
|
||||
return len_value;
|
||||
}
|
||||
|
||||
if (ecma_is_value_number (len_value))
|
||||
{
|
||||
ecma_number_t len_num;
|
||||
ecma_op_to_integer (len_value, &len_num);
|
||||
len = (ecma_integer_value_t) len_num;
|
||||
}
|
||||
}
|
||||
|
||||
bound_func_p->target_length = len;
|
||||
|
||||
if (prototype_obj_p != NULL)
|
||||
{
|
||||
ecma_deref_object (prototype_obj_p);
|
||||
|
||||
@ -557,10 +557,10 @@ ecma_op_function_has_instance (ecma_object_t *func_obj_p, /**< Function object *
|
||||
JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION);
|
||||
|
||||
/* 1. 3. */
|
||||
ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) func_obj_p;
|
||||
ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) func_obj_p;
|
||||
|
||||
func_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
|
||||
ext_function_p->u.bound_function.target_function);
|
||||
func_obj_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t,
|
||||
bound_func_p->header.u.bound_function.target_function);
|
||||
}
|
||||
|
||||
JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION
|
||||
@ -939,12 +939,12 @@ ecma_op_bound_function_get_argument_list (ecma_object_t *func_obj_p, /**< bound
|
||||
{
|
||||
JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION);
|
||||
|
||||
ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) func_obj_p;
|
||||
ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) func_obj_p;
|
||||
|
||||
func_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
|
||||
ext_function_p->u.bound_function.target_function);
|
||||
func_obj_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t,
|
||||
bound_func_p->header.u.bound_function.target_function);
|
||||
|
||||
ecma_value_t args_len_or_this = ext_function_p->u.bound_function.args_len_or_this;
|
||||
ecma_value_t args_len_or_this = bound_func_p->header.u.bound_function.args_len_or_this;
|
||||
|
||||
ecma_length_t args_length = 1;
|
||||
|
||||
@ -956,7 +956,7 @@ ecma_op_bound_function_get_argument_list (ecma_object_t *func_obj_p, /**< bound
|
||||
/* 5. */
|
||||
if (args_length != 1)
|
||||
{
|
||||
const ecma_value_t *args_p = (const ecma_value_t *) (ext_function_p + 1);
|
||||
const ecma_value_t *args_p = (const ecma_value_t *) (bound_func_p + 1);
|
||||
list_p->buffer_p[0] = *args_p;
|
||||
|
||||
if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION)
|
||||
@ -1468,41 +1468,56 @@ ecma_op_bound_function_try_to_lazy_instantiate_property (ecma_object_t *object_p
|
||||
|
||||
if (ecma_string_is_length (property_name_p))
|
||||
{
|
||||
ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) object_p;
|
||||
ecma_object_t *target_func_obj_p;
|
||||
target_func_obj_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
|
||||
ext_function_p->u.bound_function.target_function);
|
||||
|
||||
ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) object_p;
|
||||
ecma_value_t args_len_or_this = bound_func_p->header.u.bound_function.args_len_or_this;
|
||||
ecma_integer_value_t length = 0;
|
||||
ecma_integer_value_t args_length = 1;
|
||||
uint8_t length_attributes;
|
||||
|
||||
if (ecma_object_get_class_name (target_func_obj_p) == LIT_MAGIC_STRING_FUNCTION_UL)
|
||||
if (ecma_is_value_integer_number (args_len_or_this))
|
||||
{
|
||||
/* The property_name_p argument contans the 'length' string. */
|
||||
ecma_value_t get_len_value = ecma_op_object_get (target_func_obj_p, property_name_p);
|
||||
args_length = ecma_get_integer_from_value (args_len_or_this);
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (bound_func_p->header.u.bound_function.target_function))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
length_attributes = ECMA_PROPERTY_FLAG_CONFIGURABLE;
|
||||
length = bound_func_p->target_length - (args_length - 1);
|
||||
|
||||
/* Set tag bit to represent initialized 'length' property */
|
||||
ECMA_SET_FIRST_BIT_TO_POINTER_TAG (bound_func_p->header.u.bound_function.target_function);
|
||||
#else /* !ENABLED (JERRY_ES2015) */
|
||||
length_attributes = ECMA_PROPERTY_FIXED;
|
||||
|
||||
ecma_object_t *target_func_p;
|
||||
target_func_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t,
|
||||
bound_func_p->header.u.bound_function.target_function);
|
||||
|
||||
if (ecma_object_get_class_name (target_func_p) == LIT_MAGIC_STRING_FUNCTION_UL)
|
||||
{
|
||||
/* The property_name_p argument contains the 'length' string. */
|
||||
ecma_value_t get_len_value = ecma_op_object_get (target_func_p, property_name_p);
|
||||
|
||||
JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (get_len_value));
|
||||
JERRY_ASSERT (ecma_is_value_integer_number (get_len_value));
|
||||
|
||||
ecma_value_t args_len_or_this = ext_function_p->u.bound_function.args_len_or_this;
|
||||
ecma_integer_value_t args_length = 1;
|
||||
|
||||
if (ecma_is_value_integer_number (args_len_or_this))
|
||||
{
|
||||
args_length = ecma_get_integer_from_value (args_len_or_this);
|
||||
}
|
||||
|
||||
length = ecma_get_integer_from_value (get_len_value) - (args_length - 1);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
if (length < 0)
|
||||
{
|
||||
length = 0;
|
||||
}
|
||||
if (length < 0)
|
||||
{
|
||||
length = 0;
|
||||
}
|
||||
|
||||
ecma_property_t *len_prop_p;
|
||||
ecma_property_value_t *len_prop_value_p = ecma_create_named_data_property (object_p,
|
||||
property_name_p,
|
||||
ECMA_PROPERTY_FIXED,
|
||||
length_attributes,
|
||||
&len_prop_p);
|
||||
|
||||
len_prop_value_p->value = ecma_make_integer_value (length);
|
||||
@ -1619,7 +1634,8 @@ ecma_op_external_function_list_lazy_property_names (uint32_t opts, /**< listing
|
||||
* ecma_op_bound_function_try_to_lazy_instantiate_property
|
||||
*/
|
||||
void
|
||||
ecma_op_bound_function_list_lazy_property_names (uint32_t opts, /**< listing options using flags
|
||||
ecma_op_bound_function_list_lazy_property_names (ecma_object_t *object_p, /**< bound function object*/
|
||||
uint32_t opts, /**< listing options using flags
|
||||
* from ecma_list_properties_options_t */
|
||||
ecma_collection_t *main_collection_p, /**< 'main' collection */
|
||||
ecma_collection_t *non_enum_collection_p) /**< skipped
|
||||
@ -1630,8 +1646,18 @@ ecma_op_bound_function_list_lazy_property_names (uint32_t opts, /**< listing opt
|
||||
|
||||
ecma_collection_t *for_non_enumerable_p = (opts & ECMA_LIST_ENUMERABLE) ? non_enum_collection_p : main_collection_p;
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
/* Unintialized 'length' property is non-enumerable (ECMA-262 v6, 19.2.4.1) */
|
||||
ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) object_p;
|
||||
if (!ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (bound_func_p->header.u.bound_function.target_function))
|
||||
{
|
||||
ecma_collection_push_back (for_non_enumerable_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH));
|
||||
}
|
||||
#else /* !ENABLED (JERRY_ES2015) */
|
||||
JERRY_UNUSED (object_p);
|
||||
/* 'length' property is non-enumerable (ECMA-262 v5, 13.2.5) */
|
||||
ecma_collection_push_back (for_non_enumerable_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH));
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/* 'caller' property is non-enumerable (ECMA-262 v5, 13.2.5) */
|
||||
ecma_collection_push_back (for_non_enumerable_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_CALLER));
|
||||
|
||||
@ -99,7 +99,8 @@ ecma_op_external_function_list_lazy_property_names (uint32_t opts,
|
||||
ecma_collection_t *non_enum_collection_p);
|
||||
|
||||
void
|
||||
ecma_op_bound_function_list_lazy_property_names (uint32_t opts,
|
||||
ecma_op_bound_function_list_lazy_property_names (ecma_object_t *object_p,
|
||||
uint32_t opts,
|
||||
ecma_collection_t *main_collection_p,
|
||||
ecma_collection_t *non_enum_collection_p);
|
||||
|
||||
|
||||
@ -1997,7 +1997,8 @@ ecma_op_object_get_property_names (ecma_object_t *obj_p, /**< object */
|
||||
}
|
||||
case ECMA_OBJECT_TYPE_BOUND_FUNCTION:
|
||||
{
|
||||
ecma_op_bound_function_list_lazy_property_names (opts,
|
||||
ecma_op_bound_function_list_lazy_property_names (obj_p,
|
||||
opts,
|
||||
prop_names_p,
|
||||
skipped_non_enumerable_p);
|
||||
break;
|
||||
|
||||
@ -105,8 +105,10 @@ var builtin_typedArrays = [
|
||||
/* test length property of function objects */
|
||||
var normal_func = function () {};
|
||||
var arrow_func = () => {};
|
||||
var bound_func = normal_func.bind({});
|
||||
var nested_bound_func = arrow_func.bind().bind(1);
|
||||
|
||||
var functions = [normal_func, arrow_func];
|
||||
var functions = [normal_func, arrow_func, bound_func, nested_bound_func];
|
||||
|
||||
for (func of functions) {
|
||||
var desc = Object.getOwnPropertyDescriptor(func, 'length');
|
||||
@ -121,3 +123,19 @@ var builtin_typedArrays = [
|
||||
assert(func.hasOwnProperty('length') === false);
|
||||
}
|
||||
})();
|
||||
|
||||
(function() {
|
||||
/* changing the length of f affects the bound function */
|
||||
function f(a,b,c) {}
|
||||
Object.defineProperty(f, "length", { value: 30 });
|
||||
var g = f.bind(1,2)
|
||||
assert(g.length === 29);
|
||||
})();
|
||||
|
||||
(function() {
|
||||
/* changing the length of f does not affect the bound function */
|
||||
function f(a,b,c) {}
|
||||
var g = f.bind(1,2)
|
||||
Object.defineProperty(f, "length", { value: 30 });
|
||||
assert(g.length === 2);
|
||||
})();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user