This closes #105 issue on GitHub.
var = node->variable;
+ if (var->type == NJS_VARIABLE_LET) {
+ return NJS_DECLINED;
+ }
+
value = njs_scope_valid_value(vm, var->index);
if (var->type == NJS_VARIABLE_FUNCTION && njs_is_undefined(value)) {
{ NJS_VMCODE_THROW, sizeof(njs_vmcode_throw_t),
njs_str("THROW ") },
+ { NJS_VMCODE_LET, sizeof(njs_vmcode_variable_t),
+ njs_str("LET ") },
+
+ { NJS_VMCODE_LET_UPDATE, sizeof(njs_vmcode_variable_t),
+ njs_str("LET UPDATE ") },
+
+ { NJS_VMCODE_INITIALIZATION_TEST, sizeof(njs_vmcode_variable_t),
+ njs_str("INIT TEST ") },
+
+ { NJS_VMCODE_NOT_INITIALIZED, sizeof(njs_vmcode_variable_t),
+ njs_str("NOT INIT ") },
};
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,
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,
##__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(). */
switch (node->token_type) {
case NJS_TOKEN_VAR:
+ case NJS_TOKEN_LET:
return njs_generate_var_statement(vm, generator, node);
case NJS_TOKEN_IF:
{
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;
}
}
+static njs_int_t
+njs_generate_wo_dest(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_int_t ret;
+ njs_parser_scope_t *scope;
+
+ scope = njs_function_scope(node->scope);
+
+ scope->dest_disable = 1;
+
+ ret = njs_generator(vm, generator, node);
+
+ scope->dest_disable = 0;
+
+ return ret;
+}
+
+
static u_char *
njs_generate_reserve(njs_vm_t *vm, njs_generator_t *generator, size_t size)
{
njs_parser_node_t *node)
{
njs_variable_t *var;
+ njs_parser_scope_t *scope;
+ njs_vmcode_variable_t *variable;
njs_vmcode_function_copy_t *copy;
var = njs_variable_reference(vm, node);
copy->retval = node->index;
}
+ if (var->init) {
+ return NJS_OK;
+ }
+
+ if (var->type == NJS_VARIABLE_LET) {
+ scope = njs_function_scope(node->scope);
+
+ if (scope->dest_disable) {
+ njs_generate_code(generator, njs_vmcode_variable_t, variable,
+ NJS_VMCODE_NOT_INITIALIZED, 1, node);
+ variable->dst = node->index;
+ }
+ }
+
return NJS_OK;
}
njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar)
{
njs_variable_t *var;
+ njs_parser_scope_t *scope;
+ njs_vmcode_variable_t *variable;
njs_vmcode_function_copy_t *copy;
var = njs_variable_reference(vm, node);
copy->retval = node->index;
}
+ if (var->init) {
+ return NJS_OK;
+ }
+
+ if (var->type == NJS_VARIABLE_LET) {
+ scope = njs_function_scope(node->scope);
+
+ if ((!scope->dest_disable && njs_function_scope(var->scope) == scope)) {
+ njs_generate_code(generator, njs_vmcode_variable_t, variable,
+ NJS_VMCODE_NOT_INITIALIZED, 1, node);
+ variable->dst = node->index;
+ }
+ }
+
return NJS_OK;
}
+static njs_int_t
+njs_generate_variable_wo_dest(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar)
+{
+ njs_int_t ret;
+ njs_parser_scope_t *scope;
+
+ scope = njs_function_scope(node->scope);
+
+ scope->dest_disable = 1;
+
+ ret = njs_generate_variable(vm, generator, node, type, retvar);
+
+ scope->dest_disable = 0;
+
+ return ret;
+}
+
+
static njs_int_t
njs_generate_var_statement(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
lvalue = node->left;
- ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION, &var);
+ ret = njs_generate_variable_wo_dest(vm, generator, lvalue,
+ NJS_DECLARATION, &var);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
- lvalue->index = var->index;
expr = node->right;
if (expr == NULL) {
/* Variable is only declared. */
+ if (var->type == NJS_VARIABLE_LET) {
+ ret = njs_generate_let(vm, generator, node, var);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
+
+ var->init = 1;
+
return NJS_OK;
}
- expr->dest = lvalue;
+ if (var->type == NJS_VARIABLE_LET) {
+ ret = njs_generate_wo_dest(vm, generator, expr);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
- ret = njs_generator(vm, generator, expr);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ ret = njs_generate_let(vm, generator, node, var);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ } else {
+ expr->dest = lvalue;
+
+ ret = njs_generator(vm, generator, expr);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
}
+ var->init = 1;
+
/*
* lvalue and expression indexes are equal if the expression is an
* empty object or expression result is stored directly in variable.
}
+static njs_int_t
+njs_generate_let(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node, njs_variable_t *var)
+{
+ njs_vmcode_variable_t *code;
+
+ njs_generate_code(generator, njs_vmcode_variable_t, code,
+ NJS_VMCODE_LET, 0, node);
+ code->dst = var->index;
+
+ return NJS_OK;
+}
+
+
static njs_int_t
njs_generate_if_statement(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
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;
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;
/* The loop update. */
- njs_generate_patch_block(vm, generator, generator->block->continuation);
-
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);
+
ret = njs_generator(vm, generator, update);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
+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;
/* The object. */
foreach = node->left;
+ name = foreach->left->right;
- ret = njs_generator(vm, generator, foreach->left);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
+ if (name != NULL) {
+ name = name->left;
- ret = njs_generator(vm, generator, foreach->right);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ ret = njs_generate_variable_wo_dest(vm, generator, name,
+ NJS_DECLARATION, &var);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ foreach->left->index = name->index;
+
+ ret = njs_generator(vm, generator, foreach->right);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ var->init = 1;
+
+ } else {
+ ret = njs_generator(vm, generator, foreach->left);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ ret = njs_generator(vm, generator, foreach->right);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
}
njs_generate_code(generator, njs_vmcode_prop_foreach_t, prop_foreach,
/* 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);
njs_generate_statement(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
+ njs_int_t ret;
+ njs_variable_t *var;
+ njs_parser_node_t *right;
+ njs_vmcode_variable_t *code;
+
+ right = node->right;
+
+ if (right != NULL && right->token_type == NJS_TOKEN_NAME) {
+ var = njs_variable_reference(vm, right);
+ if (njs_slow_path(var == NULL)) {
+ goto statement;
+ }
+
+ if (!var->init && var->type == NJS_VARIABLE_LET) {
+ njs_generate_code(generator, njs_vmcode_variable_t, code,
+ NJS_VMCODE_INITIALIZATION_TEST, 0, right);
+ code->dst = right->index;
+ }
+
+ if (node->left == NULL) {
+ return NJS_OK;
+ }
+
+ node = node->left;
+ }
+
+statement:
ret = njs_generate_children(vm, generator, node);
if (njs_fast_path(ret == NJS_OK)) {
- return njs_generate_node_index_release(vm, generator, node->right);
+ return njs_generate_node_index_release(vm, generator, right);
}
return ret;
njs_generate_dest_index(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_index_t ret;
- njs_parser_node_t *dest;
+ njs_index_t ret;
+ njs_parser_node_t *dest;
+ njs_parser_scope_t *scope;
ret = njs_generate_children_indexes_release(vm, generator, node);
if (njs_slow_path(ret != NJS_OK)) {
dest = node->dest;
if (dest != NULL && dest->index != NJS_INDEX_NONE) {
- return dest->index;
+ scope = njs_function_scope(node->scope);
+
+ if (!scope->dest_disable) {
+ return dest->index;
+ }
}
return njs_generate_node_temp_index_get(vm, generator, node);
return NJS_ERROR;
}
- return njs_scope_index(scope->type, scope->temp++, NJS_LEVEL_TEMP);
+ return njs_scope_index(scope->type, scope->temp++, NJS_LEVEL_TEMP,
+ NJS_VARIABLE_VAR);
}
scope = njs_parser_global_scope(parser);
vm = parser->vm;
- module->index = njs_scope_index(scope->type, scope->items, NJS_LEVEL_LOCAL);
+ module->index = njs_scope_index(scope->type, scope->items, NJS_LEVEL_LOCAL,
+ NJS_VARIABLE_VAR);
scope->items++;
if (vm->modules == NULL) {
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,
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,
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,
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);
njs_int_t
njs_parser(njs_vm_t *vm, njs_parser_t *parser)
{
- njs_int_t ret;
- njs_lexer_token_t *token;
+ njs_int_t ret;
+ njs_str_t str;
+ njs_lexer_token_t *token;
+ const njs_lexer_keyword_entry_t *keyword;
parser->vm = vm;
parser->ret = NJS_OK;
}
+ /* Add this as first variable. */
+ njs_string_get(&njs_string_undefined, &str);
+
+ keyword = njs_lexer_keyword(str.start, str.length);
+ if (njs_slow_path(keyword == NULL)) {
+ return NJS_ERROR;
+ }
+
+ parser->undefined_id = (uintptr_t) keyword->value;
+
njs_queue_init(&parser->stack);
parser->target = NULL;
return NJS_ERROR;
}
- var->index = njs_scope_index(type, 0, NJS_LEVEL_LOCAL);
+ var->index = njs_scope_index(type, 0, NJS_LEVEL_LOCAL,
+ NJS_VARIABLE_VAR);
}
}
}
-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)
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);
}
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:
return NJS_DECLINED;
}
- return NJS_OK;
+ return njs_parser_after(parser, current, parser->node, 1,
+ njs_parser_statement_after);
}
}
+/*
+ * 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
*/
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);
{
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);
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;
}
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;
}
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);
}
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) {
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>? ")"
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;
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;
break;
- case NJS_TOKEN_LET:
case NJS_TOKEN_CONST:
return njs_parser_not_supported(parser, token);
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)
{
- njs_int_t ret;
- njs_lexer_token_t *next;
- njs_parser_node_t *node, *var;
+ 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 */
}
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);
}
node->token_line = next->line;
- node->left = var;
+ node->left = statement;
njs_parser_next(parser, njs_parser_expression);
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);
}
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);
}
{
njs_parser_node_t *node;
- njs_parser_scope_end(parser);
-
parser->target->right->right = parser->node;
if (token->type == NJS_TOKEN_FINALLY) {
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);
}
+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)
var = (njs_variable_t *) parser->target;
var->index = njs_scope_index(var->scope->type, var->scope->items,
- NJS_LEVEL_LOCAL);
+ NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR);
var->scope->items++;
if (var->self) {
arg->argument = 1;
var->index = njs_scope_index(parser->scope->type, parser->scope->items,
- NJS_LEVEL_LOCAL);
+ NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR);
parser->scope->items++;
lambda->self = var->index;
*vv = NULL;
var->index = njs_scope_index(var->scope->type, var->scope->items,
- NJS_LEVEL_LOCAL);
+ NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR);
var->scope->items++;
parser->target->u.value.data.u.lambda->self = var->index;
*vv = NULL;
var->index = njs_scope_index(var->scope->type, var->scope->items,
- NJS_LEVEL_LOCAL);
+ NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR);
var->scope->items++;
parser->node->u.value.data.u.lambda->self = var->index;
token->unique_id = (uintptr_t) keyword->value;
} else if (!scope->arrow_function) {
- index = njs_scope_index(scope->type, 0, NJS_LEVEL_LOCAL);
+ index = njs_scope_index(scope->type, 0, NJS_LEVEL_LOCAL,
+ NJS_VARIABLE_VAR);
var = njs_variable_scope_add(parser, scope, scope, token->unique_id,
NJS_VARIABLE_VAR, index);
njs_scope_t type:8;
uint8_t module;
uint8_t arrow_function;
+ uint8_t dest_disable;
};
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;
};
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);
return NJS_INDEX_ERROR;
}
- return njs_scope_index(NJS_SCOPE_GLOBAL, scope->temp++, NJS_LEVEL_TEMP);
+ return njs_scope_index(NJS_SCOPE_GLOBAL, scope->temp++, NJS_LEVEL_TEMP,
+ NJS_VARIABLE_VAR);
}
vm->levels[NJS_LEVEL_STATIC] = vm->scope_absolute->start;
- *retval = njs_scope_index(NJS_SCOPE_GLOBAL, index, NJS_LEVEL_STATIC);
+ *retval = njs_scope_index(NJS_SCOPE_GLOBAL, index, NJS_LEVEL_STATIC,
+ NJS_VARIABLE_VAR);
return *retval;
}
#define _NJS_SCOPE_H_INCLUDED_
-#define NJS_SCOPE_TYPE_SIZE 4
-#define NJS_SCOPE_VALUE_OFFSET (NJS_SCOPE_TYPE_SIZE + 1)
+#define NJS_SCOPE_VAR_SIZE 4
+#define NJS_SCOPE_TYPE_OFFSET (NJS_SCOPE_VAR_SIZE + 4)
+#define NJS_SCOPE_VALUE_OFFSET (NJS_SCOPE_TYPE_OFFSET + 1)
#define NJS_SCOPE_VALUE_MAX ((1 << (32 - NJS_SCOPE_VALUE_OFFSET)) - 1)
-#define NJS_SCOPE_TYPE_MASK ((NJS_SCOPE_VALUE_MAX) << NJS_SCOPE_TYPE_SIZE)
+#define NJS_SCOPE_TYPE_MASK ((NJS_SCOPE_VALUE_MAX) << NJS_SCOPE_VAR_SIZE)
#define NJS_INDEX_NONE ((njs_index_t) 0)
#define NJS_INDEX_ERROR ((njs_index_t) -1)
njs_inline njs_index_t
-njs_scope_index(njs_scope_t scope, njs_index_t index, njs_level_type_t type)
+njs_scope_index(njs_scope_t scope, njs_index_t index, njs_level_type_t type,
+ njs_variable_type_t var_type)
{
if (index > NJS_SCOPE_VALUE_MAX || type >= NJS_LEVEL_MAX
|| (scope != NJS_SCOPE_GLOBAL && scope != NJS_SCOPE_FUNCTION))
type = NJS_LEVEL_GLOBAL;
}
- return (index << NJS_SCOPE_VALUE_OFFSET) | type;
+ return (index << NJS_SCOPE_VALUE_OFFSET) | (type << NJS_SCOPE_VAR_SIZE)
+ | 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_level_type_t
njs_scope_index_type(njs_index_t index)
{
- return (njs_level_type_t) (index & ~NJS_SCOPE_TYPE_MASK);
+ return (njs_level_type_t) ((index >> NJS_SCOPE_VAR_SIZE)
+ & ~NJS_SCOPE_TYPE_MASK);
}
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);
}
njs_inline njs_index_t
njs_scope_global_this_index()
{
- return njs_scope_index(NJS_SCOPE_GLOBAL, 0, NJS_LEVEL_LOCAL);
+ return njs_scope_index(NJS_SCOPE_GLOBAL, 0, NJS_LEVEL_LOCAL,
+ NJS_VARIABLE_VAR);
}
*declr = &var->value;
- var->index = njs_scope_index(root->type, root->items, NJS_LEVEL_LOCAL);
+ var->index = njs_scope_index(root->type, root->items, NJS_LEVEL_LOCAL,
+ type);
root->items++;
}
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->function = 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;
}
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)
return NULL;
}
- var->index = njs_scope_index(root->type, root->items, NJS_LEVEL_LOCAL);
+ var->index = njs_scope_index(root->type, root->items, NJS_LEVEL_LOCAL,
+ type);
root->items++;
}
}
-static njs_bool_t
+njs_bool_t
njs_variable_closure_test(njs_parser_scope_t *root, njs_parser_scope_t *scope)
{
if (root == scope) {
/* Create new closure for scope. */
index = njs_scope_index(root->type, root->closures->items,
- NJS_LEVEL_CLOSURE);
+ NJS_LEVEL_CLOSURE, var->type);
if (njs_slow_path(index == NJS_INDEX_ERROR)) {
return NJS_INDEX_ERROR;
}
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;
}
}
- 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;
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;
njs_bool_t argument;
njs_bool_t arguments_object;
njs_bool_t self;
+ njs_bool_t init;
+ njs_bool_t closure;
njs_bool_t function;
njs_parser_scope_t *scope;
njs_variable_t *variable;
njs_parser_scope_t *scope;
njs_bool_t not_defined;
- njs_bool_t closure;
} njs_variable_reference_t;
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
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;
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);
}
op = vmcode->code.operation;
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);
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)) {
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);
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)) {
* 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;
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)
ret ^= op - NJS_VMCODE_EQUAL;
- retval = njs_vmcode_operand(vm, vmcode->operand1);
+ njs_vmcode_operand(vm, vmcode->operand1, retval);
njs_set_boolean(retval, ret);
pc += sizeof(njs_vmcode_3addr_t);
num = njs_number(value1);
- retval = njs_vmcode_operand(vm, vmcode->operand1);
+ njs_vmcode_operand(vm, vmcode->operand1, retval);
pc += sizeof(njs_vmcode_3addr_t);
switch (op) {
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);
pc += sizeof(njs_vmcode_3addr_t);
ret = sizeof(njs_vmcode_3addr_t);
}
- retval = njs_vmcode_operand(vm, vmcode->operand1);
+ njs_vmcode_operand(vm, vmcode->operand1, retval);
*retval = *value1;
pc += ret;
}
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:
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));
pc += sizeof(njs_vmcode_2addr_t);
break;
}
- retval = njs_vmcode_operand(vm, vmcode->operand1);
+ njs_vmcode_operand(vm, vmcode->operand1, retval);
njs_release(vm, retval);
*retval = vm->retval;
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;
break;
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;
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)) {
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)) {
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)) {
goto error;
break;
case NJS_VMCODE_RETURN:
- value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
+ njs_vmcode_operand(vm, (njs_index_t) value2, value2);
return njs_vmcode_return(vm, NULL, value2);
case NJS_VMCODE_FUNCTION_COPY:
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)) {
case NJS_VMCODE_PROPERTY_NEXT:
pnext = (njs_vmcode_prop_next_t *) pc;
- retval = njs_vmcode_operand(vm, pnext->retval);
+ retval = njs_scope_value(vm, pnext->retval);
next = value2->data.u.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)) {
goto error;
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;
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_INITIALIZATION_TEST:
+ 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_INITIALIZED:
+ njs_reference_error(vm, "cannot access to variable "
+ "before initialization");
+ goto error;
+
case NJS_VMCODE_ERROR:
njs_vmcode_error(vm, pc);
goto error;
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);
return sizeof(njs_vmcode_arguments_t);
.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);
NJS_VMCODE_TRY_END,
NJS_VMCODE_CATCH,
NJS_VMCODE_FINALLY,
+
+ NJS_VMCODE_LET,
+ NJS_VMCODE_LET_UPDATE,
+ NJS_VMCODE_INITIALIZATION_TEST,
+ NJS_VMCODE_NOT_INITIALIZED,
+
NJS_VMCODE_ERROR,
NJS_VMCODE_NORET = 127
} njs_vmcode_function_copy_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);
njs_str("100000000"),
1 },
+ { "for let loop 100M",
+ njs_str("let i; for (i = 0; i < 100000000; i++); i"),
+ njs_str("100000000"),
+ 1 },
+
+ { "for let closures 1M",
+ njs_str("let a = []; for (let i = 0; i < 1000000; i++) { a.push(() => i); }"
+ "a[5]()"),
+ njs_str("5"),
+ 1 },
+
{ "while loop 100M",
njs_str("var i = 0; while (i < 100000000) { i++ }; i"),
njs_str("100000000"),
{ 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("let x = (() => x)()"),
+ njs_str("ReferenceError: cannot access to variable before initialization") },
+
+ { 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 = (x, 1)"),
+ njs_str("ReferenceError: cannot access to variable before initialization") },
+
+ { njs_str("let x = x"),
+ 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") },
+
+ { njs_str("let let"),
+ njs_str("SyntaxError: Unexpected token \"let\" in 1") },
+
+ { njs_str("let null"),
+ njs_str("SyntaxError: Unexpected token \"null\" in 1") },
+
+ { njs_str("function let() {}"),
+ njs_str("SyntaxError: Unexpected token \"let\" in 1") },
+
+ { njs_str("function static() {}"),
+ njs_str("SyntaxError: Unexpected token \"static\" in 1") },
};