From 47d85a12e2c07a07169a1de0cc56d2d26f077835 Mon Sep 17 00:00:00 2001 From: Hyukwoo Park Date: Thu, 12 Mar 2020 23:00:53 +0900 Subject: [PATCH] Fix attributes of length property for function objects (#3570) fix length property of 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 --- .../ecma/operations/ecma-function-object.c | 44 +++++++++++++++++++ jerry-core/ecma/operations/ecma-objects.c | 13 ++++++ tests/jerry/es2015/length-property.js | 21 +++++++++ .../es2015/regression-test-issue-3267.js | 2 +- .../es2015/regression-test-issue-3536.js | 2 +- 5 files changed, 80 insertions(+), 2 deletions(-) diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index e5699b5c3..9a5a7b425 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -1658,6 +1658,41 @@ ecma_op_function_try_to_lazy_instantiate_property (ecma_object_t *object_p, /**< { JERRY_ASSERT (!ecma_get_object_is_builtin (object_p)); +#if ENABLED (JERRY_ES2015) + if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_LENGTH)) + { + ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; + if (!ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (ext_func_p->u.function.scope_cp)) + { + /* Initialize 'length' property */ + const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p); + uint32_t len; + if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_data_p; + len = args_p->argument_end; + } + else + { + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_data_p; + len = args_p->argument_end; + } + + /* Set tag bit to represent initialized 'length' property */ + ECMA_SET_FIRST_BIT_TO_POINTER_TAG (ext_func_p->u.function.scope_cp); + ecma_property_t *value_prop_p; + ecma_property_value_t *value_p = ecma_create_named_data_property (object_p, + property_name_p, + ECMA_PROPERTY_FLAG_CONFIGURABLE, + &value_prop_p); + value_p->value = ecma_make_uint32_value (len); + return value_prop_p; + } + + return NULL; + } +#endif /* ENABLED (JERRY_ES2015) */ + if (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING_PROTOTYPE) && ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_FUNCTION) { @@ -1824,8 +1859,17 @@ ecma_op_function_list_lazy_property_names (ecma_object_t *object_p, /**< functio ecma_collection_t *for_non_enumerable_p = (opts & ECMA_LIST_ENUMERABLE) ? non_enum_collection_p : main_collection_p; +#if ENABLED (JERRY_ES2015) + ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; + if (!ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (ext_func_p->u.function.scope_cp)) + { + /* Unintialized 'length' property is non-enumerable (ECMA-262 v6, 19.2.4.1) */ + ecma_collection_push_back (for_non_enumerable_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_LENGTH)); + } +#else /* !ENABLED (JERRY_ES2015) */ /* '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) */ const ecma_compiled_code_t *bytecode_data_p; bytecode_data_p = ecma_op_function_get_compiled_code ((ecma_extended_object_t *) object_p); diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index 9629d3a99..991a3f2a2 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -249,6 +249,7 @@ ecma_op_object_get_own_property (ecma_object_t *object_p, /**< the object */ } else if (type == ECMA_OBJECT_TYPE_FUNCTION) { +#if !ENABLED (JERRY_ES2015) if (ecma_string_is_length (property_name_p)) { if (options & ECMA_PROPERTY_GET_VALUE) @@ -274,6 +275,7 @@ ecma_op_object_get_own_property (ecma_object_t *object_p, /**< the object */ return ECMA_PROPERTY_TYPE_VIRTUAL; } +#endif /* !ENABLED (JERRY_ES2015) */ /* Get prototype physical property. */ property_p = ecma_op_function_try_to_lazy_instantiate_property (object_p, property_name_p); @@ -594,6 +596,7 @@ ecma_op_object_find_own (ecma_value_t base_value, /**< base value */ } else if (type == ECMA_OBJECT_TYPE_FUNCTION) { +#if !ENABLED (JERRY_ES2015) if (ecma_string_is_length (property_name_p)) { /* Get length virtual property. */ @@ -614,6 +617,7 @@ ecma_op_object_find_own (ecma_value_t base_value, /**< base value */ return ecma_make_uint32_value (len); } +#endif /* !ENABLED (JERRY_ES2015) */ /* Get prototype physical property. */ property_p = ecma_op_function_try_to_lazy_instantiate_property (object_p, property_name_p); @@ -1322,10 +1326,19 @@ ecma_op_object_put_with_receiver (ecma_object_t *object_p, /**< the object */ } else if (type == ECMA_OBJECT_TYPE_FUNCTION) { +#if ENABLED (JERRY_ES2015) + /* Uninitialized 'length' property is non-writable (ECMA-262 v6, 19.2.4.1) */ + if ((ecma_string_is_length (property_name_p)) + && (!ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (((ecma_extended_object_t *) object_p)->u.function.scope_cp))) + { + return ecma_reject (is_throw); + } +#else /* !ENABLED (JERRY_ES2015) */ if (ecma_string_is_length (property_name_p)) { return ecma_reject (is_throw); } +#endif /* ENABLED (JERRY_ES2015) */ /* Get prototype physical property. */ property_p = ecma_op_function_try_to_lazy_instantiate_property (object_p, property_name_p); diff --git a/tests/jerry/es2015/length-property.js b/tests/jerry/es2015/length-property.js index 9625aafa6..448cad230 100644 --- a/tests/jerry/es2015/length-property.js +++ b/tests/jerry/es2015/length-property.js @@ -100,3 +100,24 @@ var builtin_typedArrays = [ assert(ta.hasOwnProperty('length') === false); } })(); + +(function () { + /* test length property of function objects */ + var normal_func = function () {}; + var arrow_func = () => {}; + + var functions = [normal_func, arrow_func]; + + for (func of functions) { + var desc = Object.getOwnPropertyDescriptor(func, 'length'); + assert(desc.writable === false); + assert(desc.enumerable === false); + assert(desc.configurable === true); + } + + for (func of functions) { + assert(func.hasOwnProperty('length') === true); + assert(delete func.length); + assert(func.hasOwnProperty('length') === false); + } +})(); diff --git a/tests/jerry/es2015/regression-test-issue-3267.js b/tests/jerry/es2015/regression-test-issue-3267.js index 3e574fdb9..2f0f92b4e 100644 --- a/tests/jerry/es2015/regression-test-issue-3267.js +++ b/tests/jerry/es2015/regression-test-issue-3267.js @@ -14,7 +14,7 @@ var hasProp = $ => { } Object.preventExtensions(hasProp); -assert (Object.isSealed(hasProp)); +assert (Object.isSealed(hasProp) === false); var keys = Object.getOwnPropertyNames(hasProp); assert (keys.length === 1); diff --git a/tests/jerry/es2015/regression-test-issue-3536.js b/tests/jerry/es2015/regression-test-issue-3536.js index 7ac6a91ed..2a996411d 100644 --- a/tests/jerry/es2015/regression-test-issue-3536.js +++ b/tests/jerry/es2015/regression-test-issue-3536.js @@ -16,7 +16,7 @@ class A { constructor() { var hasProp = $ => {} Object.preventExtensions(hasProp); - assert(Object.isSealed(hasProp) === true); + assert(Object.isSealed(hasProp) === false); } super() { $: $