Created
April 12, 2021 07:08
-
-
Save lexborisov/f313e33ee9971d29287951d33ba256c2 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 1618211209 -10800 | |
# Mon Apr 12 10:06:49 2021 +0300 | |
# Node ID c77b6d6fdfee365deb8bf3180ee8b00918619905 | |
# Parent 8583f3bdaeb94e039de420d1cdeba302ab5fcdd4 | |
Introduced let implementation. | |
diff -r 8583f3bdaeb9 -r c77b6d6fdfee src/njs_builtin.c | |
--- a/src/njs_builtin.c Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/src/njs_builtin.c Mon Apr 12 10:06:49 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 8583f3bdaeb9 -r c77b6d6fdfee src/njs_disassembler.c | |
--- a/src/njs_disassembler.c Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/src/njs_disassembler.c Mon Apr 12 10:06:49 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 8583f3bdaeb9 -r c77b6d6fdfee src/njs_generator.c | |
--- a/src/njs_generator.c Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/src/njs_generator.c Mon Apr 12 10:06:49 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; | |
} | |
@@ -591,6 +601,7 @@ njs_generate_name(njs_vm_t *vm, njs_gene | |
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 +620,23 @@ 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) { | |
+ njs_generate_code(generator, njs_vmcode_variable_t, code, | |
+ NJS_VMCODE_NOT_INITIALIZATION, 0, node); | |
+ code->dst = node->index; | |
+ | |
+ return NJS_OK; | |
+ } | |
+ | |
+ if (var->check == NJS_VARIABLE_NOT_CHECK) { | |
+ njs_generate_code(generator, njs_vmcode_variable_t, code, | |
+ NJS_VMCODE_REFERENCE, 0, node); | |
+ code->dst = node->index; | |
+ | |
+ var->check = NJS_VARIABLE_CHECK; | |
+ } | |
} | |
return NJS_OK; | |
@@ -620,6 +648,7 @@ 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_vmcode_variable_t *code; | |
njs_vmcode_function_t *function; | |
var = njs_variable_reference(vm, node); | |
@@ -640,19 +669,28 @@ 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; | |
+ | |
+ } else if (var->type == NJS_VARIABLE_LET | |
+ && var->check == NJS_VARIABLE_NOT_CHECK && retvar == NULL) | |
+ { | |
+ njs_generate_code(generator, njs_vmcode_variable_t, code, | |
+ NJS_VMCODE_REFERENCE, 0, node); | |
+ code->dst = node->index; | |
+ | |
+ var->check = NJS_VARIABLE_CHECK; | |
} | |
- var->init = 1; | |
- | |
return NJS_OK; | |
} | |
@@ -673,21 +711,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 +754,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 +1166,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 +1190,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 +1230,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 +1284,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 +1385,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 +1442,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); | |
diff -r 8583f3bdaeb9 -r c77b6d6fdfee src/njs_parser.c | |
--- a/src/njs_parser.c Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/src/njs_parser.c Mon Apr 12 10:06:49 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, | |
@@ -506,8 +509,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 +531,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; | |
@@ -746,14 +759,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 +904,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 +4550,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 +4580,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 +4744,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 +4820,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 +4839,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 +4862,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 +4977,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 +5225,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 +5254,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 +5303,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 +5313,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 +5325,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 +5352,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 +5394,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 +5429,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); | |
@@ -8414,6 +8501,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 8583f3bdaeb9 -r c77b6d6fdfee src/njs_parser.h | |
--- a/src/njs_parser.h Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/src/njs_parser.h Mon Apr 12 10:06:49 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 8583f3bdaeb9 -r c77b6d6fdfee src/njs_variable.c | |
--- a/src/njs_variable.c Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/src/njs_variable.c Mon Apr 12 10:06:49 2021 +0300 | |
@@ -79,9 +79,7 @@ njs_variable_scope(njs_parser_scope_t *s | |
if (node != NULL) { | |
var = ((njs_variable_node_t *) node)->variable; | |
- if (var->type == NJS_VARIABLE_VAR | |
- || var->type == NJS_VARIABLE_FUNCTION) | |
- { | |
+ if (var->type <= NJS_VARIABLE_FUNCTION) { | |
*retvar = var; | |
return (type == NJS_VARIABLE_VAR) ? scope : prev; | |
@@ -112,15 +110,37 @@ 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) { | |
+ goto failed; | |
+ } | |
+ | |
+ return scope; | |
+ | |
+ case NJS_VARIABLE_VAR: | |
+ case NJS_VARIABLE_FUNCTION: | |
+ break; | |
+ | |
+ default: | |
+ return scope; | |
+ } | |
+ | |
if (type == NJS_VARIABLE_FUNCTION) { | |
root = scope; | |
} | |
@@ -129,6 +149,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) | |
@@ -284,7 +308,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) { | |
@@ -397,6 +421,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); | |
} | |
@@ -413,6 +438,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; | |
@@ -430,7 +456,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; | |
@@ -448,12 +474,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 8583f3bdaeb9 -r c77b6d6fdfee src/njs_variable.h | |
--- a/src/njs_variable.h Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/src/njs_variable.h Mon Apr 12 10:06:49 2021 +0300 | |
@@ -12,11 +12,17 @@ typedef enum { | |
NJS_VARIABLE_CONST = 0, | |
NJS_VARIABLE_LET, | |
NJS_VARIABLE_VAR, | |
+ NJS_VARIABLE_FUNCTION, | |
NJS_VARIABLE_CATCH, | |
- NJS_VARIABLE_FUNCTION, | |
} 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 +30,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 +56,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 8583f3bdaeb9 -r c77b6d6fdfee src/njs_vm.h | |
--- a/src/njs_vm.h Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/src/njs_vm.h Mon Apr 12 10:06:49 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 8583f3bdaeb9 -r c77b6d6fdfee src/njs_vmcode.c | |
--- a/src/njs_vmcode.c Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/src/njs_vmcode.c Mon Apr 12 10:06:49 2021 +0300 | |
@@ -98,6 +98,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; | |
@@ -707,6 +708,7 @@ next: | |
case NJS_VMCODE_RETURN: | |
value2 = njs_vmcode_operand(vm, (njs_index_t) value2); | |
+ | |
frame = (njs_frame_t *) vm->top_frame; | |
if (frame->native.ctor) { | |
@@ -913,6 +915,56 @@ next: | |
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; | |
@@ -1080,7 +1132,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); | |
@@ -1122,7 +1178,10 @@ 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_slow_path(value == NULL)) { | |
+ return NJS_ERROR; | |
+ } | |
if (!njs_is_primitive(value)) { | |
array = njs_array(value); | |
diff -r 8583f3bdaeb9 -r c77b6d6fdfee src/njs_vmcode.h | |
--- a/src/njs_vmcode.h Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/src/njs_vmcode.h Mon Apr 12 10:06:49 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 | |
}; | |
@@ -402,6 +407,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 8583f3bdaeb9 -r c77b6d6fdfee src/test/njs_unit_test.c | |
--- a/src/test/njs_unit_test.c Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/src/test/njs_unit_test.c Mon Apr 12 10:06:49 2021 +0300 | |
@@ -19700,6 +19700,198 @@ 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("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("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("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") }, | |
}; | |
diff -r 8583f3bdaeb9 -r c77b6d6fdfee test/njs_expect_test.exp | |
--- a/test/njs_expect_test.exp Mon Apr 12 10:05:59 2021 +0300 | |
+++ b/test/njs_expect_test.exp Mon Apr 12 10:06:49 2021 +0300 | |
@@ -143,6 +143,12 @@ njs_test { | |
"'abc'."} | |
} | |
+# let | |
+njs_test { | |
+ {"x; {x} let x\r\n" | |
+ " 1 | 00000 REFERENCE*\r\n* 1 | 00016 LET*\r\n*"} | |
+} "-d" | |
+ | |
# Global completions, global vars | |
njs_test { | |
{"var a = 1; var aa = 2\r\n" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment