Created
April 13, 2021 10:55
-
-
Save lexborisov/ec17b310d56f0a9e8c9c77bf431e6468 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# User Alexander Borisov <alexander.borisov@nginx.com> | |
# Date 1618311294 -10800 | |
# Tue Apr 13 13:54:54 2021 +0300 | |
# Node ID 5ea7552a65bad3f3cc4e11a37fa340a05dbaad15 | |
# Parent eaea20523f77b83caf2fb242a6a1850c23c6989c | |
Introduced let implementation. | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_builtin.c | |
--- a/src/njs_builtin.c Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_builtin.c Tue Apr 13 13:54:54 2021 +0300 | |
@@ -967,6 +967,11 @@ njs_global_this_prop_handler(njs_vm_t *v | |
} | |
node = (njs_variable_node_t *) rb_node; | |
+ | |
+ if (node->variable->type == NJS_VARIABLE_LET) { | |
+ return NJS_DECLINED; | |
+ } | |
+ | |
value = njs_scope_valid_value(vm, node->variable->index); | |
if (setval != NULL) { | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_disassembler.c | |
--- a/src/njs_disassembler.c Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_disassembler.c Tue Apr 13 13:54:54 2021 +0300 | |
@@ -135,6 +135,18 @@ 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_REFERENCE, sizeof(njs_vmcode_variable_t), | |
+ njs_str("REFERENCE ") }, | |
+ | |
+ { NJS_VMCODE_NOT_INITIALIZATION, sizeof(njs_vmcode_variable_t), | |
+ njs_str("NOT INIT ") }, | |
}; | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_generator.c | |
--- a/src/njs_generator.c Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_generator.c Tue Apr 13 13:54:54 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; | |
} | |
@@ -585,12 +595,48 @@ njs_lookup_line(njs_vm_code_t *code, uin | |
return 0; | |
} | |
+/* | |
+ * For lonely variables without any instructions: | |
+ * x; | |
+ * function f(){x} | |
+ */ | |
+ | |
+static njs_int_t | |
+njs_generate_alone_name(njs_vm_t *vm, njs_generator_t *generator, | |
+ njs_parser_node_t *node) | |
+{ | |
+ njs_variable_t *var; | |
+ njs_vmcode_variable_t *code; | |
+ | |
+ var = njs_variable_reference(vm, node); | |
+ if (njs_slow_path(var == NULL)) { | |
+ return njs_generate_global_reference(vm, generator, node, 1); | |
+ } | |
+ | |
+ if (var->init) { | |
+ return NJS_OK; | |
+ } | |
+ | |
+ if (var->type == NJS_VARIABLE_LET | |
+ && var->check == NJS_VARIABLE_NOT_CHECK) | |
+ { | |
+ njs_generate_code(generator, njs_vmcode_variable_t, code, | |
+ NJS_VMCODE_REFERENCE, 0, node); | |
+ code->dst = node->index; | |
+ | |
+ return NJS_OK; | |
+ } | |
+ | |
+ return NJS_OK; | |
+} | |
+ | |
static njs_int_t | |
njs_generate_name(njs_vm_t *vm, njs_generator_t *generator, | |
njs_parser_node_t *node) | |
{ | |
njs_variable_t *var; | |
+ njs_vmcode_variable_t *code; | |
njs_vmcode_function_t *function; | |
var = njs_variable_reference(vm, node); | |
@@ -609,6 +655,19 @@ njs_generate_name(njs_vm_t *vm, njs_gene | |
function->lambda = var->value.data.u.lambda; | |
var->init = 1; | |
+ | |
+ } else if (var->type == NJS_VARIABLE_LET) { | |
+ if (var->check == NJS_VARIABLE_CHECK_PROGRESS) { | |
+ if (njs_function_scope(node->scope) | |
+ == njs_function_scope(var->scope)) | |
+ { | |
+ njs_generate_code(generator, njs_vmcode_variable_t, code, | |
+ NJS_VMCODE_NOT_INITIALIZATION, 0, node); | |
+ code->dst = node->index; | |
+ | |
+ return NJS_OK; | |
+ } | |
+ } | |
} | |
return NJS_OK; | |
@@ -640,19 +699,19 @@ njs_generate_variable(njs_vm_t *vm, njs_ | |
} | |
} | |
- if (var->init || node->index != var->index) { | |
+ if (var->init) { | |
return NJS_OK; | |
} | |
- if (var->have_lambda) { | |
+ if (var->have_lambda && node->index == var->index) { | |
njs_generate_code(generator, njs_vmcode_function_t, function, | |
NJS_VMCODE_FUNCTION, 1, node); | |
function->retval = node->index; | |
function->lambda = var->value.data.u.lambda; | |
+ | |
+ var->init = 1; | |
} | |
- var->init = 1; | |
- | |
return NJS_OK; | |
} | |
@@ -673,21 +732,32 @@ njs_generate_var_statement(njs_vm_t *vm, | |
return NJS_ERROR; | |
} | |
+ ret = njs_generate_let(vm, generator, node, var); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return ret; | |
+ } | |
+ | |
lvalue->index = var->index; | |
expr = node->right; | |
if (expr == NULL) { | |
/* Variable is only declared. */ | |
+ var->init = 1; | |
return NJS_OK; | |
} | |
expr->dest = lvalue; | |
+ var->check = NJS_VARIABLE_CHECK_PROGRESS; | |
+ | |
ret = njs_generator(vm, generator, expr); | |
if (njs_slow_path(ret != NJS_OK)) { | |
return ret; | |
} | |
+ var->init = 1; | |
+ var->check = NJS_VARIABLE_CHECK; | |
+ | |
/* | |
* lvalue and expression indexes are equal if the expression is an | |
* empty object or expression result is stored directly in variable. | |
@@ -705,6 +775,22 @@ 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; | |
+ | |
+ if (var->type == NJS_VARIABLE_LET) { | |
+ 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) | |
{ | |
@@ -1101,7 +1187,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; | |
@@ -1125,9 +1211,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; | |
@@ -1154,10 +1251,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; | |
@@ -1198,13 +1305,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; | |
@@ -1217,15 +1406,38 @@ 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(vm, generator, name, NJS_DECLARATION, &var); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ foreach->left->index = name->index; | |
+ | |
+ var->check = NJS_VARIABLE_CHECK_PROGRESS; | |
+ | |
+ ret = njs_generator(vm, generator, foreach->right); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return ret; | |
+ } | |
+ | |
+ var->init = 1; | |
+ var->check = NJS_VARIABLE_CHECK; | |
+ | |
+ } 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, | |
@@ -1251,6 +1463,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); | |
@@ -1262,6 +1482,7 @@ njs_generate_for_in_statement(njs_vm_t * | |
prop_next->object = foreach->right->index; | |
prop_next->next = index; | |
prop_next->offset = loop_offset - prop_offset; | |
+ prop_next->have_invalid = 1; | |
njs_generate_patch_block_exit(vm, generator); | |
@@ -1578,6 +1799,12 @@ njs_generate_statement(njs_vm_t *vm, njs | |
{ | |
njs_int_t ret; | |
+ if (node->left == NULL && node->right != NULL | |
+ && node->right->token_type == NJS_TOKEN_NAME) | |
+ { | |
+ return njs_generate_alone_name(vm, generator, node->right); | |
+ } | |
+ | |
ret = njs_generate_children(vm, generator, node); | |
if (njs_fast_path(ret == NJS_OK)) { | |
@@ -3396,7 +3623,8 @@ njs_generate_temp_index_get(njs_vm_t *vm | |
return NJS_ERROR; | |
} | |
- return njs_scope_index(scope->type, scope->temp++, NJS_SCOPE_TYPE_TEMP); | |
+ return njs_scope_index(scope->type, scope->temp++, NJS_SCOPE_TYPE_TEMP, | |
+ NJS_VARIABLE_VAR); | |
} | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_module.c | |
--- a/src/njs_module.c Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_module.c Tue Apr 13 13:54:54 2021 +0300 | |
@@ -575,7 +575,7 @@ njs_module_insert(njs_parser_t *parser, | |
vm = parser->vm; | |
module->index = njs_scope_index(scope->type, scope->items, | |
- NJS_SCOPE_TYPE_LOCAL); | |
+ NJS_SCOPE_TYPE_LOCAL, NJS_VARIABLE_VAR); | |
scope->items++; | |
if (vm->modules == NULL) { | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_parser.c | |
--- a/src/njs_parser.c Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_parser.c Tue Apr 13 13:54:54 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,9 @@ 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_lexer_token_t *token; | |
+ const njs_lexer_keyword_entry_t *keyword; | |
parser->vm = vm; | |
@@ -527,6 +535,15 @@ njs_parser(njs_vm_t *vm, njs_parser_t *p | |
parser->level = 1; | |
} | |
+ /* Add this as first variable. */ | |
+ keyword = njs_lexer_keyword(njs_string_undefined.short_string.start, | |
+ njs_string_undefined.short_string.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; | |
@@ -648,7 +665,8 @@ njs_parser_scope_begin(njs_parser_t *par | |
return NJS_ERROR; | |
} | |
- var->index = njs_scope_index(type, 0, NJS_SCOPE_TYPE_LOCAL); | |
+ var->index = njs_scope_index(type, 0, NJS_SCOPE_TYPE_LOCAL, | |
+ NJS_VARIABLE_VAR); | |
var->init = 1; | |
} | |
} | |
@@ -746,14 +764,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) | |
{ | |
@@ -899,12 +909,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); | |
} | |
@@ -4543,7 +4555,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: | |
@@ -4573,7 +4585,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); | |
} | |
@@ -4736,12 +4749,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); | |
@@ -4792,6 +4825,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); | |
@@ -4810,14 +4844,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; | |
var->init = 0; | |
} | |
@@ -4833,7 +4867,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; | |
} | |
@@ -4934,6 +4982,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); | |
} | |
@@ -5176,14 +5230,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) { | |
@@ -5198,8 +5259,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>? ")" | |
@@ -5246,6 +5308,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; | |
@@ -5253,7 +5318,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; | |
@@ -5264,7 +5330,6 @@ njs_parser_iteration_statement_for_map(n | |
break; | |
- case NJS_TOKEN_LET: | |
case NJS_TOKEN_CONST: | |
return njs_parser_not_supported(parser, token); | |
@@ -5292,11 +5357,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 */ | |
@@ -5322,18 +5399,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); | |
@@ -5342,7 +5434,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); | |
@@ -5833,6 +5925,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); | |
} | |
@@ -5841,9 +5935,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); | |
} | |
@@ -6314,8 +6423,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) { | |
@@ -6359,7 +6466,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); | |
@@ -6367,6 +6474,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); | |
+ } | |
+ | |
+ parser->line = token->line; | |
+ | |
+ njs_lexer_consume_token(parser->lexer, 1); | |
+ | |
+ token = njs_lexer_token(parser->lexer, 0); | |
+ if (token == NULL) { | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ target = (void *) (uintptr_t) parser->line; | |
+ parser->node = NULL; | |
+ | |
+ if (token->type == NJS_TOKEN_CLOSE_BRACE) { | |
+ parser->target = target; | |
+ | |
+ njs_parser_next(parser, njs_parser_block_statement_close_brace); | |
+ return NJS_OK; | |
+ } | |
+ | |
+ njs_parser_next(parser, njs_parser_statement_list); | |
+ | |
+ return njs_parser_after(parser, current, target, 0, | |
+ njs_parser_block_statement_close_brace); | |
+} | |
+ | |
+ | |
+static njs_int_t | |
njs_parser_catch_finally(njs_parser_t *parser, njs_lexer_token_t *token, | |
njs_queue_link_t *current) | |
{ | |
@@ -6584,7 +6727,7 @@ njs_parser_function_expression_after(njs | |
var = (njs_variable_t *) parser->target; | |
var->index = njs_scope_index(var->scope->type, var->scope->items, | |
- NJS_SCOPE_TYPE_LOCAL); | |
+ NJS_SCOPE_TYPE_LOCAL, NJS_VARIABLE_VAR); | |
var->scope->items++; | |
if (var->self) { | |
@@ -6796,7 +6939,7 @@ njs_parser_arrow_function(njs_parser_t * | |
arg->init = 1; | |
var->index = njs_scope_index(parser->scope->type, parser->scope->items, | |
- NJS_SCOPE_TYPE_LOCAL); | |
+ NJS_SCOPE_TYPE_LOCAL, NJS_VARIABLE_VAR); | |
parser->scope->items++; | |
lambda->self = var->index; | |
@@ -6832,7 +6975,7 @@ njs_parser_arrow_function_args_after(njs | |
parser->target->left->u.reference.variable = NULL; | |
var->index = njs_scope_index(var->scope->type, var->scope->items, | |
- NJS_SCOPE_TYPE_LOCAL); | |
+ NJS_SCOPE_TYPE_LOCAL, NJS_VARIABLE_VAR); | |
var->scope->items++; | |
parser->target->u.value.data.u.lambda->self = var->index; | |
@@ -7491,7 +7634,7 @@ njs_parser_module_lambda_after(njs_parse | |
parser->target->left->u.reference.variable = NULL; | |
var->index = njs_scope_index(var->scope->type, var->scope->items, | |
- NJS_SCOPE_TYPE_LOCAL); | |
+ NJS_SCOPE_TYPE_LOCAL, NJS_VARIABLE_VAR); | |
var->scope->items++; | |
parser->node->u.value.data.u.lambda->self = var->index; | |
@@ -7661,7 +7804,8 @@ njs_parser_reference(njs_parser_t *parse | |
token->unique_id = (uintptr_t) keyword->value; | |
} else if (!scope->arrow_function) { | |
- index = njs_scope_index(scope->type, 0, NJS_SCOPE_TYPE_LOCAL); | |
+ index = njs_scope_index(scope->type, 0, NJS_SCOPE_TYPE_LOCAL, | |
+ NJS_VARIABLE_VAR); | |
var = njs_variable_scope_add(parser, scope, scope, token->unique_id, | |
NJS_VARIABLE_VAR, index); | |
@@ -8414,6 +8558,7 @@ njs_parser_variable_reference(njs_parser | |
rb_parse_node->key = unique_id; | |
rb_parse_node->index = NJS_INDEX_NONE; | |
+ rb_parse_node->check = 0; | |
njs_rbtree_insert(&scope->references, &rb_parse_node->node); | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_parser.h | |
--- a/src/njs_parser.h Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_parser.h Tue Apr 13 13:54:54 2021 +0300 | |
@@ -76,7 +76,9 @@ struct njs_parser_s { | |
njs_parser_node_t *node; | |
njs_parser_node_t *target; | |
njs_parser_scope_t *scope; | |
+ njs_variable_type_t var_type; | |
njs_int_t ret; | |
+ uintptr_t undefined_id; | |
njs_bool_t strict_semicolon; | |
uint32_t line; | |
uint8_t level; | |
@@ -97,6 +99,7 @@ typedef struct { | |
NJS_RBTREE_NODE (node); | |
uintptr_t key; | |
njs_index_t index; | |
+ njs_bool_t check; | |
} njs_parser_rbtree_node_t; | |
@@ -109,6 +112,8 @@ njs_int_t njs_parser(njs_vm_t *vm, njs_p | |
njs_int_t njs_parser_module_lambda(njs_parser_t *parser, | |
njs_lexer_token_t *token, njs_queue_link_t *current); | |
+njs_bool_t njs_variable_closure_test(njs_parser_scope_t *root, | |
+ njs_parser_scope_t *scope); | |
njs_variable_t *njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node); | |
njs_index_t njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node); | |
njs_bool_t njs_parser_has_side_effect(njs_parser_node_t *node); | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_scope.c | |
--- a/src/njs_scope.c Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_scope.c Tue Apr 13 13:54:54 2021 +0300 | |
@@ -21,7 +21,7 @@ njs_scope_temp_index(njs_parser_scope_t | |
} | |
return njs_scope_index(NJS_SCOPE_GLOBAL, scope->temp++, | |
- NJS_SCOPE_TYPE_TEMP); | |
+ NJS_SCOPE_TYPE_TEMP, NJS_VARIABLE_VAR); | |
} | |
@@ -95,7 +95,8 @@ njs_scope_global_index(njs_vm_t *vm, con | |
vm->scopes[NJS_SCOPE_TYPE_STATIC] = vm->scope_absolute->start; | |
- *retval = njs_scope_index(NJS_SCOPE_GLOBAL, index, NJS_SCOPE_TYPE_STATIC); | |
+ *retval = njs_scope_index(NJS_SCOPE_GLOBAL, index, NJS_SCOPE_TYPE_STATIC, | |
+ NJS_VARIABLE_VAR); | |
return *retval; | |
} | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_scope.h | |
--- a/src/njs_scope.h Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_scope.h Tue Apr 13 13:54:54 2021 +0300 | |
@@ -8,9 +8,10 @@ | |
#define _NJS_SCOPE_H_INCLUDED_ | |
-#define NJS_SCOPE_TYPE 4 | |
+#define NJS_SCOPE_VAR 4 | |
+#define NJS_SCOPE_TYPE (NJS_SCOPE_VAR + 4) | |
#define NJS_SCOPE_VALUE (NJS_SCOPE_TYPE + 1) | |
-#define NJS_SCOPE_TYPE_MASK ((NJS_SCOPE_VALUE_MAX) << NJS_SCOPE_TYPE) | |
+#define NJS_SCOPE_TYPE_MASK ((NJS_SCOPE_VALUE_MAX) << NJS_SCOPE_VAR) | |
#define NJS_INDEX_NONE ((njs_index_t) 0) | |
#define NJS_INDEX_ERROR ((njs_index_t) -1) | |
@@ -24,7 +25,8 @@ njs_index_t njs_scope_global_index(njs_v | |
njs_inline njs_index_t | |
-njs_scope_index(njs_scope_t scope, njs_index_t index, njs_scope_type_t type) | |
+njs_scope_index(njs_scope_t scope, njs_index_t index, njs_scope_type_t type, | |
+ njs_variable_type_t var_type) | |
{ | |
if (index > NJS_SCOPE_VALUE_MAX || type >= NJS_SCOPE_TYPE_LAST_ENTRY | |
|| (scope != NJS_SCOPE_GLOBAL && scope != NJS_SCOPE_FUNCTION)) | |
@@ -36,14 +38,21 @@ njs_scope_index(njs_scope_t scope, njs_i | |
type = NJS_SCOPE_TYPE_GLOBAL; | |
} | |
- return (index << NJS_SCOPE_VALUE) | type; | |
+ return (index << NJS_SCOPE_VALUE) | (type << NJS_SCOPE_VAR) | var_type; | |
+} | |
+ | |
+ | |
+njs_inline njs_variable_type_t | |
+njs_scope_index_var(njs_index_t index) | |
+{ | |
+ return (njs_variable_type_t) (index & ~NJS_SCOPE_TYPE_MASK); | |
} | |
njs_inline njs_scope_type_t | |
njs_scope_index_type(njs_index_t index) | |
{ | |
- return (njs_scope_type_t) (index & ~NJS_SCOPE_TYPE_MASK); | |
+ return (njs_scope_type_t) ((index >> NJS_SCOPE_VAR) & ~NJS_SCOPE_TYPE_MASK); | |
} | |
@@ -69,6 +78,12 @@ njs_scope_valid_value(njs_vm_t *vm, njs_ | |
value = njs_scope_value(vm, index); | |
if (!njs_is_valid(value)) { | |
+ if (njs_scope_index_var(index) == NJS_VARIABLE_LET) { | |
+ njs_reference_error(vm, "cannot access to variable " | |
+ "before initialization"); | |
+ return NULL; | |
+ } | |
+ | |
njs_set_undefined(value); | |
} | |
@@ -87,7 +102,8 @@ njs_scope_value_set(njs_vm_t *vm, njs_in | |
njs_inline njs_index_t | |
njs_scope_this_index() | |
{ | |
- return njs_scope_index(NJS_SCOPE_FUNCTION, 0, NJS_SCOPE_TYPE_LOCAL); | |
+ return njs_scope_index(NJS_SCOPE_FUNCTION, 0, NJS_SCOPE_TYPE_LOCAL, | |
+ NJS_VARIABLE_VAR); | |
} | |
@@ -101,7 +117,8 @@ njs_scope_undefined_index(njs_vm_t *vm, | |
njs_inline njs_index_t | |
njs_scope_global_this_index() | |
{ | |
- return njs_scope_index(NJS_SCOPE_GLOBAL, 0, NJS_SCOPE_TYPE_LOCAL); | |
+ return njs_scope_index(NJS_SCOPE_GLOBAL, 0, NJS_SCOPE_TYPE_LOCAL, | |
+ NJS_VARIABLE_VAR); | |
} | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_variable.c | |
--- a/src/njs_variable.c Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_variable.c Tue Apr 13 13:54:54 2021 +0300 | |
@@ -107,15 +107,42 @@ njs_variable_scope_find(njs_parser_t *pa | |
njs_parser_scope_t *root; | |
const njs_lexer_entry_t *entry; | |
- if (type != NJS_VARIABLE_VAR && type != NJS_VARIABLE_FUNCTION) { | |
- return scope; | |
- } | |
- | |
root = njs_variable_scope(scope, unique_id, &var, type); | |
if (njs_slow_path(root == NULL)) { | |
return NULL; | |
} | |
+ switch (type) { | |
+ case NJS_VARIABLE_LET: | |
+ if (scope->type == NJS_SCOPE_GLOBAL | |
+ && parser->undefined_id == unique_id) | |
+ { | |
+ goto failed; | |
+ } | |
+ | |
+ if (root != scope) { | |
+ return scope; | |
+ } | |
+ | |
+ if (var != NULL && var->scope == root) { | |
+ if (var->self) { | |
+ var->have_lambda = 0; | |
+ return scope; | |
+ } | |
+ | |
+ goto failed; | |
+ } | |
+ | |
+ return scope; | |
+ | |
+ case NJS_VARIABLE_VAR: | |
+ case NJS_VARIABLE_FUNCTION: | |
+ break; | |
+ | |
+ default: | |
+ return scope; | |
+ } | |
+ | |
if (type == NJS_VARIABLE_FUNCTION) { | |
root = scope; | |
} | |
@@ -124,6 +151,10 @@ njs_variable_scope_find(njs_parser_t *pa | |
return root; | |
} | |
+ if (var->type == NJS_VARIABLE_LET) { | |
+ goto failed; | |
+ } | |
+ | |
if (var->original->type == NJS_SCOPE_BLOCK) { | |
if (type == NJS_VARIABLE_FUNCTION | |
|| var->type == NJS_VARIABLE_FUNCTION) | |
@@ -200,7 +231,7 @@ njs_variable_scope_add(njs_parser_t *par | |
} | |
var->index = njs_scope_index(root->type, root->items, | |
- NJS_SCOPE_TYPE_LOCAL); | |
+ NJS_SCOPE_TYPE_LOCAL, type); | |
root->items++; | |
} | |
@@ -279,7 +310,7 @@ njs_label_remove(njs_vm_t *vm, njs_parse | |
} | |
-static njs_bool_t | |
+njs_bool_t | |
njs_variable_closure_test(njs_parser_scope_t *root, njs_parser_scope_t *scope) | |
{ | |
if (root == scope) { | |
@@ -370,7 +401,7 @@ njs_variable_closure(njs_vm_t *vm, njs_v | |
/* Create new closure for scope. */ | |
index = njs_scope_index(root->type, root->closures->items, | |
- NJS_SCOPE_TYPE_CLOSURE); | |
+ NJS_SCOPE_TYPE_CLOSURE, var->type); | |
if (njs_slow_path(index == NJS_INDEX_ERROR)) { | |
return NJS_INDEX_ERROR; | |
} | |
@@ -392,6 +423,7 @@ njs_variable_closure(njs_vm_t *vm, njs_v | |
} | |
parse_node->key = var->unique_id; | |
+ parse_node->check = 0; | |
njs_rbtree_insert(&scope->references, &parse_node->node); | |
} | |
@@ -408,6 +440,7 @@ njs_variable_closure(njs_vm_t *vm, njs_v | |
njs_variable_t * | |
njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node) | |
{ | |
+ njs_bool_t closure; | |
njs_rbtree_node_t *rb_node; | |
njs_parser_scope_t *scope; | |
njs_parser_rbtree_node_t *parse_node, ref_node; | |
@@ -425,7 +458,7 @@ njs_variable_reference(njs_vm_t *vm, njs | |
} | |
} | |
- ref->closure = njs_variable_closure_test(node->scope, ref->variable->scope); | |
+ closure = njs_variable_closure_test(node->scope, ref->variable->scope); | |
ref->scope = node->scope; | |
ref_node.key = ref->unique_id; | |
@@ -443,12 +476,14 @@ njs_variable_reference(njs_vm_t *vm, njs | |
return ref->variable; | |
} | |
- if (!ref->closure) { | |
+ if (!closure) { | |
node->index = ref->variable->index; | |
return ref->variable; | |
} | |
+ ref->variable->closure = closure; | |
+ | |
node->index = njs_variable_closure(vm, ref->variable, scope); | |
if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { | |
return NULL; | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_variable.h | |
--- a/src/njs_variable.h Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_variable.h Tue Apr 13 13:54:54 2021 +0300 | |
@@ -17,6 +17,13 @@ typedef enum { | |
} njs_variable_type_t; | |
+typedef enum { | |
+ NJS_VARIABLE_NOT_CHECK = 0, | |
+ NJS_VARIABLE_CHECK, | |
+ NJS_VARIABLE_CHECK_PROGRESS | |
+} njs_variable_check_t; | |
+ | |
+ | |
typedef struct { | |
uintptr_t unique_id; | |
@@ -24,8 +31,10 @@ typedef struct { | |
njs_bool_t argument; | |
njs_bool_t arguments_object; | |
njs_bool_t init; | |
+ njs_variable_check_t check; | |
njs_bool_t have_lambda; | |
njs_bool_t self; | |
+ njs_bool_t closure; | |
njs_parser_scope_t *scope; | |
njs_parser_scope_t *original; | |
@@ -48,7 +57,6 @@ typedef struct { | |
njs_variable_t *variable; | |
njs_parser_scope_t *scope; | |
njs_bool_t not_defined; | |
- njs_bool_t closure; | |
} njs_variable_reference_t; | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_vm.h | |
--- a/src/njs_vm.h Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_vm.h Tue Apr 13 13:54:54 2021 +0300 | |
@@ -112,6 +112,7 @@ enum njs_object_e { | |
}; | |
+#define NJS_SCOPE_VAR_MAX ((1 << NJS_SCOPE_VAR) - 1) | |
#define NJS_SCOPE_VALUE_MAX ((1 << (32 - NJS_SCOPE_VALUE)) - 1) | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_vmcode.c | |
--- a/src/njs_vmcode.c Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_vmcode.c Tue Apr 13 13:54:54 2021 +0300 | |
@@ -63,7 +63,13 @@ static njs_jump_off_t njs_function_frame | |
njs_bool_t ctor); | |
-#define njs_vmcode_operand(vm, index) njs_scope_valid_value(vm, index) | |
+#define njs_vmcode_operand(vm, index, _retval) \ | |
+ do { \ | |
+ _retval = njs_scope_valid_value(vm, index); \ | |
+ if (njs_slow_path(_retval == NULL)) { \ | |
+ goto error; \ | |
+ } \ | |
+ } while (0) | |
njs_int_t | |
@@ -86,6 +92,7 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_c | |
njs_property_next_t *next; | |
njs_vmcode_finally_t *finally; | |
njs_vmcode_generic_t *vmcode; | |
+ njs_vmcode_variable_t *var; | |
njs_vmcode_move_arg_t *move_arg; | |
njs_vmcode_prop_get_t *get; | |
njs_vmcode_prop_set_t *set; | |
@@ -127,12 +134,12 @@ next: | |
switch (vmcode->code.operands) { | |
case NJS_VMCODE_3OPERANDS: | |
- value2 = njs_vmcode_operand(vm, vmcode->operand3); | |
+ njs_vmcode_operand(vm, vmcode->operand3, value2); | |
/* Fall through. */ | |
case NJS_VMCODE_2OPERANDS: { | |
- value1 = njs_vmcode_operand(vm, vmcode->operand2); | |
+ njs_vmcode_operand(vm, vmcode->operand2, value1); | |
} | |
} | |
@@ -149,7 +156,7 @@ next: | |
if (op > NJS_VMCODE_NORET) { | |
if (op == NJS_VMCODE_MOVE) { | |
- retval = njs_vmcode_operand(vm, vmcode->operand1); | |
+ njs_vmcode_operand(vm, vmcode->operand1, retval); | |
*retval = *value1; | |
pc += sizeof(njs_vmcode_move_t); | |
@@ -158,7 +165,7 @@ next: | |
if (op == NJS_VMCODE_PROPERTY_GET) { | |
get = (njs_vmcode_prop_get_t *) pc; | |
- retval = njs_vmcode_operand(vm, get->value); | |
+ njs_vmcode_operand(vm, get->value, retval); | |
ret = njs_value_property(vm, value1, value2, retval); | |
if (njs_slow_path(ret == NJS_ERROR)) { | |
@@ -189,7 +196,7 @@ next: | |
njs_set_number(value1, | |
num + (1 - 2 * ((op - NJS_VMCODE_INCREMENT) >> 1))); | |
- retval = njs_vmcode_operand(vm, vmcode->operand1); | |
+ njs_vmcode_operand(vm, vmcode->operand1, retval); | |
if (op & 1) { | |
njs_set_number(retval, num); | |
@@ -203,7 +210,7 @@ next: | |
case NJS_VMCODE_GLOBAL_GET: | |
get = (njs_vmcode_prop_get_t *) pc; | |
- retval = njs_vmcode_operand(vm, get->value); | |
+ njs_vmcode_operand(vm, get->value, retval); | |
ret = njs_value_property(vm, value1, value2, retval); | |
if (njs_slow_path(ret == NJS_ERROR)) { | |
@@ -223,7 +230,7 @@ next: | |
* njs_vmcode_finally(), and jumps to the nearest try_break block. | |
*/ | |
case NJS_VMCODE_TRY_RETURN: | |
- retval = njs_vmcode_operand(vm, vmcode->operand1); | |
+ njs_vmcode_operand(vm, vmcode->operand1, retval); | |
*retval = *value1; | |
try_return = (njs_vmcode_try_return_t *) pc; | |
@@ -265,7 +272,7 @@ next: | |
goto error; | |
} | |
- retval = njs_vmcode_operand(vm, vmcode->operand1); | |
+ njs_vmcode_operand(vm, vmcode->operand1, retval); | |
if (op == NJS_VMCODE_ADDITION) { | |
if (njs_fast_path(njs_is_numeric(value1) | |
@@ -334,7 +341,7 @@ next: | |
ret ^= op - NJS_VMCODE_EQUAL; | |
- retval = njs_vmcode_operand(vm, vmcode->operand1); | |
+ njs_vmcode_operand(vm, vmcode->operand1, retval); | |
njs_set_boolean(retval, ret); | |
@@ -371,7 +378,7 @@ next: | |
} | |
num = njs_number(value1); | |
- retval = njs_vmcode_operand(vm, vmcode->operand1); | |
+ njs_vmcode_operand(vm, vmcode->operand1, retval); | |
pc += sizeof(njs_vmcode_3addr_t); | |
@@ -459,7 +466,7 @@ next: | |
goto next; | |
case NJS_VMCODE_OBJECT_COPY: | |
- value2 = njs_vmcode_operand(vm, (njs_index_t) value2); | |
+ njs_vmcode_operand(vm, (njs_index_t) value2, value2); | |
ret = njs_vmcode_object_copy(vm, value1); | |
break; | |
@@ -491,7 +498,7 @@ next: | |
ret ^= op - NJS_VMCODE_STRICT_EQUAL; | |
- retval = njs_vmcode_operand(vm, vmcode->operand1); | |
+ njs_vmcode_operand(vm, vmcode->operand1, retval); | |
njs_set_boolean(retval, ret); | |
@@ -517,7 +524,7 @@ next: | |
ret = sizeof(njs_vmcode_3addr_t); | |
} | |
- retval = njs_vmcode_operand(vm, vmcode->operand1); | |
+ njs_vmcode_operand(vm, vmcode->operand1, retval); | |
*retval = *value1; | |
pc += ret; | |
@@ -536,7 +543,7 @@ next: | |
} | |
num = njs_number(value1); | |
- retval = njs_vmcode_operand(vm, vmcode->operand1); | |
+ njs_vmcode_operand(vm, vmcode->operand1, retval); | |
switch (op) { | |
case NJS_VMCODE_UNARY_NEGATION: | |
@@ -555,7 +562,7 @@ next: | |
goto next; | |
case NJS_VMCODE_LOGICAL_NOT: | |
- retval = njs_vmcode_operand(vm, vmcode->operand1); | |
+ njs_vmcode_operand(vm, vmcode->operand1, retval); | |
njs_set_boolean(retval, !njs_is_true(value1)); | |
@@ -608,7 +615,7 @@ next: | |
break; | |
} | |
- retval = njs_vmcode_operand(vm, vmcode->operand1); | |
+ njs_vmcode_operand(vm, vmcode->operand1, retval); | |
njs_release(vm, retval); | |
*retval = vm->retval; | |
@@ -616,7 +623,7 @@ next: | |
} else { | |
switch (op) { | |
case NJS_VMCODE_STOP: | |
- value2 = njs_vmcode_operand(vm, (njs_index_t) value2); | |
+ njs_vmcode_operand(vm, (njs_index_t) value2, value2); | |
vm->retval = *value2; | |
return NJS_OK; | |
@@ -627,7 +634,7 @@ next: | |
case NJS_VMCODE_PROPERTY_SET: | |
set = (njs_vmcode_prop_set_t *) pc; | |
- retval = njs_vmcode_operand(vm, set->value); | |
+ njs_vmcode_operand(vm, set->value, retval); | |
ret = njs_value_property_set(vm, value1, value2, retval); | |
if (njs_slow_path(ret == NJS_ERROR)) { | |
@@ -639,7 +646,7 @@ next: | |
case NJS_VMCODE_PROPERTY_ACCESSOR: | |
accessor = (njs_vmcode_prop_accessor_t *) pc; | |
- function = njs_vmcode_operand(vm, accessor->value); | |
+ njs_vmcode_operand(vm, accessor->value, function); | |
ret = njs_value_to_key(vm, &name, value2); | |
if (njs_slow_path(ret != NJS_OK)) { | |
@@ -682,7 +689,7 @@ next: | |
case NJS_VMCODE_PROPERTY_INIT: | |
set = (njs_vmcode_prop_set_t *) pc; | |
- retval = njs_vmcode_operand(vm, set->value); | |
+ njs_vmcode_operand(vm, set->value, retval); | |
ret = njs_vmcode_property_init(vm, value1, value2, retval); | |
if (njs_slow_path(ret == NJS_ERROR)) { | |
@@ -692,7 +699,7 @@ next: | |
break; | |
case NJS_VMCODE_RETURN: | |
- value2 = njs_vmcode_operand(vm, (njs_index_t) value2); | |
+ njs_vmcode_operand(vm, (njs_index_t) value2, value2); | |
frame = (njs_frame_t *) vm->top_frame; | |
if (frame->native.ctor) { | |
@@ -766,7 +773,7 @@ next: | |
case NJS_VMCODE_FUNCTION_CALL: | |
vm->active_frame->native.pc = pc; | |
- value2 = njs_vmcode_operand(vm, (njs_index_t) value2); | |
+ njs_vmcode_operand(vm, (njs_index_t) value2, value2); | |
ret = njs_function_frame_invoke(vm, value2); | |
if (njs_slow_path(ret == NJS_ERROR)) { | |
@@ -778,7 +785,13 @@ next: | |
case NJS_VMCODE_PROPERTY_NEXT: | |
pnext = (njs_vmcode_prop_next_t *) pc; | |
- retval = njs_vmcode_operand(vm, pnext->retval); | |
+ | |
+ if (pnext->have_invalid) { | |
+ retval = njs_scope_value(vm, pnext->retval); | |
+ | |
+ } else { | |
+ njs_vmcode_operand(vm, pnext->retval, retval); | |
+ } | |
next = value2->data.u.next; | |
@@ -804,7 +817,7 @@ next: | |
case NJS_VMCODE_PROTO_INIT: | |
set = (njs_vmcode_prop_set_t *) pc; | |
- retval = njs_vmcode_operand(vm, set->value); | |
+ njs_vmcode_operand(vm, set->value, retval); | |
ret = njs_vmcode_proto_init(vm, value1, value2, retval); | |
if (njs_slow_path(ret == NJS_ERROR)) { | |
@@ -822,7 +835,7 @@ next: | |
break; | |
case NJS_VMCODE_THROW: | |
- value2 = njs_vmcode_operand(vm, (njs_index_t) value2); | |
+ njs_vmcode_operand(vm, (njs_index_t) value2, value2); | |
vm->retval = *value2; | |
goto error; | |
@@ -892,13 +905,63 @@ next: | |
hint = move_arg->dst; | |
value1 = &native->arguments_offset[hint]; | |
- value2 = njs_vmcode_operand(vm, move_arg->src); | |
+ njs_vmcode_operand(vm, move_arg->src, value2); | |
*value1 = *value2; | |
ret = sizeof(njs_vmcode_move_arg_t); | |
break; | |
+ case NJS_VMCODE_LET: | |
+ var = (njs_vmcode_variable_t *) pc; | |
+ value1 = njs_scope_value(vm, var->dst); | |
+ | |
+ if (njs_is_valid(value1)) { | |
+ value1 = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); | |
+ if (njs_slow_path(value1 == NULL)) { | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ njs_scope_value_set(vm, var->dst, value1); | |
+ } | |
+ | |
+ njs_set_undefined(value1); | |
+ | |
+ ret = sizeof(njs_vmcode_variable_t); | |
+ break; | |
+ | |
+ case NJS_VMCODE_LET_UPDATE: | |
+ var = (njs_vmcode_variable_t *) pc; | |
+ value2 = njs_scope_value(vm, var->dst); | |
+ | |
+ value1 = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); | |
+ if (njs_slow_path(value1 == NULL)) { | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ *value1 = *value2; | |
+ | |
+ njs_scope_value_set(vm, var->dst, value1); | |
+ | |
+ ret = sizeof(njs_vmcode_variable_t); | |
+ break; | |
+ | |
+ case NJS_VMCODE_REFERENCE: | |
+ var = (njs_vmcode_variable_t *) pc; | |
+ value1 = njs_scope_value(vm, var->dst); | |
+ | |
+ if (njs_is_valid(value1)) { | |
+ ret = sizeof(njs_vmcode_variable_t); | |
+ break; | |
+ } | |
+ | |
+ /* Fall through. */ | |
+ | |
+ case NJS_VMCODE_NOT_INITIALIZATION: | |
+ njs_reference_error(vm, "cannot access to variable " | |
+ "before initialization"); | |
+ goto error; | |
+ | |
default: | |
njs_internal_error(vm, "%d has NO retval", op); | |
goto error; | |
@@ -1065,7 +1128,11 @@ njs_vmcode_arguments(njs_vm_t *vm, u_cha | |
} | |
code = (njs_vmcode_arguments_t *) pc; | |
- value = njs_vmcode_operand(vm, code->dst); | |
+ | |
+ value = njs_scope_valid_value(vm, code->dst); | |
+ if (njs_slow_path(value == NULL)) { | |
+ return NJS_ERROR; | |
+ } | |
njs_set_object(value, frame->native.arguments_object); | |
@@ -1107,7 +1174,7 @@ njs_vmcode_template_literal(njs_vm_t *vm | |
.u.native = njs_string_prototype_concat | |
}; | |
- value = njs_vmcode_operand(vm, (njs_index_t) retval); | |
+ value = njs_scope_valid_value(vm, (njs_index_t) retval); | |
if (!njs_is_primitive(value)) { | |
array = njs_array(value); | |
diff -r eaea20523f77 -r 5ea7552a65ba src/njs_vmcode.h | |
--- a/src/njs_vmcode.h Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/njs_vmcode.h Tue Apr 13 13:54:54 2021 +0300 | |
@@ -59,6 +59,11 @@ enum { | |
NJS_VMCODE_MOVE_ARG, | |
+ NJS_VMCODE_LET, | |
+ NJS_VMCODE_LET_UPDATE, | |
+ NJS_VMCODE_REFERENCE, | |
+ NJS_VMCODE_NOT_INITIALIZATION, | |
+ | |
NJS_VMCODE_NORET = 127 | |
}; | |
@@ -287,6 +292,7 @@ typedef struct { | |
njs_index_t object; | |
njs_index_t next; | |
njs_jump_off_t offset; | |
+ njs_bool_t have_invalid; | |
} njs_vmcode_prop_next_t; | |
@@ -402,6 +408,12 @@ typedef struct { | |
} njs_vmcode_move_arg_t; | |
+typedef struct { | |
+ njs_vmcode_t code; | |
+ njs_index_t dst; | |
+} njs_vmcode_variable_t; | |
+ | |
+ | |
njs_int_t njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc); | |
njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor); | |
diff -r eaea20523f77 -r 5ea7552a65ba src/test/njs_unit_test.c | |
--- a/src/test/njs_unit_test.c Tue Apr 13 13:51:15 2021 +0300 | |
+++ b/src/test/njs_unit_test.c Tue Apr 13 13:54:54 2021 +0300 | |
@@ -19700,6 +19700,222 @@ static njs_unit_test_t njs_test[] = | |
{ njs_str("var buffer = require('buffer');" | |
"typeof buffer.constants.MAX_STRING_LENGTH === 'number' "), | |
njs_str("true") }, | |
+ | |
+ /* let */ | |
+ | |
+ { njs_str("let x"), | |
+ njs_str("undefined") }, | |
+ | |
+ { njs_str("let x = 123; x"), | |
+ njs_str("123") }, | |
+ | |
+ { njs_str("let x = [123]; x"), | |
+ njs_str("123") }, | |
+ | |
+ { njs_str("let x = () => x; x()"), | |
+ njs_str("[object Function]") }, | |
+ | |
+ { njs_str("x; let x"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
+ | |
+ { njs_str("x; let x = 123"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
+ | |
+ { njs_str("let x = x + 123"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
+ | |
+ { njs_str("let x; var x"), | |
+ njs_str("SyntaxError: \"x\" has already been declared in 1") }, | |
+ | |
+ { njs_str("var x; let x"), | |
+ njs_str("SyntaxError: \"x\" has already been declared in 1") }, | |
+ | |
+ { njs_str("let x; function x() {}"), | |
+ njs_str("SyntaxError: \"x\" has already been declared in 1") }, | |
+ | |
+ { njs_str("function x() {} let x"), | |
+ njs_str("SyntaxError: \"x\" has already been declared in 1") }, | |
+ | |
+ { njs_str("function x() {let x; var x}"), | |
+ njs_str("SyntaxError: \"x\" has already been declared in 1") }, | |
+ | |
+ { njs_str("function x() {var x; let x}"), | |
+ njs_str("SyntaxError: \"x\" has already been declared in 1") }, | |
+ | |
+ { njs_str("var x = function f() {let f}"), | |
+ njs_str("undefined") }, | |
+ | |
+ { njs_str("let a; let x = 1;" | |
+ "{let x = 2; a = x}" | |
+ "[x, a]"), | |
+ njs_str("1,2") }, | |
+ | |
+ { njs_str("let a; let x = 1;" | |
+ "if (true) {let x = 2; a = x}" | |
+ "[x, a]"), | |
+ njs_str("1,2") }, | |
+ | |
+ { njs_str("var a = 5, b = 10, arr = [];" | |
+ "{let a = 4; var b = 1; arr.push(a); arr.push(b)}" | |
+ "arr.push(a); arr.push(b); arr"), | |
+ njs_str("4,1,5,1") }, | |
+ | |
+ { njs_str("function func() {return x}" | |
+ "let x = 123;" | |
+ "func()"), | |
+ njs_str("123") }, | |
+ | |
+ { njs_str("function func() {return x}" | |
+ "func();" | |
+ "let x = 123"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
+ | |
+ { njs_str("function func() {return () => x}" | |
+ "let x = 123;" | |
+ "func()()"), | |
+ njs_str("123") }, | |
+ | |
+ { njs_str("function func() {x = x + 1; let x}"), | |
+ njs_str("undefined") }, | |
+ | |
+ { njs_str("function func() {return () => x}" | |
+ "func()();" | |
+ "let x = 123;"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
+ | |
+ { njs_str("var arr = [];" | |
+ "" | |
+ "for (var i = 0; i < 10; i++) {" | |
+ " let x = i;" | |
+ "" | |
+ " arr.push( (n) => {x += n; return x} );" | |
+ "}" | |
+ "" | |
+ "[" | |
+ " arr[0](2), arr[1](1), arr[2](4), arr[3](7), arr[4](0)," | |
+ " arr[5](1), arr[6](2), arr[7](5), arr[8](8), arr[9](10)" | |
+ "]"), | |
+ njs_str("2,2,6,10,4,6,8,12,16,19") }, | |
+ | |
+ { njs_str("var arr = [];" | |
+ "" | |
+ "for (let i = 0; i < 10; i++) {" | |
+ " arr.push( (n) => {i += n; return i} );" | |
+ "}" | |
+ "" | |
+ "[" | |
+ " arr[0](2), arr[1](1), arr[2](4), arr[3](7), arr[4](0)," | |
+ " arr[5](1), arr[6](2), arr[7](5), arr[8](8), arr[9](10)" | |
+ "]"), | |
+ njs_str("2,2,6,10,4,6,8,12,16,19") }, | |
+ | |
+ { njs_str("for (let i = 0; i < 1; i++) {" | |
+ " let i = i + 2;" | |
+ "}"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
+ | |
+ { njs_str("let arr = [], res = [];" | |
+ "for (let i = 0, f = function() { return i }; i < 5; i++) {" | |
+ " arr.push(f);" | |
+ "}" | |
+ "for (let i = 0; i < 5; i++) {" | |
+ " res.push(arr[i]());" | |
+ "} res"), | |
+ njs_str("0,0,0,0,0") }, | |
+ | |
+ { 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]())}" | |
+ "res"), | |
+ njs_str("0,1,2,3,4,5,6,7,8,9") }, | |
+ | |
+ { njs_str("let res = [];" | |
+ "for (let n in [1,2,3]) {res.push(n)}" | |
+ "res"), | |
+ njs_str("0,1,2") }, | |
+ | |
+ { njs_str("let arr = [], res = [];" | |
+ "" | |
+ "for (let n in [1,2,3]) {" | |
+ " arr.push(() => n);" | |
+ "}" | |
+ "" | |
+ "for (let n in arr) {" | |
+ " res.push(arr[n]());" | |
+ "}" | |
+ "res"), | |
+ njs_str("0,1,2") }, | |
+ | |
+ { njs_str("let arr = [];" | |
+ "" | |
+ "for (let n in [1,2,3]) {" | |
+ " let n = 1;" | |
+ " arr.push(n);" | |
+ "}" | |
+ "arr"), | |
+ njs_str("1,1,1") }, | |
+ | |
+ { njs_str("for (let n in [1,2,3]) {" | |
+ " let n = n + 1;" | |
+ "}"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
+ | |
+ { njs_str("for (let n in [1,2,3]) {}" | |
+ "n"), | |
+ njs_str("ReferenceError: \"n\" is not defined") }, | |
+ | |
+ { njs_str("for (let n in [1,n,3]) {}"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
+ | |
+ { njs_str("(function() {" | |
+ "function f() {return x + 1}" | |
+ "function abc() {f()};" | |
+ "abc();" | |
+ "let x;" | |
+ "}())"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
+ | |
+ { njs_str("function func() {var x = 1; {let x = x + 1} } func()"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
+ | |
+ { njs_str("if (false) let x = 1"), | |
+ njs_str("SyntaxError: let declaration cannot appear in a single-statement context in 1") }, | |
+ | |
+ { njs_str("while (false) let x = 1"), | |
+ njs_str("SyntaxError: let declaration cannot appear in a single-statement context in 1") }, | |
+ | |
+ { njs_str("for (;;) let x = 1"), | |
+ njs_str("SyntaxError: let declaration cannot appear in a single-statement context in 1") }, | |
+ | |
+ { njs_str("try {} catch (e) {let e}"), | |
+ njs_str("SyntaxError: \"e\" has already been declared in 1") }, | |
+ | |
+ { njs_str("let arr = [], x = 2;" | |
+ "switch(true) {default: let x = 1; arr.push(x)}" | |
+ "arr.push(x); arr"), | |
+ njs_str("1,2") }, | |
+ | |
+ { njs_str("switch(true) {case false: let x = 1; default: x = 2}"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
+ | |
+ { njs_str("let res;" | |
+ "switch(true) {case true: let x = 1; default: x = 2; res = x} res"), | |
+ njs_str("2") }, | |
+ | |
+ { njs_str("let null"), | |
+ njs_str("SyntaxError: Unexpected token \"null\" in 1") }, | |
+ | |
+ { njs_str("let continue"), | |
+ njs_str("SyntaxError: Unexpected token \"continue\" in 1") }, | |
+ | |
+ { njs_str("let undefined"), | |
+ njs_str("SyntaxError: \"undefined\" has already been declared in 1") }, | |
+ | |
+ { njs_str("let a = 1; globalThis.a"), | |
+ njs_str("undefined") }, | |
+ | |
+ { njs_str("if (false) {x = 2} else {x = 1} let x;"), | |
+ njs_str("ReferenceError: cannot access to variable before initialization") }, | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment