details: https://hg.nginx.org/njs/rev/283ae119d121 branches: changeset: 1997:283ae119d121 user: Vadim Zhestikov <v.zhesti...@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; + } + _______________________________________________ nginx-devel mailing list -- nginx-devel@nginx.org To unsubscribe send an email to nginx-devel-le...@nginx.org