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;
}
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;
}
if (!entry->optional) {
njs_parser_next(parser, entry->state);
parser->target = entry->node;
+ parser->allow_in = entry->allow_in;
return NJS_DECLINED;
}
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),
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)) {
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);
}
{
parser->target = parser->node;
parser->node = NULL;
+ parser->allow_in = 1;
njs_parser_next(parser, njs_parser_array_element_list);
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);
}
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);
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);
}
break;
case NJS_TOKEN_IN:
- if (njs_lexer_in_fail_get(parser->lexer)) {
+ if (!parser->allow_in) {
njs_parser_syntax_error(parser, "Invalid left-hand side in for-loop");
return NJS_ERROR;
}
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);
}
return njs_parser_failed(parser);
}
- njs_lexer_in_stack_pop(parser->lexer);
-
njs_lexer_consume_token(parser->lexer, 1);
node = parser->target->right;
njs_parser_node_t *node;
if (token->type != NJS_TOKEN_IN) {
- njs_lexer_in_fail_set(parser->lexer, 1);
+ parser->allow_in = 0;
/* Continue parsing of expr1 in "for (expr1;[expr2];[expr3])". */
default:
ret = njs_parser_match_arrow_expression(parser, token);
if (ret == NJS_OK) {
+ parser->allow_in = 0;
+
parser->target = NULL;
njs_parser_next(parser, njs_parser_expression);
+
goto expression_after;
} else if (ret == NJS_ERROR) {
return NJS_ERROR;
case NJS_TOKEN_LOGICAL_NOT:
case NJS_TOKEN_INCREMENT:
case NJS_TOKEN_DECREMENT:
- njs_lexer_in_fail_set(parser->lexer, 1);
+ parser->allow_in = 0;
parser->target = NULL;
njs_parser_next(parser, njs_parser_expression);
if (next->type != NJS_TOKEN_IN) {
parser->var_type = type;
- njs_lexer_in_fail_set(parser->lexer, 1);
+ parser->allow_in = 0;
njs_parser_next(parser, njs_parser_variable_declaration_list);
return NJS_OK;
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) {
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;
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);
}
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;
}
njs_variable_type_t var_type;
njs_int_t ret;
+ /* The ECMAScript [In] grammar parameter. */
+ uint8_t allow_in;
+
uint8_t use_lhs;
uint8_t module;
njs_parser_node_t *node;
- njs_bool_t optional;
+ uint8_t allow_in;
+ uint8_t optional;
} njs_parser_stack_entry_t;
njs_parser_next(parser, entry->state);
parser->target = entry->node;
+ parser->allow_in = entry->allow_in;
njs_mp_free(parser->vm->mem_pool, entry);
#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)
entry->state = state;
entry->node = node;
+ entry->allow_in = parser->allow_in;
entry->optional = is_optional;
njs_queue_insert_before(link, &entry->link);
}
+/* 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)
{
{ 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") },