[njs] Scopes refactoring.
Alexander Borisov
alexander.borisov at nginx.com
Fri Apr 30 13:04:40 UTC 2021
details: https://hg.nginx.org/njs/rev/0a2a0b5a74f4
branches:
changeset: 1629:0a2a0b5a74f4
user: Alexander Borisov <alexander.borisov at nginx.com>
date: Fri Apr 30 16:02:08 2021 +0300
description:
Scopes refactoring.
diffstat:
auto/sources | 1 +
nginx/ngx_js.c | 3 +-
src/njs.h | 7 +-
src/njs_builtin.c | 20 +-
src/njs_disassembler.c | 15 +
src/njs_function.c | 456 +++++++++++++++++++-----------
src/njs_function.h | 58 ++-
src/njs_generator.c | 466 +++++++++++++++++--------------
src/njs_generator.h | 3 +-
src/njs_lexer.h | 3 +-
src/njs_main.h | 1 +
src/njs_module.c | 12 +-
src/njs_parser.c | 667 +++++++++++++++++++++++++--------------------
src/njs_parser.h | 37 +-
src/njs_scope.c | 259 +++++++++++++++++
src/njs_scope.h | 122 ++++++++
src/njs_shell.c | 4 +-
src/njs_string.c | 139 ---------
src/njs_string.h | 3 -
src/njs_value.h | 21 +-
src/njs_variable.c | 689 ++++++++++++++++++++++++----------------------
src/njs_variable.h | 25 +-
src/njs_vm.c | 182 +++++------
src/njs_vm.h | 88 +----
src/njs_vmcode.c | 160 +++++-----
src/njs_vmcode.h | 182 ++++++-----
src/test/njs_unit_test.c | 148 +++++++++-
27 files changed, 2194 insertions(+), 1577 deletions(-)
diffs (truncated from 5843 to 1000 lines):
diff -r cb99db22e3e5 -r 0a2a0b5a74f4 auto/sources
--- a/auto/sources Fri Apr 16 15:25:22 2021 +0000
+++ b/auto/sources Fri Apr 30 16:02:08 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 cb99db22e3e5 -r 0a2a0b5a74f4 nginx/ngx_js.c
--- a/nginx/ngx_js.c Fri Apr 16 15:25:22 2021 +0000
+++ b/nginx/ngx_js.c Fri Apr 30 16:02:08 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 cb99db22e3e5 -r 0a2a0b5a74f4 src/njs.h
--- a/src/njs.h Fri Apr 16 15:25:22 2021 +0000
+++ b/src/njs.h Fri Apr 30 16:02:08 2021 +0300
@@ -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 cb99db22e3e5 -r 0a2a0b5a74f4 src/njs_builtin.c
--- a/src/njs_builtin.c Fri Apr 16 15:25:22 2021 +0000
+++ b/src/njs_builtin.c Fri Apr 30 16:02:08 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;
@@ -939,6 +941,8 @@ njs_global_this_prop_handler(njs_vm_t *v
{
njs_int_t ret;
njs_value_t *value;
+ njs_variable_t *var;
+ njs_function_t *function;
njs_rbtree_node_t *rb_node;
njs_lvlhsh_query_t lhq;
njs_variable_node_t *node, var_node;
@@ -965,7 +969,19 @@ njs_global_this_prop_handler(njs_vm_t *v
}
node = (njs_variable_node_t *) rb_node;
- value = njs_vmcode_operand(vm, node->variable->index);
+
+ var = node->variable;
+
+ value = njs_scope_valid_value(vm, var->index);
+
+ if (var->type == NJS_VARIABLE_FUNCTION && njs_is_undefined(value)) {
+ *value = var->value;
+
+ function = njs_function_value_copy(vm, value);
+ if (njs_slow_path(function == NULL)) {
+ return NJS_ERROR;
+ }
+ }
if (setval != NULL) {
*value = *setval;
diff -r cb99db22e3e5 -r 0a2a0b5a74f4 src/njs_disassembler.c
--- a/src/njs_disassembler.c Fri Apr 16 15:25:22 2021 +0000
+++ b/src/njs_disassembler.c Fri Apr 30 16:02:08 2021 +0300
@@ -35,6 +35,9 @@ static njs_code_name_t code_names[] = {
{ NJS_VMCODE_OBJECT_COPY, sizeof(njs_vmcode_object_copy_t),
njs_str("OBJECT COPY ") },
+ { NJS_VMCODE_FUNCTION_COPY, sizeof(njs_vmcode_function_copy_t),
+ njs_str("FUNCTION COPY ") },
+
{ NJS_VMCODE_PROPERTY_GET, sizeof(njs_vmcode_prop_get_t),
njs_str("PROP GET ") },
{ NJS_VMCODE_GLOBAL_GET, sizeof(njs_vmcode_prop_get_t),
@@ -186,6 +189,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;
@@ -485,6 +489,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 %uD %04Xz\n",
+ line, p - start, move_arg->dst, (size_t) move_arg->src);
+
+ p += sizeof(njs_vmcode_move_arg_t);
+
+ continue;
+ }
+
code_name = code_names;
n = njs_nitems(code_names);
diff -r cb99db22e3e5 -r 0a2a0b5a74f4 src/njs_function.c
--- a/src/njs_function.c Fri Apr 16 15:25:22 2021 +0000
+++ b/src/njs_function.c Fri Apr 30 16:02:08 2021 +0300
@@ -8,21 +8,13 @@
#include <njs_main.h>
-static njs_function_t *njs_function_copy(njs_vm_t *vm,
- njs_function_t *function);
-static njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size);
-
-
njs_function_t *
-njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda,
- njs_closure_t *closures[], njs_bool_t shared)
+njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda)
{
size_t size;
- njs_uint_t n, nesting;
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)) {
@@ -48,21 +40,8 @@ njs_function_alloc(njs_vm_t *vm, njs_fun
function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
function->object.type = NJS_FUNCTION;
- function->object.shared = shared;
function->object.extensible = 1;
- if (nesting != 0 && closures != NULL) {
- function->closure = 1;
-
- n = 0;
-
- do {
- /* GC: retain closure. */
- njs_function_closures(function)[n] = closures[n];
- n++;
- } while (n < nesting);
- }
-
return function;
fail:
@@ -121,14 +100,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)
@@ -199,17 +170,16 @@ njs_function_name_set(njs_vm_t *vm, njs_
}
-static njs_function_t *
+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, n;
+ njs_value_t **from, **to;
njs_function_t *copy;
- nesting = (function->native) ? 0 : function->u.lambda->nesting;
+ n = (function->native) ? 0 : function->u.lambda->nclosures;
- size = sizeof(njs_function_t) + nesting * sizeof(njs_closure_t *);
+ size = sizeof(njs_function_t) + n * sizeof(njs_value_t *);
copy = njs_mp_alloc(vm->mem_pool, size);
if (njs_slow_path(copy == NULL)) {
@@ -220,21 +190,19 @@ 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 (n == 0) {
return copy;
}
- copy->closure = 1;
-
- closures = njs_function_active_closures(vm, function);
-
- n = 0;
+ from = njs_function_closures(function);
+ to = njs_function_closures(copy);
do {
- /* GC: retain closure. */
- njs_function_closures(copy)[n] = closures[n];
- n++;
- } while (n < nesting);
+ n--;
+
+ to[n] = from[n];
+
+ } while (n != 0);
return copy;
}
@@ -287,7 +255,7 @@ 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;
}
@@ -326,19 +294,20 @@ njs_function_rest_parameters_init(njs_vm
return NJS_ERROR;
}
- if (n <= nargs) {
- i = 0;
- do {
- /* GC: retain. */
- array->start[i++] = frame->arguments[n++];
- } while (n <= nargs);
+ for (i = 0; i < length; i++) {
+ array->start[i] = frame->arguments[i + n - 1];
}
- 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[n] = rest_arguments;
+
return NJS_OK;
}
@@ -397,7 +366,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 +386,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 +399,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, frame_size;
+ uint32_t args_count, value_count, value_size, temp_size;
+ njs_value_t *value, *bound, **new, **temp;
njs_frame_t *frame;
njs_function_t *target;
njs_native_frame_t *native_frame;
@@ -461,76 +430,86 @@ 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;
+ args_count = function->args_offset + njs_max(nargs, lambda->nargs);
+ value_count = args_count + njs_max(args_count, lambda->nlocal);
- size = njs_frame_size(closures)
- + (function->args_offset + max_args) * sizeof(njs_value_t)
- + lambda->local_size;
+ value_size = value_count * sizeof(njs_value_t *);
+ temp_size = lambda->temp * sizeof(njs_value_t *);
- native_frame = njs_function_frame_alloc(vm, size);
+ frame_size = value_size + temp_size
+ + ((value_count + lambda->temp) * sizeof(njs_value_t));
+
+ native_frame = njs_function_frame_alloc(vm, NJS_FRAME_SIZE + frame_size);
if (njs_slow_path(native_frame == NULL)) {
return NJS_ERROR;
}
+ /* Local */
+
+ new = (njs_value_t **) ((u_char *) native_frame + NJS_FRAME_SIZE);
+ value = (njs_value_t *) ((u_char *) new + value_size + temp_size);
+
+ n = value_count + lambda->temp;
+
+ while (n != 0) {
+ n--;
+ new[n] = &value[n];
+ njs_set_invalid(new[n]);
+ }
+
+ /* Temp */
+
+ temp = (njs_value_t **) ((u_char *) native_frame + NJS_FRAME_SIZE
+ + value_size);
+
+ 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;
@@ -543,15 +522,7 @@ njs_function_frame_alloc(njs_vm_t *vm, s
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;
+ spare_size = vm->top_frame ? vm->top_frame->free_size : 0;
if (njs_fast_path(size <= spare_size)) {
frame = (njs_native_frame_t *) vm->top_frame->free;
@@ -602,7 +573,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,78 +586,50 @@ njs_function_call2(njs_vm_t *vm, njs_fun
njs_int_t
njs_function_lambda_call(njs_vm_t *vm)
{
- size_t size;
+ uint32_t n;
njs_int_t ret;
- njs_uint_t n, nesting;
njs_frame_t *frame;
- njs_value_t *dst, *src;
- njs_closure_t *closure, **closures;
+ njs_value_t *args, **local, *value;
+ njs_value_t **cur_local, **cur_closures, **cur_temp;
njs_function_t *function;
njs_function_lambda_t *lambda;
frame = (njs_frame_t *) vm->top_frame;
function = frame->native.function;
+ if (function->global && !function->closure_copied) {
+ ret = njs_function_capture_global_closures(vm, function);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+ }
+
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;
- /* 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->levels[NJS_LEVEL_LOCAL];
+ cur_closures = vm->levels[NJS_LEVEL_CLOSURE];
+ cur_temp = vm->levels[NJS_LEVEL_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->levels[NJS_LEVEL_LOCAL] = vm->top_frame->local;
+ vm->levels[NJS_LEVEL_CLOSURE] = njs_function_closures(function);
+ vm->levels[NJS_LEVEL_TEMP] = frame->native.temp;
if (lambda->rest_parameters) {
ret = njs_function_rest_parameters_init(vm, &frame->native);
@@ -695,9 +638,41 @@ 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);
+ }
+ }
+
vm->active_frame = frame;
- return njs_vmcode_interpreter(vm, lambda->start);
+ /* Closures */
+
+ n = lambda->ndeclarations;
+
+ while (n != 0) {
+ n--;
+
+ function = njs_function(lambda->declarations[n]);
+
+ ret = njs_function_capture_closure(vm, function, function->u.lambda);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+ }
+
+ ret = njs_vmcode_interpreter(vm, lambda->start);
+
+ /* Restore current level. */
+ vm->levels[NJS_LEVEL_LOCAL] = cur_local;
+ vm->levels[NJS_LEVEL_CLOSURE] = cur_closures;
+ vm->levels[NJS_LEVEL_TEMP] = cur_temp;
+
+ return ret;
}
@@ -705,7 +680,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 +715,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);
@@ -775,6 +744,142 @@ njs_function_frame_free(njs_vm_t *vm, nj
}
+njs_int_t
+njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
+ njs_function_lambda_t *lambda)
+{
+ void *start, *end;
+ uint32_t n;
+ njs_value_t *value, **closure;
+ njs_native_frame_t *frame;
+
+ if (lambda->nclosures == 0) {
+ return NJS_OK;
+ }
+
+ frame = &vm->active_frame->native;
+
+ while (frame->native) {
+ frame = frame->previous;
+ }
+
+ start = frame;
+ end = frame->free;
+
+ closure = njs_function_closures(function);
+ n = lambda->nclosures;
+
+ do {
+ n--;
+
+ value = njs_scope_value(vm, lambda->closures[n]);
+
+ if (start <= (void *) value && (void *) value < end) {
+ value = njs_scope_value_clone(vm, lambda->closures[n], value);
+ if (njs_slow_path(value == NULL)) {
+ return NJS_ERROR;
+ }
+ }
+
+ closure[n] = value;
+
+ } while (n != 0);
+
+ return NJS_OK;
+}
+
+
+njs_inline njs_value_t *
+njs_function_closure_value(njs_vm_t *vm, njs_value_t **scope, njs_index_t index,
+ void *start, void *end)
+{
+ njs_value_t *value, *newval;
+
+ value = scope[njs_scope_index_value(index)];
+
+ if (start <= (void *) value && end > (void *) value) {
+ newval = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+ if (njs_slow_path(newval == NULL)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
+
+ *newval = *value;
+ value = newval;
+ }
+
+ scope[njs_scope_index_value(index)] = value;
+
+ return value;
+}
+
+
+njs_int_t
+njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function)
+{
+ void *start, *end;
+ uint32_t n;
+ njs_value_t *value, **refs, **global;
+ njs_index_t *indexes, index;
+ njs_native_frame_t *native;
+ njs_function_lambda_t *lambda;
+
+ lambda = function->u.lambda;
+
+ if (lambda->nclosures == 0) {
+ return NJS_OK;
+ }
+
+ native = vm->top_frame;
+
+ while (native->previous->function != NULL) {
+ native = native->previous;
+ }
+
+ start = native;
+ end = native->free;
+
+ indexes = lambda->closures;
+ refs = njs_function_closures(function);
+
+ global = vm->levels[NJS_LEVEL_GLOBAL];
+
+ n = lambda->nclosures;
+
+ while (n > 0) {
+ n--;
+
+ index = indexes[n];
+
+ switch (njs_scope_index_type(index)) {
+ case NJS_LEVEL_LOCAL:
+ value = njs_function_closure_value(vm, native->local, index,
+ start, end);
+ break;
+
+ case NJS_LEVEL_GLOBAL:
+ value = njs_function_closure_value(vm, global, index, start, end);
+ break;
+
+ default:
+ njs_type_error(vm, "unexpected value type for closure \"%uD\"",
+ njs_scope_index_type(index));
+ return NJS_ERROR;
+ }
+
+ if (njs_slow_path(value == NULL)) {
+ return NJS_ERROR;
+ }
+
+ refs[n] = value;
+ }
+
+ function->closure_copied = 1;
+
+ return NJS_OK;
+}
+
+
static njs_value_t *
njs_function_property_prototype_set(njs_vm_t *vm, njs_lvlhsh_t *hash,
njs_value_t *prototype)
@@ -937,7 +1042,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 +1058,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 +1082,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);
@@ -990,11 +1097,12 @@ njs_function_constructor(njs_vm_t *vm, n
lambda = ((njs_vmcode_function_t *) generator.code_start)->lambda;
- function = njs_function_alloc(vm, lambda, NULL, 0);
+ function = njs_function_alloc(vm, lambda);
if (njs_slow_path(function == NULL)) {
return NJS_ERROR;
}
+ function->global = 1;
function->global_this = 1;
function->args_count = lambda->nargs - lambda->rest_parameters;
diff -r cb99db22e3e5 -r 0a2a0b5a74f4 src/njs_function.h
--- a/src/njs_function.h Fri Apr 16 15:25:22 2021 +0000
+++ b/src/njs_function.h Fri Apr 30 16:02:08 2021 +0300
@@ -9,23 +9,21 @@
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_value_t **declarations;
+ uint32_t ndeclarations;
- /* Function internal block closures levels. */
- uint8_t block_closures; /* 4 bits */
+ njs_index_t self;
+
+ 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,11 +33,10 @@ 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
+#define NJS_FRAME_SPARE_SIZE (4 * 1024)
struct njs_native_frame_s {
@@ -51,11 +48,15 @@ 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;
+
uint32_t nargs;
uint8_t native; /* 1 bit */
@@ -81,19 +82,14 @@ struct njs_frame_s {
njs_exception_t exception;
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_function_t *njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda);
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);
+njs_function_t *njs_function_copy(njs_vm_t *vm, njs_function_t *function);
njs_int_t njs_function_arguments_object_init(njs_vm_t *vm,
njs_native_frame_t *frame);
njs_int_t njs_function_rest_parameters_init(njs_vm_t *vm,
@@ -113,7 +109,12 @@ njs_int_t njs_function_call2(njs_vm_t *v
njs_uint_t nargs, njs_value_t *retval, njs_bool_t ctor);
njs_int_t njs_function_lambda_call(njs_vm_t *vm);
njs_int_t njs_function_native_call(njs_vm_t *vm);
+njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size);
void njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *frame);
+njs_int_t njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
+ njs_function_lambda_t *lambda);
+njs_int_t njs_function_capture_global_closures(njs_vm_t *vm,
+ njs_function_t *function);
njs_inline njs_function_lambda_t *
@@ -161,7 +162,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;
@@ -202,6 +203,13 @@ njs_native_function_same(const njs_funct
}
+njs_inline njs_value_t **
+njs_function_closures(const njs_function_t *func)
+{
+ return (njs_value_t **) ((u_char *) func + sizeof(njs_function_t));
+}
+
+
extern const njs_object_type_init_t njs_function_type_init;
extern const njs_object_init_t njs_function_instance_init;
extern const njs_object_init_t njs_arrow_instance_init;
diff -r cb99db22e3e5 -r 0a2a0b5a74f4 src/njs_generator.c
--- a/src/njs_generator.c Fri Apr 16 15:25:22 2021 +0000
+++ b/src/njs_generator.c Fri Apr 30 16:02:08 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);
More information about the nginx-devel
mailing list