[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