[njs] Introduced let implementation.

Alexander Borisov alexander.borisov at nginx.com
Fri Apr 30 13:04:45 UTC 2021


details:   https://hg.nginx.org/njs/rev/27a4a52621d5
branches:  
changeset: 1631:27a4a52621d5
user:      Alexander Borisov <alexander.borisov at nginx.com>
date:      Fri Apr 30 16:02:28 2021 +0300
description:
Introduced let implementation.

This closes #105 issue on GitHub.

diffstat:

 src/njs_builtin.c        |    4 +
 src/njs_disassembler.c   |   11 +
 src/njs_generator.c      |  336 +++++++++++++++++++++++++++++++++++++++++++---
 src/njs_module.c         |    3 +-
 src/njs_parser.c         |  224 +++++++++++++++++++++++++-----
 src/njs_parser.h         |    5 +
 src/njs_scope.c          |    6 +-
 src/njs_scope.h          |   32 +++-
 src/njs_variable.c       |   56 ++++++-
 src/njs_variable.h       |    3 +-
 src/njs_vmcode.c         |  117 ++++++++++++---
 src/njs_vmcode.h         |   12 +
 src/test/njs_benchmark.c |   11 +
 src/test/njs_unit_test.c |  237 +++++++++++++++++++++++++++++++++
 14 files changed, 942 insertions(+), 115 deletions(-)

diffs (truncated from 1982 to 1000 lines):

diff -r 28f33155ad3f -r 27a4a52621d5 src/njs_builtin.c
--- a/src/njs_builtin.c	Fri Apr 30 16:02:20 2021 +0300
+++ b/src/njs_builtin.c	Fri Apr 30 16:02:28 2021 +0300
@@ -972,6 +972,10 @@ njs_global_this_prop_handler(njs_vm_t *v
 
     var = node->variable;
 
+    if (var->type == NJS_VARIABLE_LET) {
+        return NJS_DECLINED;
+    }
+
     value = njs_scope_valid_value(vm, var->index);
 
     if (var->type == NJS_VARIABLE_FUNCTION && njs_is_undefined(value)) {
diff -r 28f33155ad3f -r 27a4a52621d5 src/njs_disassembler.c
--- a/src/njs_disassembler.c	Fri Apr 30 16:02:20 2021 +0300
+++ b/src/njs_disassembler.c	Fri Apr 30 16:02:28 2021 +0300
@@ -139,6 +139,17 @@ static njs_code_name_t  code_names[] = {
     { NJS_VMCODE_THROW, sizeof(njs_vmcode_throw_t),
           njs_str("THROW           ") },
 
+    { NJS_VMCODE_LET, sizeof(njs_vmcode_variable_t),
+          njs_str("LET             ") },
+
+    { NJS_VMCODE_LET_UPDATE, sizeof(njs_vmcode_variable_t),
+          njs_str("LET UPDATE      ") },
+
+    { NJS_VMCODE_INITIALIZATION_TEST, sizeof(njs_vmcode_variable_t),
+          njs_str("INIT TEST       ") },
+
+    { NJS_VMCODE_NOT_INITIALIZED, sizeof(njs_vmcode_variable_t),
+          njs_str("NOT INIT        ") },
 };
 
 
diff -r 28f33155ad3f -r 27a4a52621d5 src/njs_generator.c
--- a/src/njs_generator.c	Fri Apr 30 16:02:20 2021 +0300
+++ b/src/njs_generator.c	Fri Apr 30 16:02:28 2021 +0300
@@ -67,6 +67,8 @@ static njs_int_t njs_generate_variable(n
     njs_variable_t **retvar);
 static njs_int_t njs_generate_var_statement(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_let(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node, njs_variable_t *var);
 static njs_int_t njs_generate_if_statement(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_cond_expression(njs_vm_t *vm,
@@ -79,6 +81,10 @@ static njs_int_t njs_generate_do_while_s
     njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_for_statement(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_for_let_update(njs_vm_t *vm,
+    njs_generator_t *generator, njs_parser_node_t *node, size_t depth);
+static njs_int_t njs_generate_for_resolve_closure(njs_vm_t *vm,
+    njs_parser_node_t *node, size_t depth);
 static njs_int_t njs_generate_for_in_statement(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_start_block(njs_vm_t *vm,
@@ -261,6 +267,9 @@ static njs_int_t njs_generate_reference_
                           ##__VA_ARGS__)
 
 
+#define NJS_GENERATE_MAX_DEPTH  4096
+
+
 static const njs_str_t  no_label     = njs_str("");
 static const njs_str_t  return_label = njs_str("@return");
 /* GCC and Clang complain about NULL argument passed to memcmp(). */
@@ -277,6 +286,7 @@ njs_generate(njs_vm_t *vm, njs_generator
     switch (node->token_type) {
 
     case NJS_TOKEN_VAR:
+    case NJS_TOKEN_LET:
         return njs_generate_var_statement(vm, generator, node);
 
     case NJS_TOKEN_IF:
@@ -479,7 +489,7 @@ njs_generator(njs_vm_t *vm, njs_generato
 {
     njs_int_t  ret;
 
-    if (njs_slow_path(generator->count++ > 4096)) {
+    if (njs_slow_path(generator->count++ > NJS_GENERATE_MAX_DEPTH)) {
         njs_range_error(vm, "Maximum call stack size exceeded");
         return NJS_ERROR;
     }
@@ -492,6 +502,25 @@ njs_generator(njs_vm_t *vm, njs_generato
 }
 
 
+static njs_int_t
+njs_generate_wo_dest(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node)
+{
+    njs_int_t           ret;
+    njs_parser_scope_t  *scope;
+
+    scope = njs_function_scope(node->scope);
+
+    scope->dest_disable = 1;
+
+    ret = njs_generator(vm, generator, node);
+
+    scope->dest_disable = 0;
+
+    return ret;
+}
+
+
 static u_char *
 njs_generate_reserve(njs_vm_t *vm, njs_generator_t *generator, size_t size)
 {
@@ -591,6 +620,8 @@ njs_generate_name(njs_vm_t *vm, njs_gene
     njs_parser_node_t *node)
 {
     njs_variable_t              *var;
+    njs_parser_scope_t          *scope;
+    njs_vmcode_variable_t       *variable;
     njs_vmcode_function_copy_t  *copy;
 
     var = njs_variable_reference(vm, node);
@@ -605,6 +636,20 @@ njs_generate_name(njs_vm_t *vm, njs_gene
         copy->retval = node->index;
     }
 
+    if (var->init) {
+        return NJS_OK;
+    }
+
+    if (var->type == NJS_VARIABLE_LET) {
+        scope = njs_function_scope(node->scope);
+
+        if (scope->dest_disable) {
+            njs_generate_code(generator, njs_vmcode_variable_t, variable,
+                              NJS_VMCODE_NOT_INITIALIZED, 1, node);
+            variable->dst = node->index;
+        }
+    }
+
     return NJS_OK;
 }
 
@@ -614,6 +659,8 @@ njs_generate_variable(njs_vm_t *vm, njs_
     njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar)
 {
     njs_variable_t              *var;
+    njs_parser_scope_t          *scope;
+    njs_vmcode_variable_t       *variable;
     njs_vmcode_function_copy_t  *copy;
 
     var = njs_variable_reference(vm, node);
@@ -641,11 +688,44 @@ njs_generate_variable(njs_vm_t *vm, njs_
         copy->retval = node->index;
     }
 
+    if (var->init) {
+        return NJS_OK;
+    }
+
+    if (var->type == NJS_VARIABLE_LET) {
+        scope = njs_function_scope(node->scope);
+
+        if ((!scope->dest_disable && njs_function_scope(var->scope) == scope)) {
+            njs_generate_code(generator, njs_vmcode_variable_t, variable,
+                              NJS_VMCODE_NOT_INITIALIZED, 1, node);
+            variable->dst = node->index;
+        }
+    }
+
     return NJS_OK;
 }
 
 
 static njs_int_t
+njs_generate_variable_wo_dest(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar)
+{
+    njs_int_t           ret;
+    njs_parser_scope_t  *scope;
+
+    scope = njs_function_scope(node->scope);
+
+    scope->dest_disable = 1;
+
+    ret = njs_generate_variable(vm, generator, node, type, retvar);
+
+    scope->dest_disable = 0;
+
+    return ret;
+}
+
+
+static njs_int_t
 njs_generate_var_statement(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
@@ -656,26 +736,50 @@ njs_generate_var_statement(njs_vm_t *vm,
 
     lvalue = node->left;
 
-    ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION, &var);
+    ret = njs_generate_variable_wo_dest(vm, generator, lvalue,
+                                        NJS_DECLARATION, &var);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
 
-    lvalue->index = var->index;
     expr = node->right;
 
     if (expr == NULL) {
         /* Variable is only declared. */
+        if (var->type == NJS_VARIABLE_LET) {
+            ret = njs_generate_let(vm, generator, node, var);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
+            }
+        }
+
+        var->init = 1;
+
         return NJS_OK;
     }
 
-    expr->dest = lvalue;
-
-    ret = njs_generator(vm, generator, expr);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
+    if (var->type == NJS_VARIABLE_LET) {
+        ret = njs_generate_wo_dest(vm, generator, expr);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+
+        ret = njs_generate_let(vm, generator, node, var);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+
+    } else {
+        expr->dest = lvalue;
+
+        ret = njs_generator(vm, generator, expr);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
     }
 
+    var->init = 1;
+
     /*
      * lvalue and expression indexes are equal if the expression is an
      * empty object or expression result is stored directly in variable.
@@ -693,6 +797,20 @@ njs_generate_var_statement(njs_vm_t *vm,
 
 
 static njs_int_t
+njs_generate_let(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node, njs_variable_t *var)
+{
+    njs_vmcode_variable_t  *code;
+
+    njs_generate_code(generator, njs_vmcode_variable_t, code,
+                      NJS_VMCODE_LET, 0, node);
+    code->dst = var->index;
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
 njs_generate_if_statement(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
@@ -1089,7 +1207,7 @@ njs_generate_for_statement(njs_vm_t *vm,
 {
     njs_int_t               ret;
     njs_jump_off_t          jump_offset, loop_offset;
-    njs_parser_node_t       *condition, *update;
+    njs_parser_node_t       *condition, *update, *init;
     njs_vmcode_jump_t       *jump;
     njs_vmcode_cond_jump_t  *cond_jump;
 
@@ -1113,9 +1231,20 @@ njs_generate_for_statement(njs_vm_t *vm,
         return ret;
     }
 
+    init = node->left;
     node = node->right;
     condition = node->left;
 
+    /*
+     * Closures can occur in conditional and loop updates.  This must be
+     * foreseen in order to generate optimized code for let updates.
+     */
+
+    ret = njs_generate_for_resolve_closure(vm, condition, generator->count);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
     /* GCC complains about uninitialized jump_offset. */
     jump_offset = 0;
 
@@ -1142,10 +1271,20 @@ njs_generate_for_statement(njs_vm_t *vm,
 
     /* The loop update. */
 
+    update = node->right;
+
+    ret = njs_generate_for_resolve_closure(vm, update, generator->count);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    ret = njs_generate_for_let_update(vm, generator, init, generator->count);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
     njs_generate_patch_block(vm, generator, generator->block->continuation);
 
-    update = node->right;
-
     ret = njs_generator(vm, generator, update);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
@@ -1186,13 +1325,95 @@ njs_generate_for_statement(njs_vm_t *vm,
 
 
 static njs_int_t
+njs_generate_for_let_update(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node, size_t depth)
+{
+    njs_parser_node_t         *let;
+    njs_vmcode_variable_t     *code_var;
+    njs_variable_reference_t  *ref;
+
+    if (node == NULL) {
+        return NJS_OK;
+    }
+
+    if (depth >= NJS_GENERATE_MAX_DEPTH) {
+        return NJS_ERROR;
+    }
+
+    if (node->token_type != NJS_TOKEN_STATEMENT) {
+        return NJS_OK;
+    }
+
+    let = node->right;
+
+    if (let->token_type != NJS_TOKEN_LET) {
+        return NJS_OK;
+    }
+
+    ref = &let->left->u.reference;
+
+    if (ref->variable->closure) {
+        njs_generate_code(generator, njs_vmcode_variable_t, code_var,
+                          NJS_VMCODE_LET_UPDATE, 0, let);
+        code_var->dst = let->left->index;
+    }
+
+    return njs_generate_for_let_update(vm, generator, node->left, depth + 1);
+}
+
+
+static njs_int_t
+njs_generate_for_resolve_closure(njs_vm_t *vm, njs_parser_node_t *node,
+    size_t depth)
+{
+    njs_int_t       ret;
+    njs_bool_t      closure;
+    njs_variable_t  *var;
+
+    if (node == NULL) {
+        return NJS_OK;
+    }
+
+    if (node->token_type == NJS_TOKEN_NAME) {
+        var = njs_variable_resolve(vm, node);
+
+        if (njs_fast_path(var != NULL)) {
+            closure = njs_variable_closure_test(node->scope, var->scope);
+
+            if (closure) {
+                var->closure = 1;
+            }
+        }
+    }
+
+    if (depth >= NJS_GENERATE_MAX_DEPTH) {
+        njs_range_error(vm, "Maximum call stack size exceeded");
+        return NJS_ERROR;
+    }
+
+    ret = njs_generate_for_resolve_closure(vm, node->left, depth + 1);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    ret = njs_generate_for_resolve_closure(vm, node->right, depth + 1);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
 njs_generate_for_in_statement(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
     njs_int_t                  ret;
     njs_index_t                index;
+    njs_variable_t             *var;
     njs_jump_off_t             loop_offset, prop_offset;
-    njs_parser_node_t          *foreach;
+    njs_parser_node_t          *foreach, *name;
     njs_vmcode_prop_next_t     *prop_next;
     njs_vmcode_prop_foreach_t  *prop_foreach;
 
@@ -1205,15 +1426,36 @@ njs_generate_for_in_statement(njs_vm_t *
     /* The object. */
 
     foreach = node->left;
-
-    ret = njs_generator(vm, generator, foreach->left);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
-    }
-
-    ret = njs_generator(vm, generator, foreach->right);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
+    name = foreach->left->right;
+
+    if (name != NULL) {
+        name = name->left;
+
+        ret = njs_generate_variable_wo_dest(vm, generator, name,
+                                            NJS_DECLARATION, &var);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        foreach->left->index = name->index;
+
+        ret = njs_generator(vm, generator, foreach->right);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+
+        var->init = 1;
+
+    } else {
+        ret = njs_generator(vm, generator, foreach->left);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+
+        ret = njs_generator(vm, generator, foreach->right);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
     }
 
     njs_generate_code(generator, njs_vmcode_prop_foreach_t, prop_foreach,
@@ -1239,6 +1481,14 @@ njs_generate_for_in_statement(njs_vm_t *
 
     /* The loop iterator. */
 
+    if (name != NULL) {
+        ret = njs_generate_for_let_update(vm, generator, foreach->left,
+                                          generator->count);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+    }
+
     njs_generate_patch_block(vm, generator, generator->block->continuation);
 
     njs_code_set_jump_offset(generator, njs_vmcode_prop_foreach_t, prop_offset);
@@ -1564,12 +1814,38 @@ static njs_int_t
 njs_generate_statement(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
-    njs_int_t  ret;
+    njs_int_t              ret;
+    njs_variable_t         *var;
+    njs_parser_node_t      *right;
+    njs_vmcode_variable_t  *code;
+
+    right = node->right;
+
+    if (right != NULL && right->token_type == NJS_TOKEN_NAME) {
+        var = njs_variable_reference(vm, right);
+        if (njs_slow_path(var == NULL)) {
+            goto statement;
+        }
+
+        if (!var->init && var->type == NJS_VARIABLE_LET) {
+            njs_generate_code(generator, njs_vmcode_variable_t, code,
+                              NJS_VMCODE_INITIALIZATION_TEST, 0, right);
+            code->dst = right->index;
+        }
+
+        if (node->left == NULL) {
+            return NJS_OK;
+        }
+
+        node = node->left;
+    }
+
+statement:
 
     ret = njs_generate_children(vm, generator, node);
 
     if (njs_fast_path(ret == NJS_OK)) {
-        return njs_generate_node_index_release(vm, generator, node->right);
+        return njs_generate_node_index_release(vm, generator, right);
     }
 
     return ret;
@@ -3311,8 +3587,9 @@ static njs_index_t
 njs_generate_dest_index(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
-    njs_index_t        ret;
-    njs_parser_node_t  *dest;
+    njs_index_t         ret;
+    njs_parser_node_t   *dest;
+    njs_parser_scope_t  *scope;
 
     ret = njs_generate_children_indexes_release(vm, generator, node);
     if (njs_slow_path(ret != NJS_OK)) {
@@ -3322,7 +3599,11 @@ njs_generate_dest_index(njs_vm_t *vm, nj
     dest = node->dest;
 
     if (dest != NULL && dest->index != NJS_INDEX_NONE) {
-        return dest->index;
+        scope = njs_function_scope(node->scope);
+
+        if (!scope->dest_disable) {
+            return dest->index;
+        }
     }
 
     return njs_generate_node_temp_index_get(vm, generator, node);
@@ -3386,7 +3667,8 @@ njs_generate_temp_index_get(njs_vm_t *vm
         return NJS_ERROR;
     }
 
-    return njs_scope_index(scope->type, scope->temp++, NJS_LEVEL_TEMP);
+    return njs_scope_index(scope->type, scope->temp++, NJS_LEVEL_TEMP,
+                           NJS_VARIABLE_VAR);
 }
 
 
diff -r 28f33155ad3f -r 27a4a52621d5 src/njs_module.c
--- a/src/njs_module.c	Fri Apr 30 16:02:20 2021 +0300
+++ b/src/njs_module.c	Fri Apr 30 16:02:28 2021 +0300
@@ -574,7 +574,8 @@ njs_module_insert(njs_parser_t *parser, 
     scope = njs_parser_global_scope(parser);
     vm = parser->vm;
 
-    module->index = njs_scope_index(scope->type, scope->items, NJS_LEVEL_LOCAL);
+    module->index = njs_scope_index(scope->type, scope->items, NJS_LEVEL_LOCAL,
+                                    NJS_VARIABLE_VAR);
     scope->items++;
 
     if (vm->modules == NULL) {
diff -r 28f33155ad3f -r 27a4a52621d5 src/njs_parser.c
--- a/src/njs_parser.c	Fri Apr 30 16:02:20 2021 +0300
+++ b/src/njs_parser.c	Fri Apr 30 16:02:28 2021 +0300
@@ -238,6 +238,8 @@ static njs_int_t njs_parser_statement_li
 static njs_int_t njs_parser_statement_list_item(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
 
+static njs_int_t njs_parser_lexical_declaration(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current);
 static njs_int_t njs_parser_variable_statement(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
 static njs_int_t njs_parser_variable_declaration_list(njs_parser_t *parser,
@@ -287,7 +289,8 @@ static njs_int_t njs_parser_iteration_st
 static njs_int_t njs_parser_iteration_statement_for_map(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
 static njs_int_t njs_parser_for_var_binding_or_var_list(njs_parser_t *parser,
-    njs_lexer_token_t *token, njs_queue_link_t *current);
+    njs_lexer_token_t *token, njs_queue_link_t *current,
+    njs_token_type_t token_type);
 static njs_int_t njs_parser_for_var_in_statement(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
 static njs_int_t njs_parser_for_var_in_statement_after(njs_parser_t *parser,
@@ -311,6 +314,8 @@ static njs_int_t njs_parser_switch_state
     njs_lexer_token_t *token, njs_queue_link_t *current);
 static njs_int_t njs_parser_switch_block(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
+static njs_int_t njs_parser_switch_block_after(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current);
 static njs_int_t njs_parser_switch_case(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
 static njs_int_t njs_parser_switch_case_wo_def(njs_parser_t *parser,
@@ -362,6 +367,8 @@ static njs_int_t njs_parser_catch_after(
     njs_lexer_token_t *token, njs_queue_link_t *current);
 static njs_int_t njs_parser_catch_parenthesis(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
+static njs_int_t njs_parser_catch_statement_open_brace(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current);
 static njs_int_t njs_parser_catch_finally(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
 
@@ -506,8 +513,10 @@ njs_parser_reject(njs_parser_t *parser)
 njs_int_t
 njs_parser(njs_vm_t *vm, njs_parser_t *parser)
 {
-    njs_int_t          ret;
-    njs_lexer_token_t  *token;
+    njs_int_t                        ret;
+    njs_str_t                        str;
+    njs_lexer_token_t                *token;
+    const njs_lexer_keyword_entry_t  *keyword;
 
     parser->vm = vm;
 
@@ -526,6 +535,16 @@ njs_parser(njs_vm_t *vm, njs_parser_t *p
         parser->ret = NJS_OK;
     }
 
+    /* Add this as first variable. */
+    njs_string_get(&njs_string_undefined, &str);
+
+    keyword = njs_lexer_keyword(str.start, str.length);
+    if (njs_slow_path(keyword == NULL)) {
+        return NJS_ERROR;
+    }
+
+    parser->undefined_id = (uintptr_t) keyword->value;
+
     njs_queue_init(&parser->stack);
 
     parser->target = NULL;
@@ -645,7 +664,8 @@ njs_parser_scope_begin(njs_parser_t *par
                 return NJS_ERROR;
             }
 
-            var->index = njs_scope_index(type, 0, NJS_LEVEL_LOCAL);
+            var->index = njs_scope_index(type, 0, NJS_LEVEL_LOCAL,
+                                         NJS_VARIABLE_VAR);
         }
     }
 
@@ -737,14 +757,6 @@ njs_parser_class_declaration(njs_parser_
 
 
 static njs_int_t
-njs_parser_lexical_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
-    njs_queue_link_t *current)
-{
-    return njs_parser_not_supported(parser, token);
-}
-
-
-static njs_int_t
 njs_parser_function_or_generator(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current)
 {
@@ -890,12 +902,14 @@ njs_parser_expression_parenthesis(njs_pa
 
 
 static njs_int_t
-njs_parser_set_line_state(njs_parser_t *parser,
+njs_parser_iteration_statement_for_end(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current)
 {
     parser->node->token_line = (uint32_t) (uintptr_t) parser->target;
     parser->target = NULL;
 
+    njs_parser_scope_end(parser);
+
     return njs_parser_stack_pop(parser);
 }
 
@@ -4534,7 +4548,7 @@ njs_parser_declaration(njs_parser_t *par
     switch (token->type) {
     case NJS_TOKEN_CLASS:
         njs_parser_next(parser, njs_parser_class_declaration);
-        break;
+        return NJS_OK;
 
     case NJS_TOKEN_LET:
     case NJS_TOKEN_CONST:
@@ -4570,7 +4584,8 @@ njs_parser_declaration(njs_parser_t *par
         return NJS_DECLINED;
     }
 
-    return NJS_OK;
+    return njs_parser_after(parser, current, parser->node, 1,
+                            njs_parser_statement_after);
 }
 
 
@@ -4733,12 +4748,32 @@ njs_parser_statement_list_item(njs_parse
 
 
 /*
+ * 13.3.1 Let and Const Declarations
+ */
+static njs_int_t
+njs_parser_lexical_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
+    njs_queue_link_t *current)
+{
+    parser->var_type = (token->type == NJS_TOKEN_LET) ? NJS_VARIABLE_LET
+                                                      : NJS_VARIABLE_CONST;
+
+    njs_lexer_consume_token(parser->lexer, 1);
+
+    njs_parser_next(parser, njs_parser_variable_declaration_list);
+
+    return njs_parser_after(parser, current, NULL, 1, njs_parser_semicolon);
+}
+
+
+/*
  * 13.3.2 Variable Statement
  */
 static njs_int_t
 njs_parser_variable_statement(njs_parser_t *parser, njs_lexer_token_t *token,
     njs_queue_link_t *current)
 {
+    parser->var_type = NJS_VARIABLE_VAR;
+
     njs_parser_next(parser, njs_parser_variable_declaration_list);
 
     return njs_parser_after(parser, current, NULL, 1, njs_parser_semicolon);
@@ -4789,6 +4824,7 @@ njs_parser_variable_declaration(njs_pars
 {
     njs_int_t          ret;
     njs_variable_t     *var;
+    njs_token_type_t   type;
     njs_parser_node_t  *name;
 
     ret = njs_parser_binding_pattern(parser, token, current);
@@ -4807,14 +4843,14 @@ njs_parser_variable_declaration(njs_pars
         return NJS_DONE;
     }
 
-    name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR,
+    name = njs_parser_variable_node(parser, token->unique_id, parser->var_type,
                                     &var);
     if (name == NULL) {
         return NJS_ERROR;
     }
 
     if (var->self) {
-        var->type = NJS_VARIABLE_VAR;
+        var->type = parser->var_type;
         var->self = 0;
     }
 
@@ -4829,7 +4865,21 @@ njs_parser_variable_declaration(njs_pars
         return NJS_ERROR;
     }
 
-    ret = njs_parser_initializer_assign(parser, NJS_TOKEN_VAR);
+    switch (parser->var_type) {
+    case NJS_VARIABLE_LET:
+        type = NJS_TOKEN_LET;
+        break;
+
+    case NJS_VARIABLE_CONST:
+        type = NJS_TOKEN_CONST;
+        break;
+
+    default:
+        type = NJS_TOKEN_VAR;
+        break;
+    }
+
+    ret = njs_parser_initializer_assign(parser, type);
     if (ret != NJS_OK) {
         return ret;
     }
@@ -4930,6 +4980,12 @@ njs_parser_expression_statement(njs_pars
             return NJS_ERROR;
         }
 
+        if (token->type == NJS_TOKEN_NAME) {
+            njs_parser_syntax_error(parser, "let declaration cannot appear "
+                                            "in a single-statement context");
+            return NJS_DONE;
+        }
+
         if (token->type == NJS_TOKEN_OPEN_BRACKET) {
             return njs_parser_failed(parser);
         }
@@ -5172,14 +5228,21 @@ static njs_int_t
 njs_parser_iteration_statement_for(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current)
 {
+    njs_int_t  ret;
+
     if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) {
         njs_lexer_consume_token(parser->lexer, 1);
 
+        ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+
         njs_parser_next(parser, njs_parser_iteration_statement_for_map);
 
         return njs_parser_after(parser, current,
                                 (void *) (uintptr_t) parser->line, 1,
-                                njs_parser_set_line_state);
+                                njs_parser_iteration_statement_for_end);
     }
 
     if (token->type == NJS_TOKEN_AWAIT) {
@@ -5194,8 +5257,9 @@ static njs_int_t
 njs_parser_iteration_statement_for_map(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current)
 {
-    njs_int_t  ret;
-    njs_str_t  *text;
+    njs_int_t         ret;
+    njs_str_t         *text;
+    njs_token_type_t  token_type;
 
     /*
      * "var" <VariableDeclarationList> ";" <Expression>? ";" <Expression>? ")"
@@ -5242,6 +5306,9 @@ njs_parser_iteration_statement_for_map(n
         return NJS_OK;
 
     case NJS_TOKEN_VAR:
+    case NJS_TOKEN_LET:
+        token_type = token->type;
+
         token = njs_lexer_peek_token(parser->lexer, token, 0);
         if (token == NULL) {
             return NJS_ERROR;
@@ -5249,7 +5316,8 @@ njs_parser_iteration_statement_for_map(n
 
         njs_lexer_consume_token(parser->lexer, 1);
 
-        ret = njs_parser_for_var_binding_or_var_list(parser, token, current);
+        ret = njs_parser_for_var_binding_or_var_list(parser, token,
+                                                     current, token_type);
         if (ret != NJS_OK) {
             if (ret == NJS_DONE) {
                 return NJS_OK;
@@ -5260,7 +5328,6 @@ njs_parser_iteration_statement_for_map(n
 
         break;
 
-    case NJS_TOKEN_LET:
     case NJS_TOKEN_CONST:
         return njs_parser_not_supported(parser, token);
 
@@ -5288,11 +5355,23 @@ njs_parser_iteration_statement_for_map(n
 
 static njs_int_t
 njs_parser_for_var_binding_or_var_list(njs_parser_t *parser,
-    njs_lexer_token_t *token, njs_queue_link_t *current)
-{
-    njs_int_t          ret;
-    njs_lexer_token_t  *next;
-    njs_parser_node_t  *node, *var;
+    njs_lexer_token_t *token, njs_queue_link_t *current,
+    njs_token_type_t token_type)
+{
+    njs_int_t            ret;
+    njs_lexer_token_t    *next;
+    njs_parser_node_t    *node, *var, *node_type, *statement;
+    njs_variable_type_t  type;
+
+    switch (token_type) {
+    case NJS_TOKEN_LET:
+        type = NJS_VARIABLE_LET;
+        break;
+
+    default:
+        type = NJS_VARIABLE_VAR;
+        break;
+    }
 
     switch (token->type) {
     /* BindingPattern */
@@ -5318,18 +5397,33 @@ njs_parser_for_var_binding_or_var_list(n
             }
 
             if (next->type != NJS_TOKEN_IN) {
+                parser->var_type = type;
+
                 njs_parser_next(parser, njs_parser_variable_declaration_list);
                 return NJS_OK;
             }
 
+            statement = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT);
+            if (njs_slow_path(statement == NULL)) {
+                return NJS_ERROR;
+            }
+
+            node_type = njs_parser_node_new(parser, token_type);
+            if (njs_slow_path(node_type == NULL)) {
+                return NJS_ERROR;
+            }
+
             var = njs_parser_variable_node(parser, token->unique_id,
-                                            NJS_VARIABLE_VAR, NULL);
+                                           type, NULL);
             if (var == NULL) {
                 return NJS_ERROR;
             }
 
+            node_type->token_line = token->line;
             var->token_line = token->line;
 
+            statement->right = node_type;
+            node_type->left = var;
             parser->node = NULL;
 
             node = njs_parser_node_new(parser, NJS_TOKEN_IN);
@@ -5338,7 +5432,7 @@ njs_parser_for_var_binding_or_var_list(n
             }
 
             node->token_line = next->line;
-            node->left = var;
+            node->left = statement;
 
             njs_parser_next(parser, njs_parser_expression);
 
@@ -5829,6 +5923,8 @@ static njs_int_t
 njs_parser_switch_block(njs_parser_t *parser, njs_lexer_token_t *token,
     njs_queue_link_t *current)
 {
+    njs_int_t  ret;
+
     if (token->type != NJS_TOKEN_OPEN_BRACE) {
         return njs_parser_failed(parser);
     }
@@ -5837,9 +5933,24 @@ njs_parser_switch_block(njs_parser_t *pa
 
     parser->target->left = parser->node;
 
+    ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0);
+    if (ret != NJS_OK) {
+        return NJS_ERROR;
+    }
+
     njs_parser_next(parser, njs_parser_switch_case);
 
-    return NJS_OK;
+    return njs_parser_after(parser, current, NULL, 1,
+                            njs_parser_switch_block_after);
+}
+
+static njs_int_t
+njs_parser_switch_block_after(njs_parser_t *parser, njs_lexer_token_t *token,
+    njs_queue_link_t *current)
+{
+    njs_parser_scope_end(parser);
+
+    return njs_parser_stack_pop(parser);
 }
 
 
@@ -6310,8 +6421,6 @@ njs_parser_catch_after(njs_parser_t *par
 {
     njs_parser_node_t  *node;
 
-    njs_parser_scope_end(parser);
-
     parser->target->right->right = parser->node;
 
     if (token->type == NJS_TOKEN_FINALLY) {
@@ -6355,7 +6464,7 @@ njs_parser_catch_parenthesis(njs_parser_
     parser->target->right->right = parser->node;
     parser->node = NULL;
 
-    njs_parser_next(parser, njs_parser_block_statement_open_brace);
+    njs_parser_next(parser, njs_parser_catch_statement_open_brace);
 
     return njs_parser_after(parser, current, parser->target, 1,
                             njs_parser_catch_after);
@@ -6363,6 +6472,42 @@ njs_parser_catch_parenthesis(njs_parser_
 
 
 static njs_int_t
+njs_parser_catch_statement_open_brace(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current)
+{
+    void  *target;
+
+    if (token->type != NJS_TOKEN_OPEN_BRACE) {
+        return njs_parser_failed(parser);
+    }
+


More information about the nginx-devel mailing list