vm->parser is only needed during parsing phase, so it can be eliminated.
This improves dependencies tracking and readability.
As a side effect it fixes #372 issue on Github: previously, Function
constructor left VM context in inconsistent state if compilation failed.
The direct root cause was that a function object was created, but
because Function constructor ended prematurely the object was not fully
initialized. This is not a problem by itself because usually this
partially created object cannot be referenced. In the accumulative mode,
which is only enabled in CLI, vm->parser was used to store the references
to the variables from the previous iteration.
njs_lvlhsh_query_t lhq;
njs_variable_node_t var_node;
- if (njs_slow_path(vm->parser == NULL)) {
- return NULL;
- }
-
p = expression->start;
end = p + expression->length;
var_node.key = (uintptr_t) lhq.value;
- node = njs_rbtree_find(&vm->parser->scope->variables, &var_node.node);
+ node = njs_rbtree_find(vm->variables_hash, &var_node.node);
if (njs_slow_path(node == NULL)) {
return NULL;
}
njs_str_t str, file;
njs_uint_t i;
njs_lexer_t lexer;
- njs_parser_t *parser;
+ njs_parser_t parser;
njs_vm_code_t *code;
njs_function_t *function;
njs_generator_t generator;
return NJS_ERROR;
}
- vm->options.accumulative = 1;
-
- parser = njs_mp_zalloc(vm->mem_pool, sizeof(njs_parser_t));
- if (njs_slow_path(parser == NULL)) {
- return NJS_ERROR;
- }
-
- vm->parser = parser;
-
file = njs_str_value("runtime");
ret = njs_lexer_init(vm, &lexer, &file, str.start, str.start + str.length);
return ret;
}
- parser->vm = vm;
- parser->lexer = &lexer;
+ njs_memzero(&parser, sizeof(njs_parser_t));
+
+ parser.lexer = &lexer;
- ret = njs_parser(parser, NULL);
+ ret = njs_parser(vm, &parser, NULL);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
* the global object in a portable way.
*/
- node = parser->node;
+ node = parser.node;
type = &safe_ast[0];
for (; *type != NJS_TOKEN_ILLEGAL; type++, node = node->right) {
}
}
- scope = parser->scope;
+ scope = parser.scope;
ret = njs_variables_copy(vm, &scope->variables, vm->variables_hash);
if (njs_slow_path(ret != NJS_OK)) {
static njs_module_t *njs_module_find(njs_vm_t *vm, njs_str_t *name,
njs_bool_t local);
static njs_module_t *njs_module_add(njs_vm_t *vm, njs_str_t *name);
-static njs_int_t njs_module_insert(njs_vm_t *vm, njs_module_t *module);
+static njs_int_t njs_module_insert(njs_parser_t *parser, njs_module_t *module);
njs_int_t
module = (njs_module_t *) parser->target;
if (module->index == 0) {
- ret = njs_module_insert(parser->vm, module);
+ ret = njs_module_insert(parser, module);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
static njs_int_t
-njs_module_insert(njs_vm_t *vm, njs_module_t *module)
+njs_module_insert(njs_parser_t *parser, njs_module_t *module)
{
+ njs_vm_t *vm;
njs_module_t **value;
njs_parser_scope_t *scope;
- scope = njs_parser_global_scope(vm);
+ scope = njs_parser_global_scope(parser);
+ vm = parser->vm;
module->index = njs_scope_next_index(vm, scope, NJS_SCOPE_INDEX_LOCAL,
&njs_value_undefined);
njs_int_t
-njs_parser(njs_parser_t *parser, njs_parser_t *prev)
+njs_parser(njs_vm_t *vm, njs_parser_t *parser, njs_rbtree_t *prev_vars)
{
njs_int_t ret;
njs_lexer_token_t *token;
+ parser->vm = vm;
+
+ njs_set_undefined(&vm->retval);
+
ret = njs_parser_scope_begin(parser, NJS_SCOPE_GLOBAL);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
- if (prev != NULL) {
+ if (prev_vars != NULL) {
/*
* Copy the global scope variables from the previous
* iteration of the accumulative mode.
*/
- ret = njs_variables_copy(parser->vm, &parser->scope->variables,
- &prev->scope->variables);
+ ret = njs_variables_copy(vm, &parser->scope->variables, prev_vars);
if (ret != NJS_OK) {
return ret;
}
return NJS_ERROR;
}
- if (njs_is_error(&parser->vm->retval)) {
+ if (njs_is_error(&vm->retval)) {
return NJS_ERROR;
}
{
u_char *p;
njs_str_t text;
+ njs_int_t ret;
njs_lexer_t *lexer;
- njs_value_t *value;
+ njs_value_t *value, retval;
njs_regexp_flags_t flags;
njs_regexp_pattern_t *pattern;
+ static const njs_value_t string_message = njs_string("message");
+
value = &parser->node->u.value;
lexer = parser->lexer;
text.length, flags);
if (njs_slow_path(pattern == NULL)) {
+ ret = njs_value_property(parser->vm, &parser->vm->retval,
+ njs_value_arg(&string_message),
+ &retval);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ njs_string_get(&retval, &text);
+ njs_value_undefined_set(&parser->vm->retval);
+
+ njs_parser_syntax_error(parser, "%V", &text);
+
return NJS_ERROR;
}
njs_lexer_consume_token(parser->lexer, 1);
- var = njs_variable_add(parser->vm, parser->scope, unique_id,
+ var = njs_variable_add(parser, parser->scope, unique_id,
NJS_VARIABLE_FUNCTION);
if (var == NULL) {
return NJS_ERROR;
}
if (njs_lexer_token_is_binding_identifier(token)) {
- var = njs_variable_add(parser->vm, parser->scope, token->unique_id,
+ var = njs_variable_add(parser, parser->scope, token->unique_id,
NJS_VARIABLE_SHIM);
if (var == NULL) {
return NJS_ERROR;
default:
/* SingleNameBinding */
if (njs_lexer_token_is_binding_identifier(token)) {
- arg = njs_variable_add(parser->vm, parser->scope,
+ arg = njs_variable_add(parser, parser->scope,
token->unique_id, NJS_VARIABLE_VAR);
if (arg == NULL) {
return NJS_ERROR;
njs_parser_arrow_function_args_after);
} else if (njs_lexer_token_is_binding_identifier(token)) {
- arg = njs_variable_add(parser->vm, parser->scope,
+ arg = njs_variable_add(parser, parser->scope,
token->unique_id, NJS_VARIABLE_VAR);
if (arg == NULL) {
return NJS_ERROR;
njs_variable_t *var;
njs_parser_node_t *node;
- var = njs_variable_add(parser->vm, parser->scope, unique_id, type);
+ var = njs_variable_add(parser, parser->scope, unique_id, type);
if (njs_slow_path(var == NULL)) {
return NULL;
}
return NULL;
}
- var = njs_variable_add(parser->vm, scope, token->unique_id,
+ var = njs_variable_add(parser, scope, token->unique_id,
NJS_VARIABLE_VAR);
if (njs_slow_path(var == NULL)) {
return NULL;
return NULL;
}
- var = njs_variable_add(parser->vm, scope, token->unique_id,
+ var = njs_variable_add(parser, scope, token->unique_id,
NJS_VARIABLE_VAR);
if (njs_slow_path(var == NULL)) {
return NULL;
}
-u_char *
-njs_parser_trace_handler(njs_trace_t *trace, njs_trace_data_t *td,
- u_char *start)
-{
- u_char *p;
- size_t size;
- njs_vm_t *vm;
- njs_lexer_t *lexer;
- njs_parser_t *parser;
-
- size = njs_length("InternalError: ");
- memcpy(start, "InternalError: ", size);
- p = start + size;
-
- vm = trace->data;
-
- trace = trace->next;
- p = trace->handler(trace, td, p);
-
- parser = vm->parser;
-
- if (parser != NULL && parser->lexer != NULL) {
- lexer = parser->lexer;
-
- if (lexer->file.length != 0) {
- njs_internal_error(vm, "%s in %V:%uD", start, &lexer->file,
- parser->lexer->token->line);
- } else {
- njs_internal_error(vm, "%s in %uD", start,
- parser->lexer->token->line);
- }
-
- } else {
- njs_internal_error(vm, "%s", start);
- }
-
- return p;
-}
-
-
static void
njs_parser_scope_error(njs_vm_t *vm, njs_parser_scope_t *scope,
njs_object_type_t type, uint32_t line, const char *fmt, va_list args)
intptr_t njs_parser_scope_rbtree_compare(njs_rbtree_node_t *node1,
njs_rbtree_node_t *node2);
-njs_int_t njs_parser(njs_parser_t *parser, njs_parser_t *prev);
+njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser,
+ njs_rbtree_t *prev_vars);
njs_int_t njs_parser_module_lambda(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
njs_str_t *name, njs_token_type_t type);
njs_int_t njs_parser_string_create(njs_vm_t *vm, njs_lexer_token_t *token,
njs_value_t *value);
-u_char *njs_parser_trace_handler(njs_trace_t *trace, njs_trace_data_t *td,
- u_char *start);
void njs_parser_lexer_error(njs_parser_t *parser,
njs_object_type_t type, const char *fmt, ...);
void njs_parser_node_error(njs_vm_t *vm, njs_parser_node_t *node,
njs_inline njs_parser_scope_t *
-njs_parser_global_scope(njs_vm_t *vm)
+njs_parser_global_scope(njs_parser_t *parser)
{
njs_parser_scope_t *scope;
- scope = vm->parser->scope;
+ scope = parser->scope;
while (scope->type != NJS_SCOPE_GLOBAL) {
scope = scope->parent;
trace = trace->next;
p = trace->handler(trace, td, start);
- if (vm->parser != NULL && vm->parser->lexer != NULL) {
- njs_syntax_error(vm, "%*s in %uD", p - start, start,
- vm->parser->lexer->line);
-
- } else {
- njs_syntax_error(vm, "%*s", p - start, start);
- }
+ njs_syntax_error(vm, "%*s", p - start, start);
return p;
}
cmpl->length = njs_strlen(text);
cmpl->suffix_completions = NULL;
- if (vm->parser != NULL) {
- cmpl->node = njs_rbtree_min(&vm->parser->scope->variables);
+ if (vm->variables_hash != NULL) {
+ cmpl->node = njs_rbtree_min(vm->variables_hash);
}
}
switch (cmpl->phase) {
case NJS_COMPLETION_VAR:
- if (vm->parser == NULL) {
+ variables = vm->variables_hash;
+
+ if (variables == NULL) {
njs_next_phase(cmpl);
}
- variables = &vm->parser->scope->variables;
-
while (njs_rbtree_is_there_successor(variables, cmpl->node)) {
var_node = (njs_variable_node_t *) cmpl->node;
#include <njs_main.h>
-static njs_variable_t *njs_variable_scope_add(njs_vm_t *vm,
+static njs_variable_t *njs_variable_scope_add(njs_parser_t *parser,
njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type);
static njs_int_t njs_variable_reference_resolve(njs_vm_t *vm,
njs_variable_reference_t *vr, njs_parser_scope_t *node_scope);
njs_variable_t *
-njs_variable_add(njs_vm_t *vm, njs_parser_scope_t *scope, uintptr_t unique_id,
- njs_variable_type_t type)
+njs_variable_add(njs_parser_t *parser, njs_parser_scope_t *scope,
+ uintptr_t unique_id, njs_variable_type_t type)
{
njs_variable_t *var;
- var = njs_variable_scope_add(vm, scope, unique_id, type);
+ var = njs_variable_scope_add(parser, scope, unique_id, type);
if (njs_slow_path(var == NULL)) {
return NULL;
}
do {
scope = scope->parent;
- var = njs_variable_scope_add(vm, scope, unique_id, type);
+ var = njs_variable_scope_add(parser, scope, unique_id, type);
if (njs_slow_path(var == NULL)) {
return NULL;
}
static njs_variable_t *
-njs_variable_scope_add(njs_vm_t *vm, njs_parser_scope_t *scope,
+njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope,
uintptr_t unique_id, njs_variable_type_t type)
{
njs_variable_t *var;
if (scope->type == NJS_SCOPE_GLOBAL) {
- if (vm->options.module) {
+ if (parser->vm->options.module) {
if (type == NJS_VARIABLE_FUNCTION
|| var->type == NJS_VARIABLE_FUNCTION)
{
return var;
}
- var = njs_variable_alloc(vm, unique_id, type);
+ var = njs_variable_alloc(parser->vm, unique_id, type);
if (njs_slow_path(var == NULL)) {
goto memory_error;
}
- var_node_new = njs_variable_node_alloc(vm, var, unique_id);
+ var_node_new = njs_variable_node_alloc(parser->vm, var, unique_id);
if (njs_slow_path(var_node_new == NULL)) {
goto memory_error;
}
memory_error:
- njs_memory_error(vm);
+ njs_memory_error(parser->vm);
return NULL;
entry = njs_lexer_entry(unique_id);
- njs_parser_syntax_error(vm->parser,
- "\"%V\" has already been declared",
+ njs_parser_syntax_error(parser, "\"%V\" has already been declared",
&entry->name);
return NULL;
}
} njs_variable_node_t;
-njs_variable_t *njs_variable_add(njs_vm_t *vm, njs_parser_scope_t *scope,
- uintptr_t unique_id, njs_variable_type_t type);
+njs_variable_t *njs_variable_add(njs_parser_t *parser,
+ njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type);
njs_int_t njs_variables_copy(njs_vm_t *vm, njs_rbtree_t *variables,
njs_rbtree_t *prev_variables);
njs_variable_t * njs_label_add(njs_vm_t *vm, njs_parser_scope_t *scope,
vm->trace.level = NJS_LEVEL_TRACE;
vm->trace.size = 2048;
- vm->trace.handler = njs_parser_trace_handler;
vm->trace.data = vm;
njs_set_undefined(&vm->retval);
njs_int_t
njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
{
- njs_int_t ret;
- njs_str_t ast;
- njs_chb_t chain;
- njs_lexer_t lexer;
- njs_parser_t *parser, *prev;
- njs_vm_code_t *code;
- njs_generator_t generator;
- njs_parser_scope_t *scope;
-
- if (vm->parser != NULL && !vm->options.accumulative) {
- return NJS_ERROR;
- }
+ njs_int_t ret;
+ njs_str_t ast;
+ njs_chb_t chain;
+ njs_lexer_t lexer;
+ njs_parser_t parser;
+ njs_vm_code_t *code;
+ njs_generator_t generator;
if (vm->modules != NULL && vm->options.accumulative) {
njs_module_reset(vm);
}
- parser = njs_mp_zalloc(vm->mem_pool, sizeof(njs_parser_t));
- if (njs_slow_path(parser == NULL)) {
- return NJS_ERROR;
- }
-
- prev = vm->parser;
- vm->parser = parser;
-
ret = njs_lexer_init(vm, &lexer, &vm->options.file, *start, end);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
- parser->vm = vm;
- parser->lexer = &lexer;
+ njs_memzero(&parser, sizeof(njs_parser_t));
- njs_set_undefined(&vm->retval);
+ parser.lexer = &lexer;
- ret = njs_parser(parser, prev);
+ ret = njs_parser(vm, &parser, vm->variables_hash);
if (njs_slow_path(ret != NJS_OK)) {
- goto fail;
+ return NJS_ERROR;
}
- parser->lexer = NULL;
-
- scope = parser->scope;
+ *start = lexer.start;
- ret = njs_variables_scope_reference(vm, scope);
+ ret = njs_variables_scope_reference(vm, parser.scope);
if (njs_slow_path(ret != NJS_OK)) {
- goto fail;
+ return NJS_ERROR;
}
- *start = lexer.start;
-
njs_memzero(&generator, sizeof(njs_generator_t));
- code = njs_generate_scope(vm, &generator, scope, &njs_entry_main);
+ code = njs_generate_scope(vm, &generator, parser.scope, &njs_entry_main);
if (njs_slow_path(code == NULL)) {
if (!njs_is_error(&vm->retval)) {
njs_internal_error(vm, "njs_generate_scope() failed");
}
- goto fail;
+ return NJS_ERROR;
}
vm->main_index = code - (njs_vm_code_t *) vm->codes->start;
vm->global_scope = generator.local_scope;
vm->scope_size = generator.scope_size;
- vm->variables_hash = &scope->variables;
+ vm->variables_hash = &parser.scope->variables;
if (vm->options.init && !vm->options.accumulative) {
ret = njs_vm_init(vm);
if (njs_slow_path(vm->options.ast)) {
njs_chb_init(&chain, vm->mem_pool);
- ret = njs_parser_serialize_ast(parser->node, &chain);
+ ret = njs_parser_serialize_ast(parser.node, &chain);
if (njs_slow_path(ret == NJS_ERROR)) {
return ret;
}
njs_mp_free(vm->mem_pool, ast.start);
}
- return ret;
-
-fail:
-
- vm->parser = prev;
-
- return NJS_ERROR;
+ return NJS_OK;
}
size_t stack_size;
njs_vm_shared_t *shared;
- njs_parser_t *parser;
njs_regex_context_t *regex_context;
njs_regex_match_data_t *single_match_data;
njs_str("1") },
{ njs_str("var sum = new Function('a', 'b', 'return a + b');"
- "sum(2, 4);"),
+ "sum(2, 4);"),
njs_str("6") },
{ njs_str("var sum = new Function('a, b', 'return a + b');"
- "sum(2, 4);"),
+ "sum(2, 4);"),
njs_str("6") },
{ njs_str("var sum = new Function('a, b', 'c', 'return a + b + c');"
- "sum(2, 4, 4);"),
+ "sum(2, 4, 4);"),
njs_str("10") },
{ njs_str("(new Function({ toString() { return '...a'; }}, { toString() { return 'return a;' }}))(1,2,3)"),
njs_str("1,2,3") },
{ njs_str("var x = 10; function foo() { var x = 20; return new Function('return x;'); }"
- "var f = foo(); f()"),
+ "var f = foo(); f()"),
njs_str("10") },
- { njs_str("var fn = (function() { return new Function('return this'); }).call({}), o = {}; fn.call(o) == o && fn.bind(o).call(this) == o"),
+ { njs_str("var fn = (function() { return new Function('return this'); }).call({}), o = {}; "
+ "fn.call(o) == o && fn.bind(o).call(this) == o"),
njs_str("true") },
{ njs_str("(new Function('return this'))() === globalThis"),
{ njs_str("var o = {}; (new Function('return this')).call(o) === o"),
njs_str("true") },
+ { njs_str("(new Function('function foo(){return 1}; return foo()'))();"
+ "foo"),
+ njs_str("ReferenceError: \"foo\" is not defined") },
+
{ njs_str("this.NN = {}; var f = Function('eval = 42;'); f()"),
njs_str("SyntaxError: Identifier \"eval\" is forbidden as left-hand in assignment in runtime:1") },
"Number.prototype.test" ENTER),
njs_str("test") },
+ { njs_str("try {(new Function('function foo(){return 1}; ()=>{}breakhere'))} catch (e) {}" ENTER
+ "foo()" ENTER),
+ njs_str("ReferenceError: \"foo\" is not defined\n"
+ " at main (:1)\n") },
+
/* Error handling */
{ njs_str("var a = ;" ENTER