Created
April 12, 2021 07:07
-
-
Save lexborisov/a945f580d85c3281dd742d7af2c1972f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# User Alexander Borisov <alexander.borisov@nginx.com> | |
# Date 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