[njs] Introduced let implementation.
Alexander Borisov
alexander.borisov at nginx.com
Fri Apr 30 13:04:45 UTC 2021
details: https://hg.nginx.org/njs/rev/27a4a52621d5
branches:
changeset: 1631:27a4a52621d5
user: Alexander Borisov <alexander.borisov at nginx.com>
date: Fri Apr 30 16:02:28 2021 +0300
description:
Introduced let implementation.
This closes #105 issue on GitHub.
diffstat:
src/njs_builtin.c | 4 +
src/njs_disassembler.c | 11 +
src/njs_generator.c | 336 +++++++++++++++++++++++++++++++++++++++++++---
src/njs_module.c | 3 +-
src/njs_parser.c | 224 +++++++++++++++++++++++++-----
src/njs_parser.h | 5 +
src/njs_scope.c | 6 +-
src/njs_scope.h | 32 +++-
src/njs_variable.c | 56 ++++++-
src/njs_variable.h | 3 +-
src/njs_vmcode.c | 117 ++++++++++++---
src/njs_vmcode.h | 12 +
src/test/njs_benchmark.c | 11 +
src/test/njs_unit_test.c | 237 +++++++++++++++++++++++++++++++++
14 files changed, 942 insertions(+), 115 deletions(-)
diffs (truncated from 1982 to 1000 lines):
diff -r 28f33155ad3f -r 27a4a52621d5 src/njs_builtin.c
--- a/src/njs_builtin.c Fri Apr 30 16:02:20 2021 +0300
+++ b/src/njs_builtin.c Fri Apr 30 16:02:28 2021 +0300
@@ -972,6 +972,10 @@ njs_global_this_prop_handler(njs_vm_t *v
var = node->variable;
+ if (var->type == NJS_VARIABLE_LET) {
+ return NJS_DECLINED;
+ }
+
value = njs_scope_valid_value(vm, var->index);
if (var->type == NJS_VARIABLE_FUNCTION && njs_is_undefined(value)) {
diff -r 28f33155ad3f -r 27a4a52621d5 src/njs_disassembler.c
--- a/src/njs_disassembler.c Fri Apr 30 16:02:20 2021 +0300
+++ b/src/njs_disassembler.c Fri Apr 30 16:02:28 2021 +0300
@@ -139,6 +139,17 @@ static njs_code_name_t code_names[] = {
{ NJS_VMCODE_THROW, sizeof(njs_vmcode_throw_t),
njs_str("THROW ") },
+ { NJS_VMCODE_LET, sizeof(njs_vmcode_variable_t),
+ njs_str("LET ") },
+
+ { NJS_VMCODE_LET_UPDATE, sizeof(njs_vmcode_variable_t),
+ njs_str("LET UPDATE ") },
+
+ { NJS_VMCODE_INITIALIZATION_TEST, sizeof(njs_vmcode_variable_t),
+ njs_str("INIT TEST ") },
+
+ { NJS_VMCODE_NOT_INITIALIZED, sizeof(njs_vmcode_variable_t),
+ njs_str("NOT INIT ") },
};
diff -r 28f33155ad3f -r 27a4a52621d5 src/njs_generator.c
--- a/src/njs_generator.c Fri Apr 30 16:02:20 2021 +0300
+++ b/src/njs_generator.c Fri Apr 30 16:02:28 2021 +0300
@@ -67,6 +67,8 @@ static njs_int_t njs_generate_variable(n
njs_variable_t **retvar);
static njs_int_t njs_generate_var_statement(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_let(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node, njs_variable_t *var);
static njs_int_t njs_generate_if_statement(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_cond_expression(njs_vm_t *vm,
@@ -79,6 +81,10 @@ static njs_int_t njs_generate_do_while_s
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_for_statement(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_for_let_update(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node, size_t depth);
+static njs_int_t njs_generate_for_resolve_closure(njs_vm_t *vm,
+ njs_parser_node_t *node, size_t depth);
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_start_block(njs_vm_t *vm,
@@ -261,6 +267,9 @@ static njs_int_t njs_generate_reference_
##__VA_ARGS__)
+#define NJS_GENERATE_MAX_DEPTH 4096
+
+
static const njs_str_t no_label = njs_str("");
static const njs_str_t return_label = njs_str("@return");
/* GCC and Clang complain about NULL argument passed to memcmp(). */
@@ -277,6 +286,7 @@ njs_generate(njs_vm_t *vm, njs_generator
switch (node->token_type) {
case NJS_TOKEN_VAR:
+ case NJS_TOKEN_LET:
return njs_generate_var_statement(vm, generator, node);
case NJS_TOKEN_IF:
@@ -479,7 +489,7 @@ njs_generator(njs_vm_t *vm, njs_generato
{
njs_int_t ret;
- if (njs_slow_path(generator->count++ > 4096)) {
+ if (njs_slow_path(generator->count++ > NJS_GENERATE_MAX_DEPTH)) {
njs_range_error(vm, "Maximum call stack size exceeded");
return NJS_ERROR;
}
@@ -492,6 +502,25 @@ njs_generator(njs_vm_t *vm, njs_generato
}
+static njs_int_t
+njs_generate_wo_dest(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_int_t ret;
+ njs_parser_scope_t *scope;
+
+ scope = njs_function_scope(node->scope);
+
+ scope->dest_disable = 1;
+
+ ret = njs_generator(vm, generator, node);
+
+ scope->dest_disable = 0;
+
+ return ret;
+}
+
+
static u_char *
njs_generate_reserve(njs_vm_t *vm, njs_generator_t *generator, size_t size)
{
@@ -591,6 +620,8 @@ njs_generate_name(njs_vm_t *vm, njs_gene
njs_parser_node_t *node)
{
njs_variable_t *var;
+ njs_parser_scope_t *scope;
+ njs_vmcode_variable_t *variable;
njs_vmcode_function_copy_t *copy;
var = njs_variable_reference(vm, node);
@@ -605,6 +636,20 @@ njs_generate_name(njs_vm_t *vm, njs_gene
copy->retval = node->index;
}
+ if (var->init) {
+ return NJS_OK;
+ }
+
+ if (var->type == NJS_VARIABLE_LET) {
+ scope = njs_function_scope(node->scope);
+
+ if (scope->dest_disable) {
+ njs_generate_code(generator, njs_vmcode_variable_t, variable,
+ NJS_VMCODE_NOT_INITIALIZED, 1, node);
+ variable->dst = node->index;
+ }
+ }
+
return NJS_OK;
}
@@ -614,6 +659,8 @@ njs_generate_variable(njs_vm_t *vm, njs_
njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar)
{
njs_variable_t *var;
+ njs_parser_scope_t *scope;
+ njs_vmcode_variable_t *variable;
njs_vmcode_function_copy_t *copy;
var = njs_variable_reference(vm, node);
@@ -641,11 +688,44 @@ njs_generate_variable(njs_vm_t *vm, njs_
copy->retval = node->index;
}
+ if (var->init) {
+ return NJS_OK;
+ }
+
+ if (var->type == NJS_VARIABLE_LET) {
+ scope = njs_function_scope(node->scope);
+
+ if ((!scope->dest_disable && njs_function_scope(var->scope) == scope)) {
+ njs_generate_code(generator, njs_vmcode_variable_t, variable,
+ NJS_VMCODE_NOT_INITIALIZED, 1, node);
+ variable->dst = node->index;
+ }
+ }
+
return NJS_OK;
}
static njs_int_t
+njs_generate_variable_wo_dest(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar)
+{
+ njs_int_t ret;
+ njs_parser_scope_t *scope;
+
+ scope = njs_function_scope(node->scope);
+
+ scope->dest_disable = 1;
+
+ ret = njs_generate_variable(vm, generator, node, type, retvar);
+
+ scope->dest_disable = 0;
+
+ return ret;
+}
+
+
+static njs_int_t
njs_generate_var_statement(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
@@ -656,26 +736,50 @@ njs_generate_var_statement(njs_vm_t *vm,
lvalue = node->left;
- ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION, &var);
+ ret = njs_generate_variable_wo_dest(vm, generator, lvalue,
+ NJS_DECLARATION, &var);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
- lvalue->index = var->index;
expr = node->right;
if (expr == NULL) {
/* Variable is only declared. */
+ if (var->type == NJS_VARIABLE_LET) {
+ ret = njs_generate_let(vm, generator, node, var);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
+
+ var->init = 1;
+
return NJS_OK;
}
- expr->dest = lvalue;
-
- ret = njs_generator(vm, generator, expr);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ if (var->type == NJS_VARIABLE_LET) {
+ ret = njs_generate_wo_dest(vm, generator, expr);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_generate_let(vm, generator, node, var);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ } else {
+ expr->dest = lvalue;
+
+ ret = njs_generator(vm, generator, expr);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
}
+ var->init = 1;
+
/*
* lvalue and expression indexes are equal if the expression is an
* empty object or expression result is stored directly in variable.
@@ -693,6 +797,20 @@ njs_generate_var_statement(njs_vm_t *vm,
static njs_int_t
+njs_generate_let(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node, njs_variable_t *var)
+{
+ njs_vmcode_variable_t *code;
+
+ njs_generate_code(generator, njs_vmcode_variable_t, code,
+ NJS_VMCODE_LET, 0, node);
+ code->dst = var->index;
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
njs_generate_if_statement(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
@@ -1089,7 +1207,7 @@ njs_generate_for_statement(njs_vm_t *vm,
{
njs_int_t ret;
njs_jump_off_t jump_offset, loop_offset;
- njs_parser_node_t *condition, *update;
+ njs_parser_node_t *condition, *update, *init;
njs_vmcode_jump_t *jump;
njs_vmcode_cond_jump_t *cond_jump;
@@ -1113,9 +1231,20 @@ njs_generate_for_statement(njs_vm_t *vm,
return ret;
}
+ init = node->left;
node = node->right;
condition = node->left;
+ /*
+ * Closures can occur in conditional and loop updates. This must be
+ * foreseen in order to generate optimized code for let updates.
+ */
+
+ ret = njs_generate_for_resolve_closure(vm, condition, generator->count);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
/* GCC complains about uninitialized jump_offset. */
jump_offset = 0;
@@ -1142,10 +1271,20 @@ njs_generate_for_statement(njs_vm_t *vm,
/* The loop update. */
+ update = node->right;
+
+ ret = njs_generate_for_resolve_closure(vm, update, generator->count);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_generate_for_let_update(vm, generator, init, generator->count);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
njs_generate_patch_block(vm, generator, generator->block->continuation);
- update = node->right;
-
ret = njs_generator(vm, generator, update);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
@@ -1186,13 +1325,95 @@ njs_generate_for_statement(njs_vm_t *vm,
static njs_int_t
+njs_generate_for_let_update(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node, size_t depth)
+{
+ njs_parser_node_t *let;
+ njs_vmcode_variable_t *code_var;
+ njs_variable_reference_t *ref;
+
+ if (node == NULL) {
+ return NJS_OK;
+ }
+
+ if (depth >= NJS_GENERATE_MAX_DEPTH) {
+ return NJS_ERROR;
+ }
+
+ if (node->token_type != NJS_TOKEN_STATEMENT) {
+ return NJS_OK;
+ }
+
+ let = node->right;
+
+ if (let->token_type != NJS_TOKEN_LET) {
+ return NJS_OK;
+ }
+
+ ref = &let->left->u.reference;
+
+ if (ref->variable->closure) {
+ njs_generate_code(generator, njs_vmcode_variable_t, code_var,
+ NJS_VMCODE_LET_UPDATE, 0, let);
+ code_var->dst = let->left->index;
+ }
+
+ return njs_generate_for_let_update(vm, generator, node->left, depth + 1);
+}
+
+
+static njs_int_t
+njs_generate_for_resolve_closure(njs_vm_t *vm, njs_parser_node_t *node,
+ size_t depth)
+{
+ njs_int_t ret;
+ njs_bool_t closure;
+ njs_variable_t *var;
+
+ if (node == NULL) {
+ return NJS_OK;
+ }
+
+ if (node->token_type == NJS_TOKEN_NAME) {
+ var = njs_variable_resolve(vm, node);
+
+ if (njs_fast_path(var != NULL)) {
+ closure = njs_variable_closure_test(node->scope, var->scope);
+
+ if (closure) {
+ var->closure = 1;
+ }
+ }
+ }
+
+ if (depth >= NJS_GENERATE_MAX_DEPTH) {
+ njs_range_error(vm, "Maximum call stack size exceeded");
+ return NJS_ERROR;
+ }
+
+ ret = njs_generate_for_resolve_closure(vm, node->left, depth + 1);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_generate_for_resolve_closure(vm, node->right, depth + 1);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
njs_generate_for_in_statement(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
njs_int_t ret;
njs_index_t index;
+ njs_variable_t *var;
njs_jump_off_t loop_offset, prop_offset;
- njs_parser_node_t *foreach;
+ njs_parser_node_t *foreach, *name;
njs_vmcode_prop_next_t *prop_next;
njs_vmcode_prop_foreach_t *prop_foreach;
@@ -1205,15 +1426,36 @@ njs_generate_for_in_statement(njs_vm_t *
/* The object. */
foreach = node->left;
-
- ret = njs_generator(vm, generator, foreach->left);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
- ret = njs_generator(vm, generator, foreach->right);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ name = foreach->left->right;
+
+ if (name != NULL) {
+ name = name->left;
+
+ ret = njs_generate_variable_wo_dest(vm, generator, name,
+ NJS_DECLARATION, &var);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ foreach->left->index = name->index;
+
+ ret = njs_generator(vm, generator, foreach->right);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ var->init = 1;
+
+ } else {
+ ret = njs_generator(vm, generator, foreach->left);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_generator(vm, generator, foreach->right);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
}
njs_generate_code(generator, njs_vmcode_prop_foreach_t, prop_foreach,
@@ -1239,6 +1481,14 @@ njs_generate_for_in_statement(njs_vm_t *
/* The loop iterator. */
+ if (name != NULL) {
+ ret = njs_generate_for_let_update(vm, generator, foreach->left,
+ generator->count);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
+
njs_generate_patch_block(vm, generator, generator->block->continuation);
njs_code_set_jump_offset(generator, njs_vmcode_prop_foreach_t, prop_offset);
@@ -1564,12 +1814,38 @@ static njs_int_t
njs_generate_statement(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
+ njs_int_t ret;
+ njs_variable_t *var;
+ njs_parser_node_t *right;
+ njs_vmcode_variable_t *code;
+
+ right = node->right;
+
+ if (right != NULL && right->token_type == NJS_TOKEN_NAME) {
+ var = njs_variable_reference(vm, right);
+ if (njs_slow_path(var == NULL)) {
+ goto statement;
+ }
+
+ if (!var->init && var->type == NJS_VARIABLE_LET) {
+ njs_generate_code(generator, njs_vmcode_variable_t, code,
+ NJS_VMCODE_INITIALIZATION_TEST, 0, right);
+ code->dst = right->index;
+ }
+
+ if (node->left == NULL) {
+ return NJS_OK;
+ }
+
+ node = node->left;
+ }
+
+statement:
ret = njs_generate_children(vm, generator, node);
if (njs_fast_path(ret == NJS_OK)) {
- return njs_generate_node_index_release(vm, generator, node->right);
+ return njs_generate_node_index_release(vm, generator, right);
}
return ret;
@@ -3311,8 +3587,9 @@ static njs_index_t
njs_generate_dest_index(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_index_t ret;
- njs_parser_node_t *dest;
+ njs_index_t ret;
+ njs_parser_node_t *dest;
+ njs_parser_scope_t *scope;
ret = njs_generate_children_indexes_release(vm, generator, node);
if (njs_slow_path(ret != NJS_OK)) {
@@ -3322,7 +3599,11 @@ njs_generate_dest_index(njs_vm_t *vm, nj
dest = node->dest;
if (dest != NULL && dest->index != NJS_INDEX_NONE) {
- return dest->index;
+ scope = njs_function_scope(node->scope);
+
+ if (!scope->dest_disable) {
+ return dest->index;
+ }
}
return njs_generate_node_temp_index_get(vm, generator, node);
@@ -3386,7 +3667,8 @@ njs_generate_temp_index_get(njs_vm_t *vm
return NJS_ERROR;
}
- return njs_scope_index(scope->type, scope->temp++, NJS_LEVEL_TEMP);
+ return njs_scope_index(scope->type, scope->temp++, NJS_LEVEL_TEMP,
+ NJS_VARIABLE_VAR);
}
diff -r 28f33155ad3f -r 27a4a52621d5 src/njs_module.c
--- a/src/njs_module.c Fri Apr 30 16:02:20 2021 +0300
+++ b/src/njs_module.c Fri Apr 30 16:02:28 2021 +0300
@@ -574,7 +574,8 @@ njs_module_insert(njs_parser_t *parser,
scope = njs_parser_global_scope(parser);
vm = parser->vm;
- module->index = njs_scope_index(scope->type, scope->items, NJS_LEVEL_LOCAL);
+ module->index = njs_scope_index(scope->type, scope->items, NJS_LEVEL_LOCAL,
+ NJS_VARIABLE_VAR);
scope->items++;
if (vm->modules == NULL) {
diff -r 28f33155ad3f -r 27a4a52621d5 src/njs_parser.c
--- a/src/njs_parser.c Fri Apr 30 16:02:20 2021 +0300
+++ b/src/njs_parser.c Fri Apr 30 16:02:28 2021 +0300
@@ -238,6 +238,8 @@ static njs_int_t njs_parser_statement_li
static njs_int_t njs_parser_statement_list_item(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
+static njs_int_t njs_parser_lexical_declaration(njs_parser_t *parser,
+ njs_lexer_token_t *token, njs_queue_link_t *current);
static njs_int_t njs_parser_variable_statement(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
static njs_int_t njs_parser_variable_declaration_list(njs_parser_t *parser,
@@ -287,7 +289,8 @@ static njs_int_t njs_parser_iteration_st
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,
- njs_lexer_token_t *token, njs_queue_link_t *current);
+ 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,
@@ -311,6 +314,8 @@ static njs_int_t njs_parser_switch_state
njs_lexer_token_t *token, njs_queue_link_t *current);
static njs_int_t njs_parser_switch_block(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
+static njs_int_t njs_parser_switch_block_after(njs_parser_t *parser,
+ njs_lexer_token_t *token, njs_queue_link_t *current);
static njs_int_t njs_parser_switch_case(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
static njs_int_t njs_parser_switch_case_wo_def(njs_parser_t *parser,
@@ -362,6 +367,8 @@ static njs_int_t njs_parser_catch_after(
njs_lexer_token_t *token, njs_queue_link_t *current);
static njs_int_t njs_parser_catch_parenthesis(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
+static njs_int_t njs_parser_catch_statement_open_brace(njs_parser_t *parser,
+ njs_lexer_token_t *token, njs_queue_link_t *current);
static njs_int_t njs_parser_catch_finally(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
@@ -506,8 +513,10 @@ njs_parser_reject(njs_parser_t *parser)
njs_int_t
njs_parser(njs_vm_t *vm, njs_parser_t *parser)
{
- njs_int_t ret;
- njs_lexer_token_t *token;
+ njs_int_t ret;
+ njs_str_t str;
+ njs_lexer_token_t *token;
+ const njs_lexer_keyword_entry_t *keyword;
parser->vm = vm;
@@ -526,6 +535,16 @@ njs_parser(njs_vm_t *vm, njs_parser_t *p
parser->ret = NJS_OK;
}
+ /* Add this as first variable. */
+ njs_string_get(&njs_string_undefined, &str);
+
+ keyword = njs_lexer_keyword(str.start, str.length);
+ if (njs_slow_path(keyword == NULL)) {
+ return NJS_ERROR;
+ }
+
+ parser->undefined_id = (uintptr_t) keyword->value;
+
njs_queue_init(&parser->stack);
parser->target = NULL;
@@ -645,7 +664,8 @@ njs_parser_scope_begin(njs_parser_t *par
return NJS_ERROR;
}
- var->index = njs_scope_index(type, 0, NJS_LEVEL_LOCAL);
+ var->index = njs_scope_index(type, 0, NJS_LEVEL_LOCAL,
+ NJS_VARIABLE_VAR);
}
}
@@ -737,14 +757,6 @@ njs_parser_class_declaration(njs_parser_
static njs_int_t
-njs_parser_lexical_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
- njs_queue_link_t *current)
-{
- return njs_parser_not_supported(parser, token);
-}
-
-
-static njs_int_t
njs_parser_function_or_generator(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current)
{
@@ -890,12 +902,14 @@ njs_parser_expression_parenthesis(njs_pa
static njs_int_t
-njs_parser_set_line_state(njs_parser_t *parser,
+njs_parser_iteration_statement_for_end(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current)
{
parser->node->token_line = (uint32_t) (uintptr_t) parser->target;
parser->target = NULL;
+ njs_parser_scope_end(parser);
+
return njs_parser_stack_pop(parser);
}
@@ -4534,7 +4548,7 @@ njs_parser_declaration(njs_parser_t *par
switch (token->type) {
case NJS_TOKEN_CLASS:
njs_parser_next(parser, njs_parser_class_declaration);
- break;
+ return NJS_OK;
case NJS_TOKEN_LET:
case NJS_TOKEN_CONST:
@@ -4570,7 +4584,8 @@ njs_parser_declaration(njs_parser_t *par
return NJS_DECLINED;
}
- return NJS_OK;
+ return njs_parser_after(parser, current, parser->node, 1,
+ njs_parser_statement_after);
}
@@ -4733,12 +4748,32 @@ njs_parser_statement_list_item(njs_parse
/*
+ * 13.3.1 Let and Const Declarations
+ */
+static njs_int_t
+njs_parser_lexical_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
+ njs_queue_link_t *current)
+{
+ parser->var_type = (token->type == NJS_TOKEN_LET) ? NJS_VARIABLE_LET
+ : NJS_VARIABLE_CONST;
+
+ njs_lexer_consume_token(parser->lexer, 1);
+
+ njs_parser_next(parser, njs_parser_variable_declaration_list);
+
+ return njs_parser_after(parser, current, NULL, 1, njs_parser_semicolon);
+}
+
+
+/*
* 13.3.2 Variable Statement
*/
static njs_int_t
njs_parser_variable_statement(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
+ parser->var_type = NJS_VARIABLE_VAR;
+
njs_parser_next(parser, njs_parser_variable_declaration_list);
return njs_parser_after(parser, current, NULL, 1, njs_parser_semicolon);
@@ -4789,6 +4824,7 @@ njs_parser_variable_declaration(njs_pars
{
njs_int_t ret;
njs_variable_t *var;
+ njs_token_type_t type;
njs_parser_node_t *name;
ret = njs_parser_binding_pattern(parser, token, current);
@@ -4807,14 +4843,14 @@ njs_parser_variable_declaration(njs_pars
return NJS_DONE;
}
- name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR,
+ name = njs_parser_variable_node(parser, token->unique_id, parser->var_type,
&var);
if (name == NULL) {
return NJS_ERROR;
}
if (var->self) {
- var->type = NJS_VARIABLE_VAR;
+ var->type = parser->var_type;
var->self = 0;
}
@@ -4829,7 +4865,21 @@ njs_parser_variable_declaration(njs_pars
return NJS_ERROR;
}
- ret = njs_parser_initializer_assign(parser, NJS_TOKEN_VAR);
+ switch (parser->var_type) {
+ case NJS_VARIABLE_LET:
+ type = NJS_TOKEN_LET;
+ break;
+
+ case NJS_VARIABLE_CONST:
+ type = NJS_TOKEN_CONST;
+ break;
+
+ default:
+ type = NJS_TOKEN_VAR;
+ break;
+ }
+
+ ret = njs_parser_initializer_assign(parser, type);
if (ret != NJS_OK) {
return ret;
}
@@ -4930,6 +4980,12 @@ njs_parser_expression_statement(njs_pars
return NJS_ERROR;
}
+ if (token->type == NJS_TOKEN_NAME) {
+ njs_parser_syntax_error(parser, "let declaration cannot appear "
+ "in a single-statement context");
+ return NJS_DONE;
+ }
+
if (token->type == NJS_TOKEN_OPEN_BRACKET) {
return njs_parser_failed(parser);
}
@@ -5172,14 +5228,21 @@ static njs_int_t
njs_parser_iteration_statement_for(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current)
{
+ njs_int_t ret;
+
if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) {
njs_lexer_consume_token(parser->lexer, 1);
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
njs_parser_next(parser, njs_parser_iteration_statement_for_map);
return njs_parser_after(parser, current,
(void *) (uintptr_t) parser->line, 1,
- njs_parser_set_line_state);
+ njs_parser_iteration_statement_for_end);
}
if (token->type == NJS_TOKEN_AWAIT) {
@@ -5194,8 +5257,9 @@ 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_int_t ret;
+ njs_str_t *text;
+ njs_token_type_t token_type;
/*
* "var" <VariableDeclarationList> ";" <Expression>? ";" <Expression>? ")"
@@ -5242,6 +5306,9 @@ njs_parser_iteration_statement_for_map(n
return NJS_OK;
case NJS_TOKEN_VAR:
+ case NJS_TOKEN_LET:
+ token_type = token->type;
+
token = njs_lexer_peek_token(parser->lexer, token, 0);
if (token == NULL) {
return NJS_ERROR;
@@ -5249,7 +5316,8 @@ njs_parser_iteration_statement_for_map(n
njs_lexer_consume_token(parser->lexer, 1);
- ret = njs_parser_for_var_binding_or_var_list(parser, token, current);
+ ret = njs_parser_for_var_binding_or_var_list(parser, token,
+ current, token_type);
if (ret != NJS_OK) {
if (ret == NJS_DONE) {
return NJS_OK;
@@ -5260,7 +5328,6 @@ njs_parser_iteration_statement_for_map(n
break;
- case NJS_TOKEN_LET:
case NJS_TOKEN_CONST:
return njs_parser_not_supported(parser, token);
@@ -5288,11 +5355,23 @@ njs_parser_iteration_statement_for_map(n
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_int_t ret;
- njs_lexer_token_t *next;
- njs_parser_node_t *node, *var;
+ 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) {
+ case NJS_TOKEN_LET:
+ type = NJS_VARIABLE_LET;
+ break;
+
+ default:
+ type = NJS_VARIABLE_VAR;
+ break;
+ }
switch (token->type) {
/* BindingPattern */
@@ -5318,18 +5397,33 @@ njs_parser_for_var_binding_or_var_list(n
}
if (next->type != NJS_TOKEN_IN) {
+ parser->var_type = type;
+
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->unique_id,
- NJS_VARIABLE_VAR, NULL);
+ 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);
@@ -5338,7 +5432,7 @@ njs_parser_for_var_binding_or_var_list(n
}
node->token_line = next->line;
- node->left = var;
+ node->left = statement;
njs_parser_next(parser, njs_parser_expression);
@@ -5829,6 +5923,8 @@ static njs_int_t
njs_parser_switch_block(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
+ njs_int_t ret;
+
if (token->type != NJS_TOKEN_OPEN_BRACE) {
return njs_parser_failed(parser);
}
@@ -5837,9 +5933,24 @@ njs_parser_switch_block(njs_parser_t *pa
parser->target->left = parser->node;
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
njs_parser_next(parser, njs_parser_switch_case);
- return NJS_OK;
+ return njs_parser_after(parser, current, NULL, 1,
+ njs_parser_switch_block_after);
+}
+
+static njs_int_t
+njs_parser_switch_block_after(njs_parser_t *parser, njs_lexer_token_t *token,
+ njs_queue_link_t *current)
+{
+ njs_parser_scope_end(parser);
+
+ return njs_parser_stack_pop(parser);
}
@@ -6310,8 +6421,6 @@ njs_parser_catch_after(njs_parser_t *par
{
njs_parser_node_t *node;
- njs_parser_scope_end(parser);
-
parser->target->right->right = parser->node;
if (token->type == NJS_TOKEN_FINALLY) {
@@ -6355,7 +6464,7 @@ njs_parser_catch_parenthesis(njs_parser_
parser->target->right->right = parser->node;
parser->node = NULL;
- njs_parser_next(parser, njs_parser_block_statement_open_brace);
+ njs_parser_next(parser, njs_parser_catch_statement_open_brace);
return njs_parser_after(parser, current, parser->target, 1,
njs_parser_catch_after);
@@ -6363,6 +6472,42 @@ njs_parser_catch_parenthesis(njs_parser_
static njs_int_t
+njs_parser_catch_statement_open_brace(njs_parser_t *parser,
+ njs_lexer_token_t *token, njs_queue_link_t *current)
+{
+ void *target;
+
+ if (token->type != NJS_TOKEN_OPEN_BRACE) {
+ return njs_parser_failed(parser);
+ }
+
More information about the nginx-devel
mailing list