Skip to content

Instantly share code, notes, and snippets.

@lexborisov
Created April 13, 2021 10:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lexborisov/ec17b310d56f0a9e8c9c77bf431e6468 to your computer and use it in GitHub Desktop.
Save lexborisov/ec17b310d56f0a9e8c9c77bf431e6468 to your computer and use it in GitHub Desktop.
# 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