[njs] Fixed for-in loop with left and right hand side expressions.

Vadim Zhestikov v.zhestikov at f5.com
Mon Nov 14 17:26:38 UTC 2022


details:   https://hg.nginx.org/njs/rev/283ae119d121
branches:  
changeset: 1997:283ae119d121
user:      Vadim Zhestikov <v.zhestikov at f5.com>
date:      Mon Nov 14 09:18:37 2022 -0800
description:
Fixed for-in loop with left and right hand side expressions.

This fixes #351 issue on Github.

diffstat:

 src/njs_generator.c      |  365 ++++++++++++++++++++++++++++++++++++++++++++--
 src/njs_lexer.c          |  112 ++++++++++++++-
 src/njs_lexer.h          |   14 +-
 src/njs_parser.c         |  309 ++++++++++++++++++++++++++++++++++++--
 src/njs_parser.h         |    2 +
 src/test/njs_unit_test.c |   62 +++++++
 6 files changed, 822 insertions(+), 42 deletions(-)

diffs (truncated from 1087 to 1000 lines):

diff -r 1d6cea817ef4 -r 283ae119d121 src/njs_generator.c
--- a/src/njs_generator.c	Thu Nov 10 17:53:36 2022 -0800
+++ b/src/njs_generator.c	Mon Nov 14 09:18:37 2022 -0800
@@ -86,6 +86,7 @@ typedef struct {
     njs_vmcode_jump_t           *jump;
     njs_variable_t              *var;
     njs_index_t                 index;
+    njs_index_t                 index_next_value;
 } njs_generator_loop_ctx_t;
 
 
@@ -176,10 +177,22 @@ static njs_int_t njs_generate_for_resolv
     njs_parser_node_t *node);
 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_for_in_set_prop_block(njs_vm_t *vm,
+    njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_for_in_name_assign(njs_vm_t *vm,
+    njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_for_in_object(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_for_in_object_wo_decl(njs_vm_t *vm,
+    njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_for_in_object_left_hand_expr(njs_vm_t *vm,
+    njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_for_in_body(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_for_in_body_wo_decl(njs_vm_t *vm,
+    njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_for_in_body_left_hand_expr(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,
     njs_generator_t *generator, njs_generator_block_type_t type,
     const njs_str_t *label);
@@ -1994,6 +2007,181 @@ njs_generate_for_resolve_closure(njs_vm_
 
 
 static njs_int_t
+njs_generate_for_in_name_assign(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node)
+{
+    njs_int_t                 ret;
+    njs_variable_t            *var;
+    njs_parser_node_t         *foreach, *lvalue, *expr;
+    njs_vmcode_move_t         *move;
+    njs_generator_loop_ctx_t  *ctx;
+
+    ctx = generator->context;
+
+    foreach = node->left;
+    lvalue = foreach->left;
+    expr = node->right;
+
+    var = njs_variable_reference(vm, lvalue);
+
+    if (var != NULL) {
+        ctx->index_next_value = lvalue->index;
+
+    } else {
+        ctx->index_next_value = njs_generate_temp_index_get(vm, generator,
+                                                            foreach->left);
+        if (njs_slow_path(ctx->index_next_value == NJS_INDEX_ERROR)) {
+            return NJS_ERROR;
+        }
+
+        if (expr != NULL) {
+            expr->index = ctx->index_next_value;
+
+            /*
+             * lvalue and expression indexes are equal if the expression is an
+             * empty object or expression result is stored directly in variable.
+             */
+            if (lvalue->index != expr->index) {
+                njs_generate_code_move(generator, move, lvalue->index,
+                                       expr->index, expr);
+            }
+
+            ret = njs_generate_global_property_set(vm, generator, foreach->left,
+                                                   expr);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
+            }
+        }
+    }
+    return njs_generator_stack_pop(vm, generator, NULL);
+}
+
+
+static njs_int_t
+njs_generate_for_in_body_wo_decl(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node)
+{
+    njs_int_t                 ret;
+    njs_jump_off_t            prop_offset;
+    njs_parser_node_t         *foreach, *name;
+    njs_vmcode_prop_next_t    *prop_next;
+    njs_generator_loop_ctx_t  *ctx;
+
+    ctx = generator->context;
+
+    foreach = node->left;
+    name = foreach->left->right;
+
+    /* The loop iterator. */
+
+    if (name != NULL) {
+        ret = njs_generate_for_let_update(vm, generator, foreach->left);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+    }
+
+    njs_generate_patch_block(vm, generator, generator->block,
+                             NJS_GENERATOR_CONTINUATION);
+
+    njs_code_set_jump_offset(generator, njs_vmcode_prop_foreach_t,
+                             ctx->jump_offset);
+
+    njs_generate_code(generator, njs_vmcode_prop_next_t, prop_next,
+                      NJS_VMCODE_PROPERTY_NEXT, 3, node->left->left);
+    prop_offset = njs_code_offset(generator, prop_next);
+    prop_next->retval = ctx->index_next_value;
+    prop_next->object = foreach->right->index;
+    prop_next->next = ctx->index;
+    prop_next->offset = ctx->loop_offset - prop_offset;
+
+    njs_generate_patch_block_exit(vm, generator);
+
+    /*
+     * Release object and iterator indexes: an object can be a function result
+     * or a property of another object and an iterator can be given with "let".
+     */
+    ret = njs_generate_children_indexes_release(vm, generator, foreach);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    ret = njs_generate_index_release(vm, generator, ctx->index);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    return njs_generator_stack_pop(vm, generator, ctx);
+}
+
+
+static njs_int_t
+njs_generate_for_in_object_wo_decl(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node)
+{
+    njs_int_t                  ret;
+    njs_parser_node_t          *foreach, *name;
+    njs_generator_loop_ctx_t   *ctx;
+    njs_vmcode_prop_foreach_t  *prop_foreach;
+
+    ctx = generator->context;
+
+    foreach = node->left;
+    name = foreach->left->right;
+
+    if (name != NULL) {
+        ctx->var->init = 1;
+    }
+
+    njs_generate_code(generator, njs_vmcode_prop_foreach_t, prop_foreach,
+                      NJS_VMCODE_PROPERTY_FOREACH, 2, foreach);
+    ctx->jump_offset = njs_code_offset(generator, prop_foreach);
+    prop_foreach->object = foreach->right->index;
+
+    ctx->index = njs_generate_temp_index_get(vm, generator, foreach->right);
+    if (njs_slow_path(ctx->index == NJS_INDEX_ERROR)) {
+        return NJS_ERROR;
+    }
+
+    prop_foreach->next = ctx->index;
+
+    /* The loop body. */
+
+    ctx->loop_offset = njs_code_offset(generator, generator->code_end);
+
+
+    /* 1) left. */
+
+    njs_generator_next(generator, njs_generate, foreach->left);
+
+    /* 4) loop-body-end. */
+
+    ret = njs_generator_after(vm, generator,
+                               njs_queue_first(&generator->stack), node,
+                               njs_generate_for_in_body_wo_decl, ctx, 0);
+    if (ret != NJS_OK) {
+        return ret;
+    }
+
+    /* 3) loop-body. */
+
+    ret = njs_generator_after(vm, generator,
+                               njs_queue_first(&generator->stack), node->right,
+                               njs_generate, ctx, 0);
+    if (ret != NJS_OK) {
+        return ret;
+    }
+
+    /* 2) assign value to name. */
+
+    return njs_generator_after(vm, generator,
+                               njs_queue_first(&generator->stack), node,
+                               njs_generate_for_in_name_assign, ctx, 0);
+
+}
+
+
+static njs_int_t
 njs_generate_for_in_statement(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
@@ -2010,40 +2198,135 @@ njs_generate_for_in_statement(njs_vm_t *
     /* The object. */
 
     foreach = node->left;
-    name = foreach->left->right;
-
-    if (name != NULL) {
-        name = name->left;
-
-        ret = njs_generate_variable_wo_dest(vm, generator, name,
+
+    if (foreach->left->token_type != NJS_TOKEN_PROPERTY) {
+        name = foreach->left->right;
+
+        if (name != NULL) {
+            name = name->left;
+
+            ret = njs_generate_variable_wo_dest(vm, generator, name,
                                             NJS_DECLARATION, &ctx.var);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return NJS_ERROR;
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NJS_ERROR;
+            }
+
+            foreach->left->index = name->index;
+
+            njs_generator_next(generator, njs_generate, foreach->right);
+
+            return njs_generator_after(vm, generator,
+                                       njs_queue_first(&generator->stack), node,
+                                       njs_generate_for_in_object,
+                                       &ctx, sizeof(njs_generator_loop_ctx_t));
         }
 
-        foreach->left->index = name->index;
+    } else {
+
+        /* foreach->right is object in 'in object'. */
 
         njs_generator_next(generator, njs_generate, foreach->right);
 
         return njs_generator_after(vm, generator,
                                    njs_queue_first(&generator->stack), node,
-                                   njs_generate_for_in_object,
+                                   njs_generate_for_in_object_left_hand_expr,
                                    &ctx, sizeof(njs_generator_loop_ctx_t));
-    }
-
-    njs_generator_next(generator, njs_generate, foreach->left);
-
-    ret = njs_generator_after(vm, generator,
+
+    }
+
+    njs_generator_next(generator, njs_generate, foreach->right);
+
+    return  njs_generator_after(vm, generator,
                               njs_queue_first(&generator->stack), node,
-                              njs_generate_for_in_object,
+                              njs_generate_for_in_object_wo_decl,
                               &ctx, sizeof(njs_generator_loop_ctx_t));
+}
+
+
+static njs_int_t
+njs_generate_for_in_object_left_hand_expr(njs_vm_t *vm,
+    njs_generator_t *generator, njs_parser_node_t *node)
+{
+    njs_int_t                 ret;
+    njs_parser_node_t          *foreach;
+    njs_generator_loop_ctx_t   *ctx;
+    njs_vmcode_prop_foreach_t  *prop_foreach;
+
+    ctx = generator->context;
+
+    foreach = node->left;
+
+    njs_generate_code(generator, njs_vmcode_prop_foreach_t, prop_foreach,
+                      NJS_VMCODE_PROPERTY_FOREACH, 2, foreach);
+    ctx->jump_offset = njs_code_offset(generator, prop_foreach);
+    prop_foreach->object = foreach->right->index;
+
+    ctx->index = njs_generate_temp_index_get(vm, generator, foreach->right);
+    if (njs_slow_path(ctx->index == NJS_INDEX_ERROR)) {
+        return NJS_ERROR;
+    }
+
+    ctx->index_next_value = njs_generate_temp_index_get(vm, generator,
+                                                        foreach->left);
+    if (njs_slow_path(ctx->index_next_value == NJS_INDEX_ERROR)) {
+        return NJS_ERROR;
+    }
+
+    prop_foreach->next = ctx->index;
+
+    ctx->loop_offset = njs_code_offset(generator, generator->code_end);
+
+    /* Object part calculation. */
+
+    njs_generator_next(generator, njs_generate, foreach->left->left);
+
+    /* The loop body. */
+
+    ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack),
+                              node, njs_generate_for_in_body_left_hand_expr,
+                              ctx, sizeof(njs_generator_loop_ctx_t));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
+    /* set-property and block. */
+
+    ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack),
+                              node, njs_generate_for_in_set_prop_block, ctx,
+                              sizeof(njs_generator_loop_ctx_t));
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    /* Key part calculation. */
+
     return njs_generator_after(vm, generator,
                                njs_queue_first(&generator->stack),
-                               foreach->right, njs_generate, NULL, 0);
+                               foreach->left->right, njs_generate, NULL, 0);
+}
+
+
+static njs_int_t
+njs_generate_for_in_set_prop_block(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node)
+{
+    njs_parser_node_t      *foreach;
+    njs_vmcode_prop_set_t  *prop_set;
+    njs_generator_loop_ctx_t   *ctx;
+
+    ctx = generator->context;
+
+    foreach = node->left;
+
+    njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
+                      NJS_VMCODE_PROPERTY_SET, 3, foreach);
+    prop_set->object = foreach->left->left->index;
+    prop_set->property = foreach->left->right->index;
+    prop_set->value = ctx->index_next_value;
+
+    njs_generator_next(generator, njs_generate, node->right);
+
+    return NJS_OK;
 }
 
 
@@ -2089,6 +2372,54 @@ njs_generate_for_in_object(njs_vm_t *vm,
 
 
 static njs_int_t
+njs_generate_for_in_body_left_hand_expr(njs_vm_t *vm,
+    njs_generator_t *generator, njs_parser_node_t *node)
+{
+    njs_int_t                 ret;
+    njs_jump_off_t            prop_offset;
+    njs_parser_node_t         *foreach;
+    njs_vmcode_prop_next_t    *prop_next;
+    njs_generator_loop_ctx_t  *ctx;
+
+    ctx = generator->context;
+
+    foreach = node->left;
+
+    njs_generate_patch_block(vm, generator, generator->block,
+                             NJS_GENERATOR_CONTINUATION);
+
+    njs_code_set_jump_offset(generator, njs_vmcode_prop_foreach_t,
+                             ctx->jump_offset);
+
+    njs_generate_code(generator, njs_vmcode_prop_next_t, prop_next,
+                      NJS_VMCODE_PROPERTY_NEXT, 3, node->left->left);
+    prop_offset = njs_code_offset(generator, prop_next);
+    prop_next->retval = ctx->index_next_value;
+    prop_next->object = foreach->right->index;
+    prop_next->next = ctx->index;
+    prop_next->offset = ctx->loop_offset - prop_offset;
+
+    njs_generate_patch_block_exit(vm, generator);
+
+    /*
+     * Release object and iterator indexes: an object can be a function result
+     * or a property of another object and an iterator can be given with "let".
+     */
+    ret = njs_generate_children_indexes_release(vm, generator, foreach);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    ret = njs_generate_index_release(vm, generator, ctx->index);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    return njs_generator_stack_pop(vm, generator, ctx);
+}
+
+
+static njs_int_t
 njs_generate_for_in_body(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
diff -r 1d6cea817ef4 -r 283ae119d121 src/njs_lexer.c
--- a/src/njs_lexer.c	Thu Nov 10 17:53:36 2022 -0800
+++ b/src/njs_lexer.c	Mon Nov 14 09:18:37 2022 -0800
@@ -290,9 +290,13 @@ static const njs_lexer_multi_t  njs_assi
 
 njs_int_t
 njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file,
-    u_char *start, u_char *end, njs_uint_t runtime)
+    u_char *start, u_char *end, njs_uint_t runtime,
+    njs_int_t init_lexer_memory)
 {
-    njs_memzero(lexer, sizeof(njs_lexer_t));
+    if (init_lexer_memory) {
+        njs_memzero(lexer, sizeof(njs_lexer_t));
+
+    }
 
     lexer->file = *file;
     lexer->start = start;
@@ -304,6 +308,105 @@ njs_lexer_init(njs_vm_t *vm, njs_lexer_t
 
     njs_queue_init(&lexer->preread);
 
+    return njs_lexer_in_stack_init(lexer);
+}
+
+
+njs_int_t
+njs_lexer_in_stack_init(njs_lexer_t *lexer)
+{
+    lexer->in_stack_size = 128;
+    lexer->in_stack = njs_mp_zalloc(lexer->mem_pool, lexer->in_stack_size);
+    if (lexer->in_stack == NULL) {
+        return NJS_ERROR;
+    }
+
+    lexer->in_stack_ptr = 0;
+
+    return NJS_OK;
+}
+
+
+njs_int_t
+njs_lexer_in_stack_push(njs_lexer_t *lexer)
+{
+    u_char  *tmp;
+    size_t  size;
+
+    lexer->in_stack_ptr++;
+
+    if (lexer->in_stack_ptr < lexer->in_stack_size) {
+        lexer->in_stack[lexer->in_stack_ptr] = 0;
+        return NJS_OK;
+    }
+
+    /* Realloc in_stack, it is up to higher layer generate error if any. */
+
+    size = lexer->in_stack_size;
+    lexer->in_stack_size = size * 2;
+
+    tmp = njs_mp_alloc(lexer->mem_pool, size * 2);
+    if (tmp == NULL) {
+        return NJS_ERROR;
+    }
+
+    memcpy(tmp, lexer->in_stack, size);
+    memset(&tmp[size], 0, size);
+
+    njs_mp_free(lexer->mem_pool, lexer->in_stack);
+    lexer->in_stack = tmp;
+
+    return NJS_OK;
+}
+
+
+void
+njs_lexer_in_stack_pop(njs_lexer_t *lexer)
+{
+    /**
+     * if in_stack_ptr <= 0 do nothing, it is up to higher layer
+     * generate error.
+     */
+
+    if (lexer->in_stack_ptr > 0) {
+        lexer->in_stack_ptr--;
+    }
+}
+
+
+njs_int_t
+njs_lexer_in_fail_get(njs_lexer_t *lexer)
+{
+    return lexer->in_stack[lexer->in_stack_ptr];
+}
+
+
+void
+njs_lexer_in_fail_set(njs_lexer_t *lexer, njs_int_t flag)
+{
+    lexer->in_stack[lexer->in_stack_ptr] = flag;
+}
+
+
+njs_inline njs_int_t
+njs_lexer_in_stack(njs_lexer_t *lexer, njs_lexer_token_t  *token)
+{
+    switch (token->type) {
+    case NJS_TOKEN_OPEN_PARENTHESIS:
+    case NJS_TOKEN_OPEN_BRACKET:
+    case NJS_TOKEN_OPEN_BRACE:
+        return njs_lexer_in_stack_push(lexer);
+
+    case NJS_TOKEN_CLOSE_PARENTHESIS:
+    case NJS_TOKEN_CLOSE_BRACKET:
+    case NJS_TOKEN_CLOSE_BRACE:
+        njs_lexer_in_stack_pop(lexer);
+        break;
+
+    default:
+        break;
+    }
+
     return NJS_OK;
 }
 
@@ -329,6 +432,11 @@ njs_lexer_next_token(njs_lexer_t *lexer)
 
     njs_queue_insert_tail(&lexer->preread, &token->link);
 
+    ret = njs_lexer_in_stack(lexer, token);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NULL;
+    }
+
     return token;
 }
 
diff -r 1d6cea817ef4 -r 283ae119d121 src/njs_lexer.h
--- a/src/njs_lexer.h	Thu Nov 10 17:53:36 2022 -0800
+++ b/src/njs_lexer.h	Mon Nov 14 09:18:37 2022 -0800
@@ -267,11 +267,17 @@ typedef struct {
 
     u_char                          *start;
     u_char                          *end;
+
+#define NJS_INITIAL_IN_STACK_SIZE 128
+    uint8_t                        *in_stack;
+    njs_int_t                       in_stack_ptr;
+    njs_int_t                       in_stack_size;
 } njs_lexer_t;
 
 
 njs_int_t njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file,
-    u_char *start, u_char *end, njs_uint_t runtime);
+    u_char *start, u_char *end, njs_uint_t runtime,
+    njs_int_t init_lexer_memory);
 
 njs_lexer_token_t *njs_lexer_token(njs_lexer_t *lexer,
     njs_bool_t with_end_line);
@@ -279,6 +285,12 @@ njs_lexer_token_t *njs_lexer_peek_token(
     njs_lexer_token_t *current, njs_bool_t with_end_line);
 void njs_lexer_consume_token(njs_lexer_t *lexer, unsigned length);
 njs_int_t njs_lexer_make_token(njs_lexer_t *lexer, njs_lexer_token_t *token);
+njs_int_t njs_lexer_in_stack_init(njs_lexer_t *lexer);
+njs_int_t njs_lexer_in_stack_push(njs_lexer_t *lexer);
+void njs_lexer_in_stack_pop(njs_lexer_t *lexer);
+void njs_lexer_in_fail_set(njs_lexer_t *lexer, njs_int_t flag);
+njs_int_t njs_lexer_in_fail_get(njs_lexer_t *lexer);
+
 
 const njs_lexer_keyword_entry_t *njs_lexer_keyword(const u_char *key,
     size_t length);
diff -r 1d6cea817ef4 -r 283ae119d121 src/njs_parser.c
--- a/src/njs_parser.c	Thu Nov 10 17:53:36 2022 -0800
+++ b/src/njs_parser.c	Mon Nov 14 09:18:37 2022 -0800
@@ -294,6 +294,16 @@ static njs_int_t njs_parser_while_after(
 
 static njs_int_t njs_parser_iteration_statement_for(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
+static njs_int_t njs_parser_for_left_hand_side_expression_map(
+    njs_parser_t *parser, njs_lexer_token_t *token,
+    njs_queue_link_t *current);
+static njs_int_t njs_parser_expression_continue_op(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current);
+static njs_int_t njs_parser_expression_continue_assign_comma(
+    njs_parser_t *parser, njs_lexer_token_t *token,
+    njs_queue_link_t *current);
+static njs_int_t njs_parser_for_in_statement_statement(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current);
 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,
@@ -527,17 +537,9 @@ njs_parser_init(njs_vm_t *vm, njs_parser
     lexer = &parser->lexer0;
     parser->lexer = lexer;
 
-    lexer->file = *file;
-    lexer->start = start;
-    lexer->end = end;
-    lexer->line = 1;
-    lexer->keywords_hash = (runtime) ? &vm->keywords_hash
-                                     : &vm->shared->keywords_hash;
-    lexer->mem_pool = vm->mem_pool;
-
-    njs_queue_init(&lexer->preread);
-
-    return NJS_OK;
+    parser->use_lhs = 0;
+
+    return njs_lexer_init(vm, lexer, file, start, end, runtime, 0);
 }
 
 
@@ -3620,11 +3622,17 @@ njs_parser_exponentiation_expression(njs
 {
     parser->target = NULL;
 
-    njs_parser_next(parser, njs_parser_unary_expression);
-
-    /* For UpdateExpression, see njs_parser_unary_expression_after. */
-
-    return NJS_OK;
+    if (parser->use_lhs == 0) {
+        njs_parser_next(parser, njs_parser_unary_expression);
+
+        /* For UpdateExpression, see njs_parser_unary_expression_after. */
+
+        return NJS_OK;
+    } else {
+        parser->use_lhs = 0;
+
+        return njs_parser_update_expression_post(parser, token, current);
+    }
 }
 
 
@@ -3899,6 +3907,10 @@ njs_parser_relational_expression_match(n
         break;
 
     case NJS_TOKEN_IN:
+        if (njs_lexer_in_fail_get(parser->lexer)) {
+            njs_parser_syntax_error(parser, "Invalid left-hand side in for-loop");
+            return NJS_ERROR;
+        }
         operation = NJS_VMCODE_PROPERTY_IN;
         break;
 
@@ -4215,6 +4227,11 @@ njs_parser_conditional_question_mark(njs
     cond->right = node;
 
     njs_lexer_consume_token(parser->lexer, 1);
+
+    if (njs_lexer_in_stack_push(parser->lexer) != NJS_OK) {
+        return NJS_ERROR;
+    }
+
     njs_parser_next(parser, njs_parser_assignment_expression);
 
     return njs_parser_after(parser, current, cond, 1,
@@ -4232,6 +4249,8 @@ njs_parser_conditional_colon(njs_parser_
         return njs_parser_failed(parser);
     }
 
+    njs_lexer_in_stack_pop(parser->lexer);
+
     njs_lexer_consume_token(parser->lexer, 1);
 
     node = parser->target->right;
@@ -5470,6 +5489,175 @@ njs_parser_iteration_statement_for(njs_p
 
 
 static njs_int_t
+njs_parser_for_left_hand_side_expression_map(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current)
+{
+    njs_int_t          operation;
+    njs_str_t          *text;
+    njs_parser_node_t  *node;
+
+    if (parser->node == NULL) {
+        njs_lexer_in_fail_set(parser->lexer, 1);
+
+        njs_parser_next(parser, njs_parser_expression);
+
+        /*
+         * Here we pass not a node, but a token, this is important.
+         * This is necessary for correct error output.
+         */
+
+        text = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_str_t));
+        if (text == NULL) {
+            return NJS_ERROR;
+        }
+
+        *text = token->text;
+
+        return njs_parser_after(parser, current, text, 1,
+                                njs_parser_for_var_in_of_expression);
+
+    }
+
+    if (token->type != NJS_TOKEN_IN) {
+        njs_lexer_in_fail_set(parser->lexer, 1);
+
+        /* Continue parsing of expr1 in "for (expr1;[expr2];[expr3])". */
+
+        njs_parser_next(parser, njs_parser_expression_continue_op);
+
+        /*
+         * Here we pass not a node, but a token, this is important.
+         * This is necessary for correct error output.
+         */
+
+        text = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_str_t));
+        if (text == NULL) {
+            return NJS_ERROR;
+        }
+
+        *text = token->text;
+
+        return njs_parser_after(parser, current, text, 1,
+                                njs_parser_for_var_in_of_expression);
+
+    } else {
+
+        /* for-in */
+
+        if (parser->node->token_type != NJS_TOKEN_NAME &&
+            parser->node->token_type != NJS_TOKEN_PROPERTY)
+        {
+            text = (njs_str_t *) parser->target;
+
+            njs_parser_ref_error(parser, "Invalid left-hand side \"%V\" "
+                                 "in for-in statement", text);
+
+            njs_mp_free(parser->vm->mem_pool, text);
+
+            return NJS_DONE;
+        }
+
+        operation = NJS_VMCODE_PROPERTY_IN;
+
+        node = njs_parser_node_new(parser, token->type);
+        if (node == NULL) {
+            return NJS_ERROR;
+        }
+
+        node->token_line = token->line;
+        node->u.operation = operation;
+        node->left = parser->node;
+        node->left->dest = node;
+
+        njs_lexer_consume_token(parser->lexer, 1);
+
+        njs_parser_next(parser, njs_parser_expression);
+
+        return njs_parser_after(parser, current, node, 0,
+                                njs_parser_for_in_statement_statement);
+    }
+
+}
+
+
+static njs_int_t
+njs_parser_after_expr(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current)
+{
+    parser->target->right = parser->node;
+    parser->node = parser->target;
+
+    return njs_parser_stack_pop(parser);
+}
+
+
+static njs_int_t
+njs_parser_comma_expression_comma(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current)
+{
+    njs_parser_node_t  *node;
+
+    if (parser->target != NULL) {
+        parser->target->right = parser->node;
+        parser->target->right->dest = parser->target;
+        parser->node = parser->target;
+    }
+
+    if (token->type != NJS_TOKEN_COMMA) {
+        return njs_parser_stack_pop(parser);
+    }
+
+    node = njs_parser_node_new(parser, NJS_TOKEN_COMMA);
+    if (node == NULL) {
+        return NJS_ERROR;
+    }
+
+    node->token_line = token->line;
+    node->u.operation = 0;
+    node->left = parser->node;
+    node->left->dest = node;
+
+    njs_lexer_consume_token(parser->lexer, 1);
+
+    njs_parser_next(parser, njs_parser_expression);
+
+    return njs_parser_after(parser, current, node, 1, njs_parser_after_expr);
+}
+
+
+static njs_int_t
+njs_parser_expression_continue_op(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current)
+{
+    if (token->type == NJS_TOKEN_CONDITIONAL) {
+        njs_parser_next(parser, njs_parser_conditional_question_mark);
+        return njs_parser_after(parser, current, NULL, 0,
+                                njs_parser_expression_continue_assign_comma);
+    } else {
+        parser->target = NULL;
+
+        parser->use_lhs = 1;
+
+        njs_parser_next(parser, njs_parser_expression);
+
+        return njs_parser_after(parser, current, NULL, 1,
+                                njs_parser_comma_expression_comma);
+    }
+}
+
+
+static njs_int_t
+njs_parser_expression_continue_assign_comma(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current)
+{
+    njs_parser_next(parser, njs_parser_assignment_expression_after);
+
+    return njs_parser_after(parser, current, NULL, 1,
+                            njs_parser_expression_comma);
+}
+
+
+static njs_int_t
 njs_parser_iteration_statement_for_map(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current)
 {
@@ -5543,12 +5731,43 @@ njs_parser_iteration_statement_for_map(n
             return ret;
         }
 
-        break;
+        goto expression_after;
+
+    case NJS_TOKEN_AWAIT:
+        njs_parser_next(parser, njs_parser_expression);
+
+        goto expression_after;
 
     default:
-        njs_parser_next(parser, njs_parser_expression);
-        break;
-    }
+        ret = njs_parser_match_arrow_expression(parser, token);
+        if (ret == NJS_OK) {
+            parser->target = NULL;
+            njs_parser_next(parser, njs_parser_expression);
+            goto expression_after;
+        } else if (ret == NJS_ERROR) {
+            return NJS_ERROR;
+        }
+
+        parser->target = NULL;
+        njs_parser_next(parser, njs_parser_left_hand_side_expression);
+
+        /*
+         * Here we pass not a node, but a token, this is important.
+         * This is necessary for correct error output.
+         */
+
+        text = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_str_t));
+        if (text == NULL) {
+            return NJS_ERROR;
+        }
+
+        *text = token->text;
+
+        return njs_parser_after(parser, current, text, 0,
+                                njs_parser_for_left_hand_side_expression_map);
+    }
+
+expression_after:
 
     /*
      * Here we pass not a node, but a token, this is important.
@@ -5617,6 +5836,8 @@ njs_parser_for_var_binding_or_var_list(n
             if (next->type != NJS_TOKEN_IN) {
                 parser->var_type = type;
 
+                njs_lexer_in_fail_set(parser->lexer, 1);
+
                 njs_parser_next(parser, njs_parser_variable_declaration_list);
                 return NJS_OK;
             }
@@ -5726,10 +5947,16 @@ njs_parser_for_var_in_of_expression(njs_
      * "of" <AssignmentExpression> ")" <Statement>
      */
 
-    if (parser->node->token_type == NJS_TOKEN_IN) {
+    if (token->type != NJS_TOKEN_SEMICOLON &&
+        token->type != NJS_TOKEN_CLOSE_PARENTHESIS &&
+        parser->node != NULL && parser->node->token_type == NJS_TOKEN_IN)
+    {
         node = parser->node->left;
 
-        if (node->token_type != NJS_TOKEN_NAME) {
+        if (node->token_type != NJS_TOKEN_NAME &&
+            node->token_type != NJS_TOKEN_PROPERTY)
+        {
+
             text = (njs_str_t *) parser->target;
 
             njs_parser_ref_error(parser, "Invalid left-hand side \"%V\" "
@@ -5752,6 +5979,8 @@ njs_parser_for_var_in_of_expression(njs_
 
     switch (token->type) {
     case NJS_TOKEN_SEMICOLON:
+        njs_lexer_in_fail_set(parser->lexer, 0);
+
         token = njs_lexer_peek_token(parser->lexer, token, 0);
         if (token == NULL) {
             return NJS_ERROR;
@@ -5820,6 +6049,36 @@ njs_parser_for_in_statement(njs_parser_t
 
 
 static njs_int_t
+njs_parser_for_in_statement_statement(njs_parser_t *parser,
+    njs_lexer_token_t *token, njs_queue_link_t *current)
+{
+    njs_parser_node_t  *forin;
+
+    if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) {
+        return njs_parser_failed(parser);
+    }
+
+    njs_lexer_consume_token(parser->lexer, 1);
+
+    parser->target->right = parser->node;
+
+    forin = njs_parser_node_new(parser, NJS_TOKEN_FOR_IN);
+    if (forin == NULL) {
+        return NJS_ERROR;
+    }
+
+    forin->left = parser->target;
+
+    parser->node = NULL;
+
+    njs_parser_next(parser, njs_parser_statement_wo_node);
+
+    return njs_parser_after(parser, current, forin, 1,
+                            njs_parser_for_in_statement_after);
+}
+
+
+static njs_int_t
 njs_parser_for_in_statement_after(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current)
 {
@@ -8263,6 +8522,12 @@ njs_parser_template_string(njs_parser_t 
             if (p < lexer->end && *p == '{') {
                 p++;
                 text->length = p - text->start - 2;
+
+                ret = njs_lexer_in_stack_push(lexer);
+                if (njs_slow_path(ret != NJS_OK)) {
+                    return NJS_ERROR;
+                }
+



More information about the nginx-devel mailing list