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:
Hyukwoo Park 2020-04-04 09:04:02 +09:00 committed by GitHub
parent e470b13096
commit 73a78bd223
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 153 additions and 58 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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