Skip to content

Instantly share code, notes, and snippets.

@lexborisov
Created April 12, 2021 07:07
Show Gist options
  • Save lexborisov/a945f580d85c3281dd742d7af2c1972f to your computer and use it in GitHub Desktop.
Save lexborisov/a945f580d85c3281dd742d7af2c1972f to your computer and use it in GitHub Desktop.
# HG changeset patch
# User Alexander Borisov <alexander.borisov@nginx.com>
# Date 1618211159 -10800
# Mon Apr 12 10:05:59 2021 +0300
# Node ID 8583f3bdaeb94e039de420d1cdeba302ab5fcdd4
# Parent d25d92370bfd197326336b9fcec912d369e86c27
Scopes refactoring.
diff -r d25d92370bfd -r 8583f3bdaeb9 auto/sources
--- a/auto/sources Tue Mar 30 13:58:27 2021 +0000
+++ b/auto/sources Mon Apr 12 10:05:59 2021 +0300
@@ -59,6 +59,7 @@ NJS_LIB_SRCS=" \
src/njs_encoding.c \
src/njs_buffer.c \
src/njs_iterator.c \
+ src/njs_scope.c \
"
NJS_LIB_TEST_SRCS=" \
diff -r d25d92370bfd -r 8583f3bdaeb9 nginx/ngx_js.c
--- a/nginx/ngx_js.c Tue Mar 30 13:58:27 2021 +0000
+++ b/nginx/ngx_js.c Mon Apr 12 10:05:59 2021 +0300
@@ -261,7 +261,8 @@ ngx_js_ext_log(njs_vm_t *vm, njs_value_t
handler = c->log->handler;
c->log->handler = NULL;
- ngx_log_error(level, c->log, 0, "js: %*s", msg.length, msg.start);
+ ngx_log_error((ngx_uint_t) level, c->log, 0, "js: %*s",
+ msg.length, msg.start);
c->log->handler = handler;
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs.h
--- a/src/njs.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs.h Mon Apr 12 10:05:59 2021 +0300
@@ -26,7 +26,7 @@
#include <njs_sprintf.h>
-typedef uintptr_t njs_index_t;
+typedef int64_t njs_index_t;
typedef struct njs_vm_s njs_vm_t;
typedef union njs_value_s njs_value_t;
typedef struct njs_function_s njs_function_t;
@@ -210,7 +210,7 @@ typedef struct {
#define NJS_VM_OPT_UNHANDLED_REJECTION_THROW 1
/*
- * accumulative - enables "accumulative" mode to support incremental compiling.
+ * interactive - enables "interactive" mode.
* (REPL). Allows starting parent VM without cloning.
* disassemble - enables disassemble.
* backtrace - enables backtraces.
@@ -225,10 +225,9 @@ typedef struct {
* - throwing inside a Promise without a catch block.
* - throwing inside in a finally or catch block.
*/
-
+ uint8_t interactive; /* 1 bit */
uint8_t trailer; /* 1 bit */
uint8_t init; /* 1 bit */
- uint8_t accumulative; /* 1 bit */
uint8_t disassemble; /* 1 bit */
uint8_t backtrace; /* 1 bit */
uint8_t quiet; /* 1 bit */
@@ -277,7 +276,7 @@ NJS_EXPORT njs_int_t njs_vm_posted(njs_v
NJS_EXPORT njs_int_t njs_vm_call(njs_vm_t *vm, njs_function_t *function,
const njs_value_t *args, njs_uint_t nargs);
NJS_EXPORT njs_int_t njs_vm_invoke(njs_vm_t *vm, njs_function_t *function,
- const njs_value_t *args, njs_uint_t nargs, njs_index_t retval);
+ const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval);
/*
* Runs posted events.
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_builtin.c
--- a/src/njs_builtin.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_builtin.c Mon Apr 12 10:05:59 2021 +0300
@@ -320,6 +320,8 @@ njs_builtin_objects_create(njs_vm_t *vm)
vm->global_object = shared->objects[0];
vm->global_object.shared = 0;
+ njs_set_object(&vm->global_value, &vm->global_object);
+
string_object = &shared->string_object;
njs_lvlhsh_init(&string_object->hash);
string_object->shared_hash = shared->string_instance_hash;
@@ -637,7 +639,7 @@ njs_vm_expression_completions(njs_vm_t *
}
var = ((njs_variable_node_t *) node)->variable;
- value = njs_vmcode_operand(vm, var->index);
+ value = njs_scope_valid_value(vm, var->index);
if (!njs_is_object(value)) {
return NULL;
@@ -965,7 +967,7 @@ njs_global_this_prop_handler(njs_vm_t *v
}
node = (njs_variable_node_t *) rb_node;
- value = njs_vmcode_operand(vm, node->variable->index);
+ value = njs_scope_valid_value(vm, node->variable->index);
if (setval != NULL) {
*value = *setval;
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_disassembler.c
--- a/src/njs_disassembler.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_disassembler.c Mon Apr 12 10:05:59 2021 +0300
@@ -135,7 +135,6 @@ static njs_code_name_t code_names[] = {
{ NJS_VMCODE_THROW, sizeof(njs_vmcode_throw_t),
njs_str("THROW ") },
-
};
@@ -186,6 +185,7 @@ njs_disassemble(njs_vm_code_t *code)
njs_vmcode_catch_t *catch;
njs_vmcode_finally_t *finally;
njs_vmcode_try_end_t *try_end;
+ njs_vmcode_move_arg_t *move_arg;
njs_vmcode_try_start_t *try_start;
njs_vmcode_operation_t operation;
njs_vmcode_cond_jump_t *cond_jump;
@@ -312,9 +312,9 @@ njs_disassemble(njs_vm_code_t *code)
if (operation == NJS_VMCODE_FUNCTION_FRAME) {
function = (njs_vmcode_function_frame_t *) p;
- njs_printf("%5uD | %05uz FUNCTION FRAME %04Xz %uz%s\n",
+ njs_printf("%5uD | %05uz FUNCTION FRAME %04Xz %04Xz%s\n",
line, p - start, (size_t) function->name,
- function->nargs, function->ctor ? " CTOR" : "");
+ (size_t) function->nargs, function->ctor ? " CTOR" : "");
p += sizeof(njs_vmcode_function_frame_t);
@@ -324,9 +324,9 @@ njs_disassemble(njs_vm_code_t *code)
if (operation == NJS_VMCODE_METHOD_FRAME) {
method = (njs_vmcode_method_frame_t *) p;
- njs_printf("%5uD | %05uz METHOD FRAME %04Xz %04Xz %uz%s\n",
+ njs_printf("%5uD | %05uz METHOD FRAME %04Xz %04Xz %04Xz%s\n",
line, p - start, (size_t) method->object,
- (size_t) method->method, method->nargs,
+ (size_t) method->method, (size_t) method->nargs,
method->ctor ? " CTOR" : "");
p += sizeof(njs_vmcode_method_frame_t);
@@ -485,6 +485,17 @@ njs_disassemble(njs_vm_code_t *code)
continue;
}
+ if (operation == NJS_VMCODE_MOVE_ARG) {
+ move_arg = (njs_vmcode_move_arg_t *) p;
+
+ njs_printf("%5uD | %05uz MOVE ARGUMENT %04Xz %04Xz\n", line,
+ p - start, move_arg->dst, move_arg->src);
+
+ p += sizeof(njs_vmcode_move_arg_t);
+
+ continue;
+ }
+
code_name = code_names;
n = njs_nitems(code_names);
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_function.c
--- a/src/njs_function.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_function.c Mon Apr 12 10:05:59 2021 +0300
@@ -15,14 +15,13 @@ static njs_native_frame_t *njs_function_
njs_function_t *
njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda,
- njs_closure_t *closures[], njs_bool_t shared)
+ njs_bool_t shared)
{
size_t size;
- njs_uint_t n, nesting;
+ njs_value_t **closure;
njs_function_t *function;
- nesting = lambda->nesting;
- size = sizeof(njs_function_t) + nesting * sizeof(njs_closure_t *);
+ size = sizeof(njs_function_t) + lambda->nclosures * sizeof(njs_value_t *);
function = njs_mp_zalloc(vm->mem_pool, size);
if (njs_slow_path(function == NULL)) {
@@ -51,16 +50,17 @@ njs_function_alloc(njs_vm_t *vm, njs_fun
function->object.shared = shared;
function->object.extensible = 1;
- if (nesting != 0 && closures != NULL) {
- function->closure = 1;
-
- n = 0;
+ if (lambda->nclosures > 0) {
+ closure = (njs_value_t **) ((u_char *) function
+ + sizeof(njs_function_t));
+ size = lambda->nclosures;
do {
- /* GC: retain closure. */
- njs_function_closures(function)[n] = closures[n];
- n++;
- } while (n < nesting);
+ size--;
+
+ closure[size] = njs_scope_value(vm, lambda->closures[size]);
+
+ } while (size != 0);
}
return function;
@@ -121,14 +121,6 @@ njs_function_value_copy(njs_vm_t *vm, nj
}
-njs_inline njs_closure_t **
-njs_function_active_closures(njs_vm_t *vm, njs_function_t *function)
-{
- return (function->closure) ? njs_function_closures(function)
- : njs_frame_closures(vm->active_frame);
-}
-
-
njs_int_t
njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
njs_value_t *name, const char *prefix)
@@ -202,14 +194,13 @@ njs_function_name_set(njs_vm_t *vm, njs_
static njs_function_t *
njs_function_copy(njs_vm_t *vm, njs_function_t *function)
{
- size_t size;
- njs_uint_t n, nesting;
- njs_closure_t **closures;
+ size_t size, nclosure;
+ njs_value_t **from, **to;
njs_function_t *copy;
- nesting = (function->native) ? 0 : function->u.lambda->nesting;
+ nclosure = (function->native) ? 0 : function->u.lambda->nclosures;
- size = sizeof(njs_function_t) + nesting * sizeof(njs_closure_t *);
+ size = sizeof(njs_function_t) + nclosure * sizeof(njs_closure_t *);
copy = njs_mp_alloc(vm->mem_pool, size);
if (njs_slow_path(copy == NULL)) {
@@ -220,21 +211,20 @@ njs_function_copy(njs_vm_t *vm, njs_func
copy->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
copy->object.shared = 0;
- if (nesting == 0) {
+ if (nclosure == 0) {
return copy;
}
- copy->closure = 1;
-
- closures = njs_function_active_closures(vm, function);
-
- n = 0;
+ from = (njs_value_t **) (function + sizeof(njs_function_t));
+ to = (njs_value_t **) (copy + sizeof(njs_function_t));
do {
- /* GC: retain closure. */
- njs_function_closures(copy)[n] = closures[n];
- n++;
- } while (n < nesting);
+ *to = *from;
+ nclosure--;
+
+ } while (nclosure != 0);
+
+ copy->closure = 1;
return copy;
}
@@ -287,7 +277,8 @@ njs_function_arguments_object_init(njs_v
for (n = 0; n < nargs; n++) {
njs_uint32_to_string(&value, n);
- prop = njs_object_prop_alloc(vm, &value, &frame->arguments[n + 1], 1);
+ prop = njs_object_prop_alloc(vm, &value,
+ &frame->arguments[n], 1);
if (njs_slow_path(prop == NULL)) {
return NJS_ERROR;
}
@@ -328,17 +319,24 @@ njs_function_rest_parameters_init(njs_vm
if (n <= nargs) {
i = 0;
+ n--;
+
do {
/* GC: retain. */
array->start[i++] = frame->arguments[n++];
- } while (n <= nargs);
+ } while (n < nargs);
}
- rest_arguments = &frame->arguments[frame->function->u.lambda->nargs];
+ rest_arguments = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+ if (njs_slow_path(rest_arguments == NULL)) {
+ return NJS_ERROR;
+ }
/* GC: retain. */
njs_set_array(rest_arguments, array);
+ vm->top_frame->local[frame->function->u.lambda->nargs] = rest_arguments;
+
return NJS_OK;
}
@@ -397,7 +395,9 @@ njs_function_native_frame(njs_vm_t *vm,
frame->pc = NULL;
value = (njs_value_t *) ((u_char *) frame + NJS_NATIVE_FRAME_SIZE);
+
frame->arguments = value;
+ frame->arguments_offset = value + function->args_offset;
bound = function->bound;
@@ -415,8 +415,6 @@ njs_function_native_frame(njs_vm_t *vm,
} while (n != 0);
}
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value;
-
if (args != NULL) {
memcpy(value, args, nargs * sizeof(njs_value_t));
}
@@ -430,9 +428,9 @@ njs_function_lambda_frame(njs_vm_t *vm,
const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs,
njs_bool_t ctor)
{
- size_t size;
- njs_uint_t n, max_args, closures;
- njs_value_t *value, *bound;
+ size_t n;
+ uint32_t args_count, value_count;
+ njs_value_t *value, *bound, **new, **temp;
njs_frame_t *frame;
njs_function_t *target;
njs_native_frame_t *native_frame;
@@ -461,76 +459,75 @@ njs_function_lambda_frame(njs_vm_t *vm,
lambda = target->u.lambda;
}
- max_args = njs_max(nargs, lambda->nargs);
-
- closures = lambda->nesting + lambda->block_closures;
-
- size = njs_frame_size(closures)
- + (function->args_offset + max_args) * sizeof(njs_value_t)
- + lambda->local_size;
-
- native_frame = njs_function_frame_alloc(vm, size);
+ native_frame = njs_function_frame_alloc(vm, NJS_FRAME_SIZE);
if (njs_slow_path(native_frame == NULL)) {
return NJS_ERROR;
}
+ args_count = function->args_offset + njs_max(nargs, lambda->nargs);
+ value_count = args_count + njs_max(args_count, lambda->nlocal);
+
+ new = njs_scope_make(vm, value_count);
+ if (njs_slow_path(new == NULL)) {
+ return NJS_ERROR;
+ }
+
+ temp = njs_scope_make(vm, lambda->temp);
+ if (njs_slow_path(new == NULL)) {
+ return NJS_ERROR;
+ }
+
+ value = (njs_value_t *) ((u_char *) new + (value_count
+ * sizeof(njs_value_t *)));
+
+ native_frame->arguments = value;
+ native_frame->arguments_offset = value + (function->args_offset - 1);
+ native_frame->local = new + args_count;
+ native_frame->temp = temp;
native_frame->function = target;
native_frame->nargs = nargs;
native_frame->ctor = ctor;
native_frame->native = 0;
native_frame->pc = NULL;
- /* Function arguments. */
-
- value = (njs_value_t *) ((u_char *) native_frame +
- njs_frame_size(closures));
- native_frame->arguments = value;
-
- if (bound == NULL) {
- *value = *this;
+ /* Set this and bound arguments. */
+ *native_frame->local[0] = *this;
- if (njs_slow_path(function->global_this
- && njs_is_null_or_undefined(this))) {
- njs_set_object(value, &vm->global_object);
- }
+ if (njs_slow_path(function->global_this
+ && njs_is_null_or_undefined(this)))
+ {
+ njs_set_object(native_frame->local[0], &vm->global_object);
+ }
- value++;
-
- } else {
+ if (bound != NULL) {
n = function->args_offset;
native_frame->nargs += n - 1;
- if (ctor) {
- *value++ = *this;
- bound++;
- n--;
+ if (!ctor) {
+ *native_frame->local[0] = *bound;
}
+ bound++;
+ n--;
+
while (n != 0) {
*value++ = *bound++;
n--;
};
}
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value;
+ /* Copy arguments. */
if (args != NULL) {
while (nargs != 0) {
*value++ = *args++;
- max_args--;
nargs--;
}
}
- while (max_args != 0) {
- njs_set_undefined(value++);
- max_args--;
- }
-
frame = (njs_frame_t *) native_frame;
frame->exception.catch = NULL;
frame->exception.next = NULL;
- frame->local = value;
frame->previous_active_frame = vm->active_frame;
return NJS_OK;
@@ -540,47 +537,20 @@ njs_function_lambda_frame(njs_vm_t *vm,
njs_native_frame_t *
njs_function_frame_alloc(njs_vm_t *vm, size_t size)
{
- size_t spare_size, chunk_size;
njs_native_frame_t *frame;
- /*
- * The size value must be aligned to njs_value_t because vm->top_frame
- * may point to frame->free and vm->top_frame is used as a base pointer
- * in njs_vm_continuation() which is expected to return pointers aligned
- * to njs_value_t.
- */
- size = njs_align_size(size, sizeof(njs_value_t));
-
- spare_size = vm->top_frame->free_size;
-
- if (njs_fast_path(size <= spare_size)) {
- frame = (njs_native_frame_t *) vm->top_frame->free;
- chunk_size = 0;
-
- } else {
- spare_size = size + NJS_FRAME_SPARE_SIZE;
- spare_size = njs_align_size(spare_size, NJS_FRAME_SPARE_SIZE);
-
- if (vm->stack_size + spare_size > NJS_MAX_STACK_SIZE) {
- njs_range_error(vm, "Maximum call stack size exceeded");
- return NULL;
- }
-
- frame = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), spare_size);
- if (njs_slow_path(frame == NULL)) {
- njs_memory_error(vm);
- return NULL;
- }
-
- chunk_size = spare_size;
- vm->stack_size += spare_size;
+ if (vm->stack_size > NJS_MAX_STACK_SIZE) {
+ njs_range_error(vm, "Maximum call stack size exceeded");
+ return NULL;
}
- njs_memzero(frame, sizeof(njs_native_frame_t));
+ frame = njs_mp_zalloc(vm->mem_pool, size);
+ if (njs_slow_path(frame == NULL)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
- frame->size = chunk_size;
- frame->free_size = spare_size - size;
- frame->free = (u_char *) frame + size;
+ vm->stack_size++;
frame->previous = vm->top_frame;
vm->top_frame = frame;
@@ -602,7 +572,7 @@ njs_function_call2(njs_vm_t *vm, njs_fun
return ret;
}
- ret = njs_function_frame_invoke(vm, (njs_index_t) &dst);
+ ret = njs_function_frame_invoke(vm, &dst);
if (ret == NJS_OK) {
*retval = dst;
@@ -615,12 +585,11 @@ njs_function_call2(njs_vm_t *vm, njs_fun
njs_int_t
njs_function_lambda_call(njs_vm_t *vm)
{
- size_t size;
njs_int_t ret;
- njs_uint_t n, nesting;
+ njs_uint_t n;
njs_frame_t *frame;
- njs_value_t *dst, *src;
- njs_closure_t *closure, **closures;
+ njs_value_t *args, **local, **closures, *value;
+ njs_value_t **cur_local, **cur_closures, **cur_temp;
njs_function_t *function;
njs_function_lambda_t *lambda;
@@ -629,64 +598,31 @@ njs_function_lambda_call(njs_vm_t *vm)
lambda = function->u.lambda;
-#if (NJS_DEBUG)
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = NULL;
-#endif
-
- vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments;
+ args = vm->top_frame->arguments;
+ local = vm->top_frame->local + function->args_offset;
+ closures = (njs_value_t **) ((u_char *) function + sizeof(njs_function_t));
- /* Function local variables and temporary values. */
-
- vm->scopes[NJS_SCOPE_LOCAL] = frame->local;
-
- memcpy(frame->local, lambda->local_scope, lambda->local_size);
-
- /* Parent closures values. */
+ /* Move all arguments. */
- n = 0;
- nesting = lambda->nesting;
+ for (n = 0; n < function->args_count; n++) {
+ if (!njs_is_valid(args)) {
+ njs_set_undefined(args);
+ }
- if (nesting != 0) {
- closures = njs_function_active_closures(vm, function);
- do {
- closure = *closures++;
-
- njs_frame_closures(frame)[n] = closure;
- vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
-
- n++;
- } while (n < nesting);
+ *local++ = args++;
}
- /* Function closure values. */
-
- if (lambda->block_closures > 0) {
- closure = NULL;
-
- size = lambda->closure_size;
+ /* Store current level. */
- if (size != 0) {
- closure = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), size);
- if (njs_slow_path(closure == NULL)) {
- njs_memory_error(vm);
- return NJS_ERROR;
- }
+ cur_local = vm->level[NJS_SCOPE_TYPE_LOCAL];
+ cur_closures = vm->level[NJS_SCOPE_TYPE_CLOSURE];
+ cur_temp = vm->level[NJS_SCOPE_TYPE_TEMP];
- size -= sizeof(njs_value_t);
- closure->u.count = 0;
- dst = closure->values;
-
- src = lambda->closure_scope;
+ /* Replace current level. */
- do {
- *dst++ = *src++;
- size -= sizeof(njs_value_t);
- } while (size != 0);
- }
-
- njs_frame_closures(frame)[n] = closure;
- vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
- }
+ vm->level[NJS_SCOPE_TYPE_LOCAL] = vm->top_frame->local;
+ vm->level[NJS_SCOPE_TYPE_CLOSURE] = closures;
+ vm->level[NJS_SCOPE_TYPE_TEMP] = frame->native.temp;
if (lambda->rest_parameters) {
ret = njs_function_rest_parameters_init(vm, &frame->native);
@@ -695,9 +631,27 @@ njs_function_lambda_call(njs_vm_t *vm)
}
}
+ /* Self */
+
+ if (lambda->self != NJS_INDEX_NONE) {
+ value = njs_scope_value(vm, lambda->self);
+
+ if (!njs_is_valid(value)) {
+ njs_set_function(value, function);
+ njs_scope_value_set(vm, lambda->self, value);
+ }
+ }
+
vm->active_frame = frame;
- return njs_vmcode_interpreter(vm, lambda->start);
+ ret = njs_vmcode_interpreter(vm, lambda->start);
+
+ /* Restore current level. */
+ vm->level[NJS_SCOPE_TYPE_LOCAL] = cur_local;
+ vm->level[NJS_SCOPE_TYPE_CLOSURE] = cur_closures;
+ vm->level[NJS_SCOPE_TYPE_TEMP] = cur_temp;
+
+ return ret;
}
@@ -705,7 +659,6 @@ njs_int_t
njs_function_native_call(njs_vm_t *vm)
{
njs_int_t ret;
- njs_value_t *value;
njs_function_t *function, *target;
njs_native_frame_t *native, *previous;
njs_function_native_t call;
@@ -741,12 +694,7 @@ njs_function_native_call(njs_vm_t *vm)
njs_vm_scopes_restore(vm, native, previous);
if (!native->skip) {
- value = njs_vmcode_operand(vm, native->retval);
- /*
- * GC: value external/internal++ depending
- * on vm->retval and retval type
- */
- *value = vm->retval;
+ *native->retval = vm->retval;
}
njs_function_frame_free(vm, native);
@@ -763,14 +711,12 @@ njs_function_frame_free(njs_vm_t *vm, nj
do {
previous = native->previous;
- /* GC: free frame->local, etc. */
+ njs_mp_free(vm->mem_pool, native);
- if (native->size != 0) {
- vm->stack_size -= native->size;
- njs_mp_free(vm->mem_pool, native);
- }
+ vm->stack_size--;
native = previous;
+
} while (native->skip);
}
@@ -937,7 +883,7 @@ njs_function_constructor(njs_vm_t *vm, n
parser.lexer = &lexer;
- ret = njs_parser(vm, &parser, NULL);
+ ret = njs_parser(vm, &parser);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -953,7 +899,14 @@ njs_function_constructor(njs_vm_t *vm, n
type = &safe_ast[0];
for (; *type != NJS_TOKEN_ILLEGAL; type++, node = node->right) {
- if (node == NULL || node->left != NULL) {
+ if (node == NULL) {
+ goto fail;
+ }
+
+ if (node->left != NULL
+ && node->token_type != NJS_TOKEN_FUNCTION_EXPRESSION
+ && node->left->token_type != NJS_TOKEN_NAME)
+ {
goto fail;
}
@@ -970,11 +923,6 @@ njs_function_constructor(njs_vm_t *vm, n
return ret;
}
- ret = njs_variables_scope_reference(vm, scope);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
njs_memzero(&generator, sizeof(njs_generator_t));
code = njs_generate_scope(vm, &generator, scope, &njs_entry_anonymous);
@@ -986,11 +934,13 @@ njs_function_constructor(njs_vm_t *vm, n
return NJS_ERROR;
}
+ parser.lexer = NULL;
+
njs_chb_destroy(&chain);
lambda = ((njs_vmcode_function_t *) generator.code_start)->lambda;
- function = njs_function_alloc(vm, lambda, NULL, 0);
+ function = njs_function_alloc(vm, lambda, 0);
if (njs_slow_path(function == NULL)) {
return NJS_ERROR;
}
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_function.h
--- a/src/njs_function.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_function.h Mon Apr 12 10:05:59 2021 +0300
@@ -9,23 +9,18 @@
struct njs_function_lambda_s {
- uint32_t nargs;
- uint32_t local_size;
- uint32_t closure_size;
+ njs_index_t *closures;
+ uint32_t nclosures;
+ uint32_t nlocal;
+ uint32_t temp;
- /* Function nesting level. */
- uint8_t nesting; /* 4 bits */
+ njs_index_t self;
- /* Function internal block closures levels. */
- uint8_t block_closures; /* 4 bits */
+ uint32_t nargs;
uint8_t ctor; /* 1 bit */
uint8_t rest_parameters; /* 1 bit */
- /* Initial values of local scope. */
- njs_value_t *local_scope;
- njs_value_t *closure_scope;
-
u_char *start;
};
@@ -35,15 +30,13 @@ struct njs_function_lambda_s {
njs_align_size(sizeof(njs_native_frame_t), sizeof(njs_value_t))
/* The frame size must be aligned to njs_value_t. */
-#define njs_frame_size(closures) \
- njs_align_size(sizeof(njs_frame_t) + closures * sizeof(njs_closure_t *), \
- sizeof(njs_value_t))
+#define NJS_FRAME_SIZE \
+ njs_align_size(sizeof(njs_frame_t), sizeof(njs_value_t))
#define NJS_FRAME_SPARE_SIZE 512
struct njs_native_frame_s {
- u_char *free;
u_char *pc;
njs_function_t *function;
@@ -51,11 +44,13 @@ struct njs_native_frame_s {
njs_value_t *arguments;
njs_object_t *arguments_object;
-
- njs_index_t retval;
+ njs_value_t *arguments_offset;
+ njs_value_t **local;
+ njs_value_t **temp;
- uint32_t size;
- uint32_t free_size;
+ njs_value_t *retval;
+ njs_value_t **values;
+
uint32_t nargs;
uint8_t native; /* 1 bit */
@@ -82,15 +77,13 @@ struct njs_frame_s {
njs_frame_t *previous_active_frame;
- njs_value_t *local;
-
#define njs_frame_closures(frame) \
((njs_closure_t **) ((u_char *) frame + sizeof(njs_frame_t)))
};
njs_function_t *njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda,
- njs_closure_t *closures[], njs_bool_t shared);
+ njs_bool_t shared);
njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value);
njs_int_t njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
njs_value_t *name, const char *prefix);
@@ -161,7 +154,7 @@ njs_function_previous_frame(njs_native_f
njs_inline njs_int_t
-njs_function_frame_invoke(njs_vm_t *vm, njs_index_t retval)
+njs_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval)
{
njs_native_frame_t *frame;
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_generator.c
--- a/src/njs_generator.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_generator.c Mon Apr 12 10:05:59 2021 +0300
@@ -2,6 +2,7 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Dmitry Volyntsev
+ * Copyright (C) Alexander Borisov
* Copyright (C) NGINX, Inc.
*/
@@ -18,7 +19,7 @@ struct njs_generator_patch_s {
* because pointer to u_char accesses only one byte so this does not
* work on big endian platforms.
*/
- njs_jump_off_t jump_offset;
+ njs_jump_off_t jump_offset;
njs_generator_patch_t *next;
njs_str_t label;
@@ -62,7 +63,8 @@ static njs_int_t njs_generate_code_map(n
static njs_int_t njs_generate_name(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node);
static njs_int_t njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator,
- njs_parser_node_t *node, njs_reference_type_t type);
+ njs_parser_node_t *node, njs_reference_type_t type,
+ 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_if_statement(njs_vm_t *vm,
@@ -123,6 +125,8 @@ static njs_int_t njs_generate_property_a
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_array(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node);
+static njs_int_t njs_generate_function_expression(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_function(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node);
static njs_int_t njs_generate_regexp(njs_vm_t *vm, njs_generator_t *generator,
@@ -144,7 +148,7 @@ static njs_int_t njs_generate_function_d
static njs_int_t njs_generate_function_scope(njs_vm_t *vm,
njs_function_lambda_t *lambda, njs_parser_node_t *node,
const njs_str_t *name);
-static njs_int_t njs_generate_lambda_variables(njs_vm_t *vm,
+static int64_t njs_generate_lambda_variables(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_return_statement(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
@@ -154,6 +158,8 @@ static njs_int_t njs_generate_method_cal
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_call(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node);
+static njs_int_t njs_generate_move_arguments(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_try_statement(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_throw_statement(njs_vm_t *vm,
@@ -392,12 +398,13 @@ njs_generate(njs_vm_t *vm, njs_generator
case NJS_TOKEN_FALSE:
case NJS_TOKEN_NUMBER:
case NJS_TOKEN_STRING:
- node->index = njs_value_index(vm, &node->u.value, generator->runtime);
- if (njs_fast_path(node->index != NJS_INDEX_NONE)) {
- return NJS_OK;
+ node->index = njs_scope_global_index(vm, &node->u.value,
+ generator->runtime);
+ if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
}
- return NJS_ERROR;
+ return NJS_OK;
case NJS_TOKEN_OBJECT_VALUE:
node->index = node->u.object->index;
@@ -414,6 +421,9 @@ njs_generate(njs_vm_t *vm, njs_generator
return njs_generate_array(vm, generator, node);
case NJS_TOKEN_FUNCTION_EXPRESSION:
+ return njs_generate_function_expression(vm, generator, node);
+
+ case NJS_TOKEN_FUNCTION_PROPERTY:
return njs_generate_function(vm, generator, node);
case NJS_TOKEN_REGEXP:
@@ -422,31 +432,15 @@ njs_generate(njs_vm_t *vm, njs_generator
case NJS_TOKEN_TEMPLATE_LITERAL:
return njs_generate_template_literal(vm, generator, node);
- case NJS_TOKEN_THIS:
case NJS_TOKEN_EXTERNAL:
return NJS_OK;
case NJS_TOKEN_NAME:
case NJS_TOKEN_ARGUMENTS:
case NJS_TOKEN_EVAL:
- case NJS_TOKEN_NON_LOCAL_THIS:
+ case NJS_TOKEN_THIS:
return njs_generate_name(vm, generator, node);
- case NJS_TOKEN_GLOBAL_OBJECT:
- if (vm->options.module) {
- node->index = njs_value_index(vm, &njs_value_undefined,
- generator->runtime);
- if (njs_fast_path(node->index != NJS_INDEX_NONE)) {
- return NJS_OK;
- }
-
- return NJS_ERROR;
- }
-
- node->index = NJS_INDEX_GLOBAL_OBJECT;
-
- return NJS_OK;
-
case NJS_TOKEN_FUNCTION:
return njs_generate_function_declaration(vm, generator, node);
@@ -596,39 +590,45 @@ 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_object_copy_t *copy;
-
- var = njs_variable_resolve(vm, node);
-
- if (var != NULL && var->type == NJS_VARIABLE_FUNCTION) {
-
- node->index = njs_generate_dest_index(vm, generator, node);
- if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
- return node->index;
- }
-
- njs_generate_code(generator, njs_vmcode_object_copy_t, copy,
- NJS_VMCODE_OBJECT_COPY, 2, node);
- copy->retval = node->index;
- copy->object = var->index;
-
+ njs_variable_t *var;
+ njs_vmcode_function_t *function;
+
+ 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;
}
- return njs_generate_variable(vm, generator, node, NJS_REFERENCE);
+ 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;
+ }
+
+ return NJS_OK;
}
static njs_int_t
njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator,
- njs_parser_node_t *node, njs_reference_type_t type)
+ njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar)
{
- njs_index_t index;
-
- index = njs_variable_index(vm, node);
- if (njs_slow_path(index == NJS_INDEX_NONE)) {
-
+ njs_variable_t *var;
+ njs_vmcode_function_t *function;
+
+ var = njs_variable_reference(vm, node);
+
+ if (retvar != NULL) {
+ *retvar = var;
+ }
+
+ if (njs_slow_path(var == NULL)) {
switch (type) {
case NJS_DECLARATION:
return njs_generate_reference_error(vm, generator, node);
@@ -640,7 +640,18 @@ njs_generate_variable(njs_vm_t *vm, njs_
}
}
- node->index = index;
+ if (var->init || node->index != var->index) {
+ return NJS_OK;
+ }
+
+ if (var->have_lambda) {
+ 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;
return NJS_OK;
}
@@ -651,19 +662,18 @@ njs_generate_var_statement(njs_vm_t *vm,
njs_parser_node_t *node)
{
njs_int_t ret;
- njs_index_t index;
+ njs_variable_t *var;
njs_parser_node_t *lvalue, *expr;
njs_vmcode_move_t *move;
lvalue = node->left;
- index = njs_variable_index(vm, lvalue);
- if (njs_slow_path(index == NJS_INDEX_NONE)) {
+ ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION, &var);
+ if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
- lvalue->index = index;
-
+ lvalue->index = var->index;
expr = node->right;
if (expr == NULL) {
@@ -1208,6 +1218,11 @@ njs_generate_for_in_statement(njs_vm_t *
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;
@@ -1240,11 +1255,6 @@ njs_generate_for_in_statement(njs_vm_t *
njs_code_set_jump_offset(generator, njs_vmcode_prop_foreach_t, prop_offset);
- ret = njs_generator(vm, generator, node->left->left);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
njs_generate_code(generator, njs_vmcode_prop_next_t, prop_next,
NJS_VMCODE_PROPERTY_NEXT, 3, node->left->left);
prop_offset = njs_code_offset(generator, prop_next);
@@ -1635,16 +1645,15 @@ njs_generate_stop_statement(njs_vm_t *vm
njs_generate_code(generator, njs_vmcode_stop_t, stop,
NJS_VMCODE_STOP, 1, node);
- index = NJS_INDEX_NONE;
+ index = njs_scope_undefined_index(vm, 0);
node = node->right;
- if (node != NULL && node->token_type != NJS_TOKEN_FUNCTION) {
- index = node->index;
- }
-
- if (index == NJS_INDEX_NONE) {
- index = njs_value_index(vm, &njs_value_undefined,
- generator->runtime);
+ if (node != NULL) {
+ if ((node->index != 0 && node->token_type != NJS_TOKEN_FUNCTION)
+ || node->token_type == NJS_TOKEN_THIS)
+ {
+ index = node->index;
+ }
}
stop->retval = index;
@@ -1686,7 +1695,8 @@ njs_generate_assignment(njs_vm_t *vm, nj
if (lvalue->token_type == NJS_TOKEN_NAME) {
- ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION);
+ ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION,
+ NULL);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -1810,7 +1820,8 @@ njs_generate_operation_assignment(njs_vm
if (lvalue->token_type == NJS_TOKEN_NAME) {
- ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION);
+ ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION,
+ NULL);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -2029,6 +2040,50 @@ njs_generate_array(njs_vm_t *vm, njs_gen
static njs_int_t
+njs_generate_function_expression(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_int_t ret;
+ njs_variable_t *var;
+ njs_function_lambda_t *lambda;
+ njs_vmcode_function_t *function;
+ const njs_lexer_entry_t *lex_entry;
+
+ var = njs_variable_reference(vm, node->left);
+ if (njs_slow_path(var == NULL)) {
+ return njs_generate_reference_error(vm, generator, node->left);
+ }
+
+ lambda = node->u.value.data.u.lambda;
+
+ lex_entry = njs_lexer_entry(var->unique_id);
+ if (njs_slow_path(lex_entry == NULL)) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_generate_function_scope(vm, lambda, node, &lex_entry->name);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(generator, njs_vmcode_function_t, function,
+ NJS_VMCODE_FUNCTION, 1, node);
+ function->lambda = lambda;
+
+ node->index = njs_generate_object_dest_index(vm, generator, node);
+ if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ function->retval = node->index;
+
+ vm->closures = generator->closures;
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
njs_generate_function(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
@@ -2059,6 +2114,8 @@ njs_generate_function(njs_vm_t *vm, njs_
function->retval = node->index;
+ vm->closures = generator->closures;
+
return NJS_OK;
}
@@ -2263,7 +2320,7 @@ njs_generate_typeof_operation(njs_vm_t *
expr = node->left;
if (expr->token_type == NJS_TOKEN_NAME) {
- ret = njs_generate_variable(vm, generator, expr, NJS_TYPEOF);
+ ret = njs_generate_variable(vm, generator, expr, NJS_TYPEOF, NULL);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
@@ -2307,7 +2364,8 @@ njs_generate_inc_dec_operation(njs_vm_t
if (lvalue->token_type == NJS_TOKEN_NAME) {
- ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION);
+ ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION,
+ NULL);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -2403,20 +2461,25 @@ njs_generate_function_declaration(njs_vm
njs_int_t ret;
njs_variable_t *var;
njs_function_lambda_t *lambda;
+ njs_vmcode_function_t *function;
const njs_lexer_entry_t *lex_entry;
- var = njs_variable_resolve(vm, node);
+ var = njs_variable_reference(vm, node);
if (njs_slow_path(var == NULL)) {
return njs_generate_reference_error(vm, generator, node);
}
- if (!njs_is_function(&var->value)) {
- /* A variable was declared with the same name. */
- return NJS_OK;
+ lambda = var->value.data.u.lambda;
+
+ if (!var->init) {
+ njs_generate_code(generator, njs_vmcode_function_t, function,
+ NJS_VMCODE_FUNCTION, 1, node);
+ function->retval = node->index;
+ function->lambda = lambda;
+
+ var->init = 1;
}
- lambda = njs_function_lambda(&var->value);
-
lex_entry = njs_lexer_entry(node->u.reference.unique_id);
if (njs_slow_path(lex_entry == NULL)) {
return NJS_ERROR;
@@ -2427,6 +2490,8 @@ njs_generate_function_declaration(njs_vm
return ret;
}
+ vm->closures = generator->closures;
+
return ret;
}
@@ -2435,8 +2500,6 @@ static njs_int_t
njs_generate_function_scope(njs_vm_t *vm, njs_function_lambda_t *lambda,
njs_parser_node_t *node, const njs_str_t *name)
{
- size_t size;
- njs_arr_t *closure;
njs_bool_t module;
njs_vm_code_t *code;
njs_generator_t generator;
@@ -2444,9 +2507,6 @@ njs_generate_function_scope(njs_vm_t *vm
njs_memzero(&generator, sizeof(njs_generator_t));
- module = node->right->scope->module;
- file_node = module ? node->right : node;
-
node = node->right;
code = njs_generate_scope(vm, &generator, node->scope, name);
@@ -2458,24 +2518,16 @@ njs_generate_function_scope(njs_vm_t *vm
return NJS_ERROR;
}
+ module = node->right->scope->module;
+ file_node = module ? node->right : node;
+
code->file = file_node->scope->file;
- size = 0;
- closure = node->scope->values[1];
-
- if (closure != NULL) {
- lambda->block_closures = 1;
- lambda->closure_scope = closure->start;
- size = (1 + closure->items) * sizeof(njs_value_t);
- }
-
- lambda->closure_size = size;
-
- lambda->nesting = node->scope->nesting;
-
lambda->start = generator.code_start;
- lambda->local_size = generator.scope_size;
- lambda->local_scope = generator.local_scope;
+ lambda->closures = vm->closures->start;
+ lambda->nclosures = vm->closures->items;
+ lambda->nlocal = node->scope->items;
+ lambda->temp = node->scope->temp;
return NJS_OK;
}
@@ -2486,11 +2538,8 @@ njs_generate_scope(njs_vm_t *vm, njs_gen
njs_parser_scope_t *scope, const njs_str_t *name)
{
u_char *p;
- size_t size;
- uintptr_t scope_size;
- njs_int_t ret;
- njs_uint_t n, index;
- njs_value_t *value;
+ int64_t nargs;
+ njs_uint_t index;
njs_vm_code_t *code;
generator->code_size = 128;
@@ -2504,8 +2553,8 @@ njs_generate_scope(njs_vm_t *vm, njs_gen
generator->code_start = p;
generator->code_end = p;
- ret = njs_generate_lambda_variables(vm, generator, scope->top);
- if (njs_slow_path(ret != NJS_OK)) {
+ nargs = njs_generate_lambda_variables(vm, generator, scope->top);
+ if (njs_slow_path(nargs < NJS_OK)) {
return NULL;
}
@@ -2536,6 +2585,14 @@ njs_generate_scope(njs_vm_t *vm, njs_gen
generator->lines = code->lines;
}
+ generator->closures = njs_arr_create(vm->mem_pool, 4, sizeof(njs_index_t));
+ if (njs_slow_path(generator->closures == NULL)) {
+ return NULL;
+ }
+
+ scope->closures = generator->closures;
+ vm->closures = generator->closures;
+
if (njs_slow_path(njs_generator(vm, generator, scope->top) != NJS_OK)) {
return NULL;
}
@@ -2548,46 +2605,22 @@ njs_generate_scope(njs_vm_t *vm, njs_gen
generator->code_size = generator->code_end - generator->code_start;
- scope_size = njs_scope_offset(scope->next_index[0]);
-
- if (scope->type == NJS_SCOPE_GLOBAL) {
- scope_size -= NJS_INDEX_GLOBAL_OFFSET;
- }
-
- generator->local_scope = njs_mp_alloc(vm->mem_pool, scope_size);
- if (njs_slow_path(generator->local_scope == NULL)) {
- return NULL;
- }
-
- generator->scope_size = scope_size;
-
- size = scope->values[0]->items * sizeof(njs_value_t);
-
- njs_thread_log_debug("SCOPE SIZE: %uz %uz", size, scope_size);
-
- p = memcpy(generator->local_scope, scope->values[0]->start, size);
- value = (njs_value_t *) (p + size);
-
- for (n = scope_size - size; n != 0; n -= sizeof(njs_value_t)) {
- njs_set_undefined(value++);
- }
-
return code;
}
-static njs_int_t
+static int64_t
njs_generate_lambda_variables(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_index_t index;
+ int64_t nargs;
njs_variable_t *var;
njs_rbtree_node_t *rb_node;
- njs_vmcode_move_t *move;
- njs_vmcode_this_t *this;
njs_variable_node_t *var_node;
njs_vmcode_arguments_t *arguments;
+ nargs = 0;
+
rb_node = njs_rbtree_min(&node->scope->variables);
while (njs_rbtree_is_there_successor(&node->scope->variables, rb_node)) {
@@ -2598,16 +2631,8 @@ njs_generate_lambda_variables(njs_vm_t *
break;
}
- if (var->argument != 0) {
- index = njs_scope_index((var->argument - 1), NJS_SCOPE_ARGUMENTS);
-
- njs_generate_code_move(generator, move, var->index, index, node);
- }
-
- if (var->this_object) {
- njs_generate_code(generator, njs_vmcode_this_t, this,
- NJS_VMCODE_THIS, 1, NULL);
- this->dst = var->index;
+ if (var->argument) {
+ nargs++;
}
if (var->arguments_object) {
@@ -2619,7 +2644,7 @@ njs_generate_lambda_variables(njs_vm_t *
rb_node = njs_rbtree_node_successor(&node->scope->variables, rb_node);
}
- return NJS_OK;
+ return nargs;
}
@@ -2644,7 +2669,12 @@ njs_generate_return_statement(njs_vm_t *
index = node->right->index;
} else {
- index = njs_value_index(vm, &njs_value_undefined, generator->runtime);
+ index = njs_scope_global_index(vm, &njs_value_undefined,
+ generator->runtime);
+ }
+
+ if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
}
immediate = njs_generate_lookup_block(generator->block, NJS_GENERATOR_TRY,
@@ -2703,11 +2733,14 @@ static njs_int_t
njs_generate_function_call(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
+ njs_int_t ret, nargs;
njs_jump_off_t func_offset;
+ njs_variable_t *var;
njs_parser_node_t *name;
njs_vmcode_function_frame_t *func;
+ var = NULL;
+
if (node->left != NULL) {
/* Generate function code in function expression. */
ret = njs_generator(vm, generator, node->left);
@@ -2718,7 +2751,7 @@ njs_generate_function_call(njs_vm_t *vm,
name = node->left;
} else {
- ret = njs_generate_variable(vm, generator, node, NJS_REFERENCE);
+ ret = njs_generate_variable(vm, generator, node, NJS_REFERENCE, &var);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -2731,17 +2764,26 @@ njs_generate_function_call(njs_vm_t *vm,
func_offset = njs_code_offset(generator, func);
func->ctor = node->ctor;
func->name = name->index;
+ func->lambda = NULL;
+
+ if (var != NULL && var->have_lambda) {
+ func->lambda = var->value.data.u.lambda;
+ }
+
+ nargs = njs_generate_move_arguments(vm, generator, node);
+ if (njs_slow_path(nargs < 0)) {
+ return nargs;
+ }
+
+ func = njs_code_ptr(generator, njs_vmcode_function_frame_t, func_offset);
+ func->nargs = nargs;
ret = njs_generate_call(vm, generator, node);
-
- if (njs_fast_path(ret >= 0)) {
- func = njs_code_ptr(generator, njs_vmcode_function_frame_t,
- func_offset);
- func->nargs = ret;
- return NJS_OK;
+ if (njs_fast_path(ret != NJS_OK)) {
+ return ret;
}
- return ret;
+ return NJS_OK;
}
@@ -2749,7 +2791,7 @@ static njs_int_t
njs_generate_method_call(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
+ njs_int_t ret, nargs;
njs_jump_off_t method_offset;
njs_parser_node_t *prop;
njs_vmcode_method_frame_t *method;
@@ -2777,21 +2819,20 @@ njs_generate_method_call(njs_vm_t *vm, n
method->object = prop->left->index;
method->method = prop->right->index;
- ret = njs_generate_children_indexes_release(vm, generator, prop);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ nargs = njs_generate_move_arguments(vm, generator, node);
+ if (njs_slow_path(nargs < 0)) {
+ return nargs;
}
+ method = njs_code_ptr(generator, njs_vmcode_method_frame_t, method_offset);
+ method->nargs = nargs;
+
ret = njs_generate_call(vm, generator, node);
-
- if (njs_fast_path(ret >= 0)) {
- method = njs_code_ptr(generator, njs_vmcode_method_frame_t,
- method_offset);
- method->nargs = ret;
- return NJS_OK;
+ if (njs_fast_path(ret != NJS_OK)) {
+ return ret;
}
- return ret;
+ return NJS_OK;
}
@@ -2799,29 +2840,9 @@ static njs_int_t
njs_generate_call(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
- njs_uint_t nargs;
njs_index_t retval;
- njs_parser_node_t *arg;
- njs_vmcode_move_t *move;
njs_vmcode_function_call_t *call;
- nargs = 0;
-
- for (arg = node->right; arg != NULL; arg = arg->right) {
- nargs++;
-
- ret = njs_generator(vm, generator, arg->left);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
- if (arg->index != arg->left->index) {
- njs_generate_code_move(generator, move, arg->index,
- arg->left->index, node);
- }
- }
-
retval = njs_generate_dest_index(vm, generator, node);
if (njs_slow_path(retval == NJS_INDEX_ERROR)) {
return retval;
@@ -2833,6 +2854,35 @@ njs_generate_call(njs_vm_t *vm, njs_gene
NJS_VMCODE_FUNCTION_CALL, 1, node);
call->retval = retval;
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_generate_move_arguments(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_int_t ret;
+ njs_uint_t nargs;
+ njs_parser_node_t *arg;
+ njs_vmcode_move_arg_t *move_arg;
+
+ nargs = 0;
+
+ for (arg = node->right; arg != NULL; arg = arg->right) {
+ ret = njs_generator(vm, generator, arg->left);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(generator, njs_vmcode_move_arg_t, move_arg,
+ NJS_VMCODE_MOVE_ARG, 0, node);
+ move_arg->src = arg->left->index;
+ move_arg->dst = nargs;
+
+ nargs++;
+ }
+
return nargs;
}
@@ -2849,7 +2899,7 @@ njs_generate_call(njs_vm_t *vm, njs_gene
#define njs_generate_code_finally(generator, _code, _retval, _exit, node) \
do { \
njs_generate_code(generator, njs_vmcode_finally_t, _code, \
- NJS_VMCODE_FINALLY, 2, node); \
+ NJS_VMCODE_FINALLY, 1, node); \
_code->retval = _retval; \
_code->exit_value = _exit; \
_code->continue_offset = offsetof(njs_vmcode_finally_t, \
@@ -2869,6 +2919,7 @@ njs_generate_try_statement(njs_vm_t *vm,
njs_index_t exception_index, exit_index, catch_index;
njs_jump_off_t try_offset, try_end_offset, catch_offset,
catch_end_offset;
+ njs_variable_t *var;
const njs_str_t *dest_label;
njs_vmcode_catch_t *catch;
njs_vmcode_finally_t *finally;
@@ -2928,7 +2979,7 @@ njs_generate_try_statement(njs_vm_t *vm,
njs_generate_patch_block(vm, generator, try_block->exit);
njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_break,
- NJS_VMCODE_TRY_BREAK, 2, NULL);
+ NJS_VMCODE_TRY_BREAK, 1, NULL);
try_break->exit_value = exit_index;
try_break->offset = -sizeof(njs_vmcode_try_end_t);
@@ -2943,7 +2994,7 @@ njs_generate_try_statement(njs_vm_t *vm,
njs_generate_patch_block(vm, generator, try_block->continuation);
njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_continue,
- NJS_VMCODE_TRY_CONTINUE, 2, NULL);
+ NJS_VMCODE_TRY_CONTINUE, 1, NULL);
try_continue->exit_value = exit_index;
try_continue->offset = -sizeof(njs_vmcode_try_end_t);
@@ -2966,11 +3017,13 @@ njs_generate_try_statement(njs_vm_t *vm,
if (node->token_type == NJS_TOKEN_CATCH) {
/* A "try/catch" case. */
- catch_index = njs_variable_index(vm, node->left);
- if (njs_slow_path(catch_index == NJS_INDEX_NONE)) {
+ var = njs_variable_reference(vm, node->left);
+ if (njs_slow_path(var == NULL)) {
return NJS_ERROR;
}
+ catch_index = node->left->index;
+
njs_generate_code_catch(generator, catch, catch_index, node);
ret = njs_generator(vm, generator, node->right);
@@ -3025,11 +3078,13 @@ njs_generate_try_statement(njs_vm_t *vm,
if (node->left != NULL) {
/* A try/catch/finally case. */
- catch_index = njs_variable_index(vm, node->left->left);
- if (njs_slow_path(catch_index == NJS_INDEX_NONE)) {
+ var = njs_variable_reference(vm, node->left->left);
+ if (njs_slow_path(var == NULL)) {
return NJS_ERROR;
}
+ catch_index = node->left->left->index;
+
njs_generate_code_catch(generator, catch, catch_index, node);
catch_offset = njs_code_offset(generator, catch);
@@ -3057,7 +3112,7 @@ njs_generate_try_statement(njs_vm_t *vm,
njs_generate_patch_block(vm, generator, catch_block->exit);
njs_generate_code(generator, njs_vmcode_try_trampoline_t,
- try_break, NJS_VMCODE_TRY_BREAK, 2, NULL);
+ try_break, NJS_VMCODE_TRY_BREAK, 1, NULL);
try_break->exit_value = exit_index;
@@ -3074,7 +3129,7 @@ njs_generate_try_statement(njs_vm_t *vm,
catch_block->continuation);
njs_generate_code(generator, njs_vmcode_try_trampoline_t,
- try_continue, NJS_VMCODE_TRY_CONTINUE, 2,
+ try_continue, NJS_VMCODE_TRY_CONTINUE, 1,
NULL);
try_continue->exit_value = exit_index;
@@ -3206,17 +3261,20 @@ njs_generate_import_statement(njs_vm_t *
njs_int_t ret;
njs_index_t index;
njs_module_t *module;
+ njs_variable_t *var;
njs_parser_node_t *lvalue, *expr;
njs_vmcode_object_copy_t *copy;
lvalue = node->left;
expr = node->right;
- index = njs_variable_index(vm, lvalue);
- if (njs_slow_path(index == NJS_INDEX_NONE)) {
+ var = njs_variable_reference(vm, lvalue);
+ if (njs_slow_path(var == NULL)) {
return NJS_ERROR;
}
+ index = lvalue->index;
+
if (expr->left != NULL) {
ret = njs_generator(vm, generator, expr->left);
if (njs_slow_path(ret != NJS_OK)) {
@@ -3293,11 +3351,6 @@ njs_generate_object_dest_index(njs_vm_t
if (dest != NULL && dest->index != NJS_INDEX_NONE) {
index = dest->index;
- if (njs_is_callee_argument_index(index)) {
- /* Assgin object directly to a callee argument. */
- return index;
- }
-
if (node->left == NULL) {
/* Assign empty object directly to variable */
return index;
@@ -3338,14 +3391,12 @@ njs_generate_temp_index_get(njs_vm_t *vm
return *last;
}
- scope = node->scope;
-
- while (scope->type == NJS_SCOPE_BLOCK) {
- scope = scope->parent;
+ scope = njs_function_scope(node->scope);
+ if (njs_slow_path(scope == NULL)) {
+ return NJS_ERROR;
}
- return njs_scope_next_index(vm, scope, NJS_SCOPE_INDEX_LOCAL,
- &njs_value_invalid);
+ return njs_scope_index(scope->type, scope->temp++, NJS_SCOPE_TYPE_TEMP);
}
@@ -3427,7 +3478,11 @@ njs_generate_global_reference(njs_vm_t *
3, node);
prop_get->value = index;
- prop_get->object = NJS_INDEX_GLOBAL_OBJECT;
+
+ prop_get->object = njs_scope_global_this_index();
+ if (njs_slow_path(prop_get->object == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
lex_entry = njs_lexer_entry(node->u.reference.unique_id);
if (njs_slow_path(lex_entry == NULL)) {
@@ -3440,8 +3495,9 @@ njs_generate_global_reference(njs_vm_t *
return NJS_ERROR;
}
- prop_get->property = njs_value_index(vm, &property, generator->runtime);
- if (njs_slow_path(prop_get->property == NJS_INDEX_NONE)) {
+ prop_get->property = njs_scope_global_index(vm, &property,
+ generator->runtime);
+ if (njs_slow_path(prop_get->property == NJS_INDEX_ERROR)) {
return NJS_ERROR;
}
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_generator.h
--- a/src/njs_generator.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_generator.h Mon Apr 12 10:05:59 2021 +0300
@@ -13,10 +13,9 @@ typedef struct njs_generator_block_s n
struct njs_generator_s {
njs_value_t *local_scope;
- size_t scope_size;
-
njs_generator_block_t *block;
njs_arr_t *index_cache;
+ njs_arr_t *closures;
njs_arr_t *lines;
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_lexer.h
--- a/src/njs_lexer.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_lexer.h Mon Apr 12 10:05:59 2021 +0300
@@ -116,6 +116,7 @@ typedef enum {
NJS_TOKEN_NULL,
NJS_TOKEN_NUMBER,
NJS_TOKEN_TRUE,
+ NJS_TOKEN_UNDEFINED,
NJS_TOKEN_FALSE,
NJS_TOKEN_STRING,
@@ -141,6 +142,7 @@ typedef enum {
NJS_TOKEN_FUNCTION,
NJS_TOKEN_FUNCTION_EXPRESSION,
+ NJS_TOKEN_FUNCTION_PROPERTY,
NJS_TOKEN_FUNCTION_CALL,
NJS_TOKEN_METHOD_CALL,
NJS_TOKEN_ARGUMENT,
@@ -172,8 +174,6 @@ typedef enum {
NJS_TOKEN_THROW,
NJS_TOKEN_THIS,
- NJS_TOKEN_GLOBAL_OBJECT,
- NJS_TOKEN_NON_LOCAL_THIS,
NJS_TOKEN_ARGUMENTS,
NJS_TOKEN_EVAL,
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_lexer_tables.h
--- a/src/njs_lexer_tables.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_lexer_tables.h Mon Apr 12 10:05:59 2021 +0300
@@ -10,7 +10,7 @@
#define _NJS_LEXER_TABLES_H_INCLUDED_
-static const njs_keyword_t njs_lexer_kws[53] =
+static const njs_keyword_t njs_lexer_kws[54] =
{
{
.entry = { njs_str("arguments") },
@@ -301,6 +301,12 @@ static const njs_keyword_t njs_lexer_kws
},
{
+ .entry = { njs_str("undefined") },
+ .type = NJS_TOKEN_UNDEFINED,
+ .reserved = 0
+ },
+
+ {
.entry = { njs_str("var") },
.type = NJS_TOKEN_VAR,
.reserved = 1
@@ -364,10 +370,10 @@ static const njs_lexer_keyword_entry_t n
{ "null", &njs_lexer_kws[32], 4, 0 },
{ NULL, NULL, 0, 0 },
{ "do", &njs_lexer_kws[12], 2, 0 },
- { "var", &njs_lexer_kws[48], 3, 0 },
+ { "var", &njs_lexer_kws[49], 3, 0 },
{ "if", &njs_lexer_kws[23], 2, 7 },
{ "implements", &njs_lexer_kws[24], 10, 0 },
- { "with", &njs_lexer_kws[51], 4, 0 },
+ { "with", &njs_lexer_kws[52], 4, 0 },
{ NULL, NULL, 0, 0 },
{ "eval", &njs_lexer_kws[15], 4, 9 },
{ NULL, NULL, 0, 0 },
@@ -379,14 +385,14 @@ static const njs_lexer_keyword_entry_t n
{ NULL, NULL, 0, 0 },
{ NULL, NULL, 0, 0 },
{ "default", &njs_lexer_kws[10], 7, 0 },
- { "void", &njs_lexer_kws[49], 4, 0 },
+ { "void", &njs_lexer_kws[50], 4, 0 },
{ NULL, NULL, 0, 0 },
{ NULL, NULL, 0, 0 },
- { NULL, NULL, 0, 0 },
+ { "undefined", &njs_lexer_kws[48], 9, 0 },
{ "from", &njs_lexer_kws[21], 4, 0 },
{ "package", &njs_lexer_kws[34], 7, 15 },
{ NULL, NULL, 0, 0 },
- { "yield", &njs_lexer_kws[52], 5, 0 },
+ { "yield", &njs_lexer_kws[53], 5, 0 },
{ NULL, NULL, 0, 0 },
{ NULL, NULL, 0, 0 },
{ "of", &njs_lexer_kws[33], 2, 0 },
@@ -403,7 +409,7 @@ static const njs_lexer_keyword_entry_t n
{ NULL, NULL, 0, 0 },
{ NULL, NULL, 0, 0 },
{ "for", &njs_lexer_kws[20], 3, 0 },
- { "while", &njs_lexer_kws[50], 5, 0 },
+ { "while", &njs_lexer_kws[51], 5, 0 },
{ NULL, NULL, 0, 0 },
{ NULL, NULL, 0, 0 },
{ NULL, NULL, 0, 0 },
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_main.h
--- a/src/njs_main.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_main.h Mon Apr 12 10:05:59 2021 +0300
@@ -57,6 +57,7 @@
#include <njs_lexer.h>
#include <njs_parser.h>
#include <njs_generator.h>
+#include <njs_scope.h>
#include <njs_boolean.h>
#include <njs_symbol.h>
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_module.c
--- a/src/njs_module.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_module.c Mon Apr 12 10:05:59 2021 +0300
@@ -61,11 +61,12 @@ njs_module_load(njs_vm_t *vm)
module = *item;
if (module->function.native) {
- value = njs_vmcode_operand(vm, module->index);
+ value = njs_scope_valid_value(vm, module->index);
njs_set_object(value, &module->object);
} else {
- ret = njs_vm_invoke(vm, &module->function, NULL, 0, module->index);
+ ret = njs_vm_invoke(vm, &module->function, NULL, 0,
+ njs_scope_valid_value(vm, module->index));
if (ret == NJS_ERROR) {
return ret;
}
@@ -573,11 +574,9 @@ njs_module_insert(njs_parser_t *parser,
scope = njs_parser_global_scope(parser);
vm = parser->vm;
- module->index = njs_scope_next_index(vm, scope, NJS_SCOPE_INDEX_LOCAL,
- &njs_value_undefined);
- if (njs_slow_path(module->index == NJS_INDEX_ERROR)) {
- return NJS_ERROR;
- }
+ module->index = njs_scope_index(scope->type, scope->items,
+ NJS_SCOPE_TYPE_LOCAL);
+ scope->items++;
if (vm->modules == NULL) {
vm->modules = njs_arr_create(vm->mem_pool, 4, sizeof(njs_module_t *));
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_parser.c
--- a/src/njs_parser.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_parser.c Mon Apr 12 10:05:59 2021 +0300
@@ -9,7 +9,8 @@
#include <njs_main.h>
-static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type);
+static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type,
+ njs_bool_t init_this);
static void njs_parser_scope_end(njs_parser_t *parser);
static njs_int_t njs_parser_check_error_state(njs_parser_t *parser,
@@ -426,7 +427,7 @@ static njs_int_t njs_parser_export_sink(
static njs_parser_node_t *njs_parser_return_set(njs_parser_t *parser,
njs_parser_node_t *expr);
static njs_parser_node_t *njs_parser_variable_node(njs_parser_t *parser,
- uintptr_t unique_id, njs_variable_type_t type);
+ uintptr_t unique_id, njs_variable_type_t type, njs_variable_t **retvar);
static njs_parser_node_t *njs_parser_reference(njs_parser_t *parser,
njs_lexer_token_t *token);
@@ -503,7 +504,7 @@ njs_parser_reject(njs_parser_t *parser)
njs_int_t
-njs_parser(njs_vm_t *vm, njs_parser_t *parser, njs_rbtree_t *prev_vars)
+njs_parser(njs_vm_t *vm, njs_parser_t *parser)
{
njs_int_t ret;
njs_lexer_token_t *token;
@@ -512,20 +513,18 @@ njs_parser(njs_vm_t *vm, njs_parser_t *p
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_vars != NULL) {
- /*
- * Copy the global scope variables from the previous
- * iteration of the accumulative mode.
- */
- ret = njs_variables_copy(vm, &parser->scope->variables, prev_vars);
- if (ret != NJS_OK) {
- return ret;
- }
+ if (parser->scope == NULL) {
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_GLOBAL, 1);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ } else {
+ parser->scope->temp = 0;
+ parser->scope->top = NULL;
+ parser->node = NULL;
+ parser->ret = NJS_OK;
+ parser->level = 1;
}
njs_queue_init(&parser->stack);
@@ -600,33 +599,15 @@ njs_parser_failed_state(njs_parser_t *pa
static njs_int_t
-njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type)
-{
- njs_arr_t *values;
- njs_uint_t nesting;
- njs_lexer_t *lexer;
- njs_parser_scope_t *scope, *parent;
-
- nesting = 0;
-
- if (type == NJS_SCOPE_FUNCTION) {
-
- for (scope = parser->scope; scope != NULL; scope = scope->parent) {
-
- if (scope->type == NJS_SCOPE_FUNCTION) {
- nesting = scope->nesting + 1;
-
- if (nesting < NJS_MAX_NESTING) {
- break;
- }
-
- njs_parser_syntax_error(parser, "The maximum function nesting "
- "level is \"%d\"", NJS_MAX_NESTING);
-
- return NJS_ERROR;
- }
- }
- }
+njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type,
+ njs_bool_t init_this)
+{
+ njs_lexer_t *lexer;
+ njs_variable_t *var;
+ njs_parser_scope_t *scope, *parent;
+ const njs_lexer_keyword_entry_t *keyword;
+
+ static const njs_str_t njs_this_str = njs_str("this");
scope = njs_mp_zalloc(parser->vm->mem_pool, sizeof(njs_parser_scope_t));
if (njs_slow_path(scope == NULL)) {
@@ -635,40 +616,10 @@ njs_parser_scope_begin(njs_parser_t *par
scope->type = type;
- if (type == NJS_SCOPE_FUNCTION) {
- scope->next_index[0] = type;
- scope->next_index[1] = NJS_SCOPE_CLOSURE + nesting
- + sizeof(njs_value_t);
-
- } else {
- if (type == NJS_SCOPE_GLOBAL) {
- type += NJS_INDEX_GLOBAL_OFFSET;
- }
-
- scope->next_index[0] = type;
- scope->next_index[1] = 0;
- }
-
- scope->nesting = nesting;
- scope->argument_closures = 0;
-
- njs_queue_init(&scope->nested);
njs_rbtree_init(&scope->variables, njs_parser_scope_rbtree_compare);
njs_rbtree_init(&scope->labels, njs_parser_scope_rbtree_compare);
njs_rbtree_init(&scope->references, njs_parser_scope_rbtree_compare);
- values = NULL;
-
- if (scope->type < NJS_SCOPE_BLOCK) {
- values = njs_arr_create(parser->vm->mem_pool, 4, sizeof(njs_value_t));
- if (njs_slow_path(values == NULL)) {
- return NJS_ERROR;
- }
- }
-
- scope->values[0] = values;
- scope->values[1] = NULL;
-
lexer = parser->lexer;
if (lexer->file.length != 0) {
@@ -680,14 +631,30 @@ njs_parser_scope_begin(njs_parser_t *par
scope->parent = parent;
parser->scope = scope;
- if (parent != NULL) {
- njs_queue_insert_tail(&parent->nested, &scope->link);
-
- if (nesting == 0) {
- /* Inherit function nesting in blocks. */
- scope->nesting = parent->nesting;
- }
- }
+ if (type == NJS_SCOPE_FUNCTION || type == NJS_SCOPE_GLOBAL) {
+ parser->level++;
+
+ if (init_this) {
+ /* Add this as first variable. */
+ keyword = njs_lexer_keyword(njs_this_str.start,
+ njs_this_str.length);
+ if (njs_slow_path(keyword == NULL)) {
+ return NJS_ERROR;
+ }
+
+ var = njs_variable_add(parser, scope, (uintptr_t) keyword->value,
+ NJS_VARIABLE_VAR);
+ if (njs_slow_path(var == NULL)) {
+ return NJS_ERROR;
+ }
+
+ var->index = njs_scope_index(type, 0, NJS_SCOPE_TYPE_LOCAL);
+ var->init = 1;
+ }
+ }
+
+ scope->level = parser->level;
+ scope->items = 1;
return NJS_OK;
}
@@ -700,6 +667,10 @@ njs_parser_scope_end(njs_parser_t *parse
scope = parser->scope;
+ if (scope->type == NJS_SCOPE_FUNCTION || scope->type == NJS_SCOPE_GLOBAL) {
+ parser->level--;
+ }
+
parent = scope->parent;
parser->scope = parent;
}
@@ -1324,7 +1295,11 @@ njs_parser_template_literal(njs_parser_t
array->token_line = token->line;
template = parser->node;
- index = NJS_SCOPE_CALLEE_ARGUMENTS;
+
+ index = njs_scope_temp_index(template->scope);
+ if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) {
node = njs_parser_argument(parser, array, index);
@@ -1335,7 +1310,10 @@ njs_parser_template_literal(njs_parser_t
template->right = node;
temp->right = node;
- index += sizeof(njs_value_t);
+ index = njs_scope_temp_index(template->scope);
+ if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
} else {
template->left = array;
@@ -1431,7 +1409,10 @@ njs_parser_template_literal_expression(n
parent->right = node;
parent = node;
- parser->target->index += sizeof(njs_value_t);
+ parser->target->index = njs_scope_temp_index(node->scope);
+ if (njs_slow_path(parser->target->index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
} else {
ret = njs_parser_array_item(parser, template->left, parser->node);
@@ -2068,7 +2049,7 @@ njs_parser_computed_property_name_after(
/* MethodDefinition */
} else if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) {
- expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+ expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_PROPERTY);
if (expr == NULL) {
return NJS_ERROR;
}
@@ -2796,11 +2777,9 @@ njs_parser_argument_list_after(njs_parse
return NJS_ERROR;
}
- if (parser->target->index == 0) {
- node->index = NJS_SCOPE_CALLEE_ARGUMENTS;
-
- } else {
- node->index = parser->target->index + sizeof(njs_value_t);
+ node->index = njs_scope_temp_index(node->scope);
+ if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
}
node->token_line = token->line;
@@ -4628,7 +4607,7 @@ njs_parser_block_statement(njs_parser_t
void *target;
njs_int_t ret;
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK);
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0);
if (ret != NJS_OK) {
return NJS_ERROR;
}
@@ -4812,6 +4791,7 @@ njs_parser_variable_declaration(njs_pars
njs_lexer_token_t *token, njs_queue_link_t *current)
{
njs_int_t ret;
+ njs_variable_t *var;
njs_parser_node_t *name;
ret = njs_parser_binding_pattern(parser, token, current);
@@ -4830,11 +4810,18 @@ 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, NJS_VARIABLE_VAR,
+ &var);
if (name == NULL) {
return NJS_ERROR;
}
+ if (var->self) {
+ var->type = NJS_VARIABLE_VAR;
+ var->self = 0;
+ var->init = 0;
+ }
+
name->token_line = token->line;
parser->node = name;
@@ -5340,7 +5327,7 @@ njs_parser_for_var_binding_or_var_list(n
}
var = njs_parser_variable_node(parser, token->unique_id,
- NJS_VARIABLE_VAR);
+ NJS_VARIABLE_VAR, NULL);
if (var == NULL) {
return NJS_ERROR;
}
@@ -6259,7 +6246,7 @@ njs_parser_catch_or_finally(njs_parser_t
return NJS_ERROR;
}
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK);
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0);
if (ret != NJS_OK) {
return NJS_ERROR;
}
@@ -6292,7 +6279,7 @@ njs_parser_catch_or_finally(njs_parser_t
if (njs_lexer_token_is_binding_identifier(token)) {
node = njs_parser_variable_node(parser, token->unique_id,
- NJS_VARIABLE_CATCH);
+ NJS_VARIABLE_CATCH, NULL);
if (node == NULL) {
return NJS_ERROR;
}
@@ -6405,52 +6392,15 @@ njs_parser_debugger_statement(njs_parser
/*
* 14.1 Function Definitions.
*/
-static njs_function_t *
-njs_parser_function_alloc(njs_parser_t *parser, njs_variable_t *var)
-{
- njs_value_t *value;
- njs_function_t *function;
- njs_function_lambda_t *lambda;
-
- lambda = njs_function_lambda_alloc(parser->vm, 1);
- if (lambda == NULL) {
- njs_memory_error(parser->vm);
- return NULL;
- }
-
- /* TODO:
- * njs_function_t is used to pass lambda to
- * njs_generate_function_declaration() and is not actually needed.
- * real njs_function_t is created by njs_vmcode_function() in runtime.
- */
-
- function = njs_function_alloc(parser->vm, lambda, NULL, 1);
- if (function == NULL) {
- return NULL;
- }
-
- njs_set_function(&var->value, function);
-
- if (var->index != NJS_INDEX_NONE
- && njs_scope_accumulative(parser->vm, parser->scope))
- {
- value = (njs_value_t *) var->index;
- *value = var->value;
- }
-
- return function;
-}
-
-
static njs_int_t
njs_parser_function_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
- njs_int_t ret;
- uintptr_t unique_id;
- njs_variable_t *var;
- njs_function_t *function;
- njs_parser_node_t *node, *temp;
+ njs_int_t ret;
+ uintptr_t unique_id;
+ njs_variable_t *var;
+ njs_parser_node_t *node;
+ njs_function_lambda_t *lambda;
if (!njs_lexer_token_is_binding_identifier(token)) {
return njs_parser_failed(parser);
@@ -6484,32 +6434,31 @@ njs_parser_function_declaration(njs_pars
return NJS_ERROR;
}
- ret = njs_variable_reference(parser->vm, parser->scope, node,
- unique_id, NJS_DECLARATION);
+ lambda = njs_function_lambda_alloc(parser->vm, 1);
+ if (lambda == NULL) {
+ return NJS_ERROR;
+ }
+
+ var->init = 0;
+ var->type = NJS_VARIABLE_FUNCTION;
+ var->have_lambda = 1;
+ var->value.type = NJS_FUNCTION;
+ var->value.data.u.lambda = lambda;
+
+ node->u.value.data.u.lambda = lambda;
+
+ node->left = (njs_parser_node_t *) unique_id;
+
+ parser->node = node;
+
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1);
if (ret != NJS_OK) {
return NJS_ERROR;
}
- function = njs_parser_function_alloc(parser, var);
- if (function == NULL) {
- return NJS_ERROR;
- }
-
- temp = njs_parser_node_new(parser, 0);
- if (temp == NULL) {
- return NJS_ERROR;
- }
-
- temp->left = node;
- temp->u.value.data.u.lambda = function->u.lambda;
-
- node->left = (njs_parser_node_t *) function;
-
- parser->node = temp;
-
njs_parser_next(parser, njs_parser_function_parse);
- return njs_parser_after(parser, current, temp, 1,
+ return njs_parser_after(parser, current, node, 1,
njs_parser_function_declaration_after);
}
@@ -6518,18 +6467,19 @@ static njs_int_t
njs_parser_function_declaration_after(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current)
{
- njs_function_t *function;
-
- parser->node = parser->target->left;
-
- function = (njs_function_t *) parser->node->left;
-
- function->args_count = function->u.lambda->nargs
- - function->u.lambda->rest_parameters;
-
- parser->node->right = parser->target->right;
+ njs_int_t ret;
+ uintptr_t unique_id;
+
+ unique_id = (uintptr_t) parser->node->left;
+
parser->node->left = NULL;
+ ret = njs_parser_variable_reference(parser, parser->scope, parser->node,
+ unique_id, NJS_DECLARATION);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
return njs_parser_stack_pop(parser);
}
@@ -6538,13 +6488,6 @@ static njs_int_t
njs_parser_function_parse(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
- njs_int_t ret;
-
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
- if (ret != NJS_OK) {
- return NJS_ERROR;
- }
-
parser->target = parser->node;
parser->node = NULL;
@@ -6555,26 +6498,30 @@ njs_parser_function_parse(njs_parser_t *
}
+static const njs_lexer_entry_t njs_parser_anonymous_entry =
+{
+ .name = njs_str("anonymous")
+};
+
+
static njs_int_t
njs_parser_function_expression(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
njs_int_t ret;
+ uintptr_t unique_id;
njs_variable_t *var;
- njs_function_t *function;
njs_function_lambda_t *lambda;
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_SHIM);
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1);
if (ret != NJS_OK) {
return NJS_ERROR;
}
+ var = NULL;
+
if (njs_lexer_token_is_binding_identifier(token)) {
- var = njs_variable_add(parser, parser->scope, token->unique_id,
- NJS_VARIABLE_SHIM);
- if (var == NULL) {
- return NJS_ERROR;
- }
+ unique_id = token->unique_id;
njs_lexer_consume_token(parser->lexer, 1);
@@ -6583,19 +6530,8 @@ njs_parser_function_expression(njs_parse
return NJS_ERROR;
}
- function = njs_parser_function_alloc(parser, var);
- if (function == NULL) {
- return NJS_ERROR;
- }
-
- lambda = function->u.lambda;
-
} else {
- /* Anonymous function. */
- lambda = njs_function_lambda_alloc(parser->vm, 1);
- if (lambda == NULL) {
- return NJS_ERROR;
- }
+ unique_id = (uintptr_t) &njs_parser_anonymous_entry;
}
if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) {
@@ -6604,11 +6540,37 @@ njs_parser_function_expression(njs_parse
njs_lexer_consume_token(parser->lexer, 1);
+ parser->node->left = njs_parser_node_new(parser, NJS_TOKEN_NAME);
+ if (parser->node->left == NULL) {
+ return NJS_ERROR;
+ }
+
+ var = njs_variable_scope_add(parser, parser->scope, parser->scope,
+ unique_id, NJS_VARIABLE_FUNCTION, 1);
+ if (var == NULL) {
+ return NJS_ERROR;
+ }
+
+ var->init = 1;
+ var->self = 1;
+
+ ret = njs_parser_variable_reference(parser, parser->scope,
+ parser->node->left, unique_id,
+ NJS_DECLARATION);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ lambda = njs_function_lambda_alloc(parser->vm, 1);
+ if (lambda == NULL) {
+ return NJS_ERROR;
+ }
+
parser->node->u.value.data.u.lambda = lambda;
njs_parser_next(parser, njs_parser_function_parse);
- return njs_parser_after(parser, current, NULL, 1,
+ return njs_parser_after(parser, current, var, 1,
njs_parser_function_expression_after);
}
@@ -6617,7 +6579,17 @@ static njs_int_t
njs_parser_function_expression_after(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current)
{
- njs_parser_scope_end(parser);
+ njs_variable_t *var;
+
+ var = (njs_variable_t *) parser->target;
+
+ var->index = njs_scope_index(var->scope->type, var->scope->items,
+ NJS_SCOPE_TYPE_LOCAL);
+ var->scope->items++;
+
+ if (var->self) {
+ parser->node->u.value.data.u.lambda->self = var->index;
+ }
return njs_parser_stack_pop(parser);
}
@@ -6639,7 +6611,9 @@ static njs_int_t
njs_parser_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
- njs_variable_t *arg, *cur_arg;
+ njs_variable_t *arg;
+ njs_rbtree_node_t *rb_node;
+ njs_variable_node_t var_node;
njs_function_lambda_t *lambda;
lambda = parser->target->u.value.data.u.lambda;
@@ -6667,28 +6641,32 @@ njs_parser_formal_parameters(njs_parser_
default:
/* SingleNameBinding */
if (njs_lexer_token_is_binding_identifier(token)) {
- arg = njs_variable_add(parser, parser->scope,
- token->unique_id, NJS_VARIABLE_VAR);
+ var_node.key = token->unique_id;
+
+ rb_node = njs_rbtree_find(&parser->scope->variables,
+ &var_node.node);
+ if (rb_node != NULL) {
+ arg = ((njs_variable_node_t *) rb_node)->variable;
+
+ if (!arg->self) {
+ njs_parser_syntax_error(parser,
+ "Duplicate parameter names");
+ return NJS_DONE;
+ }
+
+ arg->self = 0;
+
+ } else {
+ arg = njs_variable_add(parser, parser->scope,
+ token->unique_id, NJS_VARIABLE_VAR);
+ }
+
if (arg == NULL) {
return NJS_ERROR;
}
- if (arg->index > 0) {
- njs_parser_syntax_error(parser, "Duplicate parameter names");
- return NJS_DONE;
- }
-
- cur_arg = (njs_variable_t *) parser->node;
-
- if (cur_arg == NULL) {
- arg->index = NJS_SCOPE_ARGUMENTS;
-
- /* A "this" reservation. */
- arg->index += sizeof(njs_value_t);
-
- } else {
- arg->index = cur_arg->index + sizeof(njs_value_t);
- }
+ arg->init = 1;
+ arg->argument = 1;
lambda->nargs++;
@@ -6744,8 +6722,9 @@ njs_parser_arrow_function(njs_parser_t *
njs_queue_link_t *current)
{
njs_int_t ret;
- njs_variable_t *arg;
- njs_parser_node_t *node;
+ uintptr_t unique_id;
+ njs_variable_t *arg, *var;
+ njs_parser_node_t *node, *name;
njs_function_lambda_t *lambda;
node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
@@ -6756,6 +6735,36 @@ njs_parser_arrow_function(njs_parser_t *
node->token_line = token->line;
parser->node = node;
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 0);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ name = njs_parser_node_new(parser, NJS_TOKEN_NAME);
+ if (name == NULL) {
+ return NJS_ERROR;
+ }
+
+ node->left = name;
+
+ unique_id = (uintptr_t) &njs_parser_anonymous_entry;
+
+ var = njs_variable_scope_add(parser, parser->scope, parser->scope,
+ unique_id, NJS_VARIABLE_FUNCTION, 1);
+ if (var == NULL) {
+ return NJS_ERROR;
+ }
+
+ var->init = 1;
+
+ ret = njs_parser_variable_reference(parser, parser->scope, node->left,
+ unique_id, NJS_DECLARATION);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ node->left->u.reference.variable = var;
+
lambda = njs_function_lambda_alloc(parser->vm, 0);
if (lambda == NULL) {
return NJS_ERROR;
@@ -6763,11 +6772,6 @@ njs_parser_arrow_function(njs_parser_t *
node->u.value.data.u.lambda = lambda;
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
- if (ret != NJS_OK) {
- return NJS_ERROR;
- }
-
parser->scope->arrow_function = 1;
if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) {
@@ -6782,18 +6786,21 @@ njs_parser_arrow_function(njs_parser_t *
njs_parser_arrow_function_args_after);
} else if (njs_lexer_token_is_binding_identifier(token)) {
- arg = njs_variable_add(parser, parser->scope,
- token->unique_id, NJS_VARIABLE_VAR);
+ arg = njs_variable_add(parser, parser->scope, token->unique_id,
+ NJS_VARIABLE_VAR);
if (arg == NULL) {
return NJS_ERROR;
}
- arg->index = NJS_SCOPE_ARGUMENTS;
-
- /* A "this" reservation. */
- arg->index += sizeof(njs_value_t);
-
- lambda->nargs = 1;
+ arg->argument = 1;
+ arg->init = 1;
+
+ var->index = njs_scope_index(parser->scope->type, parser->scope->items,
+ NJS_SCOPE_TYPE_LOCAL);
+ parser->scope->items++;
+
+ lambda->self = var->index;
+ lambda->nargs++;
njs_lexer_consume_token(parser->lexer, 1);
@@ -6813,12 +6820,23 @@ static njs_int_t
njs_parser_arrow_function_args_after(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current)
{
+ njs_variable_t *var;
+
if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) {
return njs_parser_failed(parser);
}
njs_lexer_consume_token(parser->lexer, 1);
+ var = parser->target->left->u.reference.variable;
+ parser->target->left->u.reference.variable = NULL;
+
+ var->index = njs_scope_index(var->scope->type, var->scope->items,
+ NJS_SCOPE_TYPE_LOCAL);
+ var->scope->items++;
+
+ parser->target->u.value.data.u.lambda->self = var->index;
+
njs_parser_next(parser, njs_parser_arrow_function_arrow);
return NJS_OK;
@@ -6929,7 +6947,7 @@ njs_parser_method_definition(njs_parser_
return njs_parser_failed(parser);
}
- expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+ expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_PROPERTY);
if (expr == NULL) {
return NJS_ERROR;
}
@@ -7003,7 +7021,7 @@ njs_parser_get_set(njs_parser_t *parser,
return njs_parser_failed(parser);
}
- expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+ expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_PROPERTY);
if (expression == NULL) {
return NJS_ERROR;
}
@@ -7047,7 +7065,7 @@ njs_parser_get_set_after(njs_parser_t *p
return njs_parser_failed(parser);
}
- expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+ expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_PROPERTY);
if (expression == NULL) {
return NJS_ERROR;
}
@@ -7152,7 +7170,7 @@ njs_parser_function_lambda(njs_parser_t
expr = parser->node;
expr->u.value.data.u.lambda = lambda;
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1);
if (ret != NJS_OK) {
return NJS_ERROR;
}
@@ -7321,7 +7339,8 @@ njs_parser_import(njs_parser_t *parser,
return NJS_DONE;
}
- name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR);
+ name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR,
+ NULL);
if (name == NULL) {
return NJS_ERROR;
}
@@ -7376,17 +7395,24 @@ njs_parser_import_after(njs_parser_t *pa
parser->target->right = parser->node;
parser->node = parser->target;
- parser->node->hoist = 1;
return njs_parser_stack_pop(parser);
}
+static const njs_lexer_entry_t njs_parser_module_entry =
+{
+ .name = njs_str("module")
+};
+
+
njs_int_t
njs_parser_module_lambda(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
njs_int_t ret;
+ uintptr_t unique_id;
+ njs_variable_t *var;
njs_parser_node_t *node, *parent;
njs_function_lambda_t *lambda;
@@ -7395,6 +7421,34 @@ njs_parser_module_lambda(njs_parser_t *p
return NJS_ERROR;
}
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 0);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ node->left = njs_parser_node_new(parser, NJS_TOKEN_NAME);
+ if (node->left == NULL) {
+ return NJS_ERROR;
+ }
+
+ unique_id = (uintptr_t) &njs_parser_module_entry;
+
+ var = njs_variable_scope_add(parser, parser->scope, parser->scope,
+ unique_id, NJS_VARIABLE_FUNCTION, 1);
+ if (var == NULL) {
+ return NJS_ERROR;
+ }
+
+ var->init = 1;
+
+ ret = njs_parser_variable_reference(parser, parser->scope, node->left,
+ unique_id, NJS_DECLARATION);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ node->left->u.reference.variable = var;
+
lambda = njs_function_lambda_alloc(parser->vm, 1);
if (lambda == NULL) {
return NJS_ERROR;
@@ -7405,11 +7459,6 @@ njs_parser_module_lambda(njs_parser_t *p
parser->node = node;
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
- if (ret != NJS_OK) {
- return NJS_ERROR;
- }
-
parser->scope->module = 1;
parent = parser->node;
@@ -7426,7 +7475,8 @@ static njs_int_t
njs_parser_module_lambda_after(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
- njs_int_t ret;
+ njs_int_t ret;
+ njs_variable_t *var;
ret = njs_parser_export_sink(parser);
if (ret != NJS_OK) {
@@ -7437,6 +7487,15 @@ njs_parser_module_lambda_after(njs_parse
parser->node = parser->target;
+ var = parser->target->left->u.reference.variable;
+ parser->target->left->u.reference.variable = NULL;
+
+ var->index = njs_scope_index(var->scope->type, var->scope->items,
+ NJS_SCOPE_TYPE_LOCAL);
+ var->scope->items++;
+
+ parser->node->u.value.data.u.lambda->self = var->index;
+
njs_parser_scope_end(parser);
return njs_parser_stack_pop(parser);
@@ -7529,7 +7588,7 @@ njs_parser_return_set(njs_parser_t *pars
static njs_parser_node_t *
njs_parser_variable_node(njs_parser_t *parser, uintptr_t unique_id,
- njs_variable_type_t type)
+ njs_variable_type_t type, njs_variable_t **retvar)
{
njs_int_t ret;
njs_variable_t *var;
@@ -7540,17 +7599,8 @@ njs_parser_variable_node(njs_parser_t *p
return NULL;
}
- if (njs_is_null(&var->value)) {
-
- switch (type) {
-
- case NJS_VARIABLE_VAR:
- njs_set_undefined(&var->value);
- break;
-
- default:
- break;
- }
+ if (retvar != NULL) {
+ *retvar = var;
}
node = njs_parser_node_new(parser, NJS_TOKEN_NAME);
@@ -7558,8 +7608,8 @@ njs_parser_variable_node(njs_parser_t *p
return NULL;
}
- ret = njs_variable_reference(parser->vm, parser->scope, node, unique_id,
- NJS_DECLARATION);
+ ret = njs_parser_variable_reference(parser, parser->scope, node, unique_id,
+ NJS_DECLARATION);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
@@ -7571,10 +7621,14 @@ njs_parser_variable_node(njs_parser_t *p
static njs_parser_node_t *
njs_parser_reference(njs_parser_t *parser, njs_lexer_token_t *token)
{
- njs_int_t ret;
- njs_variable_t *var;
- njs_parser_node_t *node;
- njs_parser_scope_t *scope;
+ njs_int_t ret;
+ njs_index_t index;
+ njs_variable_t *var;
+ njs_parser_node_t *node;
+ njs_parser_scope_t *scope;
+ const njs_lexer_keyword_entry_t *keyword;
+
+ static const njs_str_t njs_undefined_str = njs_str("undefined");
node = njs_parser_node_new(parser, token->type);
if (njs_slow_path(node == NULL)) {
@@ -7585,51 +7639,60 @@ njs_parser_reference(njs_parser_t *parse
case NJS_TOKEN_NULL:
njs_thread_log_debug("JS: null");
-
- node->u.value = njs_value_null;
break;
case NJS_TOKEN_THIS:
njs_thread_log_debug("JS: this");
- scope = njs_function_scope(parser->scope, 0);
-
- if (scope != NULL) {
- if (scope == njs_function_scope(parser->scope, 1)) {
- node->index = NJS_INDEX_THIS;
-
- } else {
- node->token_type = NJS_TOKEN_NON_LOCAL_THIS;
- node->token_line = token->line;
-
- ret = njs_variable_reference(parser->vm, scope, node,
- token->unique_id, NJS_REFERENCE);
- if (njs_slow_path(ret != NJS_OK)) {
- return NULL;
- }
-
- var = njs_variable_add(parser, scope, token->unique_id,
- NJS_VARIABLE_VAR);
- if (njs_slow_path(var == NULL)) {
- return NULL;
- }
-
- var->this_object = 1;
+ scope = njs_function_scope(parser->scope);
+ if (njs_slow_path(scope == NULL)) {
+ njs_parser_syntax_error(parser,
+ "function or global scope not found");
+ return NULL;
+ }
+
+ if (parser->vm->options.module) {
+ keyword = njs_lexer_keyword(njs_undefined_str.start,
+ njs_undefined_str.length);
+ if (njs_slow_path(keyword == NULL)) {
+ return NULL;
}
- break;
- }
-
- node->token_type = NJS_TOKEN_GLOBAL_OBJECT;
+ token->unique_id = (uintptr_t) keyword->value;
+
+ } else if (!scope->arrow_function) {
+ index = njs_scope_index(scope->type, 0, NJS_SCOPE_TYPE_LOCAL);
+
+ var = njs_variable_scope_add(parser, scope, scope, token->unique_id,
+ NJS_VARIABLE_VAR, index);
+ if (njs_slow_path(var == NULL)) {
+ return NULL;
+ }
+
+ var->init = 1;
+ }
+
+ node->token_type = NJS_TOKEN_THIS;
+ node->token_line = token->line;
+
+ ret = njs_parser_variable_reference(parser, parser->scope, node,
+ token->unique_id, NJS_REFERENCE);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NULL;
+ }
break;
case NJS_TOKEN_ARGUMENTS:
njs_thread_log_debug("JS: arguments");
- scope = njs_function_scope(parser->scope, 0);
-
- if (scope == NULL) {
+ scope = njs_function_scope(parser->scope);
+
+ while (scope->arrow_function) {
+ scope = njs_function_scope(scope->parent);
+ }
+
+ if (scope == NULL || scope->type == NJS_SCOPE_GLOBAL) {
njs_parser_syntax_error(parser, "\"%V\" object in global scope",
&token->text);
return NULL;
@@ -7637,8 +7700,8 @@ njs_parser_reference(njs_parser_t *parse
node->token_line = token->line;
- ret = njs_variable_reference(parser->vm, scope, node, token->unique_id,
- NJS_REFERENCE);
+ ret = njs_parser_variable_reference(parser, parser->scope, node,
+ token->unique_id, NJS_REFERENCE);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
@@ -7649,6 +7712,7 @@ njs_parser_reference(njs_parser_t *parse
return NULL;
}
+ var->init = 1;
var->arguments_object = 1;
break;
@@ -7665,8 +7729,8 @@ njs_parser_reference(njs_parser_t *parse
node->token_line = token->line;
- ret = njs_variable_reference(parser->vm, parser->scope, node,
- token->unique_id, NJS_REFERENCE);
+ ret = njs_parser_variable_reference(parser, parser->scope, node,
+ token->unique_id, NJS_REFERENCE);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
@@ -8322,6 +8386,41 @@ njs_parser_has_side_effect(njs_parser_no
}
+njs_int_t
+njs_parser_variable_reference(njs_parser_t *parser, njs_parser_scope_t *scope,
+ njs_parser_node_t *node, uintptr_t unique_id, njs_reference_type_t type)
+{
+ njs_rbtree_node_t *rb_node;
+ njs_variable_reference_t *vr;
+ njs_parser_rbtree_node_t parse_node, *rb_parse_node;
+
+ vr = &node->u.reference;
+
+ vr->unique_id = unique_id;
+ vr->type = type;
+
+ parse_node.key = unique_id;
+
+ rb_node = njs_rbtree_find(&scope->references, &parse_node.node);
+ if (rb_node != NULL) {
+ return NJS_OK;
+ }
+
+ rb_parse_node = njs_mp_alloc(parser->vm->mem_pool,
+ sizeof(njs_parser_rbtree_node_t));
+ if (njs_slow_path(rb_parse_node == NULL)) {
+ return NJS_ERROR;
+ }
+
+ rb_parse_node->key = unique_id;
+ rb_parse_node->index = NJS_INDEX_NONE;
+
+ njs_rbtree_insert(&scope->references, &rb_parse_node->node);
+
+ return NJS_OK;
+}
+
+
njs_token_type_t
njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser,
njs_str_t *name, njs_token_type_t type)
@@ -8632,8 +8731,6 @@ njs_parser_serialize_node(njs_chb_t *cha
njs_token_serialize(NJS_TOKEN_FINALLY);
njs_token_serialize(NJS_TOKEN_THROW);
njs_token_serialize(NJS_TOKEN_THIS);
- njs_token_serialize(NJS_TOKEN_GLOBAL_OBJECT);
- njs_token_serialize(NJS_TOKEN_NON_LOCAL_THIS);
njs_token_serialize(NJS_TOKEN_ARGUMENTS);
njs_token_serialize(NJS_TOKEN_EVAL);
njs_token_serialize(NJS_TOKEN_IMPORT);
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_parser.h
--- a/src/njs_parser.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_parser.h Mon Apr 12 10:05:59 2021 +0300
@@ -12,26 +12,21 @@
struct njs_parser_scope_s {
njs_parser_node_t *top;
- njs_queue_link_t link;
- njs_queue_t nested;
-
njs_parser_scope_t *parent;
njs_rbtree_t variables;
njs_rbtree_t labels;
njs_rbtree_t references;
-#define NJS_SCOPE_INDEX_LOCAL 0
-#define NJS_SCOPE_INDEX_CLOSURE 1
+ njs_arr_t *closures;
- njs_arr_t *values[2]; /* Array of njs_value_t. */
- njs_index_t next_index[2];
+ uint32_t temp;
+ uint32_t items;
+ uint8_t level;
njs_str_t cwd;
njs_str_t file;
njs_scope_t type:8;
- uint8_t nesting; /* 4 bits */
- uint8_t argument_closures;
uint8_t module;
uint8_t arrow_function;
};
@@ -41,7 +36,6 @@ struct njs_parser_node_s {
njs_token_type_t token_type:16;
uint8_t ctor:1;
uint8_t temporary; /* 1 bit */
- uint8_t hoist; /* 1 bit */
uint32_t token_line;
union {
@@ -85,6 +79,7 @@ struct njs_parser_s {
njs_int_t ret;
njs_bool_t strict_semicolon;
uint32_t line;
+ uint8_t level;
};
@@ -101,7 +96,7 @@ typedef struct {
typedef struct {
NJS_RBTREE_NODE (node);
uintptr_t key;
- njs_parser_node_t *parser_node;
+ njs_index_t index;
} njs_parser_rbtree_node_t;
@@ -110,14 +105,16 @@ njs_int_t njs_parser_failed_state(njs_pa
intptr_t njs_parser_scope_rbtree_compare(njs_rbtree_node_t *node1,
njs_rbtree_node_t *node2);
-njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser,
- njs_rbtree_t *prev_vars);
+njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser);
njs_int_t njs_parser_module_lambda(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
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);
+njs_int_t njs_parser_variable_reference(njs_parser_t *parser,
+ njs_parser_scope_t *scope, njs_parser_node_t *node, uintptr_t unique_id,
+ njs_reference_type_t type);
njs_token_type_t njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser,
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,
@@ -139,10 +136,6 @@ njs_int_t njs_parser_serialize_ast(njs_p
|| (node)->token_type == NJS_TOKEN_PROPERTY)
-#define njs_scope_accumulative(vm, scope) \
- ((vm)->options.accumulative && (scope)->type == NJS_SCOPE_GLOBAL)
-
-
#define njs_parser_syntax_error(parser, fmt, ...) \
njs_parser_lexer_error(parser, NJS_OBJ_TYPE_SYNTAX_ERROR, fmt, \
##__VA_ARGS__)
@@ -215,17 +208,18 @@ njs_parser_global_scope(njs_parser_t *pa
njs_inline njs_parser_scope_t *
-njs_function_scope(njs_parser_scope_t *scope, njs_bool_t any)
+njs_function_scope(njs_parser_scope_t *scope)
{
- while (scope->type != NJS_SCOPE_GLOBAL) {
- if (scope->type == NJS_SCOPE_FUNCTION
- && (any || !scope->arrow_function))
+ do {
+ if (scope->type == NJS_SCOPE_GLOBAL
+ || scope->type == NJS_SCOPE_FUNCTION)
{
return scope;
}
scope = scope->parent;
- }
+
+ } while (scope != NULL);
return NULL;
}
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_scope.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_scope.c Mon Apr 12 10:05:59 2021 +0300
@@ -0,0 +1,251 @@
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs_main.h>
+
+
+static njs_value_t *njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src,
+ njs_uint_t runtime, njs_index_t **index);
+
+
+njs_index_t
+njs_scope_temp_index(njs_parser_scope_t *scope)
+{
+ scope = njs_function_scope(scope);
+ if (njs_slow_path(scope == NULL)) {
+ return NJS_INDEX_ERROR;
+ }
+
+ return njs_scope_index(NJS_SCOPE_GLOBAL, scope->temp++,
+ NJS_SCOPE_TYPE_TEMP);
+}
+
+
+njs_value_t *
+njs_scope_create_index_value(njs_vm_t *vm, njs_index_t index)
+{
+ njs_value_t *value;
+
+ value = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+ if (njs_slow_path(value == NULL)) {
+ return NULL;
+ }
+
+ njs_scope_value_set(vm, index, value);
+
+ return value;
+}
+
+
+njs_value_t **
+njs_scope_make(njs_vm_t *vm, uint32_t count)
+{
+ size_t size;
+ njs_value_t **refs, *values;
+
+ size = (count * sizeof(njs_value_t *)) + (count * sizeof(njs_value_t));
+
+ refs = njs_mp_alloc(vm->mem_pool, size);
+ if (njs_slow_path(refs == NULL)) {
+ return NULL;
+ }
+
+ values = (njs_value_t *) ((u_char *) refs
+ + (count * sizeof(njs_value_t *)));
+
+ while (count != 0) {
+ count--;
+
+ refs[count] = &values[count];
+
+ njs_set_invalid(refs[count]);
+ }
+
+ return refs;
+}
+
+
+njs_index_t
+njs_scope_global_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime)
+{
+ njs_index_t index, *retval;
+ njs_value_t **values, *value;
+
+ value = njs_scope_value_index(vm, src, runtime, &retval);
+ if (njs_slow_path(value == NULL)) {
+ return NJS_INDEX_ERROR;
+ }
+
+ if (*retval != NJS_INDEX_ERROR) {
+ return *retval;
+ }
+
+ index = vm->level_absolute->items;
+
+ values = njs_arr_add(vm->level_absolute);
+ if (njs_slow_path(values == NULL)) {
+ return NJS_INDEX_ERROR;
+ }
+
+ *values = value;
+
+ vm->level[NJS_SCOPE_TYPE_STATIC] = vm->level_absolute->start;
+
+ *retval = njs_scope_index(NJS_SCOPE_GLOBAL, index, NJS_SCOPE_TYPE_STATIC);
+
+ return *retval;
+}
+
+
+static njs_int_t
+njs_scope_values_hash_test(njs_lvlhsh_query_t *lhq, void *data)
+{
+ njs_str_t string;
+ njs_value_t *value;
+
+ value = data;
+
+ if (njs_is_string(value)) {
+ njs_string_get(value, &string);
+
+ } else {
+ string.start = (u_char *) value;
+ string.length = sizeof(njs_value_t);
+ }
+
+ if (lhq->key.length == string.length
+ && memcmp(lhq->key.start, string.start, string.length) == 0)
+ {
+ return NJS_OK;
+ }
+
+ return NJS_DECLINED;
+}
+
+
+static const njs_lvlhsh_proto_t njs_values_hash_proto
+ njs_aligned(64) =
+{
+ NJS_LVLHSH_DEFAULT,
+ njs_scope_values_hash_test,
+ njs_lvlhsh_alloc,
+ njs_lvlhsh_free,
+};
+
+
+/*
+ * Constant values such as njs_value_true are copied to values_hash during
+ * code generation when they are used as operands to guarantee aligned value.
+ */
+
+static njs_value_t *
+njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime,
+ njs_index_t **index)
+{
+ u_char *start;
+ uint32_t value_size, size, length;
+ njs_int_t ret;
+ njs_str_t str;
+ njs_bool_t long_string;
+ njs_value_t *value;
+ njs_string_t *string;
+ njs_lvlhsh_t *values_hash;
+ njs_lvlhsh_query_t lhq;
+
+ long_string = 0;
+ value_size = sizeof(njs_value_t);
+
+ if (njs_is_string(src)) {
+ njs_string_get(src, &str);
+
+ size = (uint32_t) str.length;
+ start = str.start;
+
+ if (src->short_string.size == NJS_STRING_LONG) {
+ long_string = 1;
+ }
+
+ } else {
+ size = value_size;
+ start = (u_char *) src;
+ }
+
+ lhq.key_hash = njs_djb_hash(start, size);
+ lhq.key.length = size;
+ lhq.key.start = start;
+ lhq.proto = &njs_values_hash_proto;
+
+ if (njs_lvlhsh_find(&vm->shared->values_hash, &lhq) == NJS_OK) {
+ value = lhq.value;
+
+ *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
+
+ } else if (runtime && njs_lvlhsh_find(&vm->values_hash, &lhq) == NJS_OK) {
+ value = lhq.value;
+
+ *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
+
+ } else {
+ if (long_string) {
+ length = src->long_string.data->length;
+
+ if (size != length && length > NJS_STRING_MAP_STRIDE) {
+ size = njs_string_map_offset(size)
+ + njs_string_map_size(length);
+ }
+
+ value_size += sizeof(njs_string_t) + size;
+ }
+
+ value_size += sizeof(njs_index_t);
+
+ value = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), value_size);
+ if (njs_slow_path(value == NULL)) {
+ return NULL;
+ }
+
+ *value = *src;
+
+ if (long_string) {
+ string = (njs_string_t *) ((u_char *) value + sizeof(njs_value_t)
+ + sizeof(njs_index_t));
+
+ value->long_string.data = string;
+
+ string->start = (u_char *) string + sizeof(njs_string_t);
+ string->length = src->long_string.data->length;
+ string->retain = 0xffff;
+
+ memcpy(string->start, start, size);
+ }
+
+ *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
+ **index = NJS_INDEX_ERROR;
+
+ lhq.replace = 0;
+ lhq.value = value;
+ lhq.pool = vm->mem_pool;
+
+ values_hash = runtime ? &vm->values_hash : &vm->shared->values_hash;
+
+ ret = njs_lvlhsh_insert(values_hash, &lhq);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NULL;
+ }
+ }
+
+ if (start != (u_char *) src) {
+ /*
+ * The source node value must be updated with the shared value
+ * allocated from the permanent memory pool because the node
+ * value can be used as a variable initial value.
+ */
+ *(njs_value_t *) src = *value;
+ }
+
+ return value;
+}
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_scope.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_scope.h Mon Apr 12 10:05:59 2021 +0300
@@ -0,0 +1,108 @@
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_SCOPE_H_INCLUDED_
+#define _NJS_SCOPE_H_INCLUDED_
+
+
+#define NJS_SCOPE_TYPE 4
+#define NJS_SCOPE_VALUE (NJS_SCOPE_TYPE + 1)
+#define NJS_SCOPE_TYPE_MASK ((NJS_SCOPE_VALUE_MAX) << NJS_SCOPE_TYPE)
+
+#define NJS_INDEX_NONE ((njs_index_t) 0)
+#define NJS_INDEX_ERROR ((njs_index_t) -1)
+
+
+njs_index_t njs_scope_temp_index(njs_parser_scope_t *scope);
+njs_value_t *njs_scope_create_index_value(njs_vm_t *vm, njs_index_t index);
+njs_value_t **njs_scope_make(njs_vm_t *vm, uint32_t count);
+njs_index_t njs_scope_global_index(njs_vm_t *vm, const njs_value_t *src,
+ njs_uint_t runtime);
+
+
+njs_inline njs_index_t
+njs_scope_index(njs_scope_t scope, njs_index_t index, njs_scope_type_t type)
+{
+ if (index > NJS_SCOPE_VALUE_MAX || type >= NJS_SCOPE_TYPE_LAST_ENTRY
+ || (scope != NJS_SCOPE_GLOBAL && scope != NJS_SCOPE_FUNCTION))
+ {
+ return NJS_INDEX_ERROR;
+ }
+
+ if (scope == NJS_SCOPE_GLOBAL && type == NJS_SCOPE_TYPE_LOCAL) {
+ type = NJS_SCOPE_TYPE_GLOBAL;
+ }
+
+ return (index << NJS_SCOPE_VALUE) | type;
+}
+
+
+njs_inline njs_scope_type_t
+njs_scope_index_type(njs_index_t index)
+{
+ return (njs_scope_type_t) (index & ~NJS_SCOPE_TYPE_MASK);
+}
+
+
+njs_inline uint32_t
+njs_scope_index_value(njs_index_t index)
+{
+ return (uint32_t) (index >> NJS_SCOPE_VALUE);
+}
+
+
+njs_inline njs_value_t *
+njs_scope_value(njs_vm_t *vm, njs_index_t index)
+{
+ return vm->level[njs_scope_index_type(index)][njs_scope_index_value(index)];
+}
+
+
+njs_inline njs_value_t *
+njs_scope_valid_value(njs_vm_t *vm, njs_index_t index)
+{
+ njs_value_t *value;
+
+ value = njs_scope_value(vm, index);
+
+ if (!njs_is_valid(value)) {
+ njs_set_undefined(value);
+ }
+
+ return value;
+}
+
+
+njs_inline void
+njs_scope_value_set(njs_vm_t *vm, njs_index_t index, njs_value_t *value)
+{
+ vm->level[njs_scope_index_type(index)]
+ [njs_scope_index_value(index)] = value;
+}
+
+
+njs_inline njs_index_t
+njs_scope_this_index()
+{
+ return njs_scope_index(NJS_SCOPE_FUNCTION, 0, NJS_SCOPE_TYPE_LOCAL);
+}
+
+
+njs_inline njs_index_t
+njs_scope_undefined_index(njs_vm_t *vm, njs_uint_t runtime)
+{
+ return njs_scope_global_index(vm, &njs_value_undefined, runtime);
+}
+
+
+njs_inline njs_index_t
+njs_scope_global_this_index()
+{
+ return njs_scope_index(NJS_SCOPE_GLOBAL, 0, NJS_SCOPE_TYPE_LOCAL);
+}
+
+
+#endif /* _NJS_PARSER_H_INCLUDED_ */
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_shell.c
--- a/src/njs_shell.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_shell.c Mon Apr 12 10:05:59 2021 +0300
@@ -258,7 +258,7 @@ main(int argc, char **argv)
vm_options.file.length = njs_strlen(opts.file);
vm_options.init = 1;
- vm_options.accumulative = opts.interactive;
+ vm_options.interactive = opts.interactive;
vm_options.disassemble = opts.disassemble;
vm_options.backtrace = 1;
vm_options.quiet = opts.quiet;
@@ -797,7 +797,7 @@ njs_output(njs_opts_t *opts, njs_vm_t *v
return;
}
- if (vm->options.accumulative) {
+ if (vm->options.interactive) {
njs_print(out.start, out.length);
njs_print("\n", 1);
}
@@ -906,6 +906,71 @@ njs_process_script(njs_opts_t *opts, njs
#ifndef NJS_FUZZER_TARGET
static njs_int_t
+njs_process_line(njs_opts_t *opts, njs_console_t *console,
+ const njs_str_t *script)
+{
+ u_char *start, *end;
+ njs_vm_t *vm;
+ njs_int_t ret;
+
+ vm = console->vm;
+ start = script->start;
+ end = start + script->length;
+
+ ret = njs_vm_compile(vm, &start, end);
+
+ if (ret == NJS_OK) {
+ if (start == end) {
+ ret = njs_vm_start(vm);
+
+ } else {
+ njs_vm_error(vm, "Extra characters at the end of the script");
+ ret = NJS_ERROR;
+ }
+ }
+
+ njs_output(opts, vm, ret);
+
+ if (!opts->interactive && ret == NJS_ERROR) {
+ return NJS_ERROR;
+ }
+
+ for ( ;; ) {
+ if (!njs_vm_pending(vm)) {
+ break;
+ }
+
+ ret = njs_process_events(console);
+ if (njs_slow_path(ret != NJS_OK)) {
+ njs_stderror("njs_process_events() failed\n");
+ ret = NJS_ERROR;
+ break;
+ }
+
+ if (njs_vm_waiting(vm) && !njs_vm_posted(vm)) {
+ /*TODO: async events. */
+
+ njs_stderror("njs_process_script(): async events unsupported\n");
+ ret = NJS_ERROR;
+ break;
+ }
+
+ ret = njs_vm_run(vm);
+
+ if (ret == NJS_ERROR) {
+ njs_output(opts, vm, ret);
+
+ if (!opts->interactive) {
+ return NJS_ERROR;
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+static njs_int_t
njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options)
{
njs_vm_t *vm;
@@ -938,7 +1003,7 @@ njs_interactive_shell(njs_opts_t *opts,
if (line.length != 0) {
add_history((char *) line.start);
- njs_process_script(opts, vm_options->external, &line);
+ njs_process_line(opts, vm_options->external, &line);
}
/* editline allocs a new buffer every time. */
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_string.c
--- a/src/njs_string.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_string.c Mon Apr 12 10:05:59 2021 +0300
@@ -4788,145 +4788,6 @@ uri_error:
}
-static njs_int_t
-njs_values_hash_test(njs_lvlhsh_query_t *lhq, void *data)
-{
- njs_str_t string;
- njs_value_t *value;
-
- value = data;
-
- if (njs_is_string(value)) {
- njs_string_get(value, &string);
-
- } else {
- string.start = (u_char *) value;
- string.length = sizeof(njs_value_t);
- }
-
- if (lhq->key.length == string.length
- && memcmp(lhq->key.start, string.start, string.length) == 0)
- {
- return NJS_OK;
- }
-
- return NJS_DECLINED;
-}
-
-
-static const njs_lvlhsh_proto_t njs_values_hash_proto
- njs_aligned(64) =
-{
- NJS_LVLHSH_DEFAULT,
- njs_values_hash_test,
- njs_lvlhsh_alloc,
- njs_lvlhsh_free,
-};
-
-
-/*
- * Constant values such as njs_value_true are copied to values_hash during
- * code generation when they are used as operands to guarantee aligned value.
- */
-
-njs_index_t
-njs_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime)
-{
- u_char *start;
- uint32_t value_size, size, length;
- njs_int_t ret;
- njs_str_t str;
- njs_bool_t long_string;
- njs_value_t *value;
- njs_string_t *string;
- njs_lvlhsh_t *values_hash;
- njs_lvlhsh_query_t lhq;
-
- long_string = 0;
- value_size = sizeof(njs_value_t);
-
- if (njs_is_string(src)) {
- njs_string_get(src, &str);
-
- size = (uint32_t) str.length;
- start = str.start;
-
- if (src->short_string.size == NJS_STRING_LONG) {
- long_string = 1;
- }
-
- } else {
- size = value_size;
- start = (u_char *) src;
- }
-
- lhq.key_hash = njs_djb_hash(start, size);
- lhq.key.length = size;
- lhq.key.start = start;
- lhq.proto = &njs_values_hash_proto;
-
- if (njs_lvlhsh_find(&vm->shared->values_hash, &lhq) == NJS_OK) {
- value = lhq.value;
-
- } else if (runtime && njs_lvlhsh_find(&vm->values_hash, &lhq) == NJS_OK) {
- value = lhq.value;
-
- } else {
- if (long_string) {
- length = src->long_string.data->length;
-
- if (size != length && length > NJS_STRING_MAP_STRIDE) {
- size = njs_string_map_offset(size)
- + njs_string_map_size(length);
- }
-
- value_size += sizeof(njs_string_t) + size;
- }
-
- value = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), value_size);
- if (njs_slow_path(value == NULL)) {
- return NJS_INDEX_NONE;
- }
-
- *value = *src;
-
- if (long_string) {
- string = (njs_string_t *) ((u_char *) value + sizeof(njs_value_t));
- value->long_string.data = string;
-
- string->start = (u_char *) string + sizeof(njs_string_t);
- string->length = src->long_string.data->length;
- string->retain = 0xffff;
-
- memcpy(string->start, start, size);
- }
-
- lhq.replace = 0;
- lhq.value = value;
- lhq.pool = vm->mem_pool;
-
- values_hash = runtime ? &vm->values_hash : &vm->shared->values_hash;
-
- ret = njs_lvlhsh_insert(values_hash, &lhq);
-
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_INDEX_NONE;
- }
- }
-
- if (start != (u_char *) src) {
- /*
- * The source node value must be updated with the shared value
- * allocated from the permanent memory pool because the node
- * value can be used as a variable initial value.
- */
- *(njs_value_t *) src = *value;
- }
-
- return (njs_index_t) value;
-}
-
-
const njs_object_type_init_t njs_string_type_init = {
.constructor = njs_native_ctor(njs_string_constructor, 1, 0),
.constructor_props = &njs_string_constructor_init,
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_string.h
--- a/src/njs_string.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_string.h Mon Apr 12 10:05:59 2021 +0300
@@ -237,9 +237,6 @@ njs_int_t njs_string_encode_uri(njs_vm_t
njs_int_t njs_string_decode_uri(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t component);
-njs_index_t njs_value_index(njs_vm_t *vm, const njs_value_t *src,
- njs_uint_t runtime);
-
njs_int_t njs_string_prototype_concat(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused);
njs_int_t njs_string_get_substitution(njs_vm_t *vm, njs_value_t *matched,
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_value.h
--- a/src/njs_value.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_value.h Mon Apr 12 10:05:59 2021 +0300
@@ -267,12 +267,8 @@ struct njs_typed_array_s {
typedef struct {
- union {
- uint32_t count;
- njs_value_t values;
- } u;
-
- njs_value_t values[1];
+ njs_index_t index;
+ njs_value_t **values;
} njs_closure_t;
@@ -283,14 +279,6 @@ struct njs_function_s {
uint8_t args_count:4;
- /*
- * If "closure" is true njs_closure_t[] is available right after the
- * njs_function_t and njs_function_closures() may be used to access it.
- */
-
-#define njs_function_closures(function) \
- ((njs_closure_t **) ((u_char *) function + sizeof(njs_function_t)))
-
uint8_t closure:1;
uint8_t native:1;
uint8_t ctor:1;
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_variable.c
--- a/src/njs_variable.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_variable.c Mon Apr 12 10:05:59 2021 +0300
@@ -9,10 +9,8 @@
#include <njs_main.h>
-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);
+static njs_parser_scope_t *njs_variable_scope_find(njs_parser_t *parser,
+ njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type);
static njs_variable_t *njs_variable_alloc(njs_vm_t *vm, uintptr_t unique_id,
njs_variable_type_t type);
@@ -21,31 +19,16 @@ 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_variable_t *var;
+ njs_parser_scope_t *root;
- var = njs_variable_scope_add(parser, scope, unique_id, type);
- if (njs_slow_path(var == NULL)) {
+ root = njs_variable_scope_find(parser, scope, unique_id, type);
+ if (njs_slow_path(root == NULL)) {
+ njs_parser_ref_error(parser, "scope not found");
return NULL;
}
- if (type == NJS_VARIABLE_VAR && scope->type == NJS_SCOPE_BLOCK) {
- /* A "var" declaration is stored in function or global scope. */
- do {
- scope = scope->parent;
-
- var = njs_variable_scope_add(parser, scope, unique_id, type);
- if (njs_slow_path(var == NULL)) {
- return NULL;
- }
-
- } while (scope->type == NJS_SCOPE_BLOCK);
- }
-
- if (type == NJS_VARIABLE_FUNCTION) {
- var->type = type;
- }
-
- return var;
+ return njs_variable_scope_add(parser, root, scope, unique_id, type,
+ NJS_INDEX_NONE);
}
@@ -77,43 +60,133 @@ njs_variables_copy(njs_vm_t *vm, njs_rbt
}
-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_parser_scope_t *
+njs_variable_scope(njs_parser_scope_t *scope, uintptr_t unique_id,
+ njs_variable_t **retvar, njs_variable_type_t type)
+{
+ njs_variable_t *var;
+ njs_rbtree_node_t *node;
+ njs_parser_scope_t *prev;
+ njs_variable_node_t var_node;
+
+ prev = scope;
+ *retvar = NULL;
+
+ var_node.key = unique_id;
+
+ do {
+ node = njs_rbtree_find(&scope->variables, &var_node.node);
+
+ if (node != NULL) {
+ var = ((njs_variable_node_t *) node)->variable;
+ if (var->type == NJS_VARIABLE_VAR
+ || var->type == NJS_VARIABLE_FUNCTION)
+ {
+ *retvar = var;
+
+ return (type == NJS_VARIABLE_VAR) ? scope : prev;
+ }
+ }
+
+ if (scope->type == NJS_SCOPE_GLOBAL
+ || scope->type == NJS_SCOPE_FUNCTION)
+ {
+ return scope;
+ }
+
+ prev = scope;
+ scope = scope->parent;
+
+ } while (scope != NULL);
+
+ return NULL;
+}
+
+
+static njs_parser_scope_t *
+njs_variable_scope_find(njs_parser_t *parser, njs_parser_scope_t *scope,
+ uintptr_t unique_id, njs_variable_type_t type)
{
+ njs_bool_t module;
njs_variable_t *var;
- njs_rbtree_node_t *node;
- njs_variable_node_t var_node, *var_node_new;
+ 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;
+ }
+
+ if (type == NJS_VARIABLE_FUNCTION) {
+ root = scope;
+ }
+
+ if (var == NULL) {
+ return root;
+ }
+
+ if (var->original->type == NJS_SCOPE_BLOCK) {
+ if (type == NJS_VARIABLE_FUNCTION
+ || var->type == NJS_VARIABLE_FUNCTION)
+ {
+ if (var->original == root) {
+ goto failed;
+ }
+ }
+ }
+
+ if (type != NJS_VARIABLE_FUNCTION
+ && var->type != NJS_VARIABLE_FUNCTION)
+ {
+ return var->scope;
+ }
+
+ if (root != scope) {
+ return root;
+ }
+
+ module = parser->vm->options.module || scope->module;
+
+ if (module) {
+ if (type == NJS_VARIABLE_FUNCTION
+ || var->type == NJS_VARIABLE_FUNCTION)
+ {
+ goto failed;
+ }
+ }
+
+ return root;
+
+failed:
+
+ entry = njs_lexer_entry(unique_id);
+
+ njs_parser_syntax_error(parser, "\"%V\" has already been declared",
+ &entry->name);
+ return NULL;
+}
+
+
+njs_variable_t *
+njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope,
+ njs_parser_scope_t *original, uintptr_t unique_id,
+ njs_variable_type_t type, njs_index_t index)
+{
+ njs_variable_t *var;
+ njs_rbtree_node_t *node;
+ njs_parser_scope_t *root;
+ njs_variable_node_t var_node, *var_node_new;
+
var_node.key = unique_id;
node = njs_rbtree_find(&scope->variables, &var_node.node);
if (node != NULL) {
- var = ((njs_variable_node_t *) node)->variable;
-
- if (scope->module || scope->type == NJS_SCOPE_BLOCK) {
-
- if (type == NJS_VARIABLE_FUNCTION
- || var->type == NJS_VARIABLE_FUNCTION)
- {
- goto fail;
- }
- }
-
- if (scope->type == NJS_SCOPE_GLOBAL) {
-
- if (parser->vm->options.module) {
- if (type == NJS_VARIABLE_FUNCTION
- || var->type == NJS_VARIABLE_FUNCTION)
- {
- goto fail;
- }
- }
- }
-
- return var;
+ return ((njs_variable_node_t *) node)->variable;
}
var = njs_variable_alloc(parser->vm, unique_id, type);
@@ -121,6 +194,21 @@ njs_variable_scope_add(njs_parser_t *par
goto memory_error;
}
+ var->scope = scope;
+ var->index = index;
+ var->original = original;
+
+ if (index == NJS_INDEX_NONE) {
+ root = njs_function_scope(scope);
+ if (njs_slow_path(scope == NULL)) {
+ return NULL;
+ }
+
+ var->index = njs_scope_index(root->type, root->items,
+ NJS_SCOPE_TYPE_LOCAL);
+ root->items++;
+ }
+
var_node_new = njs_variable_node_alloc(parser->vm, var, unique_id);
if (njs_slow_path(var_node_new == NULL)) {
goto memory_error;
@@ -135,14 +223,6 @@ memory_error:
njs_memory_error(parser->vm);
return NULL;
-
-fail:
-
- entry = njs_lexer_entry(unique_id);
-
- njs_parser_syntax_error(parser, "\"%V\" has already been declared",
- &entry->name);
- return NULL;
}
@@ -204,191 +284,182 @@ njs_label_remove(njs_vm_t *vm, njs_parse
}
-njs_int_t
-njs_variable_reference(njs_vm_t *vm, njs_parser_scope_t *scope,
- njs_parser_node_t *node, uintptr_t unique_id, njs_reference_type_t type)
+static njs_bool_t
+njs_variable_closure_test(njs_parser_scope_t *root, njs_parser_scope_t *scope)
{
- njs_variable_reference_t *vr;
- njs_parser_rbtree_node_t *rb_node;
-
- vr = &node->u.reference;
-
- vr->unique_id = unique_id;
- vr->type = type;
-
- rb_node = njs_mp_alloc(vm->mem_pool, sizeof(njs_parser_rbtree_node_t));
- if (njs_slow_path(rb_node == NULL)) {
- return NJS_ERROR;
+ if (root == scope) {
+ return 0;
}
- rb_node->key = unique_id;
- rb_node->parser_node = node;
-
- njs_rbtree_insert(&scope->references, &rb_node->node);
-
- return NJS_OK;
-}
-
-
-static njs_int_t
-njs_variables_scope_resolve(njs_vm_t *vm, njs_parser_scope_t *scope,
- njs_bool_t closure)
-{
- njs_int_t ret;
- njs_queue_t *nested;
- njs_queue_link_t *lnk;
- njs_rbtree_node_t *rb_node;
- njs_parser_node_t *node;
- njs_parser_rbtree_node_t *parser_rb_node;
- njs_variable_reference_t *vr;
-
- nested = &scope->nested;
-
- for (lnk = njs_queue_first(nested);
- lnk != njs_queue_tail(nested);
- lnk = njs_queue_next(lnk))
- {
- scope = njs_queue_link_data(lnk, njs_parser_scope_t, link);
-
- ret = njs_variables_scope_resolve(vm, scope, closure);
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
+ do {
+ if (root->type == NJS_SCOPE_FUNCTION) {
+ return 1;
}
- rb_node = njs_rbtree_min(&scope->references);
-
- while (njs_rbtree_is_there_successor(&scope->references, rb_node)) {
- parser_rb_node = (njs_parser_rbtree_node_t *) rb_node;
- node = parser_rb_node->parser_node;
-
- if (node == NULL) {
- break;
- }
-
- vr = &node->u.reference;
-
- if (closure) {
- ret = njs_variable_reference_resolve(vm, vr, node->scope);
- if (njs_slow_path(ret != NJS_OK)) {
- goto next;
- }
-
- if (vr->scope_index == NJS_SCOPE_INDEX_LOCAL) {
- goto next;
- }
- }
-
- (void) njs_variable_resolve(vm, node);
-
- next:
-
- rb_node = njs_rbtree_node_successor(&scope->references, rb_node);
- }
- }
-
- return NJS_OK;
-}
-
+ root = root->parent;
-njs_int_t
-njs_variables_scope_reference(njs_vm_t *vm, njs_parser_scope_t *scope)
-{
- njs_int_t ret;
-
- /*
- * Calculating proper scope types for variables.
- * A variable is considered to be local variable if it is referenced
- * only in the local scope (reference and definition nestings are the same).
- */
-
- ret = njs_variables_scope_resolve(vm, scope, 1);
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
- }
-
- ret = njs_variables_scope_resolve(vm, scope, 0);
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
- }
+ } while (root != scope);
- return NJS_OK;
-}
-
-
-njs_index_t
-njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node)
-{
- njs_variable_t *var;
-
- if (node->index != NJS_INDEX_NONE) {
- return node->index;
- }
-
- var = njs_variable_resolve(vm, node);
-
- if (njs_fast_path(var != NULL)) {
- return var->index;
- }
-
- return NJS_INDEX_NONE;
+ return 0;
}
njs_variable_t *
njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node)
{
- njs_int_t ret;
- njs_uint_t scope_index;
- njs_index_t index;
- njs_variable_t *var;
- njs_variable_reference_t *vr;
+ njs_rbtree_node_t *rb_node;
+ njs_parser_scope_t *scope;
+ njs_variable_node_t var_node;
+ njs_variable_reference_t *ref;
+
+ ref = &node->u.reference;
+ scope = node->scope;
+
+ var_node.key = ref->unique_id;
+
+ do {
+ rb_node = njs_rbtree_find(&scope->variables, &var_node.node);
+
+ if (rb_node != NULL) {
+ return ((njs_variable_node_t *) rb_node)->variable;
+ }
+
+ scope = scope->parent;
+
+ } while (scope != NULL);
+
+ return NULL;
+}
+
+
+static njs_index_t
+njs_variable_closure(njs_vm_t *vm, njs_variable_t *var,
+ njs_parser_scope_t *scope)
+{
+ njs_index_t index, prev_index, *idx;
+ njs_rbtree_node_t *rb_node;
+ njs_parser_scope_t **p, *root;
+ njs_parser_rbtree_node_t *parse_node, ref_node;
+ njs_parser_scope_t *list[128];
+
+ ref_node.key = var->unique_id;
+
+ p = list;
+
+ do {
+ if (scope->type == NJS_SCOPE_FUNCTION) {
+ *p++ = scope;
+ }
+
+ scope = scope->parent;
+
+ } while (scope != var->scope && scope->type != NJS_SCOPE_GLOBAL);
+
+ prev_index = var->index;
+
+ while (p != list) {
+ p--;
+
+ scope = *p;
+
+ rb_node = njs_rbtree_find(&scope->references, &ref_node.node);
+
+ parse_node = ((njs_parser_rbtree_node_t *) rb_node);
+
+ if (parse_node != NULL && p != list && parse_node->index != 0) {
+ prev_index = parse_node->index;
+ continue;
+ }
- vr = &node->u.reference;
+ root = njs_function_scope(scope);
+
+ /* Create new closure for scope. */
+ index = njs_scope_index(root->type, root->closures->items,
+ NJS_SCOPE_TYPE_CLOSURE);
+ if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+ return NJS_INDEX_ERROR;
+ }
+
+ idx = njs_arr_add(root->closures);
+ if (njs_slow_path(idx == NULL)) {
+ return NJS_INDEX_ERROR;
+ }
+
+ *idx = prev_index;
+
+ if (parse_node == NULL) {
+ /* Create new reference for closure. */
+
+ parse_node = njs_mp_alloc(vm->mem_pool,
+ sizeof(njs_parser_rbtree_node_t));
+ if (njs_slow_path(parse_node == NULL)) {
+ return NJS_INDEX_ERROR;
+ }
+
+ parse_node->key = var->unique_id;
+
+ njs_rbtree_insert(&scope->references, &parse_node->node);
+ }
+
+ parse_node->index = index;
- ret = njs_variable_reference_resolve(vm, vr, node->scope);
+ prev_index = index;
+ }
+
+ return prev_index;
+}
+
+
+njs_variable_t *
+njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node)
+{
+ njs_rbtree_node_t *rb_node;
+ njs_parser_scope_t *scope;
+ njs_parser_rbtree_node_t *parse_node, ref_node;
+ njs_variable_reference_t *ref;
- if (njs_slow_path(ret != NJS_OK)) {
- node->u.reference.not_defined = 1;
+ ref = &node->u.reference;
+ scope = node->scope;
+
+ if (ref->variable == NULL) {
+ ref->variable = njs_variable_resolve(vm, node);
+ if (njs_slow_path(ref->variable == NULL)) {
+ ref->not_defined = 1;
+
+ return NULL;
+ }
+ }
+
+ ref->closure = njs_variable_closure_test(node->scope, ref->variable->scope);
+ ref->scope = node->scope;
+
+ ref_node.key = ref->unique_id;
+
+ rb_node = njs_rbtree_find(&scope->references, &ref_node.node);
+ if (njs_slow_path(rb_node == NULL)) {
return NULL;
}
- scope_index = vr->scope_index;
-
- var = vr->variable;
- index = var->index;
-
- if (index != NJS_INDEX_NONE) {
-
- if (scope_index == NJS_SCOPE_INDEX_LOCAL
- || njs_scope_type(index) != NJS_SCOPE_ARGUMENTS)
- {
- node->index = index;
+ parse_node = ((njs_parser_rbtree_node_t *) rb_node);
- return var;
- }
-
- vr->scope->argument_closures++;
- index = (index >> NJS_SCOPE_SHIFT) + 1;
+ if (parse_node->index != NJS_INDEX_NONE) {
+ node->index = parse_node->index;
- if (index > 255 || vr->scope->argument_closures == 0) {
- njs_internal_error(vm, "too many argument closures");
-
- return NULL;
- }
-
- var->argument = index;
+ return ref->variable;
}
- index = njs_scope_next_index(vm, vr->scope, scope_index, &var->value);
+ if (!ref->closure) {
+ node->index = ref->variable->index;
- if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+ return ref->variable;
+ }
+
+ node->index = njs_variable_closure(vm, ref->variable, scope);
+ if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
return NULL;
}
- var->index = index;
- node->index = index;
-
- return var;
+ return ref->variable;
}
@@ -415,120 +486,6 @@ njs_label_find(njs_vm_t *vm, njs_parser_
}
-static njs_int_t
-njs_variable_reference_resolve(njs_vm_t *vm, njs_variable_reference_t *vr,
- njs_parser_scope_t *node_scope)
-{
- njs_rbtree_node_t *node;
- njs_parser_scope_t *scope, *previous;
- njs_variable_node_t var_node;
-
- var_node.key = vr->unique_id;
-
- scope = node_scope;
- previous = NULL;
-
- for ( ;; ) {
- node = njs_rbtree_find(&scope->variables, &var_node.node);
-
- if (node != NULL) {
- vr->variable = ((njs_variable_node_t *) node)->variable;
-
- if (scope->type == NJS_SCOPE_BLOCK
- && vr->variable->type == NJS_VARIABLE_VAR)
- {
- scope = scope->parent;
- continue;
- }
-
- if (scope->type == NJS_SCOPE_SHIM) {
- scope = previous;
-
- } else {
- /*
- * Variables declared in a block with "let" or "const"
- * keywords are actually stored in function or global scope.
- */
- while (scope->type == NJS_SCOPE_BLOCK) {
- scope = scope->parent;
- }
- }
-
- vr->scope = scope;
-
- vr->scope_index = NJS_SCOPE_INDEX_LOCAL;
-
- if (vr->scope->type > NJS_SCOPE_GLOBAL
- && node_scope->nesting != vr->scope->nesting)
- {
- vr->scope_index = NJS_SCOPE_INDEX_CLOSURE;
- }
-
- return NJS_OK;
- }
-
- if (scope->parent == NULL) {
- /* A global scope. */
- vr->scope = scope;
-
- return NJS_DECLINED;
- }
-
- previous = scope;
- scope = scope->parent;
- }
-}
-
-
-njs_index_t
-njs_scope_next_index(njs_vm_t *vm, njs_parser_scope_t *scope,
- njs_uint_t scope_index, const njs_value_t *default_value)
-{
- njs_arr_t *values;
- njs_index_t index;
- njs_value_t *value;
-
- if (njs_scope_accumulative(vm, scope)) {
- /*
- * When non-clonable VM runs in accumulative mode all
- * global variables should be allocated in absolute scope
- * to share them among consecutive VM invocations.
- */
- value = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
- sizeof(njs_value_t));
- if (njs_slow_path(value == NULL)) {
- return NJS_INDEX_ERROR;
- }
-
- index = (njs_index_t) value;
-
- } else {
- values = scope->values[scope_index];
-
- if (values == NULL) {
- values = njs_arr_create(vm->mem_pool, 4, sizeof(njs_value_t));
- if (njs_slow_path(values == NULL)) {
- return NJS_INDEX_ERROR;
- }
-
- scope->values[scope_index] = values;
- }
-
- value = njs_arr_add(values);
- if (njs_slow_path(value == NULL)) {
- return NJS_INDEX_ERROR;
- }
-
- index = scope->next_index[scope_index];
- scope->next_index[scope_index] += sizeof(njs_value_t);
- }
-
- *value = *default_value;
-
- return index;
-}
-
-
static njs_variable_t *
njs_variable_alloc(njs_vm_t *vm, uintptr_t unique_id, njs_variable_type_t type)
{
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_variable.h
--- a/src/njs_variable.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_variable.h Mon Apr 12 10:05:59 2021 +0300
@@ -11,9 +11,8 @@
typedef enum {
NJS_VARIABLE_CONST = 0,
NJS_VARIABLE_LET,
+ NJS_VARIABLE_VAR,
NJS_VARIABLE_CATCH,
- NJS_VARIABLE_SHIM,
- NJS_VARIABLE_VAR,
NJS_VARIABLE_FUNCTION,
} njs_variable_type_t;
@@ -22,9 +21,14 @@ typedef struct {
uintptr_t unique_id;
njs_variable_type_t type:8; /* 3 bits */
- uint8_t argument;
- uint8_t this_object;
- uint8_t arguments_object;
+ njs_bool_t argument;
+ njs_bool_t arguments_object;
+ njs_bool_t init;
+ njs_bool_t have_lambda;
+ njs_bool_t self;
+
+ njs_parser_scope_t *scope;
+ njs_parser_scope_t *original;
njs_index_t index;
njs_value_t value;
@@ -43,8 +47,8 @@ typedef struct {
uintptr_t unique_id;
njs_variable_t *variable;
njs_parser_scope_t *scope;
- njs_uint_t scope_index; /* NJS_SCOPE_INDEX_... */
njs_bool_t not_defined;
+ njs_bool_t closure;
} njs_variable_reference_t;
@@ -65,12 +69,10 @@ njs_variable_t *njs_label_find(njs_vm_t
uintptr_t unique_id);
njs_int_t njs_label_remove(njs_vm_t *vm, njs_parser_scope_t *scope,
uintptr_t unique_id);
-njs_int_t njs_variable_reference(njs_vm_t *vm, njs_parser_scope_t *scope,
- njs_parser_node_t *node, uintptr_t unique_id, njs_reference_type_t type);
-njs_int_t njs_variables_scope_reference(njs_vm_t *vm,
- njs_parser_scope_t *scope);
-njs_index_t njs_scope_next_index(njs_vm_t *vm, njs_parser_scope_t *scope,
- njs_uint_t scope_index, const njs_value_t *default_value);
+njs_variable_t *njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node);
+njs_variable_t *njs_variable_scope_add(njs_parser_t *parser,
+ njs_parser_scope_t *scope, njs_parser_scope_t *original,
+ uintptr_t unique_id, njs_variable_type_t type, njs_index_t index);
njs_int_t njs_name_copy(njs_vm_t *vm, njs_str_t *dst, const njs_str_t *src);
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_vm.c
--- a/src/njs_vm.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_vm.c Mon Apr 12 10:05:59 2021 +0300
@@ -81,6 +81,16 @@ njs_vm_create(njs_vm_opt_t *options)
vm->symbol_generator = NJS_SYMBOL_KNOWN_MAX;
+ vm->level_absolute = njs_arr_create(vm->mem_pool, 512,
+ sizeof(njs_value_t *));
+ if (njs_slow_path(vm->level_absolute == NULL)) {
+ return NULL;
+ }
+
+ vm->level[NJS_SCOPE_TYPE_STATIC] = vm->level_absolute->start;
+
+ (void) njs_scope_undefined_index(vm, 0);
+
return vm;
}
@@ -116,15 +126,21 @@ njs_vm_destroy(njs_vm_t *vm)
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;
- njs_vm_code_t *code;
- njs_generator_t generator;
+ njs_int_t ret;
+ njs_str_t ast;
+ njs_chb_t chain;
+ njs_value_t **global, **new;
+ njs_lexer_t lexer;
+ njs_parser_t parser;
+ njs_vm_code_t *code;
+ njs_generator_t generator;
+ njs_parser_scope_t *scope;
- if (vm->modules != NULL && vm->options.accumulative) {
+ njs_memzero(&parser, sizeof(njs_parser_t));
+
+ parser.scope = vm->global_scope;
+
+ if (parser.scope != NULL && vm->modules != NULL) {
njs_module_reset(vm);
}
@@ -133,25 +149,19 @@ njs_vm_compile(njs_vm_t *vm, u_char **st
return NJS_ERROR;
}
- njs_memzero(&parser, sizeof(njs_parser_t));
-
parser.lexer = &lexer;
- ret = njs_parser(vm, &parser, vm->variables_hash);
+ ret = njs_parser(vm, &parser);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
*start = lexer.start;
-
- ret = njs_variables_scope_reference(vm, parser.scope);
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
- }
+ scope = parser.scope;
njs_memzero(&generator, sizeof(njs_generator_t));
- code = njs_generate_scope(vm, &generator, parser.scope, &njs_entry_main);
+ code = njs_generate_scope(vm, &generator, scope, &njs_entry_main);
if (njs_slow_path(code == NULL)) {
if (!njs_is_error(&vm->retval)) {
njs_internal_error(vm, "njs_generate_scope() failed");
@@ -160,17 +170,55 @@ njs_vm_compile(njs_vm_t *vm, u_char **st
return NJS_ERROR;
}
- vm->start = generator.code_start;
- vm->global_scope = generator.local_scope;
- vm->scope_size = generator.scope_size;
+ vm->global_scope = scope;
- vm->variables_hash = &parser.scope->variables;
+ if (scope->items > vm->global_items) {
+ global = vm->level[NJS_SCOPE_TYPE_GLOBAL];
- if (vm->options.init && !vm->options.accumulative) {
- ret = njs_vm_init(vm);
- if (njs_slow_path(ret != NJS_OK)) {
+ new = njs_scope_make(vm, scope->items);
+ if (njs_slow_path(new == NULL)) {
return ret;
}
+
+ vm->level[NJS_SCOPE_TYPE_GLOBAL] = new;
+
+ if (global != NULL) {
+ while (vm->global_items != 0) {
+ vm->global_items--;
+
+ *new++ = *global++;
+ }
+
+ njs_mp_free(vm->mem_pool, global);
+ }
+ }
+
+ /* globalThis and this */
+ njs_scope_value_set(vm, njs_scope_global_this_index(), &vm->global_value);
+
+ vm->start = generator.code_start;
+ vm->variables_hash = &scope->variables;
+ vm->global_items = scope->items;
+
+ if (vm->temp != NULL) {
+ njs_mp_free(vm->mem_pool, vm->temp);
+ vm->temp = NULL;
+ }
+
+ vm->level[NJS_SCOPE_TYPE_TEMP] = NULL;
+
+ if (scope->temp != 0) {
+ new = njs_scope_make(vm, scope->temp);
+ if (njs_slow_path(new == NULL)) {
+ return ret;
+ }
+
+ vm->level[NJS_SCOPE_TYPE_TEMP] = new;
+ }
+
+ if (vm->active_frame != NULL) {
+ vm->top_frame = &vm->active_frame->native;
+ vm->active_frame->native.temp = vm->temp;
}
if (vm->options.disassemble) {
@@ -201,13 +249,14 @@ njs_vm_compile(njs_vm_t *vm, u_char **st
njs_vm_t *
njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external)
{
- njs_mp_t *nmp;
- njs_vm_t *nvm;
- njs_int_t ret;
+ njs_mp_t *nmp;
+ njs_vm_t *nvm;
+ njs_int_t ret;
+ njs_frame_t *frame;
njs_thread_log_debug("CLONE:");
- if (vm->options.accumulative) {
+ if (vm->options.interactive) {
return NULL;
}
@@ -227,11 +276,30 @@ njs_vm_clone(njs_vm_t *vm, njs_external_
nvm->trace.data = nvm;
nvm->external = external;
+ frame = njs_mp_align(nvm->mem_pool, sizeof(njs_value_t), NJS_FRAME_SIZE);
+ if (njs_slow_path(frame == NULL)) {
+ return NULL;
+ }
+
+ njs_memzero(frame, NJS_FRAME_SIZE);
+
+ nvm->active_frame = frame;
+
ret = njs_vm_init(nvm);
if (njs_slow_path(ret != NJS_OK)) {
goto fail;
}
+ nvm->level_absolute = vm->level_absolute;
+ nvm->temp = vm->temp;
+
+ njs_set_object(&nvm->global_value, &nvm->global_object);
+
+ /* globalThis and this */
+ njs_scope_value_set(nvm, njs_scope_global_this_index(), &nvm->global_value);
+
+ nvm->level[NJS_SCOPE_TYPE_LOCAL] = NULL;
+
return nvm;
fail:
@@ -245,53 +313,36 @@ fail:
static njs_int_t
njs_vm_init(njs_vm_t *vm)
{
- size_t size, scope_size;
- u_char *values;
njs_int_t ret;
- njs_value_t *global;
njs_frame_t *frame;
- scope_size = vm->scope_size + NJS_INDEX_GLOBAL_OFFSET;
-
- size = njs_frame_size(0) + scope_size + NJS_FRAME_SPARE_SIZE;
- size = njs_align_size(size, NJS_FRAME_SPARE_SIZE);
-
- frame = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), size);
+ frame = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), NJS_FRAME_SIZE);
if (njs_slow_path(frame == NULL)) {
return NJS_ERROR;
}
- njs_memzero(frame, njs_frame_size(0));
-
- vm->top_frame = &frame->native;
- vm->active_frame = frame;
-
- frame->native.size = size;
- frame->native.free_size = size - (njs_frame_size(0) + scope_size);
+ njs_memzero(frame, NJS_FRAME_SIZE);
- values = (u_char *) frame + njs_frame_size(0);
-
- frame->native.free = values + scope_size;
+ vm->active_frame = frame;
+ vm->top_frame = &frame->native;
- vm->scopes[NJS_SCOPE_GLOBAL] = (njs_value_t *) values;
-
- memcpy(values + NJS_INDEX_GLOBAL_OFFSET, vm->global_scope, vm->scope_size);
+ if (vm->temp != NULL) {
+ vm->active_frame->native.temp = vm->temp;
+ }
ret = njs_regexp_init(vm);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
- global = (njs_value_t *) (values + NJS_INDEX_GLOBAL_OBJECT_OFFSET);
-
- ret = njs_builtin_objects_clone(vm, global);
+ ret = njs_builtin_objects_clone(vm, &vm->global_value);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
njs_lvlhsh_init(&vm->modules_hash);
+ njs_lvlhsh_init(&vm->events_hash);
- njs_lvlhsh_init(&vm->events_hash);
njs_queue_init(&vm->posted_events);
njs_queue_init(&vm->promise_events);
@@ -303,13 +354,13 @@ njs_int_t
njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
njs_uint_t nargs)
{
- return njs_vm_invoke(vm, function, args, nargs, (njs_index_t) &vm->retval);
+ return njs_vm_invoke(vm, function, args, nargs, &vm->retval);
}
njs_int_t
njs_vm_invoke(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
- njs_uint_t nargs, njs_index_t retval)
+ njs_uint_t nargs, njs_value_t *retval)
{
njs_int_t ret;
@@ -327,55 +378,17 @@ void
njs_vm_scopes_restore(njs_vm_t *vm, njs_native_frame_t *native,
njs_native_frame_t *previous)
{
- njs_uint_t n, nesting;
- njs_value_t *args;
- njs_frame_t *frame;
- njs_closure_t **closures;
- njs_function_t *function;
+ njs_frame_t *frame;
vm->top_frame = previous;
- args = previous->arguments;
- function = previous->function;
-
- if (function != NULL) {
- args += function->args_offset;
- }
-
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = args;
-
- function = native->function;
-
- if (function->native) {
+ if (native->function->native) {
return;
}
- if (function->closure) {
- /* GC: release function closures. */
- }
-
frame = (njs_frame_t *) native;
frame = frame->previous_active_frame;
vm->active_frame = frame;
-
- /* GC: arguments, local, and local block closures. */
-
- vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments;
- vm->scopes[NJS_SCOPE_LOCAL] = frame->local;
-
- function = frame->native.function;
-
- nesting = (function != NULL) ? function->u.lambda->nesting : 0;
- closures = njs_frame_closures(frame);
-
- for (n = 0; n <= nesting; n++) {
- vm->scopes[NJS_SCOPE_CLOSURE + n] = &closures[n]->u.values;
- }
-
- while (n < NJS_MAX_NESTING) {
- vm->scopes[NJS_SCOPE_CLOSURE + n] = NULL;
- n++;
- }
}
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_vm.h
--- a/src/njs_vm.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_vm.h Mon Apr 12 10:05:59 2021 +0300
@@ -8,7 +8,7 @@
#define _NJS_VM_H_INCLUDED_
-#define NJS_MAX_STACK_SIZE (256 * 1024)
+#define NJS_MAX_STACK_SIZE 512
/*
@@ -29,49 +29,12 @@ typedef struct njs_generator_s nj
typedef enum {
- NJS_SCOPE_ABSOLUTE = 0,
- NJS_SCOPE_GLOBAL = 1,
- NJS_SCOPE_CALLEE_ARGUMENTS = 2,
- /*
- * The argument and local VM scopes should be separated because a
- * function may be called with any number of arguments.
- */
- NJS_SCOPE_ARGUMENTS = 3,
- NJS_SCOPE_LOCAL = 4,
- NJS_SCOPE_FUNCTION = NJS_SCOPE_LOCAL,
-
- NJS_SCOPE_CLOSURE = 5,
- /*
- * The block and shim scopes are not really VM scopes.
- * They are used only on parsing phase.
- */
- NJS_SCOPE_BLOCK = 16,
- NJS_SCOPE_SHIM = 17,
+ NJS_SCOPE_GLOBAL = 0,
+ NJS_SCOPE_FUNCTION,
+ NJS_SCOPE_BLOCK
} njs_scope_t;
-/*
- * The maximum possible function nesting level is (16 - NJS_SCOPE_CLOSURE),
- * that is 11. The 8 is reasonable limit.
- */
-#define NJS_MAX_NESTING 8
-
-#define NJS_SCOPES (NJS_SCOPE_CLOSURE + NJS_MAX_NESTING)
-
-#define NJS_SCOPE_SHIFT 4
-#define NJS_SCOPE_MASK ((uintptr_t) ((1 << NJS_SCOPE_SHIFT) - 1))
-
-#define NJS_INDEX_NONE ((njs_index_t) 0)
-#define NJS_INDEX_ERROR ((njs_index_t) -1)
-#define NJS_INDEX_THIS ((njs_index_t) (0 | NJS_SCOPE_ARGUMENTS))
-
-#define njs_scope_type(index) \
- ((uintptr_t) (index) & NJS_SCOPE_MASK)
-
-#define njs_is_callee_argument_index(index) \
- (((index) & NJS_SCOPE_CALLEE_ARGUMENTS) == NJS_SCOPE_CALLEE_ARGUMENTS)
-
-
typedef enum {
NJS_OBJ_TYPE_OBJECT = 0,
NJS_OBJ_TYPE_ARRAY,
@@ -149,30 +112,7 @@ enum njs_object_e {
};
-#define njs_scope_index(value, type) \
- ((njs_index_t) (((value) << NJS_SCOPE_SHIFT) | (type)))
-
-
-#define njs_global_scope_index(value) \
- (njs_scope_index(value, NJS_SCOPE_GLOBAL))
-
-
-#define NJS_INDEX_GLOBAL_OBJECT njs_global_scope_index(0)
-#define NJS_INDEX_GLOBAL_OBJECT_OFFSET njs_scope_index(0, 0)
-
-
-#define NJS_INDEX_GLOBAL_RETVAL njs_global_scope_index(1)
-#define NJS_INDEX_GLOBAL_OFFSET njs_scope_index(1, 0)
-
-
-#define njs_scope_offset(index) \
- ((uintptr_t) (index) & ~NJS_SCOPE_MASK)
-
-
-#define njs_vmcode_operand(vm, index) \
- ((njs_value_t *) \
- ((u_char *) vm->scopes[(uintptr_t) (index) & NJS_SCOPE_MASK] \
- + njs_scope_offset(index)))
+#define NJS_SCOPE_VALUE_MAX ((1 << (32 - NJS_SCOPE_VALUE)) - 1)
enum njs_hook_e {
@@ -181,14 +121,33 @@ enum njs_hook_e {
};
+typedef enum {
+ NJS_SCOPE_TYPE_LOCAL = 0,
+ NJS_SCOPE_TYPE_CLOSURE,
+ NJS_SCOPE_TYPE_GLOBAL,
+ NJS_SCOPE_TYPE_ARGUMENTS,
+ NJS_SCOPE_TYPE_STATIC,
+ NJS_SCOPE_TYPE_TEMP,
+ NJS_SCOPE_TYPE_LAST_ENTRY
+} njs_scope_type_t;
+
+
struct njs_vm_s {
/* njs_vm_t must be aligned to njs_value_t due to scratch value. */
njs_value_t retval;
njs_arr_t *paths;
njs_arr_t *protos;
+ njs_arr_t values;
- njs_value_t *scopes[NJS_SCOPES];
+ njs_arr_t *level_absolute;
+ njs_value_t **level[NJS_SCOPE_TYPE_LAST_ENTRY];
+ unsigned nlevel;
+ size_t global_items;
+
+ njs_value_t **temp;
+
+ njs_arr_t *closures;
njs_external_ptr_t external;
@@ -221,8 +180,6 @@ struct njs_vm_s {
njs_mp_t *mem_pool;
u_char *start;
- njs_value_t *global_scope;
- size_t scope_size;
size_t stack_size;
njs_vm_shared_t *shared;
@@ -232,6 +189,8 @@ struct njs_vm_s {
njs_array_t *promise_reason;
+ njs_parser_scope_t *global_scope;
+
/*
* MemoryError is statically allocated immutable Error object
* with the InternalError prototype.
@@ -240,6 +199,7 @@ struct njs_vm_s {
njs_object_t string_object;
njs_object_t global_object;
+ njs_value_t global_value;
njs_arr_t *codes; /* of njs_vm_code_t */
njs_arr_t *functions_name_cache;
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_vmcode.c
--- a/src/njs_vmcode.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_vmcode.c Mon Apr 12 10:05:59 2021 +0300
@@ -16,12 +16,13 @@ struct njs_property_next_s {
static njs_jump_off_t njs_vmcode_object(njs_vm_t *vm);
static njs_jump_off_t njs_vmcode_array(njs_vm_t *vm, u_char *pc);
static njs_jump_off_t njs_vmcode_function(njs_vm_t *vm, u_char *pc);
+static njs_value_t *njs_vmcode_function_create(njs_vm_t *vm,
+ njs_function_lambda_t *lambda, njs_index_t retval);
static njs_jump_off_t njs_vmcode_arguments(njs_vm_t *vm, u_char *pc);
static njs_jump_off_t njs_vmcode_regexp(njs_vm_t *vm, u_char *pc);
static njs_jump_off_t njs_vmcode_template_literal(njs_vm_t *vm,
njs_value_t *inlvd1, njs_value_t *inlvd2);
-static njs_jump_off_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *invld);
+static njs_jump_off_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value);
static njs_jump_off_t njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value,
njs_value_t *key, njs_value_t *retval);
@@ -67,6 +68,10 @@ static njs_jump_off_t njs_function_frame
njs_value_t *value, const njs_value_t *this, uintptr_t nargs,
njs_bool_t ctor);
+
+#define njs_vmcode_operand(vm, index) njs_scope_valid_value(vm, index)
+
+
/*
* The nJSVM is optimized for an ABIs where the first several arguments
* are passed in registers (AMD64, ARM32/64): two pointers to the operand
@@ -89,10 +94,11 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_c
njs_value_t numeric1, numeric2, primitive1, primitive2;
njs_frame_t *frame;
njs_jump_off_t ret;
- njs_vmcode_this_t *this;
njs_native_frame_t *previous, *native;
njs_property_next_t *next;
+ njs_vmcode_finally_t *finally;
njs_vmcode_generic_t *vmcode;
+ njs_vmcode_move_arg_t *move_arg;
njs_vmcode_prop_get_t *get;
njs_vmcode_prop_set_t *set;
njs_vmcode_operation_t op;
@@ -102,6 +108,7 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_c
njs_vmcode_try_return_t *try_return;
njs_vmcode_method_frame_t *method_frame;
njs_vmcode_prop_accessor_t *accessor;
+ njs_vmcode_try_trampoline_t *try_trampoline;
njs_vmcode_function_frame_t *function_frame;
next:
@@ -136,8 +143,9 @@ next:
/* Fall through. */
- case NJS_VMCODE_2OPERANDS:
- value1 = njs_vmcode_operand(vm, vmcode->operand2);
+ case NJS_VMCODE_2OPERANDS: {
+ value1 = njs_vmcode_operand(vm, vmcode->operand2);
+ }
}
op = vmcode->code.operation;
@@ -339,6 +347,7 @@ next:
ret ^= op - NJS_VMCODE_EQUAL;
retval = njs_vmcode_operand(vm, vmcode->operand1);
+
njs_set_boolean(retval, ret);
pc += sizeof(njs_vmcode_3addr_t);
@@ -374,8 +383,8 @@ next:
}
num = njs_number(value1);
+ retval = njs_vmcode_operand(vm, vmcode->operand1);
- retval = njs_vmcode_operand(vm, vmcode->operand1);
pc += sizeof(njs_vmcode_3addr_t);
switch (op) {
@@ -462,7 +471,9 @@ next:
goto next;
case NJS_VMCODE_OBJECT_COPY:
- ret = njs_vmcode_object_copy(vm, value1, value2);
+ value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
+
+ ret = njs_vmcode_object_copy(vm, value1);
break;
case NJS_VMCODE_TEMPLATE_LITERAL:
@@ -494,6 +505,7 @@ next:
ret ^= op - NJS_VMCODE_STRICT_EQUAL;
retval = njs_vmcode_operand(vm, vmcode->operand1);
+
njs_set_boolean(retval, ret);
pc += sizeof(njs_vmcode_3addr_t);
@@ -557,6 +569,7 @@ next:
case NJS_VMCODE_LOGICAL_NOT:
retval = njs_vmcode_operand(vm, vmcode->operand1);
+
njs_set_boolean(retval, !njs_is_true(value1));
pc += sizeof(njs_vmcode_2addr_t);
@@ -609,13 +622,15 @@ next:
}
retval = njs_vmcode_operand(vm, vmcode->operand1);
+
njs_release(vm, retval);
*retval = vm->retval;
} else {
switch (op) {
case NJS_VMCODE_STOP:
- value2 = njs_vmcode_operand(vm, value2);
+ value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
+
vm->retval = *value2;
return NJS_OK;
@@ -682,6 +697,7 @@ next:
case NJS_VMCODE_PROPERTY_INIT:
set = (njs_vmcode_prop_set_t *) pc;
retval = njs_vmcode_operand(vm, set->value);
+
ret = njs_vmcode_property_init(vm, value1, value2, retval);
if (njs_slow_path(ret == NJS_ERROR)) {
goto error;
@@ -690,33 +706,22 @@ next:
break;
case NJS_VMCODE_RETURN:
- value2 = njs_vmcode_operand(vm, value2);
-
+ value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
frame = (njs_frame_t *) vm->top_frame;
if (frame->native.ctor) {
if (njs_is_object(value2)) {
- njs_release(vm, vm->scopes[NJS_SCOPE_ARGUMENTS]);
+ njs_release(vm, frame->native.local[0]);
} else {
- value2 = vm->scopes[NJS_SCOPE_ARGUMENTS];
+ value2 = frame->native.local[0];
}
}
previous = njs_function_previous_frame(&frame->native);
-
njs_vm_scopes_restore(vm, &frame->native, previous);
- /*
- * If a retval is in a callee arguments scope it
- * must be in the previous callee arguments scope.
- */
- retval = njs_vmcode_operand(vm, frame->native.retval);
-
- /*
- * GC: value external/internal++ depending on
- * value and retval type
- */
+ retval = frame->native.retval;
*retval = *value2;
njs_function_frame_free(vm, &frame->native);
@@ -775,7 +780,9 @@ next:
case NJS_VMCODE_FUNCTION_CALL:
vm->active_frame->native.pc = pc;
- ret = njs_function_frame_invoke(vm, (njs_index_t) value2);
+ value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
+
+ ret = njs_function_frame_invoke(vm, value2);
if (njs_slow_path(ret == NJS_ERROR)) {
goto error;
}
@@ -801,16 +808,6 @@ next:
ret = sizeof(njs_vmcode_prop_next_t);
break;
- case NJS_VMCODE_THIS:
- frame = vm->active_frame;
- this = (njs_vmcode_this_t *) pc;
-
- retval = njs_vmcode_operand(vm, this->dst);
- *retval = frame->native.arguments[0];
-
- ret = sizeof(njs_vmcode_this_t);
- break;
-
case NJS_VMCODE_ARGUMENTS:
ret = njs_vmcode_arguments(vm, pc);
if (njs_slow_path(ret == NJS_ERROR)) {
@@ -822,6 +819,7 @@ next:
case NJS_VMCODE_PROTO_INIT:
set = (njs_vmcode_prop_set_t *) pc;
retval = njs_vmcode_operand(vm, set->value);
+
ret = njs_vmcode_proto_init(vm, value1, value2, retval);
if (njs_slow_path(ret == NJS_ERROR)) {
goto error;
@@ -838,15 +836,21 @@ next:
break;
case NJS_VMCODE_THROW:
- value2 = njs_vmcode_operand(vm, value2);
+ value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
vm->retval = *value2;
goto error;
case NJS_VMCODE_TRY_BREAK:
+ try_trampoline = (njs_vmcode_try_trampoline_t *) pc;
+ value1 = njs_scope_value(vm, try_trampoline->exit_value);
+
ret = njs_vmcode_try_break(vm, value1, value2);
break;
case NJS_VMCODE_TRY_CONTINUE:
+ try_trampoline = (njs_vmcode_try_trampoline_t *) pc;
+ value1 = njs_scope_value(vm, try_trampoline->exit_value);
+
ret = njs_vmcode_try_continue(vm, value1, value2);
break;
@@ -877,6 +881,9 @@ next:
break;
case NJS_VMCODE_FINALLY:
+ finally = (njs_vmcode_finally_t *) pc;
+ value1 = njs_scope_value(vm, finally->exit_value);
+
ret = njs_vmcode_finally(vm, value1, value2, pc);
switch (ret) {
@@ -892,6 +899,20 @@ next:
njs_vmcode_error(vm, pc);
goto error;
+ case NJS_VMCODE_MOVE_ARG:
+ move_arg = (njs_vmcode_move_arg_t *) pc;
+ native = vm->top_frame;
+
+ hint = move_arg->dst;
+
+ value1 = &native->arguments_offset[hint];
+ value2 = njs_vmcode_operand(vm, move_arg->src);
+
+ *value1 = *value2;
+
+ ret = sizeof(njs_vmcode_move_arg_t);
+ break;
+
default:
njs_internal_error(vm, "%d has NO retval", op);
goto error;
@@ -930,11 +951,9 @@ error:
lambda_call = (native == &vm->active_frame->native);
njs_vm_scopes_restore(vm, native, previous);
+ njs_mp_free(vm->mem_pool, native);
- if (native->size != 0) {
- vm->stack_size -= native->size;
- njs_mp_free(vm->mem_pool, native);
- }
+ vm->stack_size--;
if (lambda_call) {
break;
@@ -1004,27 +1023,45 @@ njs_vmcode_array(njs_vm_t *vm, u_char *p
static njs_jump_off_t
njs_vmcode_function(njs_vm_t *vm, u_char *pc)
{
- njs_function_t *function;
- njs_function_lambda_t *lambda;
+ njs_value_t *value;
njs_vmcode_function_t *code;
code = (njs_vmcode_function_t *) pc;
- lambda = code->lambda;
+
+ value = njs_vmcode_function_create(vm, code->lambda, code->retval);
+ if (njs_slow_path(value == NULL)) {
+ return NJS_ERROR;
+ }
+
+ return sizeof(njs_vmcode_function_t);
+}
+
- function = njs_function_alloc(vm, lambda,
- njs_frame_closures(vm->active_frame), 0);
+static njs_value_t *
+njs_vmcode_function_create(njs_vm_t *vm, njs_function_lambda_t *lambda,
+ njs_index_t retval)
+{
+ njs_value_t *value;
+ njs_function_t *function;
+
+ function = njs_function_alloc(vm, lambda, 0);
if (njs_slow_path(function == NULL)) {
- return NJS_ERROR;
+ return NULL;
}
function->args_count = lambda->nargs - lambda->rest_parameters;
+ value = njs_scope_value(vm, retval);
+
+ njs_set_function(value, function);
+
njs_set_function(&vm->retval, function);
- return sizeof(njs_vmcode_function_t);
+ return value;
}
+
static njs_jump_off_t
njs_vmcode_arguments(njs_vm_t *vm, u_char *pc)
{
@@ -1043,8 +1080,8 @@ njs_vmcode_arguments(njs_vm_t *vm, u_cha
}
code = (njs_vmcode_arguments_t *) pc;
+ value = njs_vmcode_operand(vm, code->dst);
- value = njs_vmcode_operand(vm, code->dst);
njs_set_object(value, frame->native.arguments_object);
return sizeof(njs_vmcode_arguments_t);
@@ -1085,7 +1122,7 @@ njs_vmcode_template_literal(njs_vm_t *vm
.u.native = njs_string_prototype_concat
};
- value = njs_vmcode_operand(vm, retval);
+ value = njs_vmcode_operand(vm, (njs_index_t) retval);
if (!njs_is_primitive(value)) {
array = njs_array(value);
@@ -1097,7 +1134,7 @@ njs_vmcode_template_literal(njs_vm_t *vm
return ret;
}
- ret = njs_function_frame_invoke(vm, (njs_index_t) retval);
+ ret = njs_function_frame_invoke(vm, value);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
@@ -1108,7 +1145,7 @@ njs_vmcode_template_literal(njs_vm_t *vm
static njs_jump_off_t
-njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value)
{
njs_object_t *object;
njs_function_t *function;
@@ -1674,20 +1711,17 @@ njs_function_new_object(njs_vm_t *vm, nj
static njs_jump_off_t
njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
{
- njs_value_t *value;
njs_frame_t *frame;
njs_native_frame_t *previous;
- value = njs_vmcode_operand(vm, retval);
-
frame = (njs_frame_t *) vm->top_frame;
if (frame->native.ctor) {
- if (njs_is_object(value)) {
- njs_release(vm, vm->scopes[NJS_SCOPE_ARGUMENTS]);
+ if (njs_is_object(retval)) {
+ njs_release(vm, frame->native.local[0]);
} else {
- value = vm->scopes[NJS_SCOPE_ARGUMENTS];
+ retval = frame->native.local[0];
}
}
@@ -1695,14 +1729,7 @@ njs_vmcode_return(njs_vm_t *vm, njs_valu
njs_vm_scopes_restore(vm, &frame->native, previous);
- /*
- * If a retval is in a callee arguments scope it
- * must be in the previous callee arguments scope.
- */
- retval = njs_vmcode_operand(vm, frame->native.retval);
-
- /* GC: value external/internal++ depending on value and retval type */
- *retval = *value;
+ *frame->native.retval = *retval;
njs_function_frame_free(vm, &frame->native);
@@ -1743,7 +1770,7 @@ njs_vmcode_try_start(njs_vm_t *vm, njs_v
njs_set_invalid(exception_value);
try_start = (njs_vmcode_try_start_t *) pc;
- exit_value = njs_vmcode_operand(vm, try_start->exit_value);
+ exit_value = njs_scope_value(vm, try_start->exit_value);
njs_set_invalid(exit_value);
njs_number(exit_value) = 0;
@@ -1826,7 +1853,7 @@ njs_vmcode_finally(njs_vm_t *vm, njs_val
njs_value_t *exception_value, *exit_value;
njs_vmcode_finally_t *finally;
- exception_value = njs_vmcode_operand(vm, retval);
+ exception_value = njs_scope_value(vm, (njs_index_t) retval);
if (njs_is_valid(exception_value)) {
vm->retval = *exception_value;
@@ -1835,7 +1862,8 @@ njs_vmcode_finally(njs_vm_t *vm, njs_val
}
finally = (njs_vmcode_finally_t *) pc;
- exit_value = njs_vmcode_operand(vm, finally->exit_value);
+
+ exit_value = njs_scope_value(vm, finally->exit_value);
/*
* exit_value is set by:
diff -r d25d92370bfd -r 8583f3bdaeb9 src/njs_vmcode.h
--- a/src/njs_vmcode.h Tue Mar 30 13:58:27 2021 +0000
+++ b/src/njs_vmcode.h Mon Apr 12 10:05:59 2021 +0300
@@ -28,100 +28,100 @@ typedef uint8_t
#define NJS_VMCODE_3OPERANDS 0
#define NJS_VMCODE_2OPERANDS 1
-#define NJS_VMCODE_1OPERAND 2
-#define NJS_VMCODE_NO_OPERAND 3
+
-#define NJS_VMCODE_NO_RETVAL 0
-#define NJS_VMCODE_RETVAL 1
-
-#define VMCODE0(n) (n)
-#define VMCODE1(n) ((n) + 128)
+enum {
+ NJS_VMCODE_STOP = 0,
+ NJS_VMCODE_JUMP,
+ NJS_VMCODE_PROPERTY_SET,
+ NJS_VMCODE_PROPERTY_ACCESSOR,
+ NJS_VMCODE_IF_TRUE_JUMP,
+ NJS_VMCODE_IF_FALSE_JUMP,
+ NJS_VMCODE_IF_EQUAL_JUMP,
+ NJS_VMCODE_PROPERTY_INIT,
+ NJS_VMCODE_RETURN,
+ NJS_VMCODE_FUNCTION_FRAME,
+ NJS_VMCODE_METHOD_FRAME,
+ NJS_VMCODE_FUNCTION_CALL,
+ NJS_VMCODE_PROPERTY_NEXT,
+ NJS_VMCODE_THIS,
+ NJS_VMCODE_ARGUMENTS,
+ NJS_VMCODE_PROTO_INIT,
-#define NJS_VMCODE_STOP VMCODE0(0)
-#define NJS_VMCODE_JUMP VMCODE0(1)
-#define NJS_VMCODE_PROPERTY_SET VMCODE0(2)
-#define NJS_VMCODE_PROPERTY_ACCESSOR VMCODE0(3)
-#define NJS_VMCODE_IF_TRUE_JUMP VMCODE0(4)
-#define NJS_VMCODE_IF_FALSE_JUMP VMCODE0(5)
-#define NJS_VMCODE_IF_EQUAL_JUMP VMCODE0(6)
-#define NJS_VMCODE_PROPERTY_INIT VMCODE0(7)
-#define NJS_VMCODE_RETURN VMCODE0(8)
-#define NJS_VMCODE_FUNCTION_FRAME VMCODE0(9)
-#define NJS_VMCODE_METHOD_FRAME VMCODE0(10)
-#define NJS_VMCODE_FUNCTION_CALL VMCODE0(11)
-#define NJS_VMCODE_PROPERTY_NEXT VMCODE0(16)
-#define NJS_VMCODE_THIS VMCODE0(17)
-#define NJS_VMCODE_ARGUMENTS VMCODE0(18)
-#define NJS_VMCODE_PROTO_INIT VMCODE0(19)
+ NJS_VMCODE_TRY_START,
+ NJS_VMCODE_THROW,
+ NJS_VMCODE_TRY_BREAK,
+ NJS_VMCODE_TRY_CONTINUE,
+ NJS_VMCODE_TRY_END,
+ NJS_VMCODE_CATCH,
+ NJS_VMCODE_FINALLY,
+ NJS_VMCODE_ERROR,
+
+ NJS_VMCODE_MOVE_ARG,
-#define NJS_VMCODE_TRY_START VMCODE0(32)
-#define NJS_VMCODE_THROW VMCODE0(33)
-#define NJS_VMCODE_TRY_BREAK VMCODE0(34)
-#define NJS_VMCODE_TRY_CONTINUE VMCODE0(35)
-#define NJS_VMCODE_TRY_END VMCODE0(37)
-#define NJS_VMCODE_CATCH VMCODE0(38)
-#define NJS_VMCODE_FINALLY VMCODE0(39)
-#define NJS_VMCODE_ERROR VMCODE0(40)
+ NJS_VMCODE_NORET = 127
+};
+
-#define NJS_VMCODE_NORET 127
-
-#define NJS_VMCODE_MOVE VMCODE1(0)
-#define NJS_VMCODE_PROPERTY_GET VMCODE1(1)
-#define NJS_VMCODE_INCREMENT VMCODE1(2)
-#define NJS_VMCODE_POST_INCREMENT VMCODE1(3)
-#define NJS_VMCODE_DECREMENT VMCODE1(4)
-#define NJS_VMCODE_POST_DECREMENT VMCODE1(5)
-#define NJS_VMCODE_TRY_RETURN VMCODE1(6)
-#define NJS_VMCODE_GLOBAL_GET VMCODE1(7)
+enum {
+ NJS_VMCODE_MOVE = NJS_VMCODE_NORET + 1,
+ NJS_VMCODE_PROPERTY_GET,
+ NJS_VMCODE_INCREMENT,
+ NJS_VMCODE_POST_INCREMENT,
+ NJS_VMCODE_DECREMENT,
+ NJS_VMCODE_POST_DECREMENT,
+ NJS_VMCODE_TRY_RETURN,
+ NJS_VMCODE_GLOBAL_GET,
-#define NJS_VMCODE_LESS VMCODE1(8)
-#define NJS_VMCODE_GREATER VMCODE1(9)
-#define NJS_VMCODE_LESS_OR_EQUAL VMCODE1(10)
-#define NJS_VMCODE_GREATER_OR_EQUAL VMCODE1(11)
-#define NJS_VMCODE_ADDITION VMCODE1(12)
-#define NJS_VMCODE_EQUAL VMCODE1(13)
-#define NJS_VMCODE_NOT_EQUAL VMCODE1(14)
+ NJS_VMCODE_LESS,
+ NJS_VMCODE_GREATER,
+ NJS_VMCODE_LESS_OR_EQUAL,
+ NJS_VMCODE_GREATER_OR_EQUAL,
+ NJS_VMCODE_ADDITION,
+ NJS_VMCODE_EQUAL,
+ NJS_VMCODE_NOT_EQUAL,
+
+ NJS_VMCODE_SUBSTRACTION,
+ NJS_VMCODE_MULTIPLICATION,
+ NJS_VMCODE_EXPONENTIATION,
+ NJS_VMCODE_DIVISION,
+ NJS_VMCODE_REMAINDER,
+ NJS_VMCODE_BITWISE_AND,
+ NJS_VMCODE_BITWISE_OR,
+ NJS_VMCODE_BITWISE_XOR,
+ NJS_VMCODE_LEFT_SHIFT,
+ NJS_VMCODE_RIGHT_SHIFT,
+ NJS_VMCODE_UNSIGNED_RIGHT_SHIFT,
+ NJS_VMCODE_OBJECT_COPY,
+ NJS_VMCODE_TEMPLATE_LITERAL,
+ NJS_VMCODE_PROPERTY_IN,
+ NJS_VMCODE_PROPERTY_DELETE,
+ NJS_VMCODE_PROPERTY_FOREACH,
-#define NJS_VMCODE_SUBSTRACTION VMCODE1(16)
-#define NJS_VMCODE_MULTIPLICATION VMCODE1(17)
-#define NJS_VMCODE_EXPONENTIATION VMCODE1(18)
-#define NJS_VMCODE_DIVISION VMCODE1(19)
-#define NJS_VMCODE_REMAINDER VMCODE1(20)
-#define NJS_VMCODE_BITWISE_AND VMCODE1(21)
-#define NJS_VMCODE_BITWISE_OR VMCODE1(22)
-#define NJS_VMCODE_BITWISE_XOR VMCODE1(23)
-#define NJS_VMCODE_LEFT_SHIFT VMCODE1(24)
-#define NJS_VMCODE_RIGHT_SHIFT VMCODE1(25)
-#define NJS_VMCODE_UNSIGNED_RIGHT_SHIFT VMCODE1(26)
-#define NJS_VMCODE_OBJECT_COPY VMCODE1(27)
-#define NJS_VMCODE_TEMPLATE_LITERAL VMCODE1(28)
-#define NJS_VMCODE_PROPERTY_IN VMCODE1(29)
-#define NJS_VMCODE_PROPERTY_DELETE VMCODE1(30)
-#define NJS_VMCODE_PROPERTY_FOREACH VMCODE1(31)
+ NJS_VMCODE_STRICT_EQUAL,
+ NJS_VMCODE_STRICT_NOT_EQUAL,
-#define NJS_VMCODE_STRICT_EQUAL VMCODE1(32)
-#define NJS_VMCODE_STRICT_NOT_EQUAL VMCODE1(33)
+ NJS_VMCODE_TEST_IF_TRUE,
+ NJS_VMCODE_TEST_IF_FALSE,
+
+ NJS_VMCODE_COALESCE,
-#define NJS_VMCODE_TEST_IF_TRUE VMCODE1(34)
-#define NJS_VMCODE_TEST_IF_FALSE VMCODE1(35)
-
-#define NJS_VMCODE_COALESCE VMCODE1(36)
+ NJS_VMCODE_UNARY_PLUS,
+ NJS_VMCODE_UNARY_NEGATION,
+ NJS_VMCODE_BITWISE_NOT,
+ NJS_VMCODE_LOGICAL_NOT,
+ NJS_VMCODE_OBJECT,
+ NJS_VMCODE_ARRAY,
+ NJS_VMCODE_FUNCTION,
+ NJS_VMCODE_REGEXP,
-#define NJS_VMCODE_UNARY_PLUS VMCODE1(37)
-#define NJS_VMCODE_UNARY_NEGATION VMCODE1(38)
-#define NJS_VMCODE_BITWISE_NOT VMCODE1(39)
-#define NJS_VMCODE_LOGICAL_NOT VMCODE1(40)
-#define NJS_VMCODE_OBJECT VMCODE1(41)
-#define NJS_VMCODE_ARRAY VMCODE1(42)
-#define NJS_VMCODE_FUNCTION VMCODE1(43)
-#define NJS_VMCODE_REGEXP VMCODE1(44)
+ NJS_VMCODE_INSTANCE_OF,
+ NJS_VMCODE_TYPEOF,
+ NJS_VMCODE_VOID,
+ NJS_VMCODE_DELETE,
-#define NJS_VMCODE_INSTANCE_OF VMCODE1(45)
-#define NJS_VMCODE_TYPEOF VMCODE1(46)
-#define NJS_VMCODE_VOID VMCODE1(47)
-#define NJS_VMCODE_DELETE VMCODE1(48)
-
-#define NJS_VMCODE_NOP 255
+ NJS_VMCODE_NOP = 255
+};
typedef struct {
@@ -303,6 +303,7 @@ typedef struct {
njs_index_t nargs;
njs_index_t name;
uint8_t ctor; /* 1 bit */
+ njs_function_lambda_t *lambda;
} njs_vmcode_function_frame_t;
@@ -394,6 +395,13 @@ typedef struct {
} njs_vmcode_error_t;
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t src;
+ njs_uint_t dst;
+} njs_vmcode_move_arg_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 d25d92370bfd -r 8583f3bdaeb9 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Tue Mar 30 13:58:27 2021 +0000
+++ b/src/test/njs_unit_test.c Mon Apr 12 10:05:59 2021 +0300
@@ -146,6 +146,47 @@ static njs_unit_test_t njs_test[] =
{ njs_str("var f = 1; function f() {}; f"),
njs_str("1") },
+ { njs_str("var f = 1; function f() {}; f"),
+ njs_str("1") },
+
+ { njs_str("function f(a) {return function (x) {return a(x)}} f(1)(0)"),
+ njs_str("TypeError: number is not a function") },
+
+ { njs_str("var x = 0;"
+ ""
+ "function f1() {"
+ " function f2() {"
+ " return x;"
+ " };"
+ ""
+ " return f2();"
+ ""
+ " var x = 1;"
+ "}"
+ ""
+ "f1() === undefined"),
+ njs_str("true") },
+
+ { njs_str("var fn = function fn() {return fn.test}; fn.test = 'test'; fn()"),
+ njs_str("test") },
+
+ { njs_str("var body;"
+ "var n = 'outside';"
+ "var before = function() {return n};"
+ ""
+ "var func = function n() {"
+ " var n;"
+ " body = function() {return n};"
+ "};"
+ ""
+ "func();"
+ ""
+ "[before(), body()]"),
+ njs_str("outside,") },
+
+ { njs_str("var func = function x(x) {return x}; func()"),
+ njs_str("undefined") },
+
#if 0 /* TODO */
{ njs_str("var a; Object.getOwnPropertyDescriptor(this, 'a').value"),
njs_str("undefined") },
@@ -3660,6 +3701,9 @@ static njs_unit_test_t njs_test[] =
{ njs_str("delete undefined"),
njs_str("SyntaxError: Delete of an unqualified identifier in 1") },
+ { njs_str("delete this !== true"),
+ njs_str("false") },
+
/* Object shorthand methods. */
{ njs_str("var o = {m(){}}; new o.m();"),
@@ -9305,7 +9349,7 @@ static njs_unit_test_t njs_test[] =
"})"
"})"
"})"),
- njs_str("SyntaxError: The maximum function nesting level is \"8\" in 1") },
+ njs_str("[object Function]") },
{ njs_str("Function.prototype.toString = function () {return 'X'};"
"eval"),
@@ -11283,6 +11327,38 @@ static njs_unit_test_t njs_test[] =
{ njs_str("function f(){}; (function(){try {f(f((new RegExp('a**'))))} catch (e) { return 1}})()"),
njs_str("1") },
+ { njs_str("var before, during, after;"
+ ""
+ "try {"
+ " throw 'exception';"
+ "} catch (err) {"
+ " before = err;"
+ ""
+ " for (var err in { name: null }) {"
+ " during = err;"
+ " }"
+ ""
+ " after = err;"
+ "}"
+ ""
+ "[before === 'exception', during === 'name', after === 'name']"),
+ njs_str("true,true,true") },
+
+ { njs_str("var arr = [];"
+ "foo = \"outside\";"
+ ""
+ "try {"
+ " throw new Error();"
+ "}"
+ "catch (foo) {"
+ " var foo = \"inside\";"
+ " arr.push(foo);"
+ "}"
+ ""
+ "arr.push(foo);"
+ "arr"),
+ njs_str("inside,outside") },
+
{ njs_str("var o = { valueOf: function() { return '3' } }; --o"),
njs_str("2") },
@@ -20330,7 +20406,7 @@ static njs_unit_test_t njs_shell_test[]
{ njs_str("/abc/i.test('ABC')" ENTER),
njs_str("true") },
- /* Accumulative mode. */
+ /* Interactive mode. */
{ njs_str("var a = 1" ENTER
"a" ENTER),
@@ -20775,7 +20851,7 @@ njs_interactive_test(njs_unit_test_t tes
njs_vm_opt_init(&options);
options.init = 1;
- options.accumulative = 1;
+ options.interactive = 1;
options.backtrace = 1;
vm = njs_vm_create(&options);
diff -r d25d92370bfd -r 8583f3bdaeb9 test/njs_expect_test.exp
--- a/test/njs_expect_test.exp Tue Mar 30 13:58:27 2021 +0000
+++ b/test/njs_expect_test.exp Mon Apr 12 10:05:59 2021 +0300
@@ -805,7 +805,7 @@ njs_test {
{"(new Function('return this'))() === globalThis\r\n"
"true\r\n"}
{"new Function('return this;')\r\n"
- "TypeError: function constructor is disabled in \"safe\" mode\r\n"}
+ "[Function]"}
{"new Function('return thi')\r\n"
"TypeError: function constructor is disabled in \"safe\" mode\r\n"}
} "-u"
diff -r d25d92370bfd -r 8583f3bdaeb9 utils/lexer_keyword.py
--- a/utils/lexer_keyword.py Tue Mar 30 13:58:27 2021 +0000
+++ b/utils/lexer_keyword.py Mon Apr 12 10:05:59 2021 +0300
@@ -6,6 +6,7 @@ global_keywords = {
"null": 1,
"false": 1,
"true": 1,
+ "undefined": 0,
# Operators.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment