March 28, 2017 12:26PM
details: http://hg.nginx.org/njs/rev/a095dc0cd361
branches:
changeset: 321:a095dc0cd361
user: Igor Sysoev <igor@sysoev.ru>
date: Tue Mar 28 07:50:05 2017 +0300
description:
Closures support.

diffstat:

njs/njs_array.c | 2 +-
njs/njs_boolean.c | 2 +-
njs/njs_date.c | 4 +-
njs/njs_function.c | 159 +++++++++++++++++++-----
njs/njs_function.h | 29 +++-
njs/njs_generator.c | 109 +++++++++++++---
njs/njs_number.c | 2 +-
njs/njs_parser.c | 87 ++++++++-----
njs/njs_parser.h | 26 ++-
njs/njs_string.c | 2 +-
njs/njs_variable.c | 291 ++++++++++++++++++++++++++++++++++------------
njs/njs_variable.h | 18 +-
njs/njs_vm.c | 237 ++++++++++++++++++++++++-------------
njs/njs_vm.h | 68 +++++++---
njs/njscript.c | 14 +-
njs/test/njs_unit_test.c | 35 +++++-
16 files changed, 773 insertions(+), 312 deletions(-)

diffs (truncated from 2072 to 1000 lines):

diff -r 0bde7f156477 -r a095dc0cd361 njs/njs_array.c
--- a/njs/njs_array.c Sat Mar 25 13:42:40 2017 +0300
+++ b/njs/njs_array.c Tue Mar 28 07:50:05 2017 +0300
@@ -725,7 +725,7 @@ njs_array_prototype_to_string_continuati
nxt_uint_t nargs, njs_index_t retval)
{
/* Skip retval update. */
- vm->frame->skip = 1;
+ vm->top_frame->skip = 1;

return NXT_OK;
}
diff -r 0bde7f156477 -r a095dc0cd361 njs/njs_boolean.c
--- a/njs/njs_boolean.c Sat Mar 25 13:42:40 2017 +0300
+++ b/njs/njs_boolean.c Tue Mar 28 07:50:05 2017 +0300
@@ -34,7 +34,7 @@ njs_boolean_constructor(njs_vm_t *vm, nj
value = njs_is_true(&args[1]) ? &njs_value_true : &njs_value_false;
}

- if (vm->frame->ctor) {
+ if (vm->top_frame->ctor) {
object = njs_object_value_alloc(vm, value, value->type);
if (nxt_slow_path(object == NULL)) {
return NXT_ERROR;
diff -r 0bde7f156477 -r a095dc0cd361 njs/njs_date.c
--- a/njs/njs_date.c Sat Mar 25 13:42:40 2017 +0300
+++ b/njs/njs_date.c Tue Mar 28 07:50:05 2017 +0300
@@ -91,7 +91,7 @@ njs_date_constructor(njs_vm_t *vm, njs_v
njs_date_t *date;
struct tm tm;

- if (vm->frame->ctor) {
+ if (vm->top_frame->ctor) {

if (nargs == 1) {
time = njs_gettime();
@@ -1892,7 +1892,7 @@ njs_date_prototype_to_json_continuation(
nxt_uint_t nargs, njs_index_t retval)
{
/* Skip retval update. */
- vm->frame->skip = 1;
+ vm->top_frame->skip = 1;

return NXT_OK;
}
diff -r 0bde7f156477 -r a095dc0cd361 njs/njs_function.c
--- a/njs/njs_function.c Sat Mar 25 13:42:40 2017 +0300
+++ b/njs/njs_function.c Tue Mar 28 07:50:05 2017 +0300
@@ -60,7 +60,9 @@ njs_function_alloc(njs_vm_t *vm)
njs_function_t *
njs_function_value_copy(njs_vm_t *vm, njs_value_t *value)
{
- njs_function_t *function;
+ size_t size;
+ nxt_uint_t n, nesting;
+ njs_function_t *function, *copy;

function = value->data.u.function;

@@ -68,17 +70,36 @@ njs_function_value_copy(njs_vm_t *vm, nj
return function;
}

- function = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_function_t));
+ nesting = (function->native) ? 0 : function->u.lambda->nesting;

- if (nxt_fast_path(function != NULL)) {
- *function = *value->data.u.function;
- function->object.__proto__ =
- &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object;
- function->object.shared = 0;
- value->data.u.function = function;
+ size = sizeof(njs_function_t) + nesting * sizeof(njs_closure_t *);
+
+ copy = nxt_mem_cache_alloc(vm->mem_cache_pool, size);
+ if (nxt_slow_path(copy == NULL)) {
+ return copy;
}

- return function;
+ value->data.u.function = copy;
+
+ *copy = *function;
+ copy->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object;
+ copy->object.shared = 0;
+
+ if (nesting == 0) {
+ return copy;
+ }
+
+ copy->closure = 1;
+
+ n = 0;
+
+ do {
+ /* GC: retain closure. */
+ copy->closures[n] = vm->active_frame->closures[n];
+ n++;
+ } while (n < nesting);
+
+ return copy;
}


@@ -107,6 +128,7 @@ njs_function_native_frame(njs_vm_t *vm,
frame->ctor = ctor;

value = (njs_value_t *) (njs_continuation(frame) + reserve);
+ frame->arguments = value;

bound = function->bound;

@@ -124,7 +146,6 @@ njs_function_native_frame(njs_vm_t *vm,
} while (n != 0);
}

- frame->arguments = value;
vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value;

if (args != NULL) {
@@ -140,17 +161,23 @@ njs_function_frame(njs_vm_t *vm, njs_fun
const njs_value_t *this, njs_value_t *args, nxt_uint_t nargs,
nxt_bool_t ctor)
{
- size_t size;
- nxt_uint_t n, max_args;
- njs_value_t *value, *bound;
- njs_frame_t *frame;
- njs_native_frame_t *native_frame;
+ size_t size;
+ nxt_uint_t n, max_args, closures;;
+ njs_value_t *value, *bound;
+ njs_frame_t *frame;
+ njs_native_frame_t *native_frame;
+ njs_function_lambda_t *lambda;

- max_args = nxt_max(nargs, function->u.lambda->nargs);
+ lambda = function->u.lambda;
+
+ max_args = nxt_max(nargs, lambda->nargs);
+
+ closures = lambda->nesting + lambda->block_closures;

size = NJS_FRAME_SIZE
+ (function->args_offset + max_args) * sizeof(njs_value_t)
- + function->u.lambda->local_size;
+ + lambda->local_size
+ + closures * sizeof(njs_closure_t *);

native_frame = njs_function_frame_alloc(vm, size);
if (nxt_slow_path(native_frame == NULL)) {
@@ -161,7 +188,10 @@ njs_function_frame(njs_vm_t *vm, njs_fun
native_frame->nargs = nargs;
native_frame->ctor = ctor;

+ /* Function arguments. */
+
value = (njs_value_t *) ((u_char *) native_frame + NJS_FRAME_SIZE);
+ native_frame->arguments = value;

bound = function->bound;

@@ -177,7 +207,6 @@ njs_function_frame(njs_vm_t *vm, njs_fun
} while (n != 0);
}

- native_frame->arguments = value;
vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value;

if (args != NULL) {
@@ -196,9 +225,6 @@ njs_function_frame(njs_vm_t *vm, njs_fun
frame = (njs_frame_t *) native_frame;
frame->local = value;

- memcpy(frame->local, function->u.lambda->local_scope,
- function->u.lambda->local_size);
-
return NXT_OK;
}

@@ -213,10 +239,10 @@ njs_function_frame_alloc(njs_vm_t *vm, s
size_t spare_size, chunk_size;
njs_native_frame_t *frame;

- spare_size = vm->frame->free_size;
+ spare_size = vm->top_frame->free_size;

if (nxt_fast_path(size <= spare_size)) {
- frame = (njs_native_frame_t *) vm->frame->free;
+ frame = (njs_native_frame_t *) vm->top_frame->free;
chunk_size = 0;

} else {
@@ -244,8 +270,8 @@ njs_function_frame_alloc(njs_vm_t *vm, s
frame->free_size = spare_size - size;
frame->free = (u_char *) frame + size;

- frame->previous = vm->frame;
- vm->frame = frame;
+ frame->previous = vm->top_frame;
+ vm->top_frame = frame;

return frame;
}
@@ -290,25 +316,86 @@ njs_function_apply(njs_vm_t *vm, njs_fun
nxt_noinline njs_ret_t
njs_function_call(njs_vm_t *vm, njs_index_t retval, size_t advance)
{
- njs_frame_t *frame;
- njs_function_t *function;
+ size_t size;
+ nxt_uint_t n, nesting;
+ njs_frame_t *frame;
+ njs_value_t *value;
+ njs_closure_t *closure, **closures;
+ njs_function_t *function;
+ njs_function_lambda_t *lambda;

- frame = (njs_frame_t *) vm->frame;
+ frame = (njs_frame_t *) vm->top_frame;

frame->retval = retval;

function = frame->native.function;
frame->return_address = vm->current + advance;
- vm->current = function->u.lambda->u.start;

- frame->prev_arguments = vm->scopes[NJS_SCOPE_ARGUMENTS];
- vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments
- - function->args_offset;
+ lambda = function->u.lambda;
+ vm->current = lambda->u.start;
+
#if (NXT_DEBUG)
vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = NULL;
#endif
- frame->prev_local = vm->scopes[NJS_SCOPE_FUNCTION];
- vm->scopes[NJS_SCOPE_FUNCTION] = frame->local;
+
+ vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments;
+
+ /* 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. */
+
+ n = 0;
+ nesting = lambda->nesting;
+
+ if (nesting != 0) {
+ closures = (function->closure) ? function->closures
+ : vm->active_frame->closures;
+ do {
+ closure = *closures++;
+
+ frame->closures[n] = closure;
+ vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
+
+ n++;
+ } while (n < nesting);
+ }
+
+ /* Function closure values. */
+
+ if (lambda->block_closures > 0) {
+ closure = NULL;
+
+ size = lambda->closure_size;
+
+ if (size != 0) {
+ closure = nxt_mem_cache_align(vm->mem_cache_pool,
+ sizeof(njs_value_t), size);
+ if (nxt_slow_path(closure == NULL)) {
+ return NXT_ERROR;
+ }
+
+ /* TODO: copy initialzed values. */
+
+ size -= sizeof(njs_value_t);
+ closure->u.count = 0;
+ value = closure->values;
+
+ do {
+ *value++ = njs_value_void;
+ size -= sizeof(njs_value_t);
+ } while (size != 0);
+ }
+
+ frame->closures[n] = closure;
+ vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
+ }
+
+ frame->previous_active_frame = vm->active_frame;
+ vm->active_frame = frame;

return NJS_APPLIED;
}
@@ -498,7 +585,7 @@ njs_function_activate(njs_vm_t *vm, njs_
}

/* Skip the "call/apply" method frame. */
- vm->frame->previous->skip = 1;
+ vm->top_frame->previous->skip = 1;

cont = njs_vm_continuation(vm);

@@ -520,7 +607,7 @@ njs_function_activate(njs_vm_t *vm, njs_
}

/* Skip the "call/apply" method frame. */
- vm->frame->previous->skip = 1;
+ vm->top_frame->previous->skip = 1;

return njs_function_call(vm, retval, sizeof(njs_vmcode_function_call_t));
}
diff -r 0bde7f156477 -r a095dc0cd361 njs/njs_function.h
--- a/njs/njs_function.h Sat Mar 25 13:42:40 2017 +0300
+++ b/njs/njs_function.h Tue Mar 28 07:50:05 2017 +0300
@@ -22,6 +22,13 @@
struct njs_function_lambda_s {
uint32_t nargs;
uint32_t local_size;
+ uint32_t closure_size;
+
+ /* Function nesting level. */
+ uint8_t nesting; /* 4 bits */
+
+ /* Function internal block closures levels. */
+ uint8_t block_closures; /* 4 bits */

/* Initial values of local scope. */
njs_value_t *local_scope;
@@ -57,7 +64,7 @@ typedef struct {


#define njs_vm_continuation(vm) \
- (void *) njs_continuation((vm)->frame)
+ (void *) njs_continuation((vm)->top_frame)

#define njs_continuation(frame) \
((u_char *) frame + NJS_NATIVE_FRAME_SIZE)
@@ -69,7 +76,7 @@ typedef struct {


#define njs_vm_trap_value(vm, val) \
- (vm)->frame->trap_scratch.data.u.value = val
+ (vm)->top_frame->trap_scratch.data.u.value = val



@@ -119,17 +126,21 @@ struct njs_native_frame_s {
};


-typedef struct {
+struct njs_frame_s {
njs_native_frame_t native;

+ njs_index_t retval;
+
u_char *return_address;
- njs_value_t *prev_arguments;
- njs_value_t *prev_local;
+ njs_frame_t *previous_active_frame;
+
njs_value_t *local;
- njs_value_t *closure;
-
- njs_index_t retval;
-} njs_frame_t;
+#if (NXT_SUNC)
+ njs_closure_t *closures[1];
+#else
+ njs_closure_t *closures[];
+#endif
+};


njs_function_t *njs_function_alloc(njs_vm_t *vm);
diff -r 0bde7f156477 -r a095dc0cd361 njs/njs_generator.c
--- a/njs/njs_generator.c Sat Mar 25 13:42:40 2017 +0300
+++ b/njs/njs_generator.c Tue Mar 28 07:50:05 2017 +0300
@@ -92,6 +92,8 @@ static nxt_int_t njs_generate_function_d
njs_parser_t *parser, njs_parser_node_t *node);
static nxt_int_t njs_generate_function_scope(njs_vm_t *vm,
njs_function_lambda_t *lambda, njs_parser_node_t *node);
+static void njs_generate_argument_closures(njs_parser_t *parser,
+ njs_parser_node_t *node);
static nxt_int_t njs_generate_return_statement(njs_vm_t *vm,
njs_parser_t *parser, njs_parser_node_t *node);
static nxt_int_t njs_generate_function_call(njs_vm_t *vm, njs_parser_t *parser,
@@ -338,7 +340,7 @@ njs_generate_name(njs_vm_t *vm, njs_pars
njs_variable_t *var;
njs_vmcode_object_copy_t *copy;

- var = njs_variable_get(vm, node, NJS_NAME_REFERENCE);
+ var = njs_variable_get(vm, node);
if (nxt_slow_path(var == NULL)) {
return NXT_ERROR;
}
@@ -371,7 +373,7 @@ njs_generate_builtin_object(njs_vm_t *vm
njs_index_t index;
njs_vmcode_object_copy_t *copy;

- index = njs_variable_index(vm, node, NJS_NAME_REFERENCE);
+ index = njs_variable_index(vm, node);
if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
@@ -398,7 +400,7 @@ njs_generate_variable(njs_vm_t *vm, njs_
{
njs_index_t index;

- index = njs_variable_index(vm, node, NJS_NAME_REFERENCE);
+ index = njs_variable_index(vm, node);
if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
@@ -420,7 +422,7 @@ njs_generate_var_statement(njs_vm_t *vm,

lvalue = node->left;

- index = njs_variable_index(vm, lvalue, NJS_NAME_DECLARATION);
+ index = njs_variable_index(vm, lvalue);
if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
@@ -1307,7 +1309,7 @@ njs_generate_assignment(njs_vm_t *vm, nj

if (lvalue->token == NJS_TOKEN_NAME) {

- index = njs_variable_index(vm, lvalue, NJS_NAME_REFERENCE);
+ index = njs_variable_index(vm, lvalue);
if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
@@ -1813,7 +1815,7 @@ njs_generate_typeof_operation(njs_vm_t *
expr = node->left;

if (expr->token == NJS_TOKEN_NAME) {
- index = njs_variable_index(vm, expr, NJS_NAME_TYPEOF);
+ index = njs_variable_typeof(vm, expr);
if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
@@ -1964,7 +1966,7 @@ njs_generate_function_declaration(njs_vm
njs_variable_t *var;
njs_function_lambda_t *lambda;

- var = njs_variable_get(vm, node, NJS_NAME_DECLARATION);
+ var = njs_variable_get(vm, node);
if (nxt_slow_path(var == NULL)) {
return NXT_ERROR;
}
@@ -1979,14 +1981,34 @@ static nxt_int_t
njs_generate_function_scope(njs_vm_t *vm, njs_function_lambda_t *lambda,
njs_parser_node_t *node)
{
- nxt_int_t ret;
-
- ret = njs_generate_scope(vm, lambda->u.parser, node->right);
+ size_t size;
+ nxt_int_t ret;
+ nxt_array_t *closure;
+ njs_parser_t *parser;
+
+ parser = lambda->u.parser;
+ node = node->right;
+
+ parser->code_size += node->scope->argument_closures
+ * sizeof(njs_vmcode_move_t);
+
+ ret = njs_generate_scope(vm, parser, node);

if (nxt_fast_path(ret == NXT_OK)) {
- lambda->local_size = lambda->u.parser->scope_size;
- lambda->local_scope = lambda->u.parser->local_scope;
- lambda->u.start = lambda->u.parser->code_start;
+ size = 0;
+ closure = node->scope->values[1];
+
+ if (closure != NULL) {
+ lambda->block_closures = 1;
+ size = (1 + closure->items) * sizeof(njs_value_t);
+ }
+
+ lambda->nesting = node->scope->nesting;
+ lambda->closure_size = size;
+
+ lambda->local_size = parser->scope_size;
+ lambda->local_scope = parser->local_scope;
+ lambda->u.start = parser->code_start;
}

return ret;
@@ -2004,6 +2026,8 @@ njs_generate_scope(njs_vm_t *vm, njs_par
njs_vm_code_t *code;
njs_parser_scope_t *scope;

+ scope = node->scope;
+
p = nxt_mem_cache_alloc(vm->mem_cache_pool, parser->code_size);
if (nxt_slow_path(p == NULL)) {
return NXT_ERROR;
@@ -2012,12 +2036,12 @@ njs_generate_scope(njs_vm_t *vm, njs_par
parser->code_start = p;
parser->code_end = p;

+ njs_generate_argument_closures(parser, node);
+
if (nxt_slow_path(njs_generator(vm, parser, node) != NXT_OK)) {
return NXT_ERROR;
}

- scope = node->scope;
-
code_size = parser->code_end - parser->code_start;

nxt_thread_log_debug("SCOPE CODE SIZE: %uz %uz",
@@ -2028,7 +2052,7 @@ njs_generate_scope(njs_vm_t *vm, njs_par
return NXT_ERROR;
}

- scope_size = njs_offset(scope->next_index);
+ scope_size = njs_scope_offset(scope->next_index[0]);

if (scope->type == NJS_SCOPE_GLOBAL) {
scope_size -= NJS_INDEX_GLOBAL_OFFSET;
@@ -2041,11 +2065,11 @@ njs_generate_scope(njs_vm_t *vm, njs_par

parser->scope_size = scope_size;

- size = scope->values->items * sizeof(njs_value_t);
+ size = scope->values[0]->items * sizeof(njs_value_t);

nxt_thread_log_debug("SCOPE SIZE: %uz %uz", size, scope_size);

- p = memcpy(parser->local_scope, scope->values->start, size);
+ p = memcpy(parser->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)) {
@@ -2072,6 +2096,44 @@ njs_generate_scope(njs_vm_t *vm, njs_par
}


+static void
+njs_generate_argument_closures(njs_parser_t *parser, njs_parser_node_t *node)
+{
+ nxt_uint_t n;
+ njs_index_t index;
+ njs_variable_t *var;
+ njs_vmcode_move_t *move;
+ nxt_lvlhsh_each_t lhe;
+
+ n = node->scope->argument_closures;
+
+ if (n == 0) {
+ return;
+ }
+
+ memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t));
+ lhe.proto = &njs_variables_hash_proto;
+
+ do {
+ var = nxt_lvlhsh_each(&node->scope->variables, &lhe);
+
+ if (var->argument != 0) {
+ index = njs_scope_index((var->argument - 1), NJS_SCOPE_ARGUMENTS);
+
+ njs_generate_code(parser, njs_vmcode_move_t, move);
+ move->code.operation = njs_vmcode_move;
+ move->code.operands = NJS_VMCODE_2OPERANDS;
+ move->code.retval = NJS_VMCODE_RETVAL;
+ move->dst = var->index;
+ move->src = index;
+
+ n--;
+ }
+
+ } while(n != 0);
+}
+
+
static nxt_int_t
njs_generate_return_statement(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node)
@@ -2281,7 +2343,7 @@ njs_generate_try_statement(njs_vm_t *vm,
if (node->token == NJS_TOKEN_CATCH) {
/* A "try/catch" case. */

- catch_index = njs_variable_index(vm, node->left, NJS_NAME_DECLARATION);
+ catch_index = njs_variable_index(vm, node->left);
if (nxt_slow_path(catch_index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
@@ -2306,8 +2368,7 @@ 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,
- NJS_NAME_DECLARATION);
+ catch_index = njs_variable_index(vm, node->left->left);
if (nxt_slow_path(catch_index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
@@ -2479,7 +2540,7 @@ njs_generator_temp_index_get(njs_vm_t *v
scope = scope->parent;
}

- value = nxt_array_add(scope->values, &njs_array_mem_proto,
+ value = nxt_array_add(scope->values[0], &njs_array_mem_proto,
vm->mem_cache_pool);
if (nxt_slow_path(value == NULL)) {
return NJS_INDEX_ERROR;
@@ -2487,8 +2548,8 @@ njs_generator_temp_index_get(njs_vm_t *v

*value = njs_value_invalid;

- index = scope->next_index;
- scope->next_index += sizeof(njs_value_t);
+ index = scope->next_index[0];
+ scope->next_index[0] += sizeof(njs_value_t);

return index;
}
diff -r 0bde7f156477 -r a095dc0cd361 njs/njs_number.c
--- a/njs/njs_number.c Sat Mar 25 13:42:40 2017 +0300
+++ b/njs/njs_number.c Tue Mar 28 07:50:05 2017 +0300
@@ -242,7 +242,7 @@ njs_number_constructor(njs_vm_t *vm, njs
value = &args[1];
}

- if (vm->frame->ctor) {
+ if (vm->top_frame->ctor) {
object = njs_object_value_alloc(vm, value, value->type);
if (nxt_slow_path(object == NULL)) {
return NXT_ERROR;
diff -r 0bde7f156477 -r a095dc0cd361 njs/njs_parser.c
--- a/njs/njs_parser.c Sat Mar 25 13:42:40 2017 +0300
+++ b/njs/njs_parser.c Tue Mar 28 07:50:05 2017 +0300
@@ -142,9 +142,32 @@ njs_parser(njs_vm_t *vm, njs_parser_t *p
static njs_ret_t
njs_parser_scope_begin(njs_vm_t *vm, njs_parser_t *parser, njs_scope_t type)
{
+ nxt_uint_t nesting;
nxt_array_t *values;
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;
+ }
+
+ nxt_alert(&vm->trace, NXT_LEVEL_ERROR, "SyntaxError: "
+ "The maximum function nesting level is \"%d\"",
+ NJS_MAX_NESTING);
+
+ return NXT_ERROR;
+ }
+ }
+ }
+
scope = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_parser_scope_t));
if (nxt_slow_path(scope == NULL)) {
return NXT_ERROR;
@@ -152,14 +175,26 @@ njs_parser_scope_begin(njs_vm_t *vm, njs

scope->type = type;

- if (type == NJS_SCOPE_GLOBAL) {
- type += NJS_INDEX_GLOBAL_OFFSET;
+ 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->next_index = type;
-
- scope->inclusive = 0;
+ scope->nesting = nesting;
+ scope->argument_closures = 0;
+
+ nxt_queue_init(&scope->nested);
nxt_lvlhsh_init(&scope->variables);
+ nxt_lvlhsh_init(&scope->references);

values = NULL;

@@ -171,17 +206,17 @@ njs_parser_scope_begin(njs_vm_t *vm, njs
}
}

- scope->values = values;
+ scope->values[0] = values;
+ scope->values[1] = NULL;

parent = parser->scope;
-
- if (parent != NULL) {
- parent->inclusive++;
- }
-
scope->parent = parent;
parser->scope = scope;

+ if (parent != NULL) {
+ nxt_queue_insert_tail(&parent->nested, &scope->link);
+ }
+
return NXT_OK;
}

@@ -194,18 +229,6 @@ njs_parser_scope_end(njs_vm_t *vm, njs_p
scope = parser->scope;

parent = scope->parent;
-
-#if 0
- if (scope->inclusive == 0
- && scope->type == NJS_SCOPE_BLOCK
- && nxt_lvlhsh_is_empty(&scope->variables))
- {
- parent->inclusive--;
-
- nxt_mem_cache_free(vm->mem_cache_pool, scope);
- }
-#endif
-
parser->scope = parent;
}

@@ -411,7 +434,7 @@ njs_parser_function_declaration(njs_vm_t
return NJS_TOKEN_ERROR;
}

- ret = njs_variable_reference(vm, parser, node);
+ ret = njs_variable_reference(vm, parser, node, 0);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
@@ -608,7 +631,7 @@ njs_parser_function_lambda(njs_vm_t *vm,
}
}

- lambda->nargs = njs_index_size(index) / sizeof(njs_value_t) - 1;
+ lambda->nargs = njs_scope_offset(index) / sizeof(njs_value_t) - 1;

token = njs_parser_token(parser);
if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
@@ -784,7 +807,7 @@ njs_parser_var_statement(njs_vm_t *vm, n

name->token = NJS_TOKEN_NAME;

- ret = njs_variable_reference(vm, parser, name);
+ ret = njs_variable_reference(vm, parser, name, 0);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
@@ -1275,7 +1298,7 @@ njs_parser_for_var_statement(njs_vm_t *v

name->token = NJS_TOKEN_NAME;

- ret = njs_variable_reference(vm, parser, name);
+ ret = njs_variable_reference(vm, parser, name, 0);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
@@ -1557,7 +1580,7 @@ njs_parser_try_statement(njs_vm_t *vm, n
return NJS_TOKEN_ERROR;
}

- var = njs_variable_add(vm, parser, NJS_VARIABLE_LET);
+ var = njs_variable_add(vm, parser, NJS_VARIABLE_CATCH);
if (nxt_slow_path(var == NULL)) {
return NJS_TOKEN_ERROR;
}
@@ -1569,7 +1592,7 @@ njs_parser_try_statement(njs_vm_t *vm, n

node->token = NJS_TOKEN_NAME;

- ret = njs_variable_reference(vm, parser, node);
+ ret = njs_variable_reference(vm, parser, node, 0);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
@@ -1788,7 +1811,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa
break;
}

- ret = njs_variable_reference(vm, parser, node);
+ ret = njs_variable_reference(vm, parser, node, 1);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
@@ -2001,7 +2024,7 @@ njs_parser_builtin_object(njs_vm_t *vm,
var->value.type = NJS_OBJECT;
var->value.data.truth = 1;

- ret = njs_variable_reference(vm, parser, node);
+ ret = njs_variable_reference(vm, parser, node, 1);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
@@ -2034,7 +2057,7 @@ njs_parser_builtin_function(njs_vm_t *vm
var->value.type = NJS_FUNCTION;
var->value.data.truth = 1;

- ret = njs_variable_reference(vm, parser, node);
+ ret = njs_variable_reference(vm, parser, node, 1);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
diff -r 0bde7f156477 -r a095dc0cd361 njs/njs_parser.h
--- a/njs/njs_parser.h Sat Mar 25 13:42:40 2017 +0300
+++ b/njs/njs_parser.h Tue Mar 28 07:50:05 2017 +0300
@@ -216,16 +216,20 @@ typedef struct {
((node)->token == NJS_TOKEN_NAME || (node)->token == NJS_TOKEN_PROPERTY)


-typedef struct njs_parser_scope_s njs_parser_scope_t;
-
struct njs_parser_scope_s {
- nxt_array_t *values; /* Array of njs_value_t. */
+ nxt_queue_link_t link;
+ nxt_queue_t nested;

- nxt_lvlhsh_t variables;
njs_parser_scope_t *parent;
- njs_index_t next_index;
- uint32_t inclusive;
+ nxt_lvlhsh_t variables;
+ nxt_lvlhsh_t references;
+
+ nxt_array_t *values[2]; /* Array of njs_value_t. */
+ njs_index_t next_index[2];
+
njs_scope_t type:8;
+ uint8_t nesting; /* 4 bits */
+ uint8_t argument_closures;
};


@@ -235,6 +239,7 @@ struct njs_parser_node_s {
njs_token_t token:16;
uint8_t ctor:1; /* 1 bit */
uint8_t temporary; /* 1 bit */
+ uint8_t reference; /* 1 bit */
uint32_t token_line;
uint32_t variable_name_hash;

@@ -353,11 +358,10 @@ njs_token_t njs_parser_property_token(nj
njs_token_t njs_parser_token(njs_parser_t *parser);
nxt_int_t njs_parser_string_create(njs_vm_t *vm, njs_value_t *value);
njs_ret_t njs_variable_reference(njs_vm_t *vm, njs_parser_t *parser,
- njs_parser_node_t *node);
-njs_variable_t *njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node,
- njs_name_reference_t reference);
-njs_index_t njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node,
- njs_name_reference_t reference);
+ njs_parser_node_t *node, nxt_bool_t reference);
+njs_variable_t *njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node);
+njs_index_t njs_variable_typeof(njs_vm_t *vm, njs_parser_node_t *node);
+njs_index_t njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node);
nxt_bool_t njs_parser_has_side_effect(njs_parser_node_t *node);
u_char *njs_parser_trace_handler(nxt_trace_t *trace, nxt_trace_data_t *td,
u_char *start);
diff -r 0bde7f156477 -r a095dc0cd361 njs/njs_string.c
--- a/njs/njs_string.c Sat Mar 25 13:42:40 2017 +0300
+++ b/njs/njs_string.c Tue Mar 28 07:50:05 2017 +0300
@@ -367,7 +367,7 @@ njs_string_constructor(njs_vm_t *vm, njs
value = &args[1];
}

- if (vm->frame->ctor) {
+ if (vm->top_frame->ctor) {
object = njs_object_value_alloc(vm, value, value->type);
if (nxt_slow_path(object == NULL)) {
return NXT_ERROR;
diff -r 0bde7f156477 -r a095dc0cd361 njs/njs_variable.c
--- a/njs/njs_variable.c Sat Mar 25 13:42:40 2017 +0300
+++ b/njs/njs_variable.c Tue Mar 28 07:50:05 2017 +0300
@@ -23,6 +23,15 @@
#include <string.h>


+typedef struct {
+ nxt_lvlhsh_query_t lhq;
+ njs_variable_t *variable;
+ njs_parser_scope_t *scope;
+} njs_variable_scope_t;
+
+
+static njs_ret_t njs_variable_find(njs_vm_t *vm, njs_parser_node_t *node,
+ njs_variable_scope_t *vs);
static njs_variable_t *njs_variable_alloc(njs_vm_t *vm, nxt_str_t *name,
njs_variable_type_t type);

@@ -42,7 +51,7 @@ njs_variables_hash_test(nxt_lvlhsh_query
}


-static const nxt_lvlhsh_proto_t njs_variables_hash_proto
+const nxt_lvlhsh_proto_t njs_variables_hash_proto
nxt_aligned(64) =
{
NXT_LVLHSH_DEFAULT,
@@ -155,103 +164,195 @@ njs_variable_add(njs_vm_t *vm, njs_parse
}


+static nxt_int_t
+njs_reference_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ njs_parser_node_t *node;
+
+ node = data;
+
+ if (nxt_strstr_eq(&lhq->key, &node->u.variable_name)) {
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+const nxt_lvlhsh_proto_t njs_reference_hash_proto
+ nxt_aligned(64) =
+{
+ NXT_LVLHSH_DEFAULT,
+ 0,
+ njs_reference_hash_test,
+ njs_lvlhsh_alloc,
+ njs_lvlhsh_free,
+};
+
+
njs_ret_t
njs_variable_reference(njs_vm_t *vm, njs_parser_t *parser,
- njs_parser_node_t *node)
+ njs_parser_node_t *node, nxt_bool_t reference)
{
- njs_ret_t ret;
+ njs_ret_t ret;
+ nxt_lvlhsh_query_t lhq;

ret = njs_name_copy(vm, &node->u.variable_name, &parser->lexer->text);

if (nxt_fast_path(ret == NXT_OK)) {
node->variable_name_hash = parser->lexer->key_hash;
node->scope = parser->scope;
+ node->reference = reference;
+
+ lhq.key_hash = node->variable_name_hash;
+ lhq.key = node->u.variable_name;
+ lhq.proto = &njs_reference_hash_proto;
+ lhq.replace = 0;
+ lhq.value = node;
+ lhq.pool = vm->mem_cache_pool;
+
+ ret = nxt_lvlhsh_insert(&parser->scope->references, &lhq);
+
+ if (nxt_slow_path(ret != NXT_ERROR)) {
+ ret = NXT_OK;
+ }
}

return ret;
}


-njs_variable_t *
-njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node,
- njs_name_reference_t reference)
+njs_ret_t
+njs_variables_scope_reference(njs_vm_t *vm, njs_parser_scope_t *scope)
{
- nxt_array_t *values;
- njs_index_t index;
- njs_value_t *value;
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

[njs] Closures support.

Igor Sysoev 776 March 28, 2017 12:26PM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 312
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready