[njs] Introduced const implementation.
Alexander Borisov
alexander.borisov at nginx.com
Fri Apr 30 13:04:47 UTC 2021
details: https://hg.nginx.org/njs/rev/f58a06be33dc
branches:
changeset: 1632:f58a06be33dc
user: Alexander Borisov <alexander.borisov at nginx.com>
date: Fri Apr 30 16:02:34 2021 +0300
description:
Introduced const implementation.
diffstat:
src/njs_builtin.c | 2 +-
src/njs_disassembler.c | 3 +
src/njs_generator.c | 57 +++++++++++-
src/njs_parser.c | 18 +++-
src/njs_scope.h | 2 +-
src/njs_variable.c | 3 +-
src/njs_vmcode.c | 4 +
src/njs_vmcode.h | 1 +
src/test/njs_unit_test.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 266 insertions(+), 16 deletions(-)
diffs (524 lines):
diff -r 27a4a52621d5 -r f58a06be33dc src/njs_builtin.c
--- a/src/njs_builtin.c Fri Apr 30 16:02:28 2021 +0300
+++ b/src/njs_builtin.c Fri Apr 30 16:02:34 2021 +0300
@@ -972,7 +972,7 @@ njs_global_this_prop_handler(njs_vm_t *v
var = node->variable;
- if (var->type == NJS_VARIABLE_LET) {
+ if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) {
return NJS_DECLINED;
}
diff -r 27a4a52621d5 -r f58a06be33dc src/njs_disassembler.c
--- a/src/njs_disassembler.c Fri Apr 30 16:02:28 2021 +0300
+++ b/src/njs_disassembler.c Fri Apr 30 16:02:34 2021 +0300
@@ -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 -r 27a4a52621d5 -r f58a06be33dc src/njs_generator.c
--- a/src/njs_generator.c Fri Apr 30 16:02:28 2021 +0300
+++ b/src/njs_generator.c Fri Apr 30 16:02:34 2021 +0300
@@ -287,6 +287,7 @@ njs_generate(njs_vm_t *vm, njs_generator
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_gene
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_
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,
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,
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
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
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, nj
{
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, nj
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
{
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
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
{
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
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 -r 27a4a52621d5 -r f58a06be33dc src/njs_parser.c
--- a/src/njs_parser.c Fri Apr 30 16:02:28 2021 +0300
+++ b/src/njs_parser.c Fri Apr 30 16:02:34 2021 +0300
@@ -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_pars
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(n
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(n
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(n
type = NJS_VARIABLE_LET;
break;
+ case NJS_TOKEN_CONST:
+ type = NJS_VARIABLE_CONST;
+ break;
+
default:
type = NJS_VARIABLE_VAR;
break;
diff -r 27a4a52621d5 -r f58a06be33dc src/njs_scope.h
--- a/src/njs_scope.h Fri Apr 30 16:02:28 2021 +0300
+++ b/src/njs_scope.h Fri Apr 30 16:02:34 2021 +0300
@@ -82,7 +82,7 @@ njs_scope_valid_value(njs_vm_t *vm, njs_
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 -r 27a4a52621d5 -r f58a06be33dc src/njs_variable.c
--- a/src/njs_variable.c Fri Apr 30 16:02:28 2021 +0300
+++ b/src/njs_variable.c Fri Apr 30 16:02:34 2021 +0300
@@ -184,6 +184,7 @@ njs_variable_scope_find(njs_parser_t *pa
}
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 *pa
return root;
}
- if (var->type == NJS_VARIABLE_LET) {
+ if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) {
goto failed;
}
diff -r 27a4a52621d5 -r f58a06be33dc src/njs_vmcode.c
--- a/src/njs_vmcode.c Fri Apr 30 16:02:28 2021 +0300
+++ b/src/njs_vmcode.c Fri Apr 30 16:02:34 2021 +0300
@@ -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 -r 27a4a52621d5 -r f58a06be33dc src/njs_vmcode.h
--- a/src/njs_vmcode.h Fri Apr 30 16:02:28 2021 +0300
+++ b/src/njs_vmcode.h Fri Apr 30 16:02:34 2021 +0300
@@ -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 -r 27a4a52621d5 -r f58a06be33dc src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Fri Apr 30 16:02:28 2021 +0300
+++ b/src/test/njs_unit_test.c Fri Apr 30 16:02:34 2021 +0300
@@ -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") },
};
More information about the nginx-devel
mailing list