From 36bf1ec09dda339b9ddd8f009f1fba1c2ee3b295 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Mon, 8 Jun 2020 13:43:50 +0200 Subject: [PATCH] Implement argument redefinition with var statement. (#3811) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/include/jerryscript-snapshot.h | 2 +- jerry-core/parser/js/byte-code.c | 2 +- jerry-core/parser/js/byte-code.h | 2 + jerry-core/parser/js/js-scanner-util.c | 60 ++++++++++++++++++++--- jerry-core/parser/js/js-scanner.c | 10 ++++ jerry-core/parser/js/js-scanner.h | 7 ++- jerry-core/vm/vm.c | 29 +++++++++++ jerry-core/vm/vm.h | 2 + tests/jerry/es2015/function-scope2.js | 52 ++++++++++++++++++++ 9 files changed, 155 insertions(+), 11 deletions(-) diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 15871f5be..ab236b3db 100644 --- a/jerry-core/include/jerryscript-snapshot.h +++ b/jerry-core/include/jerryscript-snapshot.h @@ -30,7 +30,7 @@ extern "C" /** * Jerry snapshot format version. */ -#define JERRY_SNAPSHOT_VERSION (47u) +#define JERRY_SNAPSHOT_VERSION (48u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/parser/js/byte-code.c b/jerry-core/parser/js/byte-code.c index 1acadd6ea..cbfe79474 100644 --- a/jerry-core/parser/js/byte-code.c +++ b/jerry-core/parser/js/byte-code.c @@ -27,7 +27,7 @@ JERRY_STATIC_ASSERT ((sizeof (cbc_uint16_arguments_t) % sizeof (jmem_cpointer_t) */ JERRY_STATIC_ASSERT (CBC_END == 238, number_of_cbc_opcodes_changed); -JERRY_STATIC_ASSERT (CBC_EXT_END == 118, +JERRY_STATIC_ASSERT (CBC_EXT_END == 119, number_of_cbc_ext_opcodes_changed); #if ENABLED (JERRY_PARSER) diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index c5e4510d6..1eb4cf64f 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -570,6 +570,8 @@ VM_OC_EXT_VAR_EVAL) \ CBC_OPCODE (CBC_EXT_CREATE_VAR_FUNC_EVAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, \ VM_OC_EXT_VAR_EVAL) \ + CBC_OPCODE (CBC_EXT_COPY_FROM_ARG, CBC_HAS_LITERAL_ARG, 0, \ + VM_OC_COPY_FROM_ARG) \ CBC_OPCODE (CBC_EXT_STRING_CONCAT, CBC_NO_FLAG, -1, \ VM_OC_STRING_CONCAT | VM_OC_GET_STACK_STACK | VM_OC_PUT_STACK) \ CBC_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL, CBC_HAS_LITERAL_ARG, 0, \ diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c index be6766bdf..2e401d0dd 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -148,7 +148,9 @@ scanner_get_stream_size (scanner_info_t *info_p, /**< scanner info block */ #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ case SCANNER_STREAM_TYPE_ARG: #if ENABLED (JERRY_ES2015) + case SCANNER_STREAM_TYPE_ARG_VAR: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: + case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR: #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ case SCANNER_STREAM_TYPE_ARG_FUNC: #if ENABLED (JERRY_ES2015) @@ -555,7 +557,12 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ continue; } - type = (uint8_t) ((type & ~SCANNER_LITERAL_IS_FUNC) | SCANNER_LITERAL_IS_VAR); + if (!(type & SCANNER_LITERAL_IS_ARG)) + { + type |= SCANNER_LITERAL_IS_VAR; + } + + type &= (uint8_t) ~SCANNER_LITERAL_IS_FUNC; literal_p->type = type; } #endif /* ENABLED (JERRY_ES2015) */ @@ -802,6 +809,14 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ { type = SCANNER_STREAM_TYPE_DESTRUCTURED_ARG; } + + if (literal_p->type & SCANNER_LITERAL_IS_VAR) + { + type = (uint8_t) (type + 1); + + JERRY_ASSERT (type == SCANNER_STREAM_TYPE_ARG_VAR + || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR); + } #endif /* ENABLED (JERRY_ES2015) */ } #if ENABLED (JERRY_ES2015) @@ -1731,7 +1746,9 @@ scanner_is_context_needed (parser_context_t *context_p, /**< context */ || type == SCANNER_STREAM_TYPE_CONST || type == SCANNER_STREAM_TYPE_LOCAL || type == SCANNER_STREAM_TYPE_ARG + || type == SCANNER_STREAM_TYPE_ARG_VAR || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG + || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR || type == SCANNER_STREAM_TYPE_ARG_FUNC || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC || type == SCANNER_STREAM_TYPE_FUNC); @@ -1769,16 +1786,13 @@ scanner_is_context_needed (parser_context_t *context_p, /**< context */ if (JERRY_UNLIKELY (check_type == PARSER_CHECK_FUNCTION_CONTEXT)) { - if (SCANNER_STREAM_TYPE_IS_ARG (type)) - { - continue; - } - - if (SCANNER_STREAM_TYPE_IS_ARG_FUNC (type)) + if (SCANNER_STREAM_TYPE_IS_ARG_FUNC (type) + || type == SCANNER_STREAM_TYPE_ARG_VAR + || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR) { /* The return value is true, if the variable is stored in the lexical environment * or all registers have already been used for function arguments. This can be - * inprecise in the latter case, but that is a very rare corner case. A more + * inprecise in the latter case, but this is a very rare corner case. A more * sophisticated check would require to decode the literal. */ if ((data & SCANNER_STREAM_NO_REG) || scope_stack_reg_top >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS) @@ -1787,6 +1801,11 @@ scanner_is_context_needed (parser_context_t *context_p, /**< context */ } continue; } + + if (SCANNER_STREAM_TYPE_IS_ARG (type)) + { + continue; + } } #endif /* ENABLED (JERRY_ES2015) */ @@ -2072,6 +2091,24 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ { if (option_flags & SCANNER_CREATE_VARS_IS_FUNCTION_BODY) { +#if ENABLED (JERRY_ES2015) + if ((context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED) + && (type == SCANNER_STREAM_TYPE_ARG_VAR || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR)) + { + literal.length = data_p[1]; + literal.type = LEXER_IDENT_LITERAL; + literal.has_escape = (data_p[0] & SCANNER_STREAM_HAS_ESCAPE) ? 1 : 0; + + /* Literal must be exists. */ + lexer_construct_literal_object (context_p, &literal, LEXER_IDENT_LITERAL); + + if (context_p->lit_object.index < PARSER_REGISTER_START) + { + parser_emit_cbc_ext_literal_from_token (context_p, CBC_EXT_COPY_FROM_ARG); + } + } +#endif /* ENABLED (JERRY_ES2015) */ + literal.char_p += data_p[1]; continue; } @@ -2174,7 +2211,9 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ } case SCANNER_STREAM_TYPE_LET: case SCANNER_STREAM_TYPE_ARG: + case SCANNER_STREAM_TYPE_ARG_VAR: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: + case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR: case SCANNER_STREAM_TYPE_ARG_FUNC: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC: { @@ -2208,6 +2247,7 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ case SCANNER_STREAM_TYPE_LET: case SCANNER_STREAM_TYPE_CONST: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: + case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR: case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC: { scope_stack_map_to |= PARSER_SCOPE_STACK_NO_FUNCTION_COPY; @@ -2261,6 +2301,7 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (type == SCANNER_STREAM_TYPE_LOCAL || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG + || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR || type == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC); opcode = CBC_CREATE_LOCAL; @@ -2276,6 +2317,9 @@ scanner_create_variables (parser_context_t *context_p, /**< context */ break; } case SCANNER_STREAM_TYPE_ARG: +#if ENABLED (JERRY_ES2015) + case SCANNER_STREAM_TYPE_ARG_VAR: +#endif /* ENABLED (JERRY_ES2015) */ case SCANNER_STREAM_TYPE_ARG_FUNC: { #if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 4dcc677da..1df226971 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -3301,11 +3301,21 @@ scan_completed: break; } #if ENABLED (JERRY_ES2015) + case SCANNER_STREAM_TYPE_ARG_VAR: + { + JERRY_DEBUG_MSG (" ARG_VAR "); + break; + } case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG: { JERRY_DEBUG_MSG (" DESTRUCTURED_ARG "); break; } + case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR: + { + JERRY_DEBUG_MSG (" DESTRUCTURED_ARG_VAR "); + break; + } #endif /* ENABLED (JERRY_ES2015) */ case SCANNER_STREAM_TYPE_ARG_FUNC: { diff --git a/jerry-core/parser/js/js-scanner.h b/jerry-core/parser/js/js-scanner.h index 675d3a75b..c2b08e172 100644 --- a/jerry-core/parser/js/js-scanner.h +++ b/jerry-core/parser/js/js-scanner.h @@ -154,9 +154,14 @@ typedef enum #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) SCANNER_STREAM_TYPE_IMPORT, /**< module import */ #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ + /* The next four types must be in this order (see SCANNER_STREAM_TYPE_IS_ARG). */ SCANNER_STREAM_TYPE_ARG, /**< argument declaration */ #if ENABLED (JERRY_ES2015) + SCANNER_STREAM_TYPE_ARG_VAR, /**< argument declaration which is later copied + * into a variable declared by var statement */ SCANNER_STREAM_TYPE_DESTRUCTURED_ARG, /**< destructuring argument declaration */ + SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR, /**< destructuring argument declaration which is later + * copied into a variable declared by var statement */ #endif /* ENABLED (JERRY_ES2015) */ /* Function types should be at the end. See the SCANNER_STREAM_TYPE_IS_FUNCTION macro. */ SCANNER_STREAM_TYPE_ARG_FUNC, /**< argument declaration which @@ -184,7 +189,7 @@ typedef enum * Checks whether the decoded type represents a function argument. */ #define SCANNER_STREAM_TYPE_IS_ARG(type) \ - ((type) == SCANNER_STREAM_TYPE_ARG || (type) == SCANNER_STREAM_TYPE_DESTRUCTURED_ARG) + ((type) >= SCANNER_STREAM_TYPE_ARG && (type) <= SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_VAR) /** * Checks whether the decoded type represents both a function argument and a function declaration. diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 3079852f2..ad0cf1654 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -1686,6 +1686,35 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ goto free_left_value; } + case VM_OC_COPY_FROM_ARG: + { + uint32_t literal_index; + READ_LITERAL_INDEX (literal_index); + JERRY_ASSERT (literal_index >= register_end); + + ecma_string_t *name_p = ecma_get_string_from_value (literal_start_p[literal_index]); + ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p; + ecma_object_t *arg_lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + + JERRY_ASSERT ((lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) + && ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); + JERRY_ASSERT (arg_lex_env_p != NULL + && !(arg_lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK) + && ecma_get_lex_env_type (arg_lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); + + ecma_property_value_t *property_value_p; + property_value_p = ecma_create_named_data_property (lex_env_p, + name_p, + ECMA_PROPERTY_FLAG_WRITABLE, + NULL); + + ecma_property_t *property_p = ecma_find_named_property (arg_lex_env_p, name_p); + JERRY_ASSERT (property_p != NULL); + + ecma_property_value_t *arg_prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); + property_value_p->value = ecma_copy_value_if_not_object (arg_prop_value_p->value); + continue; + } case VM_OC_CLONE_CONTEXT: { JERRY_ASSERT (byte_code_start_p[0] == CBC_EXT_OPCODE); diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 1b3f64dc8..09332676d 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -242,6 +242,7 @@ typedef enum VM_OC_INIT_BINDING, /**< create and intialize a binding */ VM_OC_THROW_CONST_ERROR, /**< throw invalid assignment to const variable error */ VM_OC_COPY_TO_GLOBAL, /**< copy value to global lex env */ + VM_OC_COPY_FROM_ARG, /**< copy value from arg lex env */ VM_OC_CLONE_CONTEXT, /**< clone lexical environment with let/const declarations */ VM_OC_SET_COMPUTED_PROPERTY, /**< set computed property */ @@ -310,6 +311,7 @@ typedef enum VM_OC_INIT_BINDING = VM_OC_NONE, /**< create and intialize a binding */ VM_OC_THROW_CONST_ERROR = VM_OC_NONE, /**< throw invalid assignment to const variable error */ VM_OC_COPY_TO_GLOBAL = VM_OC_NONE, /**< copy value to global lex env */ + VM_OC_COPY_FROM_ARG = VM_OC_NONE, /**< copy value from arg lex env */ VM_OC_CLONE_CONTEXT = VM_OC_NONE, /**< clone lexical environment with let/const declarations */ VM_OC_SET_COMPUTED_PROPERTY = VM_OC_NONE, /**< set computed property is unused */ diff --git a/tests/jerry/es2015/function-scope2.js b/tests/jerry/es2015/function-scope2.js index a35ca1f12..6f8643691 100644 --- a/tests/jerry/es2015/function-scope2.js +++ b/tests/jerry/es2015/function-scope2.js @@ -43,3 +43,55 @@ function f(x = eval("eval('var a = 3; function b() { return 4 } () => a')"), y = assert(y() === 4); } f() + +function g() { + 'use strict' + + function h(x, y = function() { return x }) { + var x = 2; + + /* This should not overwrite y. */ + eval("var y = 3; assert (y === 3)"); + + assert(x === 2); + assert(typeof y === "function"); + assert(y() === 1); + } + h(1); +} +g(); + +function h(a, get = () => a, set = (v) => a = v) { + assert(a === 1); + + var a = 2; + + assert(a === 2); + assert(get() === 1); + + set(3) + a = 4; + + assert(a === 4); + assert(get() === 3); +} +h(1); + +function i([a], get = () => a, set = (v) => a = v) { + assert(a === 1); + + var a; + assert(a === 1); + + a = 2; + + assert(a === 2); + assert(get() === 1); + + set(3) + a = 4; + + assert(a === 4); + assert(get() === 3); +} +i([1]);