]> git.kaiwu.me - njs.git/commitdiff
Closures support.
authorIgor Sysoev <igor@sysoev.ru>
Tue, 28 Mar 2017 04:50:05 +0000 (07:50 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Tue, 28 Mar 2017 04:50:05 +0000 (07:50 +0300)
16 files changed:
njs/njs_array.c
njs/njs_boolean.c
njs/njs_date.c
njs/njs_function.c
njs/njs_function.h
njs/njs_generator.c
njs/njs_number.c
njs/njs_parser.c
njs/njs_parser.h
njs/njs_string.c
njs/njs_variable.c
njs/njs_variable.h
njs/njs_vm.c
njs/njs_vm.h
njs/njscript.c
njs/test/njs_unit_test.c

index 0b85a12ddcfc741710a128d813b1731b16ca8d08..9149cbf7ac9a414d9c3ecce3c08874d1a40fecaa 100644 (file)
@@ -725,7 +725,7 @@ njs_array_prototype_to_string_continuation(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t retval)
 {
     /* Skip retval update. */
-    vm->frame->skip = 1;
+    vm->top_frame->skip = 1;
 
     return NXT_OK;
 }
index 77e8624969d0cd936392e8e4c15769308fb25266..d75476a10c7e9fb607d8df9cf386c904d3bf28d4 100644 (file)
@@ -34,7 +34,7 @@ njs_boolean_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
         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;
index e1bcec111a218270d3eddabfab26cb1740b80d5d..8d5bfe6d1815ce100ec4f18f4b15768839000d24 100644 (file)
@@ -91,7 +91,7 @@ njs_date_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     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(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t retval)
 {
     /* Skip retval update. */
-    vm->frame->skip = 1;
+    vm->top_frame->skip = 1;
 
     return NXT_OK;
 }
index a44bd0aa4a010e3fec1ec093a2eda46a7d85bf06..df620d853fb464f32677cf6001303bdcf90755da 100644 (file)
@@ -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, njs_value_t *value)
         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, njs_function_t *function,
     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, njs_function_t *function,
         } 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_function_t *function,
     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;
+
+    lambda = function->u.lambda;
 
-    max_args = nxt_max(nargs, function->u.lambda->nargs);
+    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_function_t *function,
     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_function_t *function,
         } 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_function_t *function,
     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, size_t size)
     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, size_t size)
     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_function_t *function, njs_value_t *args,
 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_function_t *function, njs_value_t *this,
         }
 
         /* 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_function_t *function, njs_value_t *this,
     }
 
     /* 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));
 }
index c30fd171ba8891a4f232352a3c75c8da1c07a1c8..6bcba89b5897578f2af79acdbda58322310e9366 100644 (file)
 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_value_t                    *local;
-    njs_value_t                    *closure;
+    njs_frame_t                    *previous_active_frame;
 
-    njs_index_t                    retval;
-} njs_frame_t;
+    njs_value_t                    *local;
+#if (NXT_SUNC)
+    njs_closure_t                  *closures[1];
+#else
+    njs_closure_t                  *closures[];
+#endif
+};
 
 
 njs_function_t *njs_function_alloc(njs_vm_t *vm);
index 5476ab5da6c41671f72cc20d4fa23a6e84de498c..ceeb12a4526595425e9c830f3a2fc38cdfab815b 100644 (file)
@@ -92,6 +92,8 @@ static nxt_int_t njs_generate_function_declaration(njs_vm_t *vm,
     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_parser_t *parser, njs_parser_node_t *node)
     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_parser_t *parser,
     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_parser_t *parser,
 {
     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, njs_parser_t *parser,
 
     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, njs_parser_t *parser,
 
     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 *vm, njs_parser_t *parser,
     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_t *vm, njs_parser_t *parser,
     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;
+    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, lambda->u.parser, node->right);
+    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_parser_t *parser, njs_parser_node_t *node)
     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_parser_t *parser, njs_parser_node_t *node)
     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_parser_t *parser, njs_parser_node_t *node)
         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_parser_t *parser, njs_parser_node_t *node)
 
     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_parser_t *parser, njs_parser_node_t *node)
 }
 
 
+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, njs_parser_t *parser,
     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, njs_parser_t *parser,
         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 *vm, njs_parser_t *parser,
          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 *vm, njs_parser_t *parser,
 
     *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;
 }
index 45e816e258b20a7875ea55df5e44346506fe49ef..544c71478c84a2e46cb5f4b10e2724837e1eefcd 100644 (file)
@@ -242,7 +242,7 @@ njs_number_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
         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;
index f45bb635cc76bf3cbaed53b7dd647a5632f9e8f7..c2eca2b56764e6432f20d42fbe9d201b2f60cb0b 100644 (file)
@@ -142,9 +142,32 @@ njs_parser(njs_vm_t *vm, njs_parser_t *parser)
 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_parser_t *parser, njs_scope_t type)
 
     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->nesting = nesting;
+    scope->argument_closures = 0;
 
-    scope->inclusive = 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_parser_t *parser, njs_scope_t type)
         }
     }
 
-    scope->values = values;
+    scope->values[0] = values;
+    scope->values[1] = NULL;
 
     parent = parser->scope;
+    scope->parent = parent;
+    parser->scope = scope;
 
     if (parent != NULL) {
-        parent->inclusive++;
+        nxt_queue_insert_tail(&parent->nested, &scope->link);
     }
 
-    scope->parent = parent;
-    parser->scope = scope;
-
     return NXT_OK;
 }
 
@@ -194,18 +229,6 @@ njs_parser_scope_end(njs_vm_t *vm, njs_parser_t *parser)
     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 *vm, njs_parser_t *parser)
         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, njs_function_lambda_t *lambda,
         }
     }
 
-    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, njs_parser_t *parser)
 
         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 *vm, njs_parser_t *parser)
 
         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, njs_parser_t *parser)
             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, njs_parser_t *parser)
 
         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_parser_t *parser, njs_token_t token)
             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, njs_parser_t *parser,
     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, njs_parser_t *parser,
     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;
     }
index 502efa93f63f7d048932cb439e1dc768419570f4..96ba7fd8cf22ebf4cac9844ddfd769738852ca36 100644 (file)
@@ -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(njs_parser_t *parser);
 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);
index 39c8806f19b3a053f420670d92116fd35a13ac3d..d78c79239fbb9b007ce57a17125c1392c4053300 100644 (file)
@@ -367,7 +367,7 @@ njs_string_constructor(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
         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;
index 7cc48bfc06ec3ccd8cde978a8e4ccd347ecdf8a8..f6e05591b897c94e7e8ff2450cc06f577c14fade 100644 (file)
 #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_t *lhq, void *data)
 }
 
 
-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_parser_t *parser, njs_variable_type_t type)
 }
 
 
+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;
+    njs_ret_t           ret;
+    nxt_queue_t         *nested;
     njs_variable_t      *var;
-    njs_parser_scope_t  *scope, *parent, *inclusive;
-    nxt_lvlhsh_query_t  lhq;
+    nxt_queue_link_t    *lnk;
+    njs_parser_node_t   *node;
+    nxt_lvlhsh_each_t   lhe;
 
-    lhq.key_hash = node->variable_name_hash;
-    lhq.key = node->u.variable_name;
-    lhq.proto = &njs_variables_hash_proto;
+    nested = &scope->nested;
 
-    inclusive = NULL;
-    scope = node->scope;
+    for (lnk = nxt_queue_first(nested);
+         lnk != nxt_queue_tail(nested);
+         lnk = nxt_queue_next(lnk))
+    {
+        scope = nxt_queue_link_data(lnk, njs_parser_scope_t, link);
 
-    for ( ;; ) {
-        if (nxt_lvlhsh_find(&scope->variables, &lhq) == NXT_OK) {
-            var = lhq.value;
+        ret = njs_variables_scope_reference(vm, scope);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NXT_ERROR;
+        }
 
-            if (scope->type == NJS_SCOPE_SHIM) {
-                scope = inclusive;
+        memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t));
+        lhe.proto = &njs_variables_hash_proto;
 
-            } 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;
-                }
+        for ( ;; ) {
+            node = nxt_lvlhsh_each(&scope->references, &lhe);
+
+            if (node == NULL) {
+                break;
             }
 
-            goto found;
+            var = njs_variable_get(vm, node);
+            if (nxt_slow_path(var == NULL)) {
+                return NXT_ERROR;
+            }
         }
+    }
 
-        parent = scope->parent;
+    return NXT_OK;
+}
 
-        if (parent == NULL) {
-            /* A global scope. */
-            break;
-        }
 
-        inclusive = scope;
-        scope = parent;
+njs_index_t
+njs_variable_typeof(njs_vm_t *vm, njs_parser_node_t *node)
+{
+    nxt_int_t             ret;
+    njs_variable_scope_t  vs;
+
+    if (node->index != NJS_INDEX_NONE) {
+        return node->index;
     }
 
-    if (reference != NJS_NAME_TYPEOF) {
-        goto not_found;
+    ret = njs_variable_find(vm, node, &vs);
+
+    if (nxt_fast_path(ret == NXT_OK)) {
+        return vs.variable->index;
     }
 
-    var = njs_variable_alloc(vm, &lhq.key, NJS_VARIABLE_TYPEOF);
-    if (nxt_slow_path(var == NULL)) {
-        return NULL;
+    return NJS_INDEX_NONE;
+}
+
+
+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->index = NJS_INDEX_NONE;
+    var = njs_variable_get(vm, node);
 
-    return var;
+    if (nxt_fast_path(var != NULL)) {
+        return var->index;
+    }
+
+    return NJS_INDEX_ERROR;
+}
+
+
+njs_variable_t *
+njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node)
+{
+    nxt_int_t             ret;
+    nxt_uint_t            n;
+    nxt_array_t           *values;
+    njs_index_t           index;
+    njs_value_t           *value;
+    njs_variable_t        *var;
+    njs_variable_scope_t  vs;
 
-found:
+    ret = njs_variable_find(vm, node, &vs);
 
-    if (reference == NJS_NAME_REFERENCE && var->type == NJS_VARIABLE_TYPEOF) {
+    if (nxt_slow_path(ret != NXT_OK)) {
         goto not_found;
     }
 
+    n = (node->scope->nesting != vs.scope->nesting);
+
+    var = vs.variable;
     index = var->index;
 
     if (index != NJS_INDEX_NONE) {
-        node->index = index;
-        return var;
+
+        if (n == 0 || njs_scope_type(index) != NJS_SCOPE_ARGUMENTS) {
+            node->index = index;
+
+            return var;
+        }
+
+        vs.scope->argument_closures++;
+        index = (index >> NJS_SCOPE_SHIFT) + 1;
+
+        if (index > 255 || vs.scope->argument_closures == 0) {
+            nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
+                      "InternalError: too many argument closures");
+
+            return NULL;
+        }
+
+        var->argument = index;
     }
 
-    if (reference == NJS_NAME_REFERENCE && var->type <= NJS_VARIABLE_LET) {
+    if (node->reference && var->type <= NJS_VARIABLE_LET) {
         goto not_found;
     }
 
-    values = scope->values;
+    values = vs.scope->values[n];
 
     if (values == NULL) {
         values = nxt_array_create(4, sizeof(njs_value_t), &njs_array_mem_proto,
@@ -260,7 +361,7 @@ found:
             return NULL;
         }
 
-        scope->values = values;
+        vs.scope->values[n] = values;
     }
 
     value = nxt_array_add(values, &njs_array_mem_proto, vm->mem_cache_pool);
@@ -275,8 +376,8 @@ found:
         *value = njs_value_void;
     }
 
-    index = scope->next_index;
-    scope->next_index += sizeof(njs_value_t);
+    index = vs.scope->next_index[n];
+    vs.scope->next_index[n] += sizeof(njs_value_t);
 
     var->index = index;
     node->index = index;
@@ -287,25 +388,59 @@ not_found:
 
     nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
               "ReferenceError: \"%.*s\" is not defined",
-              (int) lhq.key.length, lhq.key.start);
+              (int) vs.lhq.key.length, vs.lhq.key.start);
 
     return NULL;
 }
 
 
-njs_index_t
-njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node,
-    njs_name_reference_t reference)
+static njs_ret_t
+njs_variable_find(njs_vm_t *vm, njs_parser_node_t *node,
+    njs_variable_scope_t *vs)
 {
-    njs_variable_t  *var;
+    njs_parser_scope_t  *scope, *parent, *previous;
 
-    var = njs_variable_get(vm, node, reference);
+    vs->lhq.key_hash = node->variable_name_hash;
+    vs->lhq.key = node->u.variable_name;
+    vs->lhq.proto = &njs_variables_hash_proto;
 
-    if (nxt_fast_path(var != NULL)) {
-        return var->index;
-    }
+    previous = NULL;
+    scope = node->scope;
 
-    return NJS_INDEX_ERROR;
+    for ( ;; ) {
+        if (nxt_lvlhsh_find(&scope->variables, &vs->lhq) == NXT_OK) {
+            vs->variable = vs->lhq.value;
+
+            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;
+                }
+            }
+
+            vs->scope = scope;
+
+            return NXT_OK;
+        }
+
+        parent = scope->parent;
+
+        if (parent == NULL) {
+            /* A global scope. */
+            vs->scope = scope;
+
+            return NXT_DECLINED;
+        }
+
+        previous = scope;
+        scope = parent;
+    }
 }
 
 
index 60294b37c5a103f404a4971c2a392f530fe958d4..c86e9e2fa5789505a88c887bce89598d1b1e45d5 100644 (file)
 typedef enum {
     NJS_VARIABLE_CONST = 0,
     NJS_VARIABLE_LET,
-    NJS_VARIABLE_TYPEOF,
+    NJS_VARIABLE_CATCH,
     NJS_VARIABLE_SHIM,
     NJS_VARIABLE_VAR,
     NJS_VARIABLE_FUNCTION,
 } njs_variable_type_t;
 
 
-typedef enum {
-    NJS_NAME_REFERENCE = 0,
-    NJS_NAME_DECLARATION,
-    NJS_NAME_TYPEOF,
-} njs_name_reference_t;
-
-
 typedef struct {
     nxt_str_t             name;
+
     njs_variable_type_t   type:8;    /* 3 bits */
+    uint8_t               closure;   /* 1 bit  */
+    uint8_t               argument;
 
     njs_index_t           index;
     njs_value_t           value;
@@ -36,15 +32,19 @@ typedef struct {
 
 #define njs_global_variable_value(vm, var)                                    \
     (njs_value_t *) ((u_char *) vm->global_scope                              \
-                      + njs_offset((var)->index) - NJS_INDEX_GLOBAL_OFFSET)
+         + njs_scope_offset((var)->index) - NJS_INDEX_GLOBAL_OFFSET)
 
 
 njs_variable_t *njs_builtin_add(njs_vm_t *vm, njs_parser_t *parser);
 njs_variable_t *njs_variable_add(njs_vm_t *vm, njs_parser_t *parser,
     njs_variable_type_t type);
+njs_ret_t njs_variables_scope_reference(njs_vm_t *vm,
+    njs_parser_scope_t *scope);
 njs_ret_t njs_name_copy(njs_vm_t *vm, nxt_str_t *dst, nxt_str_t *src);
 
 nxt_str_t *njs_vm_export_functions(njs_vm_t *vm);
 
+extern const nxt_lvlhsh_proto_t  njs_variables_hash_proto;
+
 
 #endif /* _NJS_VARIABLE_H_INCLUDED_ */
index 7b15e8f54a78a91e704bbe1844a77ad1805c9e7d..d16cbf608ff8000d372483ce564b3735c9233f59 100644 (file)
@@ -85,6 +85,8 @@ static nxt_noinline njs_ret_t njs_values_compare(const njs_value_t *val1,
 static njs_ret_t njs_function_frame_create(njs_vm_t *vm, njs_value_t *value,
     const njs_value_t *this, uintptr_t nargs, nxt_bool_t ctor);
 static njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *value);
+static void njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame,
+    njs_native_frame_t *previous);
 static njs_ret_t njs_vmcode_continuation(njs_vm_t *vm, njs_value_t *invld1,
     njs_value_t *invld2);
 static njs_native_frame_t *
@@ -257,7 +259,7 @@ start:
     if (ret == NXT_ERROR) {
 
         for ( ;; ) {
-            frame = (njs_frame_t *) vm->frame;
+            frame = (njs_frame_t *) vm->top_frame;
             catch = frame->native.exception.catch;
 
             if (catch != NULL) {
@@ -270,13 +272,7 @@ start:
                 return NXT_ERROR;
             }
 
-            vm->frame = previous;
-
-            /* GC: NJS_SCOPE_ARGUMENTS and NJS_SCOPE_FUNCTION. */
-
-            vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = previous->arguments;
-            vm->scopes[NJS_SCOPE_FUNCTION] = frame->prev_local;
-            vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->prev_arguments;
+            njs_vm_scopes_restore(vm, frame, previous);
 
             if (frame->native.size != 0) {
                 vm->stack_size -= frame->native.size;
@@ -409,28 +405,45 @@ njs_vmcode_array(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
 njs_ret_t
 njs_vmcode_function(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
 {
+    size_t                 size;
+    nxt_uint_t             n, nesting;
     njs_function_t         *function;
+    njs_function_lambda_t  *lambda;
     njs_vmcode_function_t  *code;
 
-    function = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_function_t));
+    code = (njs_vmcode_function_t *) vm->current;
+    lambda = code->lambda;
+    nesting = lambda->nesting;
 
-    if (nxt_fast_path(function != NULL)) {
-        function->object.shared_hash = vm->shared->function_prototype_hash;
-        function->object.__proto__ =
-                                &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object;
-        function->args_offset = 1;
+    size = sizeof(njs_function_t) + nesting * sizeof(njs_closure_t *);
 
-        code = (njs_vmcode_function_t *) vm->current;
-        function->u.lambda = code->lambda;
+    function = nxt_mem_cache_zalloc(vm->mem_cache_pool, size);
+    if (nxt_slow_path(function == NULL)) {
+        return NXT_ERROR;
+    }
 
-        vm->retval.data.u.function = function;
-        vm->retval.type = NJS_FUNCTION;
-        vm->retval.data.truth = 1;
+    function->u.lambda = lambda;
+    function->object.shared_hash = vm->shared->function_prototype_hash;
+    function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object;
+    function->args_offset = 1;
 
-        return sizeof(njs_vmcode_function_t);
+    if (nesting != 0) {
+        function->closure = 1;
+
+        n = 0;
+
+        do {
+            /* GC: retain closure. */
+            function->closures[n] = vm->active_frame->closures[n];
+            n++;
+        } while (n < nesting);
     }
 
-    return NXT_ERROR;
+    vm->retval.data.u.function = function;
+    vm->retval.type = NJS_FUNCTION;
+    vm->retval.data.truth = 1;
+
+    return sizeof(njs_vmcode_function_t);
 }
 
 
@@ -2331,16 +2344,22 @@ njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
     njs_continuation_t  *cont;
     njs_native_frame_t  *frame;
 
-    function = vm->frame->function;
+    frame = vm->top_frame;
+    function = frame->function;
 
     if (!function->native) {
-        (void) njs_function_call(vm, (njs_index_t) retval,
-                                 sizeof(njs_vmcode_function_call_t));
-        return 0;
+        ret = njs_function_call(vm, (njs_index_t) retval,
+                                sizeof(njs_vmcode_function_call_t));
+
+        if (nxt_fast_path(ret != NJS_ERROR)) {
+            return 0;
+        }
+
+        return ret;
     }
 
-    args = vm->frame->arguments - function->args_offset;
-    nargs = vm->frame->nargs;
+    args = frame->arguments;
+    nargs = frame->nargs;
 
     ret = njs_normalize_args(vm, args, function->args_types, nargs);
     if (ret != NJS_OK) {
@@ -2373,16 +2392,17 @@ njs_vmcode_function_call(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
      * for NJS_APPLIED and NXT_AGAIN cases.
      */
     if (ret == NXT_OK) {
-        frame = vm->frame;
+        frame = vm->top_frame;
 
-        vm->frame = njs_function_previous_frame(frame);
+        vm->top_frame = njs_function_previous_frame(frame);
         (void) njs_function_frame_free(vm, frame);
 
         /*
          * If a retval is in a callee arguments scope it
          * must be in the previous callee arguments scope.
          */
-        vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = vm->frame->arguments;
+        vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] =
+                              vm->top_frame->arguments + function->args_offset;
 
         retval = njs_vmcode_operand(vm, retval);
         /*
@@ -2540,12 +2560,11 @@ njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
 {
     njs_value_t         *value;
     njs_frame_t         *frame;
-    njs_value_t         *args;
     njs_native_frame_t  *previous;
 
     value = njs_vmcode_operand(vm, retval);
 
-    frame = (njs_frame_t *) vm->frame;
+    frame = (njs_frame_t *) vm->top_frame;
 
     if (frame->native.ctor) {
         if (njs_is_object(value)) {
@@ -2557,12 +2576,8 @@ njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
     }
 
     previous = njs_function_previous_frame(&frame->native);
-    vm->frame = previous;
 
-    vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = previous->arguments;
-    vm->scopes[NJS_SCOPE_FUNCTION] = frame->prev_local;
-    args = vm->scopes[NJS_SCOPE_ARGUMENTS];
-    vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->prev_arguments;
+    njs_vm_scopes_restore(vm, frame, previous);
 
     /*
      * If a retval is in a callee arguments scope it
@@ -2575,11 +2590,59 @@ njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
 
     vm->current = frame->return_address;
 
-    /* GC: arguments and local. */
+    return njs_function_frame_free(vm, &frame->native);
+}
 
-    njs_release(vm, &args[0]);
 
-    return njs_function_frame_free(vm, &frame->native);
+static void
+njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame,
+    njs_native_frame_t *previous)
+{
+    nxt_uint_t      n, nesting;
+    njs_value_t     *args;
+    njs_function_t  *function;
+
+    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 = frame->native.function;
+
+    if (function->native) {
+        return;
+    }
+
+    if (function->closure) {
+        /* GC: release function closures. */
+    }
+
+    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;
+
+    for (n = 0; n <= nesting; n++) {
+        vm->scopes[NJS_SCOPE_CLOSURE + n] = &frame->closures[n]->u.values;
+    }
+
+    while (n < NJS_MAX_NESTING) {
+        vm->scopes[NJS_SCOPE_CLOSURE + n] = NULL;
+        n++;
+    }
 }
 
 
@@ -2596,12 +2659,13 @@ njs_vmcode_continuation(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
     njs_ret_t           ret;
     nxt_bool_t          skip;
     njs_value_t         *args, *retval;
+    njs_function_t      *function;
     njs_native_frame_t  *frame;
     njs_continuation_t  *cont;
 
     cont = njs_vm_continuation(vm);
-    frame = vm->frame;
-    args = frame->arguments - frame->function->args_offset;
+    frame = vm->top_frame;
+    args = frame->arguments;
 
     if (cont->args_types != NULL) {
         ret = njs_normalize_args(vm, args, cont->args_types, frame->nargs);
@@ -2616,16 +2680,23 @@ njs_vmcode_continuation(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
 
     case NXT_OK:
 
-        frame = vm->frame;
+        frame = vm->top_frame;
         skip = frame->skip;
 
-        vm->frame = njs_function_previous_frame(frame);
+        vm->top_frame = njs_function_previous_frame(frame);
 
         /*
          * If a retval is in a callee arguments scope it
          * must be in the previous callee arguments scope.
          */
-        vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = vm->frame->arguments;
+        args = vm->top_frame->arguments;
+        function = vm->top_frame->function;
+
+        if (function != NULL) {
+            args += function->args_offset;
+        }
+
+        vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = args;
 
         if (!skip) {
             retval = njs_vmcode_operand(vm, cont->retval);
@@ -2712,17 +2783,17 @@ njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *value, njs_value_t *offset)
 {
     njs_exception_t  *e;
 
-    if (vm->frame->exception.catch != NULL) {
+    if (vm->top_frame->exception.catch != NULL) {
         e = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_exception_t));
         if (nxt_slow_path(e == NULL)) {
             return NXT_ERROR;
         }
 
-        *e = vm->frame->exception;
-        vm->frame->exception.next = e;
+        *e = vm->top_frame->exception;
+        vm->top_frame->exception.next = e;
     }
 
-    vm->frame->exception.catch = vm->current + (njs_ret_t) offset;
+    vm->top_frame->exception.catch = vm->current + (njs_ret_t) offset;
 
     njs_set_invalid(value);
 
@@ -2740,13 +2811,13 @@ njs_vmcode_try_end(njs_vm_t *vm, njs_value_t *invld, njs_value_t *offset)
 {
     njs_exception_t  *e;
 
-    e = vm->frame->exception.next;
+    e = vm->top_frame->exception.next;
 
     if (e == NULL) {
-        vm->frame->exception.catch = NULL;
+        vm->top_frame->exception.catch = NULL;
 
     } else {
-        vm->frame->exception = *e;
+        vm->top_frame->exception = *e;
         nxt_mem_cache_free(vm->mem_cache_pool, e);
     }
 
@@ -2784,7 +2855,7 @@ njs_vmcode_catch(njs_vm_t *vm, njs_value_t *exception, njs_value_t *offset)
         return njs_vmcode_try_end(vm, exception, offset);
     }
 
-    vm->frame->exception.catch = vm->current + (njs_ret_t) offset;
+    vm->top_frame->exception.catch = vm->current + (njs_ret_t) offset;
 
     return sizeof(njs_vmcode_catch_t);
 }
@@ -2884,7 +2955,7 @@ njs_vm_trap(njs_vm_t *vm, nxt_uint_t trap, njs_value_t *value1,
 {
     njs_native_frame_t  *frame;
 
-    frame = vm->frame;
+    frame = vm->top_frame;
 
     /*
      * The trap_scratch value is for results of "valueOf" and "toString"
@@ -2916,7 +2987,7 @@ njs_vm_trap_argument(njs_vm_t *vm, nxt_uint_t trap)
     njs_value_t         *value;
     njs_native_frame_t  *frame;
 
-    frame = vm->frame;
+    frame = vm->top_frame;
     value = frame->trap_scratch.data.u.value;
     njs_set_invalid(&frame->trap_scratch);
 
@@ -2939,7 +3010,7 @@ njs_vmcode_number_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
     njs_ret_t    ret;
     njs_value_t  *value;
 
-    value = &vm->frame->trap_values[(uintptr_t) narg];
+    value = &vm->top_frame->trap_values[(uintptr_t) narg];
 
     ret = njs_primitive_value(vm, value, 0);
 
@@ -2968,7 +3039,7 @@ njs_vmcode_string_primitive(njs_vm_t *vm, njs_value_t *invld, njs_value_t *narg)
     njs_ret_t    ret;
     njs_value_t  *value;
 
-    value = &vm->frame->trap_values[(uintptr_t) narg];
+    value = &vm->top_frame->trap_values[(uintptr_t) narg];
 
     ret = njs_primitive_value(vm, value, 1);
 
@@ -2992,7 +3063,7 @@ njs_vmcode_number_argument(njs_vm_t *vm, njs_value_t *invld1,
     njs_ret_t    ret;
     njs_value_t  *value;
 
-    value = &vm->frame->trap_values[0];
+    value = &vm->top_frame->trap_values[0];
 
     ret = njs_primitive_value(vm, value, 0);
 
@@ -3008,10 +3079,10 @@ njs_vmcode_number_argument(njs_vm_t *vm, njs_value_t *invld1,
             njs_number_set(value, num);
         }
 
-        *vm->frame->trap_values[1].data.u.value = *value;
+        *vm->top_frame->trap_values[1].data.u.value = *value;
 
-        vm->current = vm->frame->trap_restart;
-        vm->frame->trap_restart = NULL;
+        vm->current = vm->top_frame->trap_restart;
+        vm->top_frame->trap_restart = NULL;
 
         return 0;
     }
@@ -3027,7 +3098,7 @@ njs_vmcode_string_argument(njs_vm_t *vm, njs_value_t *invld1,
     njs_ret_t    ret;
     njs_value_t  *value;
 
-    value = &vm->frame->trap_values[0];
+    value = &vm->top_frame->trap_values[0];
 
     ret = njs_primitive_value(vm, value, 1);
 
@@ -3035,10 +3106,10 @@ njs_vmcode_string_argument(njs_vm_t *vm, njs_value_t *invld1,
         ret = njs_primitive_value_to_string(vm, value, value);
 
         if (nxt_fast_path(ret == NXT_OK)) {
-            *vm->frame->trap_values[1].data.u.value = *value;
+            *vm->top_frame->trap_values[1].data.u.value = *value;
 
-            vm->current = vm->frame->trap_restart;
-            vm->frame->trap_restart = NULL;
+            vm->current = vm->top_frame->trap_restart;
+            vm->top_frame->trap_restart = NULL;
         }
     }
 
@@ -3072,7 +3143,7 @@ njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
     };
 
     if (!njs_is_primitive(value)) {
-        retval = &vm->frame->trap_scratch;
+        retval = &vm->top_frame->trap_scratch;
 
         if (!njs_is_primitive(retval)) {
 
@@ -3080,8 +3151,8 @@ njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
                 vm->exception = &njs_exception_type_error;
                 ret = NXT_ERROR;
 
-                if (njs_is_object(value) && vm->frame->trap_tries < 2) {
-                    hint ^= vm->frame->trap_tries++;
+                if (njs_is_object(value) && vm->top_frame->trap_tries < 2) {
+                    hint ^= vm->top_frame->trap_tries++;
 
                     lhq.key_hash = hashes[hint];
                     lhq.key = names[hint];
@@ -3134,7 +3205,7 @@ njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
         njs_set_invalid(retval);
     }
 
-    vm->frame->trap_tries = 0;
+    vm->top_frame->trap_tries = 0;
 
     return 1;
 }
@@ -3149,7 +3220,7 @@ njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
     njs_native_frame_t    *frame;
     njs_vmcode_generic_t  *vmcode;
 
-    frame = vm->frame;
+    frame = vm->top_frame;
     restart = frame->trap_restart;
     frame->trap_restart = NULL;
     vm->current = restart;
@@ -3253,14 +3324,14 @@ njs_object_value_to_string(njs_vm_t *vm, njs_value_t *value)
     current = vm->current;
     vm->current = (u_char *) value_to_string;
 
-    njs_set_invalid(&vm->frame->trap_scratch);
-    vm->frame->trap_values[0] = *value;
+    njs_set_invalid(&vm->top_frame->trap_scratch);
+    vm->top_frame->trap_values[0] = *value;
 
     ret = njs_vmcode_interpreter(vm);
 
     if (ret == NJS_STOP) {
         ret = NXT_OK;
-        *value = vm->frame->trap_values[0];
+        *value = vm->top_frame->trap_values[0];
     }
 
     vm->current = current;
@@ -3275,7 +3346,7 @@ njs_vmcode_value_to_string(njs_vm_t *vm, njs_value_t *invld1,
 {
     njs_ret_t  ret;
 
-    ret = njs_primitive_value(vm, &vm->frame->trap_values[0], 1);
+    ret = njs_primitive_value(vm, &vm->top_frame->trap_values[0], 1);
 
     if (nxt_fast_path(ret > 0)) {
         return NJS_STOP;
index 772526af23f96ee0fc22731463aa99397f12c969..a50285d38f66a37ad7d8f5d09f8d12e9a473b4c9 100644 (file)
@@ -9,6 +9,7 @@
 
 
 #include <nxt_trace.h>
+#include <nxt_queue.h>
 #include <nxt_regex.h>
 
 
@@ -122,8 +123,10 @@ typedef struct njs_regexp_s           njs_regexp_t;
 typedef struct njs_regexp_pattern_s   njs_regexp_pattern_t;
 typedef struct njs_date_s             njs_date_t;
 typedef struct njs_extern_s           njs_extern_t;
+typedef struct njs_frame_s            njs_frame_t;
 typedef struct njs_native_frame_s     njs_native_frame_t;
 typedef struct njs_property_next_s    njs_property_next_t;
+typedef struct njs_parser_scope_s     njs_parser_scope_t;
 
 
 union njs_value_s {
@@ -225,6 +228,16 @@ struct njs_array_s {
 };
 
 
+typedef struct {
+    union {
+        uint32_t                      count;
+        njs_value_t                   values;
+    } u;
+
+    njs_value_t                       values[1];
+} njs_closure_t;
+
+
 #define NJS_ARGS_TYPES_MAX            5
 
 struct njs_function_s {
@@ -234,6 +247,9 @@ struct njs_function_s {
     uint8_t                           args_offset;
     uint8_t                           continuation_size;
 
+    /* Function is a closure. */
+    uint8_t                           closure:1;
+
     uint8_t                           native:1;
     uint8_t                           ctor:1;
 
@@ -243,6 +259,11 @@ struct njs_function_s {
     } u;
 
     njs_value_t                       *bound;
+#if (NXT_SUNC)
+    njs_closure_t                     *closures[1];
+#else
+    njs_closure_t                     *closures[];
+#endif
 };
 
 
@@ -667,14 +688,17 @@ typedef struct {
 
 typedef enum {
     NJS_SCOPE_ABSOLUTE = 0,
-    NJS_SCOPE_GLOBAL,
-    NJS_SCOPE_FUNCTION,
-    NJS_SCOPE_CALLEE_ARGUMENTS,
-    NJS_SCOPE_ARGUMENTS,
-    NJS_SCOPE_CLOSURE,
-    NJS_SCOPE_PARENT_LOCAL,
-    NJS_SCOPE_PARENT_ARGUMENTS,
-    NJS_SCOPE_PARENT_CLOSURE,
+    NJS_SCOPE_GLOBAL = 1,
+    NJS_SCOPE_CALLEE_ARGUMENTS = 2,
+    /*
+     * The argument and local VM scopes should separate 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 used only on parsing phase.
@@ -684,7 +708,13 @@ typedef enum {
 } njs_scope_t;
 
 
-#define NJS_SCOPES             (NJS_SCOPE_PARENT_CLOSURE + 1)
+/*
+ * The maximum possible function nesting level is (16 - NJS_SCOPE_CLOSURE),
+ * that is 11.  The 5 is reasonable limit.
+ */
+#define NJS_MAX_NESTING        5
+
+#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))
@@ -695,6 +725,9 @@ typedef enum {
 #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)
 
@@ -754,8 +787,8 @@ enum njs_function_e {
 };
 
 
-#define njs_scope_index(value)                                                \
-    ((njs_index_t) ((value) << NJS_SCOPE_SHIFT))
+#define njs_scope_index(value, type)                                          \
+    ((njs_index_t) (((value) << NJS_SCOPE_SHIFT) | (type)))
 
 #define njs_global_scope_index(value)                                         \
     ((njs_index_t) (((value) << NJS_SCOPE_SHIFT) | NJS_SCOPE_GLOBAL))
@@ -772,21 +805,17 @@ enum njs_function_e {
 #define NJS_INDEX_DATE           njs_global_scope_index(NJS_CONSTRUCTOR_DATE)
 
 #define NJS_INDEX_GLOBAL_RETVAL  njs_global_scope_index(NJS_CONSTRUCTOR_MAX)
-#define NJS_INDEX_GLOBAL_OFFSET  njs_scope_index(NJS_CONSTRUCTOR_MAX + 1)
+#define NJS_INDEX_GLOBAL_OFFSET  njs_scope_index(NJS_CONSTRUCTOR_MAX + 1, 0)
 
 
-#define njs_offset(index)                                                     \
+#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_offset(index)))
-
-
-#define njs_index_size(index)                                                 \
-    njs_offset(index)
+      + njs_scope_offset(index)))
 
 
 typedef struct {
@@ -811,7 +840,8 @@ struct njs_vm_s {
 
     void                     **external;
 
-    njs_native_frame_t       *frame;
+    njs_native_frame_t       *top_frame;
+    njs_frame_t              *active_frame;
 
     const njs_value_t        *exception;
 
index 6b1b261b5b83dc4eea454b353ad39e099646b5a2..d8c8dd94f7f6e2dac9bc231a94d98081bbbb79a7 100644 (file)
@@ -217,6 +217,11 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end, nxt_str_t **export)
         return NJS_ERROR;
     }
 
+    ret = njs_variables_scope_reference(vm, parser->scope);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NJS_ERROR;
+    }
+
     *start = parser->lexer->start;
 
     ret = njs_generate_scope(vm, parser, node);
@@ -293,7 +298,8 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external)
 
         memset(frame, 0, NJS_GLOBAL_FRAME_SIZE);
 
-        nvm->frame = &frame->native;
+        nvm->top_frame = &frame->native;
+        nvm->active_frame = frame;
 
         frame->native.size = size;
         frame->native.free_size = size - (NJS_GLOBAL_FRAME_SIZE + scope_size);
@@ -353,7 +359,6 @@ njs_vm_call(njs_vm_t *vm, njs_function_t *function, njs_opaque_value_t *args,
 
     ret = njs_function_frame(vm, function, this,
                              (njs_value_t *) args, nargs, 0);
-
     if (nxt_slow_path(ret != NXT_OK)) {
         return ret;
     }
@@ -361,7 +366,10 @@ njs_vm_call(njs_vm_t *vm, njs_function_t *function, njs_opaque_value_t *args,
     current = vm->current;
     vm->current = (u_char *) stop;
 
-    (void) njs_function_call(vm, NJS_INDEX_GLOBAL_RETVAL, 0);
+    ret = njs_function_call(vm, NJS_INDEX_GLOBAL_RETVAL, 0);
+    if (nxt_slow_path(ret == NXT_ERROR)) {
+        return ret;
+    }
 
     ret = njs_vmcode_interpreter(vm);
 
index b01576ec3d40da2f380921938744442214020483..495e02882bbd8baf33a3a2cecc4b6187b18cc962 100644 (file)
@@ -4127,6 +4127,39 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("(function f(a) { return (a > 1) ? a * f(a - 1) : 1 })(10)"),
       nxt_string("3628800") },
 
+    /* Nested functions and closures. */
+
+    { nxt_string("function f() { var x = 4; "
+                 "function g() { return x }; return g(); } f()"),
+      nxt_string("4") },
+
+    { nxt_string("function f(a) { function g(b) { return a + b } return g }"
+                 "var k = f('a'); k('b')"),
+      nxt_string("ab") },
+
+    { nxt_string("function f(a) { return function(b) { return a + b } }"
+                 "var k = f('a'); k('b')"),
+      nxt_string("ab") },
+
+    { nxt_string("function f(a) { return function(b) { return a + b } }"
+                 "var k = f('a'), m = f('b'); k('c') + m('d')"),
+      nxt_string("acbd") },
+
+    { nxt_string("function f(a) { return "
+                 "function(b) { return function(c) { return a + b + c } } }"
+                 "var g = f('a'), k = g('b'), m = g('c'); k('d') + m('e')"),
+      nxt_string("abdace") },
+
+    { nxt_string("function f(a) {"
+                 "function g() { return a }; return g; }"
+                 "var y = f(4); y()"),
+      nxt_string("4") },
+
+    { nxt_string("function f() { var x = 4; "
+                 "return function() { return x } }"
+                 "var y = f(); y()"),
+      nxt_string("4") },
+
     /* Recursive fibonacci. */
 
     { nxt_string("function fibo(n) {"
@@ -4396,10 +4429,8 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("[object Function]1") },
 #endif
 
-#if 0
     { nxt_string("function f() {}; function g() { return f }; g()"),
       nxt_string("[object Function]") },
-#endif
 
     { nxt_string("function f(a) { return this+a }; var a = f; a.call('0', 1)"),
       nxt_string("01") },