[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