From: Alexander Borisov Date: Fri, 30 Apr 2021 13:02:34 +0000 (+0300) Subject: Introduced const implementation. X-Git-Tag: 0.6.0~29 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=9564eafd146bb4bd6f5d611130c618713fd065d6;p=njs.git Introduced const implementation. --- diff --git a/src/njs_builtin.c b/src/njs_builtin.c index abb6ceee..5b03e721 100644 --- a/src/njs_builtin.c +++ b/src/njs_builtin.c @@ -972,7 +972,7 @@ njs_global_this_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop, var = node->variable; - if (var->type == NJS_VARIABLE_LET) { + if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) { return NJS_DECLINED; } diff --git a/src/njs_disassembler.c b/src/njs_disassembler.c index 3a193871..acfc5e26 100644 --- a/src/njs_disassembler.c +++ b/src/njs_disassembler.c @@ -150,6 +150,9 @@ static njs_code_name_t code_names[] = { { NJS_VMCODE_NOT_INITIALIZED, sizeof(njs_vmcode_variable_t), njs_str("NOT INIT ") }, + + { NJS_VMCODE_ASSIGNMENT_ERROR, sizeof(njs_vmcode_variable_t), + njs_str("ASSIGNMENT ERROR") }, }; diff --git a/src/njs_generator.c b/src/njs_generator.c index 55f274f7..56cb6fe2 100644 --- a/src/njs_generator.c +++ b/src/njs_generator.c @@ -287,6 +287,7 @@ njs_generate(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) case NJS_TOKEN_VAR: case NJS_TOKEN_LET: + case NJS_TOKEN_CONST: return njs_generate_var_statement(vm, generator, node); case NJS_TOKEN_IF: @@ -640,7 +641,7 @@ njs_generate_name(njs_vm_t *vm, njs_generator_t *generator, return NJS_OK; } - if (var->type == NJS_VARIABLE_LET) { + if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) { scope = njs_function_scope(node->scope); if (scope->dest_disable) { @@ -692,7 +693,7 @@ njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator, return NJS_OK; } - if (var->type == NJS_VARIABLE_LET) { + if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) { scope = njs_function_scope(node->scope); if ((!scope->dest_disable && njs_function_scope(var->scope) == scope)) { @@ -746,6 +747,12 @@ njs_generate_var_statement(njs_vm_t *vm, njs_generator_t *generator, if (expr == NULL) { /* Variable is only declared. */ + + if (var->type == NJS_VARIABLE_CONST) { + njs_syntax_error(vm, "missing initializer in const declaration"); + return NJS_ERROR; + } + if (var->type == NJS_VARIABLE_LET) { ret = njs_generate_let(vm, generator, node, var); if (njs_slow_path(ret != NJS_OK)) { @@ -758,7 +765,7 @@ njs_generate_var_statement(njs_vm_t *vm, njs_generator_t *generator, return NJS_OK; } - if (var->type == NJS_VARIABLE_LET) { + if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) { ret = njs_generate_wo_dest(vm, generator, expr); if (njs_slow_path(ret != NJS_OK)) { return ret; @@ -1346,7 +1353,9 @@ njs_generate_for_let_update(njs_vm_t *vm, njs_generator_t *generator, let = node->right; - if (let->token_type != NJS_TOKEN_LET) { + if (let->token_type != NJS_TOKEN_LET + && let->token_type != NJS_TOKEN_CONST) + { return NJS_OK; } @@ -1827,7 +1836,9 @@ njs_generate_statement(njs_vm_t *vm, njs_generator_t *generator, goto statement; } - if (!var->init && var->type == NJS_VARIABLE_LET) { + if (!var->init && (var->type == NJS_VARIABLE_LET + || var->type == NJS_VARIABLE_CONST)) + { njs_generate_code(generator, njs_vmcode_variable_t, code, NJS_VMCODE_INITIALIZATION_TEST, 0, right); code->dst = right->index; @@ -1950,8 +1961,10 @@ njs_generate_assignment(njs_vm_t *vm, njs_generator_t *generator, { njs_int_t ret; njs_index_t index, src; + njs_variable_t *var; njs_parser_node_t *lvalue, *expr, *object, *property; njs_vmcode_move_t *move; + njs_vmcode_variable_t *var_code; njs_vmcode_prop_set_t *prop_set; lvalue = node->left; @@ -1961,11 +1974,19 @@ njs_generate_assignment(njs_vm_t *vm, njs_generator_t *generator, if (lvalue->token_type == NJS_TOKEN_NAME) { ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION, - NULL); + &var); if (njs_slow_path(ret != NJS_OK)) { return ret; } + if (var != NULL && var->type == NJS_VARIABLE_CONST) { + njs_generate_code(generator, njs_vmcode_variable_t, var_code, + NJS_VMCODE_ASSIGNMENT_ERROR, 0, node); + var_code->dst = var->index; + + return NJS_OK; + } + expr->dest = lvalue; ret = njs_generator(vm, generator, expr); @@ -2075,9 +2096,11 @@ njs_generate_operation_assignment(njs_vm_t *vm, njs_generator_t *generator, { njs_int_t ret; njs_index_t index, src; + njs_variable_t *var; njs_parser_node_t *lvalue, *expr, *object, *property; njs_vmcode_move_t *move; njs_vmcode_3addr_t *code; + njs_vmcode_variable_t *var_code; njs_vmcode_prop_get_t *prop_get; njs_vmcode_prop_set_t *prop_set; @@ -2086,11 +2109,19 @@ njs_generate_operation_assignment(njs_vm_t *vm, njs_generator_t *generator, if (lvalue->token_type == NJS_TOKEN_NAME) { ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION, - NULL); + &var); if (njs_slow_path(ret != NJS_OK)) { return ret; } + if (var != NULL && var->type == NJS_VARIABLE_CONST) { + njs_generate_code(generator, njs_vmcode_variable_t, var_code, + NJS_VMCODE_ASSIGNMENT_ERROR, 0, node); + var_code->dst = var->index; + + return NJS_OK; + } + index = lvalue->index; expr = node->right; @@ -2616,8 +2647,10 @@ njs_generate_inc_dec_operation(njs_vm_t *vm, njs_generator_t *generator, { njs_int_t ret; njs_index_t index, dest_index; + njs_variable_t *var; njs_parser_node_t *lvalue; njs_vmcode_3addr_t *code; + njs_vmcode_variable_t *var_code; njs_vmcode_prop_get_t *prop_get; njs_vmcode_prop_set_t *prop_set; @@ -2626,11 +2659,19 @@ njs_generate_inc_dec_operation(njs_vm_t *vm, njs_generator_t *generator, if (lvalue->token_type == NJS_TOKEN_NAME) { ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION, - NULL); + &var); if (njs_slow_path(ret != NJS_OK)) { return ret; } + if (var != NULL && var->type == NJS_VARIABLE_CONST) { + njs_generate_code(generator, njs_vmcode_variable_t, var_code, + NJS_VMCODE_ASSIGNMENT_ERROR, 0, node); + var_code->dst = var->index; + + return NJS_OK; + } + index = njs_generate_dest_index(vm, generator, node); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return index; diff --git a/src/njs_parser.c b/src/njs_parser.c index 616b832f..aae187da 100644 --- a/src/njs_parser.c +++ b/src/njs_parser.c @@ -4946,6 +4946,7 @@ static njs_int_t njs_parser_expression_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { + njs_token_type_t type; njs_lexer_token_t *next; switch (token->type) { @@ -4974,15 +4975,20 @@ njs_parser_expression_statement(njs_parser_t *parser, njs_lexer_token_t *token, break; + case NJS_TOKEN_CONST: case NJS_TOKEN_LET: + type = token->type; + token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_NAME) { - njs_parser_syntax_error(parser, "let declaration cannot appear " - "in a single-statement context"); + njs_parser_syntax_error(parser, "%s declaration cannot appear " + "in a single-statement context", + (type == NJS_TOKEN_CONST ? "const" + : "let" )); return NJS_DONE; } @@ -5307,6 +5313,7 @@ njs_parser_iteration_statement_for_map(njs_parser_t *parser, case NJS_TOKEN_VAR: case NJS_TOKEN_LET: + case NJS_TOKEN_CONST: token_type = token->type; token = njs_lexer_peek_token(parser->lexer, token, 0); @@ -5328,9 +5335,6 @@ njs_parser_iteration_statement_for_map(njs_parser_t *parser, break; - case NJS_TOKEN_CONST: - return njs_parser_not_supported(parser, token); - default: njs_parser_next(parser, njs_parser_expression); break; @@ -5368,6 +5372,10 @@ njs_parser_for_var_binding_or_var_list(njs_parser_t *parser, type = NJS_VARIABLE_LET; break; + case NJS_TOKEN_CONST: + type = NJS_VARIABLE_CONST; + break; + default: type = NJS_VARIABLE_VAR; break; diff --git a/src/njs_scope.h b/src/njs_scope.h index 6771e58b..94e58be5 100644 --- a/src/njs_scope.h +++ b/src/njs_scope.h @@ -82,7 +82,7 @@ njs_scope_valid_value(njs_vm_t *vm, njs_index_t index) value = njs_scope_value(vm, index); if (!njs_is_valid(value)) { - if (njs_scope_index_var(index) == NJS_VARIABLE_LET) { + if (njs_scope_index_var(index) <= NJS_VARIABLE_LET) { njs_reference_error(vm, "cannot access to variable " "before initialization"); return NULL; diff --git a/src/njs_variable.c b/src/njs_variable.c index 5a532fb3..f775aeff 100644 --- a/src/njs_variable.c +++ b/src/njs_variable.c @@ -184,6 +184,7 @@ njs_variable_scope_find(njs_parser_t *parser, njs_parser_scope_t *scope, } switch (type) { + case NJS_VARIABLE_CONST: case NJS_VARIABLE_LET: if (scope->type == NJS_SCOPE_GLOBAL && parser->undefined_id == unique_id) @@ -222,7 +223,7 @@ njs_variable_scope_find(njs_parser_t *parser, njs_parser_scope_t *scope, return root; } - if (var->type == NJS_VARIABLE_LET) { + if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) { goto failed; } diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index 1a99fc73..94788488 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -938,6 +938,10 @@ next: njs_vmcode_error(vm, pc); goto error; + case NJS_VMCODE_ASSIGNMENT_ERROR: + njs_type_error(vm, "assignment to constant variable"); + goto error; + default: njs_internal_error(vm, "%d has NO retval", op); goto error; diff --git a/src/njs_vmcode.h b/src/njs_vmcode.h index f7db9758..8a28d999 100644 --- a/src/njs_vmcode.h +++ b/src/njs_vmcode.h @@ -62,6 +62,7 @@ enum { NJS_VMCODE_LET_UPDATE, NJS_VMCODE_INITIALIZATION_TEST, NJS_VMCODE_NOT_INITIALIZED, + NJS_VMCODE_ASSIGNMENT_ERROR, NJS_VMCODE_ERROR, diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index f473dc4a..4e46e21b 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -9481,6 +9481,27 @@ static njs_unit_test_t njs_test[] = { njs_str("function static() {}"), njs_str("SyntaxError: Unexpected token \"static\" in 1") }, + { njs_str("var arr = [];" + "function fn(one) {" + " var x = one + 1;" + " let y = one + 2;" + " const u = one + 4;" + " {" + " {" + " let z = one + 3;" + " const v = one + 5;" + " function f() {" + " arr.push(one); arr.push(x);" + " arr.push(y); arr.push(z);" + " arr.push(u); arr.push(v);" + " }" + " f();" + " }" + " }" + "}" + "fn(1); arr"), + njs_str("1,2,3,4,5,6") }, + /* Recursive factorial. */ { njs_str("function f(a) {" @@ -20009,6 +20030,177 @@ static njs_unit_test_t njs_test[] = { njs_str("function static() {}"), njs_str("SyntaxError: Unexpected token \"static\" in 1") }, + + /* const */ + + { njs_str("const x"), + njs_str("SyntaxError: missing initializer in const declaration") }, + + { njs_str("const x = 1; x"), + njs_str("1") }, + + { njs_str("const x = 1; x = 1"), + njs_str("TypeError: assignment to constant variable") }, + + { njs_str("function abc() {const x}"), + njs_str("SyntaxError: missing initializer in const declaration") }, + + { njs_str("const x = [123]; x"), + njs_str("123") }, + + { njs_str("const x = () => x; x()"), + njs_str("[object Function]") }, + + { njs_str("const x = (() => x)()"), + njs_str("ReferenceError: cannot access to variable before initialization") }, + + { njs_str("x; const x = 123"), + njs_str("ReferenceError: cannot access to variable before initialization") }, + + { njs_str("const x = x + 123"), + njs_str("ReferenceError: cannot access to variable before initialization") }, + + { njs_str("const x; var x"), + njs_str("SyntaxError: \"x\" has already been declared in 1") }, + + { njs_str("const x; let x"), + njs_str("SyntaxError: \"x\" has already been declared in 1") }, + + { njs_str("let x; const x"), + njs_str("SyntaxError: \"x\" has already been declared in 1") }, + + { njs_str("const x = 1; function x() {}"), + njs_str("SyntaxError: \"x\" has already been declared in 1") }, + + { njs_str("function x() {} const x = 1"), + njs_str("SyntaxError: \"x\" has already been declared in 1") }, + + { njs_str("function x() {const x; var x}"), + njs_str("SyntaxError: \"x\" has already been declared in 1") }, + + { njs_str("function x() {var x; const x}"), + njs_str("SyntaxError: \"x\" has already been declared in 1") }, + + { njs_str("const x = function f() {const f = 1}"), + njs_str("undefined") }, + + { njs_str("let res; const x = 1;" + "{const x = 2; res = x}" + "[x, res]"), + njs_str("1,2") }, + + { njs_str("let res; const x = 1;" + "if (true) {const x = 2; res = x}" + "[x, res]"), + njs_str("1,2") }, + + { njs_str("function func() {return x}" + "const x = 123;" + "func()"), + njs_str("123") }, + + { njs_str("function func() {return x}" + "func();" + "const x = 123"), + njs_str("ReferenceError: cannot access to variable before initialization") }, + + { njs_str("function func() {return () => x}" + "const x = 123;" + "func()()"), + njs_str("123") }, + + { njs_str("function func() {return () => x++}" + "const x = 123;" + "func()()"), + njs_str("TypeError: assignment to constant variable") }, + + { njs_str("for (const i = 0; i < 1; i++) {}"), + njs_str("TypeError: assignment to constant variable") }, + + { njs_str("let res = [];" + "for (const n in [1,2,3]) {res.push(n)}" + "res"), + njs_str("0,1,2") }, + + { njs_str("let arr = [], res = [];" + "" + "for (const n in [1,2,3]) {" + " arr.push(() => n);" + "}" + "" + "for (let n in arr) {" + " res.push(arr[n]());" + "}" + "res"), + njs_str("0,1,2") }, + + { njs_str("let arr = [];" + "" + "for (const n in [1,2,3]) {" + " let n = 1;" + " arr.push(n);" + "}" + "arr"), + njs_str("1,1,1") }, + + { njs_str("for (const n in [1,2,3]) {" + " let n = n + 1;" + "}"), + njs_str("ReferenceError: cannot access to variable before initialization") }, + + { njs_str("for (const n in [1,2,3]) {}" + "n"), + njs_str("ReferenceError: \"n\" is not defined") }, + + { njs_str("for (const n in [1,n,3]) {}"), + njs_str("ReferenceError: cannot access to variable before initialization") }, + + { njs_str("(function() {" + "function f() {return x + 1}" + "function abc() {f()};" + "abc();" + "const x = 1;" + "}())"), + njs_str("ReferenceError: cannot access to variable before initialization") }, + + { njs_str("if (false) const x = 1"), + njs_str("SyntaxError: const declaration cannot appear in a single-statement context in 1") }, + + { njs_str("while (false) const x = 1"), + njs_str("SyntaxError: const declaration cannot appear in a single-statement context in 1") }, + + { njs_str("for (;;) const x = 1"), + njs_str("SyntaxError: const declaration cannot appear in a single-statement context in 1") }, + + { njs_str("try {} catch (e) {const e = 1}"), + njs_str("SyntaxError: \"e\" has already been declared in 1") }, + + { njs_str("let arr = []; const x = 2;" + "switch(true) {default: const x = 1; arr.push(x)}" + "arr.push(x); arr"), + njs_str("1,2") }, + + { njs_str("let res;" + "switch(true) {case true: const x = 1; default: x = 2; res = x} res"), + njs_str("TypeError: assignment to constant variable") }, + + { njs_str("const null"), + njs_str("SyntaxError: Unexpected token \"null\" in 1") }, + + { njs_str("const continue"), + njs_str("SyntaxError: Unexpected token \"continue\" in 1") }, + + { njs_str("const undefined"), + njs_str("SyntaxError: \"undefined\" has already been declared in 1") }, + + { njs_str("const a = 1; globalThis.a"), + njs_str("undefined") }, + + { njs_str("if (false) {x = 2} else {x = 1} const x = 0"), + njs_str("ReferenceError: cannot access to variable before initialization") }, + + { njs_str("const const"), + njs_str("SyntaxError: Unexpected token \"const\" in 1") }, };