[njs] Closures support.

Igor Sysoev igor at sysoev.ru
Tue Mar 28 16:25:00 UTC 2017


details:   http://hg.nginx.org/njs/rev/a095dc0cd361
branches:  
changeset: 321:a095dc0cd361
user:      Igor Sysoev <igor at 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;


More information about the nginx-devel mailing list