diff --git a/src/njs_generator.c b/src/njs_generator.c index 1cd57df6e..37ecea083 100644 --- a/src/njs_generator.c +++ b/src/njs_generator.c @@ -117,6 +117,15 @@ njs_generate_is_property_lvalue(njs_parser_node_t *node) } +static njs_bool_t +njs_generate_is_declaration(njs_parser_node_t *node) +{ + return node->token_type == NJS_TOKEN_VAR + || node->token_type == NJS_TOKEN_LET + || node->token_type == NJS_TOKEN_CONST; +} + + njs_inline njs_bool_t njs_generate_is_property_call_source(njs_parser_node_t *node) { @@ -295,8 +304,6 @@ 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, @@ -2085,28 +2092,40 @@ njs_generate_for_end(njs_vm_t *vm, njs_generator_t *generator, static njs_int_t -njs_generate_for_let_update(njs_vm_t *vm, njs_generator_t *generator, - njs_parser_node_t *node) +njs_generate_let_update(njs_vm_t *vm, njs_generator_t *generator, + njs_parser_node_t *let) { - njs_parser_node_t *let; njs_vmcode_variable_t *code_var; njs_variable_reference_t *ref; - while (node != NULL && node->token_type == NJS_TOKEN_STATEMENT) { - let = node->right; + if (let->token_type != NJS_TOKEN_LET + && let->token_type != NJS_TOKEN_CONST) + { + return NJS_DECLINED; + } - if (let->token_type != NJS_TOKEN_LET - && let->token_type != NJS_TOKEN_CONST) - { - return NJS_OK; - } + ref = &let->left->u.reference; - ref = &let->left->u.reference; + if (ref->variable->closure) { + njs_generate_code(generator, njs_vmcode_variable_t, code_var, + NJS_VMCODE_LET_UPDATE, let); + code_var->dst = let->left->index; + } + + return NJS_OK; +} + + +static njs_int_t +njs_generate_for_let_update(njs_vm_t *vm, njs_generator_t *generator, + njs_parser_node_t *node) +{ + njs_int_t ret; - if (ref->variable->closure) { - njs_generate_code(generator, njs_vmcode_variable_t, code_var, - NJS_VMCODE_LET_UPDATE, let); - code_var->dst = let->left->index; + while (node != NULL && node->token_type == NJS_TOKEN_STATEMENT) { + ret = njs_generate_let_update(vm, generator, node->right); + if (ret != NJS_OK) { + return (ret == NJS_DECLINED) ? NJS_OK : ret; } node = node->left; @@ -2198,81 +2217,18 @@ njs_generate_for_in_name_assign(njs_vm_t *vm, njs_generator_t *generator, } -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, 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_parser_node_t *foreach; 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, foreach); @@ -2299,7 +2255,7 @@ njs_generate_for_in_object_wo_decl(njs_vm_t *vm, njs_generator_t *generator, ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, - njs_generate_for_in_body_wo_decl, ctx, 0); + njs_generate_for_in_body, ctx, 0); if (ret != NJS_OK) { return ret; } @@ -2340,29 +2296,25 @@ njs_generate_for_in_statement(njs_vm_t *vm, njs_generator_t *generator, foreach = node->left; - if (!njs_generate_is_property_lvalue(foreach->left)) { - name = foreach->left->right; + if (njs_generate_is_declaration(foreach->left)) { + name = foreach->left->left; - if (name != NULL) { - name = name->left; - - ret = njs_generate_variable_wo_dest(vm, generator, name, + 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; + ctx.index_next_value = name->index; - njs_generator_next(generator, njs_generate, foreach->right); + 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)); - } + return njs_generator_after(vm, generator, + njs_queue_first(&generator->stack), node, + njs_generate_for_in_object, + &ctx, sizeof(njs_generator_loop_ctx_t)); - } else { + } else if (njs_generate_is_property_lvalue(foreach->left)) { /* foreach->right is object in 'in object'. */ @@ -2373,14 +2325,19 @@ njs_generate_for_in_statement(njs_vm_t *vm, njs_generator_t *generator, njs_generate_for_in_object_left_hand_expr, &ctx, sizeof(njs_generator_loop_ctx_t)); + } else if (foreach->left->token_type == NJS_TOKEN_NAME) { + + 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_wo_decl, + &ctx, sizeof(njs_generator_loop_ctx_t)); } - njs_generator_next(generator, njs_generate, foreach->right); + njs_internal_error(vm, "unexpected for-in target"); - return njs_generator_after(vm, generator, - njs_queue_first(&generator->stack), node, - njs_generate_for_in_object_wo_decl, - &ctx, sizeof(njs_generator_loop_ctx_t)); + return NJS_ERROR; } @@ -2487,18 +2444,14 @@ static njs_int_t njs_generate_for_in_object(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { - njs_parser_node_t *foreach, *name; + njs_parser_node_t *foreach; 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; - } + ctx->var->init = 1; njs_generate_code(generator, njs_vmcode_prop_foreach_t, prop_foreach, NJS_VMCODE_PROPERTY_FOREACH, foreach); @@ -2578,20 +2531,19 @@ njs_generate_for_in_body(njs_vm_t *vm, njs_generator_t *generator, { njs_int_t ret; njs_jump_off_t prop_offset; - njs_parser_node_t *foreach, *name; + njs_parser_node_t *foreach; 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)) { + if (njs_generate_is_declaration(foreach->left)) { + ret = njs_generate_let_update(vm, generator, foreach->left); + if (njs_slow_path(ret != NJS_OK && ret != NJS_DECLINED)) { return ret; } } @@ -2605,7 +2557,7 @@ njs_generate_for_in_body(njs_vm_t *vm, njs_generator_t *generator, njs_generate_code(generator, njs_vmcode_prop_next_t, prop_next, NJS_VMCODE_PROPERTY_NEXT, node->left->left); prop_offset = njs_code_offset(generator, prop_next); - prop_next->retval = foreach->left->index; + 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; diff --git a/src/njs_lexer.c b/src/njs_lexer.c index b5c2ce121..b0448a516 100644 --- a/src/njs_lexer.c +++ b/src/njs_lexer.c @@ -304,105 +304,6 @@ njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file, 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->vm->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->vm->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->vm->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; } @@ -428,11 +329,6 @@ 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 --git a/src/njs_lexer.h b/src/njs_lexer.h index 98b8dd87d..a0b37b4ba 100644 --- a/src/njs_lexer.h +++ b/src/njs_lexer.h @@ -266,11 +266,6 @@ 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; @@ -283,11 +278,6 @@ njs_lexer_token_t *njs_lexer_peek_token(njs_lexer_t *lexer, 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, diff --git a/src/njs_parser.c b/src/njs_parser.c index c54cd55df..661728eef 100644 --- a/src/njs_parser.c +++ b/src/njs_parser.c @@ -26,14 +26,6 @@ static njs_int_t njs_parser_template_literal_string(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_template_literal_expression( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); -static njs_int_t njs_parser_cover_parenthesized_expression( - njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); -static njs_int_t njs_parser_binding_identifier_pattern(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current); -static njs_int_t njs_parser_cover_parenthesized_expression_after( - njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); -static njs_int_t njs_parser_cover_parenthesized_expression_end( - njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_array_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); @@ -326,17 +318,6 @@ static njs_int_t njs_parser_while_after(njs_parser_t *parser, 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_expression_map_continue( - njs_parser_t *parser, njs_lexer_token_t *token, - njs_queue_link_t *current); -static njs_int_t njs_parser_for_expression_map_reparse( - 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, @@ -344,14 +325,8 @@ static njs_int_t njs_parser_iteration_statement_for_map(njs_parser_t *parser, 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_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, - njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_var_in_of_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); -static njs_int_t njs_parser_for_in_statement(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_in_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_expression(njs_parser_t *parser, @@ -550,6 +525,8 @@ njs_parser_reject(njs_parser_t *parser) if (!entry->optional) { njs_parser_next(parser, entry->state); parser->target = entry->node; + parser->allow_in = entry->allow_in; + parser->var_type = entry->var_type; return NJS_DECLINED; } @@ -572,8 +549,6 @@ njs_parser_init(njs_vm_t *vm, njs_parser_t *parser, njs_parser_scope_t *scope, lexer = &parser->lexer0; parser->lexer = lexer; - parser->use_lhs = 0; - return njs_lexer_init(vm, lexer, file, start, end); } @@ -605,6 +580,7 @@ njs_parser(njs_vm_t *vm, njs_parser_t *parser) njs_queue_init(&parser->stack); parser->target = NULL; + parser->allow_in = 1; njs_parser_next(parser, njs_parser_statement_list); ret = njs_parser_after(parser, njs_queue_first(&parser->stack), @@ -1195,15 +1171,12 @@ njs_parser_primary_expression_test(njs_parser_t *parser, case NJS_TOKEN_OPEN_PARENTHESIS: njs_lexer_consume_token(parser->lexer, 1); - /* TODO: By specification. */ - (void) njs_parser_cover_parenthesized_expression; - parser->node = NULL; njs_parser_next(parser, njs_parser_expression); - return njs_parser_after(parser, current, NULL, 0, - njs_parser_close_parenthesis); + return njs_parser_after_in(parser, current, NULL, 0, + njs_parser_close_parenthesis); default: if (njs_lexer_token_is_identifier_reference(token)) { @@ -1449,8 +1422,8 @@ njs_parser_template_literal_string(njs_parser_t *parser, njs_lexer_consume_token(parser->lexer, 1); - return njs_parser_after(parser, current, parser->target, 0, - njs_parser_template_literal_expression); + return njs_parser_after_in(parser, current, parser->target, 0, + njs_parser_template_literal_expression); } @@ -1507,145 +1480,6 @@ njs_parser_template_literal_expression(njs_parser_t *parser, } -static njs_int_t -njs_parser_cover_parenthesized_expression(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current) -{ - switch (token->type) { - case NJS_TOKEN_CLOSE_PARENTHESIS: - (void) njs_parser_stack_pop(parser); - break; - - case NJS_TOKEN_ELLIPSIS: - njs_parser_next(parser, njs_parser_binding_identifier_pattern); - break; - - default: - parser->node = NULL; - - njs_parser_next(parser, njs_parser_expression); - - return njs_parser_after(parser, current, NULL, 0, - njs_parser_cover_parenthesized_expression_after); - } - - njs_lexer_consume_token(parser->lexer, 1); - - return NJS_OK; -} - - -static njs_int_t -njs_parser_binding_identifier_pattern(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current) -{ - /* - * BindingIdentifier ) - * BindingPattern ) - */ - - switch (token->type) { - - /* BindingIdentifier */ - case NJS_TOKEN_NAME: - njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); - break; - - case NJS_TOKEN_YIELD: - njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); - break; - - case NJS_TOKEN_AWAIT: - njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); - break; - - /* BindingPattern */ - case NJS_TOKEN_OPEN_BRACKET: - njs_parser_next(parser, njs_parser_array_binding_pattern); - - njs_lexer_consume_token(parser->lexer, 1); - return njs_parser_after(parser, current, NULL, 0, - njs_parser_cover_parenthesized_expression_end); - - case NJS_TOKEN_OPEN_BRACE: - njs_parser_next(parser, njs_parser_object_binding_pattern); - - njs_lexer_consume_token(parser->lexer, 1); - return njs_parser_after(parser, current, NULL, 0, - njs_parser_cover_parenthesized_expression_end); - - default: - return NJS_ERROR; - } - - njs_lexer_consume_token(parser->lexer, 1); - - return NJS_OK; -} - - -static njs_int_t -njs_parser_cover_parenthesized_expression_after(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current) -{ - /* - * ) - * ,) - * , ... BindingIdentifier ) - * , ... BindingPattern ) - */ - - if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { - goto shift_stack; - } - - if (token->type != NJS_TOKEN_COMMA) { - return njs_parser_failed(parser); - } - - njs_lexer_consume_token(parser->lexer, 1); - - token = njs_lexer_token(parser->lexer, 0); - if (njs_slow_path(token == NULL)) { - return NJS_ERROR; - } - - if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { - goto shift_stack; - } - - if(token->type != NJS_TOKEN_ELLIPSIS) { - return njs_parser_failed(parser); - } - - njs_lexer_consume_token(parser->lexer, 1); - - njs_parser_next(parser, njs_parser_binding_identifier_pattern); - - return NJS_OK; - -shift_stack: - - njs_lexer_consume_token(parser->lexer, 1); - - return njs_parser_stack_pop(parser); -} - - -static njs_int_t -njs_parser_cover_parenthesized_expression_end(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current) -{ - if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { - return njs_parser_failed(parser); - } - - njs_lexer_consume_token(parser->lexer, 1); - - return njs_parser_stack_pop(parser); -} - - /* * 12.2.5 Array Initializer. */ @@ -1655,6 +1489,7 @@ njs_parser_array_literal(njs_parser_t *parser, njs_lexer_token_t *token, { parser->target = parser->node; parser->node = NULL; + parser->allow_in = 1; njs_parser_next(parser, njs_parser_array_element_list); @@ -1779,8 +1614,8 @@ njs_parser_object_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_parser_next(parser, njs_parser_property_definition_list); - return njs_parser_after(parser, current, node, 1, - njs_parser_object_literal_after); + return njs_parser_after_in(parser, current, node, 1, + njs_parser_object_literal_after); } @@ -2309,8 +2144,8 @@ njs_parser_property(njs_parser_t *parser, njs_lexer_token_t *token, njs_parser_next(parser, njs_parser_expression); - return njs_parser_after(parser, current, node, 1, - njs_parser_member_expression_bracket); + return njs_parser_after_in(parser, current, node, 1, + njs_parser_member_expression_bracket); case NJS_TOKEN_DOT: token = njs_lexer_peek_token(parser->lexer, token, 0); @@ -3077,8 +2912,8 @@ njs_parser_arguments(njs_parser_t *parser, njs_lexer_token_t *token, njs_parser_next(parser, njs_parser_argument_list); - return njs_parser_after(parser, current, NULL, 1, - njs_parser_parenthesis_or_comma); + return njs_parser_after_in(parser, current, NULL, 1, + njs_parser_parenthesis_or_comma); } @@ -3923,17 +3758,11 @@ njs_parser_exponentiation_expression(njs_parser_t *parser, { parser->target = NULL; - if (parser->use_lhs == 0) { - njs_parser_next(parser, njs_parser_unary_expression); + njs_parser_next(parser, njs_parser_unary_expression); - /* For UpdateExpression, see njs_parser_unary_expression_after. */ + /* 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); - } + return NJS_OK; } @@ -4208,10 +4037,10 @@ njs_parser_relational_expression_match(njs_parser_t *parser, 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; + if (!parser->allow_in) { + return njs_parser_stack_pop(parser); } + operation = NJS_VMCODE_PROPERTY_IN; break; @@ -4529,14 +4358,10 @@ njs_parser_conditional_question_mark(njs_parser_t *parser, 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, - njs_parser_conditional_colon); + return njs_parser_after_in(parser, current, cond, 1, + njs_parser_conditional_colon); } @@ -4550,8 +4375,6 @@ njs_parser_conditional_colon(njs_parser_t *parser, njs_lexer_token_t *token, return njs_parser_failed(parser); } - njs_lexer_in_stack_pop(parser->lexer); - njs_lexer_consume_token(parser->lexer, 1); node = parser->target->right; @@ -4592,16 +4415,14 @@ njs_parser_assignment_expression(njs_parser_t *parser, { njs_int_t ret; - if (!parser->use_lhs) { - ret = njs_parser_match_arrow_expression(parser, token); - if (ret == NJS_OK) { - njs_parser_next(parser, njs_parser_arrow_function); + ret = njs_parser_match_arrow_expression(parser, token); + if (ret == NJS_OK) { + njs_parser_next(parser, njs_parser_arrow_function); - return NJS_OK; + return NJS_OK; - } else if (ret == NJS_ERROR) { - return NJS_ERROR; - } + } else if (ret == NJS_ERROR) { + return NJS_ERROR; } njs_parser_next(parser, njs_parser_conditional_expression); @@ -5801,182 +5622,11 @@ njs_parser_iteration_statement_for(njs_parser_t *parser, } -static njs_int_t -njs_parser_for_expression_map_reparse(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current) -{ - if (parser->ret != NJS_OK && parser->node != NULL) { - return njs_parser_failed(parser); - } - - if (parser->node == NULL) { - njs_lexer_in_fail_set(parser->lexer, 1); - - njs_parser_next(parser, njs_parser_expression); - - return NJS_OK; - } - - return njs_parser_stack_pop(parser); -} - - -static njs_int_t -njs_parser_for_expression_map_continue(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 (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 (!njs_parser_is_lvalue(parser->node)) { - 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; - } - - parser->node = njs_parser_lvalue_ref(parser->node); - if (parser->node == NULL) { - return NJS_ERROR; - } - - 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) -{ - return njs_parser_right_link_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) -{ - if (parser->ret != NJS_OK) { - return njs_parser_failed(parser); - } - - 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) { njs_int_t ret; - njs_str_t *text; njs_token_type_t token_type; /* @@ -6047,61 +5697,18 @@ njs_parser_iteration_statement_for_map(njs_parser_t *parser, goto expression_after; - case NJS_TOKEN_AWAIT: - njs_parser_next(parser, njs_parser_expression); - - goto expression_after; - default: - 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->allow_in = 0; 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; - - ret = njs_parser_after(parser, current, text, 0, - njs_parser_for_expression_map_reparse); - if (ret != NJS_OK) { - return NJS_ERROR; - } + njs_parser_next(parser, njs_parser_expression); - return njs_parser_after(parser, current, text, 1, - njs_parser_for_expression_map_continue); + goto expression_after; } expression_after: - /* - * 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, + return njs_parser_after(parser, current, NULL, 1, njs_parser_for_var_in_of_expression); } @@ -6111,9 +5718,6 @@ njs_parser_for_var_binding_or_var_list(njs_parser_t *parser, 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) { @@ -6141,113 +5745,17 @@ njs_parser_for_var_binding_or_var_list(njs_parser_t *parser, return NJS_DONE; default: - if (njs_lexer_token_is_binding_identifier(token)) { - if (njs_parser_restricted_identifier(token->type)) { - njs_parser_syntax_error(parser, "Identifier \"%V\" is forbidden" - " in var declaration", &token->text); - return NJS_DONE; - } - - next = njs_lexer_peek_token(parser->lexer, token, 0); - if (next == NULL) { - return NJS_ERROR; - } - - 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; - } - - 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->atom_id, 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); - if (node == NULL) { - return NJS_ERROR; - } - - node->token_line = next->line; - node->left = statement; - - njs_parser_next(parser, njs_parser_expression); - - ret = njs_parser_after(parser, current, node, 1, - njs_parser_for_var_in_statement); - if (ret != NJS_OK) { - return NJS_ERROR; - } - - njs_lexer_consume_token(parser->lexer, 2); - - return NJS_DONE; - - } else { + if (!njs_lexer_token_is_binding_identifier(token)) { return njs_parser_failed(parser); } - } -} + parser->var_type = type; + parser->allow_in = 0; -static njs_int_t -njs_parser_for_var_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, - njs_queue_link_t *current) -{ - if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { - return njs_parser_failed(parser); - } + njs_parser_next(parser, njs_parser_variable_declaration_list); - njs_lexer_consume_token(parser->lexer, 1); - - parser->target->right = parser->node; - parser->node = NULL; - - njs_parser_next(parser, njs_parser_statement_wo_node); - - return njs_parser_after(parser, current, parser->target, 1, - njs_parser_for_var_in_statement_after); -} - - -static njs_int_t -njs_parser_for_var_in_statement_after(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current) -{ - njs_parser_node_t *foreach; - - foreach = njs_parser_node_new(parser, NJS_TOKEN_FOR_IN); - if (foreach == NULL) { - return NJS_ERROR; + return NJS_OK; } - - foreach->left = parser->target; - foreach->right = parser->node; - - parser->node = foreach; - - return njs_parser_stack_pop(parser); } @@ -6255,7 +5763,6 @@ static njs_int_t njs_parser_for_var_in_of_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { - njs_str_t *text; njs_parser_node_t *node; /* @@ -6264,44 +5771,9 @@ njs_parser_for_var_in_of_expression(njs_parser_t *parser, * "of" ")" */ - 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 (!njs_parser_is_lvalue(node)) { - - 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; - } - - node = njs_parser_lvalue_ref(node); - if (node == NULL) { - return NJS_ERROR; - } - - parser->node->left = node; - - njs_parser_next(parser, njs_parser_for_in_statement); - return NJS_OK; - } - - if (parser->target != NULL) { - text = (njs_str_t *) parser->target; - - njs_mp_free(parser->vm->mem_pool, text); - } - switch (token->type) { case NJS_TOKEN_SEMICOLON: - njs_lexer_in_fail_set(parser->lexer, 0); + parser->allow_in = 1; token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { @@ -6327,46 +5799,62 @@ njs_parser_for_var_in_of_expression(njs_parser_t *parser, return NJS_OK; - case NJS_TOKEN_OF: - return njs_parser_not_supported(parser, token); + case NJS_TOKEN_IN: + if (njs_parser_is_declaration(parser->node)) { + if (parser->node->left != NULL + || parser->node->right->right != NULL) + { + njs_parser_syntax_error(parser, + "Invalid left-hand side in for-loop"); + return NJS_ERROR; + } - default: - return njs_parser_failed(parser); - } -} + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; + } + node->token_line = token->line; + node->left = parser->node->right; -static njs_int_t -njs_parser_for_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, - njs_queue_link_t *current) -{ - njs_parser_node_t *node, *forin; + } else { + if (!njs_parser_is_lvalue(parser->node)) { + njs_parser_syntax_error(parser, + "Invalid left-hand side in for-loop"); + return NJS_ERROR; + } - if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { - return njs_parser_failed(parser); - } + parser->node = njs_parser_lvalue_ref(parser->node); + if (parser->node == NULL) { + return NJS_ERROR; + } - njs_lexer_consume_token(parser->lexer, 1); + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; + } - node = parser->node; + node->token_line = token->line; + node->u.operation = NJS_VMCODE_PROPERTY_IN; + node->left = parser->node; + node->left->dest = node; + } - if (node->right != NULL && node->right->token_type == NJS_TOKEN_VAR) { - return NJS_ERROR; - } + njs_lexer_consume_token(parser->lexer, 1); - forin = njs_parser_node_new(parser, NJS_TOKEN_FOR_IN); - if (forin == NULL) { - return NJS_ERROR; - } + parser->allow_in = 1; - forin->left = parser->node; + njs_parser_next(parser, njs_parser_expression); - parser->node = NULL; + return njs_parser_after(parser, current, node, 0, + njs_parser_for_in_statement_statement); - njs_parser_next(parser, njs_parser_statement_wo_node); + case NJS_TOKEN_OF: + return njs_parser_not_supported(parser, token); - return njs_parser_after(parser, current, forin, 1, - njs_parser_for_in_statement_after); + default: + return njs_parser_failed(parser); + } } @@ -7860,8 +7348,8 @@ njs_parser_arrow_function_arrow(njs_parser_t *parser, njs_parser_next(parser, njs_parser_statement_list); - return njs_parser_after(parser, current, parser->target, 1, - njs_parser_function_lambda_body_after); + return njs_parser_after_in(parser, current, parser->target, 1, + njs_parser_function_lambda_body_after); } parser->node = NULL; @@ -8222,8 +7710,8 @@ njs_parser_function_lambda_args_after(njs_parser_t *parser, njs_parser_next(parser, njs_parser_statement_list); - return njs_parser_after(parser, current, parser->target, 1, - njs_parser_function_lambda_body_after); + return njs_parser_after_in(parser, current, parser->target, 1, + njs_parser_function_lambda_body_after); } @@ -8959,11 +8447,6 @@ njs_parser_template_string(njs_parser_t *parser, njs_lexer_token_t *token) p++; text->length = p - text->start - 2; - ret = njs_lexer_in_stack_push(lexer); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; - } - goto done; } diff --git a/src/njs_parser.h b/src/njs_parser.h index 1e760454d..1347fb33f 100644 --- a/src/njs_parser.h +++ b/src/njs_parser.h @@ -79,7 +79,8 @@ struct njs_parser_s { njs_variable_type_t var_type; njs_int_t ret; - uint8_t use_lhs; + /* The ECMAScript [In] grammar parameter. */ + uint8_t allow_in; uint8_t module; @@ -94,7 +95,9 @@ typedef struct { njs_parser_node_t *node; - njs_bool_t optional; + njs_variable_type_t var_type; + uint8_t allow_in; + uint8_t optional; } njs_parser_stack_entry_t; @@ -157,6 +160,14 @@ njs_int_t njs_parser_serialize_ast(njs_parser_node_t *node, njs_chb_t *chain); || (node)->token_type == NJS_TOKEN_PROPERTY_REF) +#define njs_parser_is_declaration(node) \ + ((node) != NULL && (node)->token_type == NJS_TOKEN_STATEMENT \ + && (node)->right != NULL \ + && ((node)->right->token_type == NJS_TOKEN_VAR \ + || (node)->right->token_type == NJS_TOKEN_LET \ + || (node)->right->token_type == NJS_TOKEN_CONST)) + + #define njs_parser_is_primitive(node) \ ((node)->token_type >= NJS_TOKEN_NULL \ && (node)->token_type <= NJS_TOKEN_STRING) @@ -301,6 +312,8 @@ njs_parser_stack_pop(njs_parser_t *parser) njs_parser_next(parser, entry->state); parser->target = entry->node; + parser->allow_in = entry->allow_in; + parser->var_type = entry->var_type; njs_mp_free(parser->vm->mem_pool, entry); @@ -326,6 +339,11 @@ njs_parser_stack_pop(njs_parser_t *parser) #endif + +#define njs_parser_after_in(_p, _l, _n, _opt, _state) \ + njs_parser_allow_in(_p, njs_parser_after(_p, _l, _n, _opt, _state)) + + njs_inline njs_int_t _njs_parser_after(njs_parser_t *parser, njs_queue_link_t *link, void *node, njs_bool_t is_optional, njs_parser_state_func_t state) @@ -340,6 +358,8 @@ _njs_parser_after(njs_parser_t *parser, njs_queue_link_t *link, void *node, entry->state = state; entry->node = node; + entry->allow_in = parser->allow_in; + entry->var_type = parser->var_type; entry->optional = is_optional; njs_queue_insert_before(link, &entry->link); @@ -348,6 +368,21 @@ _njs_parser_after(njs_parser_t *parser, njs_queue_link_t *link, void *node, } +/* Opens a [+In] region; the frame must already carry the enclosing value. */ + +njs_inline njs_int_t +njs_parser_allow_in(njs_parser_t *parser, njs_int_t ret) +{ + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + parser->allow_in = 1; + + return NJS_OK; +} + + njs_inline njs_int_t njs_parser_failed(njs_parser_t *parser) { diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 8ffd3a97b..4d104d949 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -3403,11 +3403,56 @@ static njs_unit_test_t njs_test[] = /* for in. */ + /* An invalid for-in target is a SyntaxError, parenthesized or not. */ + { njs_str("for (null in undefined);"), - njs_str("ReferenceError: Invalid left-hand side \"null\" in for-in statement") }, + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for ((null) in undefined);"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for (1 in {});"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for (this in {});"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for (function(){} in x);"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for ((a = 1) in {});"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for ((x => x) in {});"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("var r; try { Function('for (null in undefined);') }" + "catch (e) { r = e.name } r"), + njs_str("SyntaxError") }, + + { njs_str("var d = {}; for (d.a in {x:1}); d.a"), + njs_str("x") }, + + { njs_str("var d = {}; for ((d.a) in {x:1}); d.a"), + njs_str("x") }, { njs_str("for (var a, b in []);"), - njs_str("SyntaxError: Unexpected token \"in\"") }, + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for (var x = 0 in o);"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + /* [+In] is restored past the initializer. */ + + { njs_str("var b = 'a', c = {a:1}, n = 0;" + "for (var k in b in c) { n++ } n"), + njs_str("0") }, + + { njs_str("var a = 0; for (; 1 in [1]; 1 in [1]) break; a"), + njs_str("0") }, + + { njs_str("var a = 0; for (a < 1 in {};;) break; a"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, { njs_str("var s = ''; for (var p in [1,2]) {s += p}; s"), njs_str("01") }, @@ -3418,6 +3463,53 @@ static njs_unit_test_t njs_test[] = { njs_str("var s = ''; for (var p in {a:1, b:2}) {s += p}; s"), njs_str("ab") }, + /* + * A declaration header is parsed once, under [~In], and classified by the + * token it stopped at, like an expression header. + */ + + { njs_str("for (let a, b in []);"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for (let a = 0 in {});"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for (const a = 0 in {});"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for (var a = 0, b in {});"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("var s = ''; for (const k in {a:1, b:2}) {s += k}; s"), + njs_str("ab") }, + + /* The three for-in target routes, each observed over two iterations. */ + + { njs_str("var r = []; for (let k in {a:1, b:2}) {r.push(() => k)};" + "r[0]() + ',' + r[1]()"), + njs_str("a,b") }, + + { njs_str("var r = []; for (var k in {a:1, b:2}) {r.push(() => k)};" + "r[0]() + ',' + r[1]()"), + njs_str("b,b") }, + + { njs_str("var k, s = ''; for (k in {a:1, b:2}) {s += k}; s + k"), + njs_str("abb") }, + + { njs_str("var o = {}, s = ''; for (o.p in {a:1, b:2}) {s += o.p};" + "s + o.p"), + njs_str("abb") }, + + { njs_str("var o = [], s = ''; for (o[0] in {a:1, b:2}) {s += o[0]};" + "s + o[0]"), + njs_str("abb") }, + + { njs_str("var k, s = ''; for ((k) in {a:1, b:2}) {s += k}; s + k"), + njs_str("abb") }, + + { njs_str("var s = 'x'; for (let k in {}) {s = 'y'}; s"), + njs_str("x") }, + { njs_str("var s = '';" "var o = Object.defineProperty({}, 'x', {value:1});" "Object.defineProperty(o, 'y', {value:2, enumerable:true});" @@ -3488,6 +3580,56 @@ static njs_unit_test_t njs_test[] = { njs_str("for (in + j;;) {}"), njs_str("SyntaxError: Unexpected token \"in\"") }, + { njs_str("for (var x = [1 in {}];;) break;"), + njs_str("undefined") }, + + { njs_str("for (var x = true ? 1 in {} : 0;;) break;"), + njs_str("undefined") }, + + /* + * A delimiter in the conditional consequent used to desynchronize the + * [In] parameter from the parser position. + */ + + { njs_str("for (a ? (b) : c in d; false;);"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for (a ? b : c in d; false;);"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("var a=1,b=2,c=3; for (a ? (b) : c; false;) break; a"), + njs_str("1") }, + + /* [+In] regions, past the speculative left-hand side parse. */ + + { njs_str("var a=1; for (a, (1 in {});;) break; a"), + njs_str("1") }, + + { njs_str("var a=1; for (a, `${1 in {}}`;;) break; a"), + njs_str("1") }, + + { njs_str("var a=1; for (a, function(){ return 1 in {} };;) break; a"), + njs_str("1") }, + + /* ... and [~In] must be restored after each of them. */ + + { njs_str("for (a, [1], b in c;;) break;"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for (a, `${1}`, b in c;;) break;"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + /* An arrow concise body inherits [In], an arrow block body does not. */ + + { njs_str("for (x => 1 in {};;) break;"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("for (x => x, 1 in {};;) break;"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("var a=1; for (x => { return 1 in {} };;) break; a"), + njs_str("1") }, + { njs_str("for (true ? 0 in {}: 0; false; ) ;"), njs_str("undefined") }, @@ -3530,6 +3672,39 @@ static njs_unit_test_t njs_test[] = { njs_str("for(i;;)for(-new+3;;)break;"), njs_str("SyntaxError: Unexpected token \"+\"") }, + { njs_str("for(function(){r({/a/;0;1)1"), + njs_str("SyntaxError: Unexpected token \"/\"") }, + + { njs_str("for(a(function(){r({/a/;0;1)1"), + njs_str("SyntaxError: Unexpected token \"/\"") }, + + { njs_str("for(async function(){r({/a/;0;1)1"), + njs_str("SyntaxError: Unexpected token \"/\"") }, + + { njs_str("for({/a/;0;1)1"), + njs_str("SyntaxError: Unexpected token \"/\"") }, + + { njs_str("for(function f(){}.x in {a:1}); 1"), + njs_str("1") }, + + { njs_str("var o={}; for(function(){return o}().x in {a:1,b:2}); o.x"), + njs_str("b") }, + + { njs_str("async function f(){for(await p in o;;)break;} f()"), + njs_str("SyntaxError: Invalid left-hand side in for-loop") }, + + { njs_str("var a=0; for(-a;;)break; a"), + njs_str("0") }, + + { njs_str("var a=0; for(typeof a;;)break; a"), + njs_str("0") }, + + { njs_str("var a=0; for(++a;;)break; a"), + njs_str("1") }, + + { njs_str("var a={b:0}; for(delete a.b;;)break; a.b"), + njs_str("undefined") }, + /* switch. */ { njs_str("switch"), @@ -20563,6 +20738,49 @@ static njs_unit_test_t njs_test[] = "} res"), njs_str("0,0,0,0,0") }, + /* + * A declaration nested in a declarator initializer must not change the + * kind of the declarators that follow it. + */ + + { njs_str("let out = [];" + "for (let a = function() { var z; }, b = 0; b < 2; b++) {" + " out.push(() => b);" + "} out[0]() + ',' + out[1]()"), + njs_str("0,1") }, + + { njs_str("let out = [];" + "for (let a = 1, b = function() { var z; }, c = 0; c < 2; c++) {" + " out.push(() => c);" + "} out[0]() + ',' + out[1]()"), + njs_str("0,1") }, + + { njs_str("let out = [];" + "for (var a = function() { let z; }, b = 0; b < 2; b++) {" + " out.push(() => b);" + "} out[0]() + ',' + out[1]()"), + njs_str("2,2") }, + + { njs_str("var r;" + "for (const a = function() { var z; }, b = 2; b > 0;) {" + " try { b = 3 } catch (e) { r = e.name } break" + "} r"), + njs_str("TypeError") }, + + /* The same state serves an ordinary declaration list. */ + + { njs_str("var r;" + "const a = function() { var z; }, b = 2;" + "try { b = 3 } catch (e) { r = e.name } r"), + njs_str("TypeError") }, + + { njs_str("let a = function() { var z; }, b = 0;" + "for (; b < 2; b++) {} b"), + njs_str("2") }, + + { njs_str("let a = function() { var ; }, b = 0;"), + njs_str("SyntaxError: Unexpected token \";\"") }, + { njs_str("let arr = [], res = [];" "for (let i = 0; arr.push(() => i), i < 10; i++) {}" "for (let k = 0; k < 10; k++) {res.push(arr[k]())}" @@ -21754,6 +21972,16 @@ static njs_unit_test_t njs_externals_test[] = "f().then($r.retval)"), njs_str("X:4") }, + { njs_str("async function f() {" + " var r = [];" + " for (var k in await Promise.resolve({a:1, b:2})) {" + " r.push(k);" + " }" + " return r.join(':');" + "}" + "f().then($r.retval)"), + njs_str("a:b") }, + { njs_str("async function f() {" " return ((...a) => a[1] + ':' + a[2] + ':' + a[0].length)" " `a${await Promise.resolve(2)}b"