Scopes refactoring.
diff --git a/auto/sources b/auto/sources
index 9391a01..e275618 100644
--- a/auto/sources
+++ b/auto/sources
@@ -59,6 +59,7 @@
    src/njs_encoding.c \
    src/njs_buffer.c \
    src/njs_iterator.c \
+   src/njs_scope.c \
 "
 
 NJS_LIB_TEST_SRCS=" \
diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c
index 64db047..9365596 100644
--- a/nginx/ngx_js.c
+++ b/nginx/ngx_js.c
@@ -261,7 +261,8 @@
     handler = c->log->handler;
     c->log->handler = NULL;
 
-    ngx_log_error(level, c->log, 0, "js: %*s", msg.length, msg.start);
+    ngx_log_error((ngx_uint_t) level, c->log, 0, "js: %*s",
+                  msg.length, msg.start);
 
     c->log->handler = handler;
 
diff --git a/src/njs.h b/src/njs.h
index bac7d11..adb7b90 100644
--- a/src/njs.h
+++ b/src/njs.h
@@ -210,7 +210,7 @@
 #define NJS_VM_OPT_UNHANDLED_REJECTION_THROW    1
 
 /*
- * accumulative  - enables "accumulative" mode to support incremental compiling.
+ * interactive  - enables "interactive" mode.
  *  (REPL). Allows starting parent VM without cloning.
  * disassemble   - enables disassemble.
  * backtrace     - enables backtraces.
@@ -225,10 +225,9 @@
  *   - throwing inside a Promise without a catch block.
  *   - throwing inside in a finally or catch block.
  */
-
+    uint8_t                         interactive;     /* 1 bit */
     uint8_t                         trailer;         /* 1 bit */
     uint8_t                         init;            /* 1 bit */
-    uint8_t                         accumulative;    /* 1 bit */
     uint8_t                         disassemble;     /* 1 bit */
     uint8_t                         backtrace;       /* 1 bit */
     uint8_t                         quiet;           /* 1 bit */
@@ -277,7 +276,7 @@
 NJS_EXPORT njs_int_t njs_vm_call(njs_vm_t *vm, njs_function_t *function,
     const njs_value_t *args, njs_uint_t nargs);
 NJS_EXPORT njs_int_t njs_vm_invoke(njs_vm_t *vm, njs_function_t *function,
-    const njs_value_t *args, njs_uint_t nargs, njs_index_t retval);
+    const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval);
 
 /*
  * Runs posted events.
diff --git a/src/njs_builtin.c b/src/njs_builtin.c
index 6a06052..dae0710 100644
--- a/src/njs_builtin.c
+++ b/src/njs_builtin.c
@@ -320,6 +320,8 @@
     vm->global_object = shared->objects[0];
     vm->global_object.shared = 0;
 
+    njs_set_object(&vm->global_value, &vm->global_object);
+
     string_object = &shared->string_object;
     njs_lvlhsh_init(&string_object->hash);
     string_object->shared_hash = shared->string_instance_hash;
@@ -637,7 +639,7 @@
     }
 
     var = ((njs_variable_node_t *) node)->variable;
-    value = njs_vmcode_operand(vm, var->index);
+    value = njs_scope_valid_value(vm, var->index);
 
     if (!njs_is_object(value)) {
         return NULL;
@@ -939,6 +941,8 @@
 {
     njs_int_t            ret;
     njs_value_t          *value;
+    njs_variable_t       *var;
+    njs_function_t       *function;
     njs_rbtree_node_t    *rb_node;
     njs_lvlhsh_query_t   lhq;
     njs_variable_node_t  *node, var_node;
@@ -965,7 +969,19 @@
     }
 
     node = (njs_variable_node_t *) rb_node;
-    value = njs_vmcode_operand(vm, node->variable->index);
+
+    var = node->variable;
+
+    value = njs_scope_valid_value(vm, var->index);
+
+    if (var->type == NJS_VARIABLE_FUNCTION && njs_is_undefined(value)) {
+        *value = var->value;
+
+        function = njs_function_value_copy(vm, value);
+        if (njs_slow_path(function == NULL)) {
+            return NJS_ERROR;
+        }
+    }
 
     if (setval != NULL) {
         *value = *setval;
diff --git a/src/njs_disassembler.c b/src/njs_disassembler.c
index dd67fa6..87d6fdd 100644
--- a/src/njs_disassembler.c
+++ b/src/njs_disassembler.c
@@ -35,6 +35,9 @@
     { NJS_VMCODE_OBJECT_COPY, sizeof(njs_vmcode_object_copy_t),
           njs_str("OBJECT COPY     ") },
 
+    { NJS_VMCODE_FUNCTION_COPY, sizeof(njs_vmcode_function_copy_t),
+          njs_str("FUNCTION COPY   ") },
+
     { NJS_VMCODE_PROPERTY_GET, sizeof(njs_vmcode_prop_get_t),
           njs_str("PROP GET        ") },
     { NJS_VMCODE_GLOBAL_GET, sizeof(njs_vmcode_prop_get_t),
@@ -186,6 +189,7 @@
     njs_vmcode_catch_t           *catch;
     njs_vmcode_finally_t         *finally;
     njs_vmcode_try_end_t         *try_end;
+    njs_vmcode_move_arg_t        *move_arg;
     njs_vmcode_try_start_t       *try_start;
     njs_vmcode_operation_t       operation;
     njs_vmcode_cond_jump_t       *cond_jump;
@@ -485,6 +489,17 @@
             continue;
         }
 
+        if (operation == NJS_VMCODE_MOVE_ARG) {
+            move_arg = (njs_vmcode_move_arg_t *) p;
+
+            njs_printf("%5uD | %05uz MOVE ARGUMENT     %uD %04Xz\n",
+                       line, p - start, move_arg->dst, (size_t) move_arg->src);
+
+            p += sizeof(njs_vmcode_move_arg_t);
+
+            continue;
+        }
+
         code_name = code_names;
         n = njs_nitems(code_names);
 
diff --git a/src/njs_function.c b/src/njs_function.c
index c8679c4..087765d 100644
--- a/src/njs_function.c
+++ b/src/njs_function.c
@@ -8,21 +8,13 @@
 #include <njs_main.h>
 
 
-static njs_function_t *njs_function_copy(njs_vm_t *vm,
-    njs_function_t *function);
-static njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size);
-
-
 njs_function_t *
-njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda,
-    njs_closure_t *closures[], njs_bool_t shared)
+njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda)
 {
     size_t          size;
-    njs_uint_t      n, nesting;
     njs_function_t  *function;
 
-    nesting = lambda->nesting;
-    size = sizeof(njs_function_t) + nesting * sizeof(njs_closure_t *);
+    size = sizeof(njs_function_t) + lambda->nclosures * sizeof(njs_value_t *);
 
     function = njs_mp_zalloc(vm->mem_pool, size);
     if (njs_slow_path(function == NULL)) {
@@ -48,21 +40,8 @@
 
     function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
     function->object.type = NJS_FUNCTION;
-    function->object.shared = shared;
     function->object.extensible = 1;
 
-    if (nesting != 0 && closures != NULL) {
-        function->closure = 1;
-
-        n = 0;
-
-        do {
-            /* GC: retain closure. */
-            njs_function_closures(function)[n] = closures[n];
-            n++;
-        } while (n < nesting);
-    }
-
     return function;
 
 fail:
@@ -121,14 +100,6 @@
 }
 
 
-njs_inline njs_closure_t **
-njs_function_active_closures(njs_vm_t *vm, njs_function_t *function)
-{
-    return (function->closure) ? njs_function_closures(function)
-                               : njs_frame_closures(vm->active_frame);
-}
-
-
 njs_int_t
 njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
     njs_value_t *name, const char *prefix)
@@ -199,17 +170,16 @@
 }
 
 
-static njs_function_t *
+njs_function_t *
 njs_function_copy(njs_vm_t *vm, njs_function_t *function)
 {
-    size_t          size;
-    njs_uint_t      n, nesting;
-    njs_closure_t   **closures;
+    size_t          size, n;
+    njs_value_t     **from, **to;
     njs_function_t  *copy;
 
-    nesting = (function->native) ? 0 : function->u.lambda->nesting;
+    n = (function->native) ? 0 : function->u.lambda->nclosures;
 
-    size = sizeof(njs_function_t) + nesting * sizeof(njs_closure_t *);
+    size = sizeof(njs_function_t) + n * sizeof(njs_value_t *);
 
     copy = njs_mp_alloc(vm->mem_pool, size);
     if (njs_slow_path(copy == NULL)) {
@@ -220,21 +190,19 @@
     copy->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
     copy->object.shared = 0;
 
-    if (nesting == 0) {
+    if (n == 0) {
         return copy;
     }
 
-    copy->closure = 1;
-
-    closures = njs_function_active_closures(vm, function);
-
-    n = 0;
+    from = njs_function_closures(function);
+    to = njs_function_closures(copy);
 
     do {
-        /* GC: retain closure. */
-        njs_function_closures(copy)[n] = closures[n];
-        n++;
-    } while (n < nesting);
+        n--;
+
+        to[n] = from[n];
+
+    } while (n != 0);
 
     return copy;
 }
@@ -287,7 +255,7 @@
     for (n = 0; n < nargs; n++) {
         njs_uint32_to_string(&value, n);
 
-        prop = njs_object_prop_alloc(vm, &value, &frame->arguments[n + 1], 1);
+        prop = njs_object_prop_alloc(vm, &value, &frame->arguments[n], 1);
         if (njs_slow_path(prop == NULL)) {
             return NJS_ERROR;
         }
@@ -326,19 +294,20 @@
         return NJS_ERROR;
     }
 
-    if (n <= nargs) {
-        i = 0;
-        do {
-            /* GC: retain. */
-            array->start[i++] = frame->arguments[n++];
-        } while (n <= nargs);
+    for (i = 0; i < length; i++) {
+        array->start[i] = frame->arguments[i + n - 1];
     }
 
-    rest_arguments = &frame->arguments[frame->function->u.lambda->nargs];
+    rest_arguments = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+    if (njs_slow_path(rest_arguments == NULL)) {
+        return NJS_ERROR;
+    }
 
     /* GC: retain. */
     njs_set_array(rest_arguments, array);
 
+    vm->top_frame->local[n] = rest_arguments;
+
     return NJS_OK;
 }
 
@@ -397,7 +366,9 @@
     frame->pc = NULL;
 
     value = (njs_value_t *) ((u_char *) frame + NJS_NATIVE_FRAME_SIZE);
+
     frame->arguments = value;
+    frame->arguments_offset = value + function->args_offset;
 
     bound = function->bound;
 
@@ -415,8 +386,6 @@
         } while (n != 0);
     }
 
-    vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value;
-
     if (args != NULL) {
         memcpy(value, args, nargs * sizeof(njs_value_t));
     }
@@ -430,9 +399,9 @@
     const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs,
     njs_bool_t ctor)
 {
-    size_t                 size;
-    njs_uint_t             n, max_args, closures;
-    njs_value_t            *value, *bound;
+    size_t                 n, frame_size;
+    uint32_t               args_count, value_count, value_size, temp_size;
+    njs_value_t            *value, *bound, **new, **temp;
     njs_frame_t            *frame;
     njs_function_t         *target;
     njs_native_frame_t     *native_frame;
@@ -461,76 +430,86 @@
         lambda = target->u.lambda;
     }
 
-    max_args = njs_max(nargs, lambda->nargs);
+    args_count = function->args_offset + njs_max(nargs, lambda->nargs);
+    value_count = args_count + njs_max(args_count, lambda->nlocal);
 
-    closures = lambda->nesting + lambda->block_closures;
+    value_size = value_count * sizeof(njs_value_t *);
+    temp_size = lambda->temp * sizeof(njs_value_t *);
 
-    size = njs_frame_size(closures)
-           + (function->args_offset + max_args) * sizeof(njs_value_t)
-           + lambda->local_size;
+    frame_size = value_size + temp_size
+                        + ((value_count + lambda->temp) * sizeof(njs_value_t));
 
-    native_frame = njs_function_frame_alloc(vm, size);
+    native_frame = njs_function_frame_alloc(vm, NJS_FRAME_SIZE + frame_size);
     if (njs_slow_path(native_frame == NULL)) {
         return NJS_ERROR;
     }
 
+    /* Local */
+
+    new = (njs_value_t **) ((u_char *) native_frame + NJS_FRAME_SIZE);
+    value = (njs_value_t *) ((u_char *) new + value_size + temp_size);
+
+    n = value_count + lambda->temp;
+
+    while (n != 0) {
+        n--;
+        new[n] = &value[n];
+        njs_set_invalid(new[n]);
+    }
+
+    /* Temp */
+
+    temp = (njs_value_t **) ((u_char *) native_frame + NJS_FRAME_SIZE
+                                                     + value_size);
+
+    native_frame->arguments = value;
+    native_frame->arguments_offset = value + (function->args_offset - 1);
+    native_frame->local = new + args_count;
+    native_frame->temp = temp;
     native_frame->function = target;
     native_frame->nargs = nargs;
     native_frame->ctor = ctor;
     native_frame->native = 0;
     native_frame->pc = NULL;
 
-    /* Function arguments. */
+    /* Set this and bound arguments. */
+    *native_frame->local[0] = *this;
 
-    value = (njs_value_t *) ((u_char *) native_frame +
-                             njs_frame_size(closures));
-    native_frame->arguments = value;
+    if (njs_slow_path(function->global_this
+                      && njs_is_null_or_undefined(this)))
+    {
+        njs_set_object(native_frame->local[0], &vm->global_object);
+    }
 
-    if (bound == NULL) {
-        *value = *this;
-
-        if (njs_slow_path(function->global_this
-                          && njs_is_null_or_undefined(this))) {
-            njs_set_object(value, &vm->global_object);
-        }
-
-        value++;
-
-    } else {
+    if (bound != NULL) {
         n = function->args_offset;
         native_frame->nargs += n - 1;
 
-        if (ctor) {
-            *value++ = *this;
-            bound++;
-            n--;
+        if (!ctor) {
+            *native_frame->local[0] = *bound;
         }
 
+        bound++;
+        n--;
+
         while (n != 0) {
             *value++ = *bound++;
             n--;
         };
     }
 
-    vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value;
+    /* Copy arguments. */
 
     if (args != NULL) {
         while (nargs != 0) {
             *value++ = *args++;
-            max_args--;
             nargs--;
         }
     }
 
-    while (max_args != 0) {
-        njs_set_undefined(value++);
-        max_args--;
-    }
-
     frame = (njs_frame_t *) native_frame;
     frame->exception.catch = NULL;
     frame->exception.next = NULL;
-    frame->local = value;
     frame->previous_active_frame = vm->active_frame;
 
     return NJS_OK;
@@ -543,15 +522,7 @@
     size_t              spare_size, chunk_size;
     njs_native_frame_t  *frame;
 
-    /*
-     * The size value must be aligned to njs_value_t because vm->top_frame
-     * may point to frame->free and vm->top_frame is used as a base pointer
-     * in njs_vm_continuation() which is expected to return pointers aligned
-     * to njs_value_t.
-     */
-    size = njs_align_size(size, sizeof(njs_value_t));
-
-    spare_size = vm->top_frame->free_size;
+    spare_size = vm->top_frame ? vm->top_frame->free_size : 0;
 
     if (njs_fast_path(size <= spare_size)) {
         frame = (njs_native_frame_t *) vm->top_frame->free;
@@ -602,7 +573,7 @@
         return ret;
     }
 
-    ret = njs_function_frame_invoke(vm, (njs_index_t) &dst);
+    ret = njs_function_frame_invoke(vm, &dst);
 
     if (ret == NJS_OK) {
         *retval = dst;
@@ -615,79 +586,51 @@
 njs_int_t
 njs_function_lambda_call(njs_vm_t *vm)
 {
-    size_t                 size;
+    uint32_t               n;
     njs_int_t              ret;
-    njs_uint_t             n, nesting;
     njs_frame_t            *frame;
-    njs_value_t            *dst, *src;
-    njs_closure_t          *closure, **closures;
+    njs_value_t            *args, **local, *value;
+    njs_value_t            **cur_local, **cur_closures, **cur_temp;
     njs_function_t         *function;
     njs_function_lambda_t  *lambda;
 
     frame = (njs_frame_t *) vm->top_frame;
     function = frame->native.function;
 
+    if (function->global && !function->closure_copied) {
+        ret = njs_function_capture_global_closures(vm, function);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+    }
+
     lambda = function->u.lambda;
 
-#if (NJS_DEBUG)
-    vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = NULL;
-#endif
+    args = vm->top_frame->arguments;
+    local = vm->top_frame->local + function->args_offset;
 
-    vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments;
+    /* Move all 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 = njs_function_active_closures(vm, function);
-        do {
-            closure = *closures++;
-
-            njs_frame_closures(frame)[n] = closure;
-            vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
-
-            n++;
-        } while (n < nesting);
-    }
-
-    /* Function closure values. */
-
-    if (lambda->block_closures > 0) {
-        closure = NULL;
-
-        size = lambda->closure_size;
-
-        if (size != 0) {
-            closure = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), size);
-            if (njs_slow_path(closure == NULL)) {
-                njs_memory_error(vm);
-                return NJS_ERROR;
-            }
-
-            size -= sizeof(njs_value_t);
-            closure->u.count = 0;
-            dst = closure->values;
-
-            src = lambda->closure_scope;
-
-            do {
-                *dst++ = *src++;
-                size -= sizeof(njs_value_t);
-            } while (size != 0);
+    for (n = 0; n < function->args_count; n++) {
+        if (!njs_is_valid(args)) {
+            njs_set_undefined(args);
         }
 
-        njs_frame_closures(frame)[n] = closure;
-        vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
+        *local++ = args++;
     }
 
+    /* Store current level. */
+
+    cur_local = vm->levels[NJS_LEVEL_LOCAL];
+    cur_closures = vm->levels[NJS_LEVEL_CLOSURE];
+    cur_temp = vm->levels[NJS_LEVEL_TEMP];
+
+    /* Replace current level. */
+
+    vm->levels[NJS_LEVEL_LOCAL] = vm->top_frame->local;
+    vm->levels[NJS_LEVEL_CLOSURE] = njs_function_closures(function);
+    vm->levels[NJS_LEVEL_TEMP] = frame->native.temp;
+
     if (lambda->rest_parameters) {
         ret = njs_function_rest_parameters_init(vm, &frame->native);
         if (njs_slow_path(ret != NJS_OK)) {
@@ -695,9 +638,41 @@
         }
     }
 
+    /* Self */
+
+    if (lambda->self != NJS_INDEX_NONE) {
+        value = njs_scope_value(vm, lambda->self);
+
+        if (!njs_is_valid(value)) {
+            njs_set_function(value, function);
+        }
+    }
+
     vm->active_frame = frame;
 
-    return njs_vmcode_interpreter(vm, lambda->start);
+    /* Closures */
+
+    n = lambda->ndeclarations;
+
+    while (n != 0) {
+        n--;
+
+        function = njs_function(lambda->declarations[n]);
+
+        ret = njs_function_capture_closure(vm, function, function->u.lambda);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+    }
+
+    ret = njs_vmcode_interpreter(vm, lambda->start);
+
+    /* Restore current level. */
+    vm->levels[NJS_LEVEL_LOCAL] = cur_local;
+    vm->levels[NJS_LEVEL_CLOSURE] = cur_closures;
+    vm->levels[NJS_LEVEL_TEMP] = cur_temp;
+
+    return ret;
 }
 
 
@@ -705,7 +680,6 @@
 njs_function_native_call(njs_vm_t *vm)
 {
     njs_int_t              ret;
-    njs_value_t            *value;
     njs_function_t         *function, *target;
     njs_native_frame_t     *native, *previous;
     njs_function_native_t  call;
@@ -741,12 +715,7 @@
     njs_vm_scopes_restore(vm, native, previous);
 
     if (!native->skip) {
-        value = njs_vmcode_operand(vm, native->retval);
-        /*
-         * GC: value external/internal++ depending
-         * on vm->retval and retval type
-         */
-        *value = vm->retval;
+        *native->retval = vm->retval;
     }
 
     njs_function_frame_free(vm, native);
@@ -775,6 +744,142 @@
 }
 
 
+njs_int_t
+njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
+    njs_function_lambda_t *lambda)
+{
+    void                *start, *end;
+    uint32_t            n;
+    njs_value_t         *value, **closure;
+    njs_native_frame_t  *frame;
+
+    if (lambda->nclosures == 0) {
+        return NJS_OK;
+    }
+
+    frame = &vm->active_frame->native;
+
+    while (frame->native) {
+        frame = frame->previous;
+    }
+
+    start = frame;
+    end = frame->free;
+
+    closure = njs_function_closures(function);
+    n = lambda->nclosures;
+
+    do {
+        n--;
+
+        value = njs_scope_value(vm, lambda->closures[n]);
+
+        if (start <= (void *) value && (void *) value < end) {
+            value = njs_scope_value_clone(vm, lambda->closures[n], value);
+            if (njs_slow_path(value == NULL)) {
+                return NJS_ERROR;
+            }
+        }
+
+        closure[n] = value;
+
+    } while (n != 0);
+
+    return NJS_OK;
+}
+
+
+njs_inline njs_value_t *
+njs_function_closure_value(njs_vm_t *vm, njs_value_t **scope, njs_index_t index,
+    void *start, void *end)
+{
+    njs_value_t  *value, *newval;
+
+    value = scope[njs_scope_index_value(index)];
+
+    if (start <= (void *) value && end > (void *) value) {
+        newval = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+        if (njs_slow_path(newval == NULL)) {
+            njs_memory_error(vm);
+            return NULL;
+        }
+
+        *newval = *value;
+        value = newval;
+    }
+
+    scope[njs_scope_index_value(index)] = value;
+
+    return value;
+}
+
+
+njs_int_t
+njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function)
+{
+    void                   *start, *end;
+    uint32_t               n;
+    njs_value_t            *value, **refs, **global;
+    njs_index_t            *indexes, index;
+    njs_native_frame_t     *native;
+    njs_function_lambda_t  *lambda;
+
+    lambda = function->u.lambda;
+
+    if (lambda->nclosures == 0) {
+        return NJS_OK;
+    }
+
+    native = vm->top_frame;
+
+    while (native->previous->function != NULL) {
+        native = native->previous;
+    }
+
+    start = native;
+    end = native->free;
+
+    indexes = lambda->closures;
+    refs = njs_function_closures(function);
+
+    global = vm->levels[NJS_LEVEL_GLOBAL];
+
+    n = lambda->nclosures;
+
+    while (n > 0) {
+        n--;
+
+        index = indexes[n];
+
+        switch (njs_scope_index_type(index)) {
+        case NJS_LEVEL_LOCAL:
+            value = njs_function_closure_value(vm, native->local, index,
+                                               start, end);
+            break;
+
+        case NJS_LEVEL_GLOBAL:
+            value = njs_function_closure_value(vm, global, index, start, end);
+            break;
+
+        default:
+            njs_type_error(vm, "unexpected value type for closure \"%uD\"",
+                           njs_scope_index_type(index));
+            return NJS_ERROR;
+        }
+
+        if (njs_slow_path(value == NULL)) {
+            return NJS_ERROR;
+        }
+
+        refs[n] = value;
+    }
+
+    function->closure_copied = 1;
+
+    return NJS_OK;
+}
+
+
 static njs_value_t *
 njs_function_property_prototype_set(njs_vm_t *vm, njs_lvlhsh_t *hash,
     njs_value_t *prototype)
@@ -937,7 +1042,7 @@
 
     parser.lexer = &lexer;
 
-    ret = njs_parser(vm, &parser, NULL);
+    ret = njs_parser(vm, &parser);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -953,7 +1058,14 @@
         type = &safe_ast[0];
 
         for (; *type != NJS_TOKEN_ILLEGAL; type++, node = node->right) {
-            if (node == NULL || node->left != NULL) {
+            if (node == NULL) {
+                goto fail;
+            }
+
+            if (node->left != NULL
+                && node->token_type != NJS_TOKEN_FUNCTION_EXPRESSION
+                && node->left->token_type != NJS_TOKEN_NAME)
+            {
                 goto fail;
             }
 
@@ -970,11 +1082,6 @@
         return ret;
     }
 
-    ret = njs_variables_scope_reference(vm, scope);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
-    }
-
     njs_memzero(&generator, sizeof(njs_generator_t));
 
     code = njs_generate_scope(vm, &generator, scope, &njs_entry_anonymous);
@@ -990,11 +1097,12 @@
 
     lambda = ((njs_vmcode_function_t *) generator.code_start)->lambda;
 
-    function = njs_function_alloc(vm, lambda, NULL, 0);
+    function = njs_function_alloc(vm, lambda);
     if (njs_slow_path(function == NULL)) {
         return NJS_ERROR;
     }
 
+    function->global = 1;
     function->global_this = 1;
     function->args_count = lambda->nargs - lambda->rest_parameters;
 
diff --git a/src/njs_function.h b/src/njs_function.h
index 20cda07..7f4b814 100644
--- a/src/njs_function.h
+++ b/src/njs_function.h
@@ -9,23 +9,21 @@
 
 
 struct njs_function_lambda_s {
+    njs_index_t                    *closures;
+    uint32_t                       nclosures;
+    uint32_t                       nlocal;
+    uint32_t                       temp;
+
+    njs_value_t                    **declarations;
+    uint32_t                       ndeclarations;
+
+    njs_index_t                    self;
+
     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 */
 
     uint8_t                        ctor;              /* 1 bit */
     uint8_t                        rest_parameters;   /* 1 bit */
 
-    /* Initial values of local scope. */
-    njs_value_t                    *local_scope;
-    njs_value_t                    *closure_scope;
-
     u_char                         *start;
 };
 
@@ -35,11 +33,10 @@
     njs_align_size(sizeof(njs_native_frame_t), sizeof(njs_value_t))
 
 /* The frame size must be aligned to njs_value_t. */
-#define njs_frame_size(closures)                                              \
-    njs_align_size(sizeof(njs_frame_t) + closures * sizeof(njs_closure_t *),  \
-                   sizeof(njs_value_t))
+#define NJS_FRAME_SIZE                                                        \
+    njs_align_size(sizeof(njs_frame_t), sizeof(njs_value_t))
 
-#define NJS_FRAME_SPARE_SIZE       512
+#define NJS_FRAME_SPARE_SIZE       (4 * 1024)
 
 
 struct njs_native_frame_s {
@@ -51,11 +48,15 @@
 
     njs_value_t                    *arguments;
     njs_object_t                   *arguments_object;
-
-    njs_index_t                    retval;
+    njs_value_t                    *arguments_offset;
+    njs_value_t                    **local;
+    njs_value_t                    **temp;
 
     uint32_t                       size;
     uint32_t                       free_size;
+
+    njs_value_t                    *retval;
+
     uint32_t                       nargs;
 
     uint8_t                        native;            /* 1 bit  */
@@ -81,19 +82,14 @@
     njs_exception_t                exception;
 
     njs_frame_t                    *previous_active_frame;
-
-    njs_value_t                    *local;
-
-#define njs_frame_closures(frame)                                             \
-    ((njs_closure_t **) ((u_char *) frame + sizeof(njs_frame_t)))
 };
 
 
-njs_function_t *njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda,
-    njs_closure_t *closures[], njs_bool_t shared);
+njs_function_t *njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda);
 njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value);
 njs_int_t njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
     njs_value_t *name, const char *prefix);
+njs_function_t *njs_function_copy(njs_vm_t *vm, njs_function_t *function);
 njs_int_t njs_function_arguments_object_init(njs_vm_t *vm,
     njs_native_frame_t *frame);
 njs_int_t njs_function_rest_parameters_init(njs_vm_t *vm,
@@ -113,7 +109,12 @@
     njs_uint_t nargs, njs_value_t *retval, njs_bool_t ctor);
 njs_int_t njs_function_lambda_call(njs_vm_t *vm);
 njs_int_t njs_function_native_call(njs_vm_t *vm);
+njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size);
 void njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *frame);
+njs_int_t njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
+     njs_function_lambda_t *lambda);
+njs_int_t njs_function_capture_global_closures(njs_vm_t *vm,
+    njs_function_t *function);
 
 
 njs_inline njs_function_lambda_t *
@@ -161,7 +162,7 @@
 
 
 njs_inline njs_int_t
-njs_function_frame_invoke(njs_vm_t *vm, njs_index_t retval)
+njs_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval)
 {
     njs_native_frame_t  *frame;
 
@@ -202,6 +203,13 @@
 }
 
 
+njs_inline njs_value_t **
+njs_function_closures(const njs_function_t *func)
+{
+    return (njs_value_t **) ((u_char *) func + sizeof(njs_function_t));
+}
+
+
 extern const njs_object_type_init_t  njs_function_type_init;
 extern const njs_object_init_t  njs_function_instance_init;
 extern const njs_object_init_t  njs_arrow_instance_init;
diff --git a/src/njs_generator.c b/src/njs_generator.c
index 1730986..4b1e7e3 100644
--- a/src/njs_generator.c
+++ b/src/njs_generator.c
@@ -2,6 +2,7 @@
 /*
  * Copyright (C) Igor Sysoev
  * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) Alexander Borisov
  * Copyright (C) NGINX, Inc.
  */
 
@@ -18,7 +19,7 @@
      * because pointer to u_char accesses only one byte so this does not
      * work on big endian platforms.
      */
-    njs_jump_off_t                       jump_offset;
+    njs_jump_off_t                  jump_offset;
     njs_generator_patch_t           *next;
 
     njs_str_t                       label;
@@ -62,7 +63,8 @@
 static njs_int_t njs_generate_name(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node);
 static njs_int_t njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator,
-    njs_parser_node_t *node, njs_reference_type_t type);
+    njs_parser_node_t *node, njs_reference_type_t type,
+    njs_variable_t **retvar);
 static njs_int_t njs_generate_var_statement(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_if_statement(njs_vm_t *vm,
@@ -123,6 +125,8 @@
     njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_array(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node);
+static njs_int_t njs_generate_function_expression(njs_vm_t *vm,
+    njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_function(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node);
 static njs_int_t njs_generate_regexp(njs_vm_t *vm, njs_generator_t *generator,
@@ -144,7 +148,7 @@
 static njs_int_t njs_generate_function_scope(njs_vm_t *vm,
     njs_function_lambda_t *lambda, njs_parser_node_t *node,
     const njs_str_t *name);
-static njs_int_t njs_generate_lambda_variables(njs_vm_t *vm,
+static int64_t njs_generate_lambda_variables(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_return_statement(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
@@ -154,6 +158,8 @@
     njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_call(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node);
+static njs_int_t njs_generate_move_arguments(njs_vm_t *vm,
+    njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_try_statement(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
 static njs_int_t njs_generate_throw_statement(njs_vm_t *vm,
@@ -392,12 +398,13 @@
     case NJS_TOKEN_FALSE:
     case NJS_TOKEN_NUMBER:
     case NJS_TOKEN_STRING:
-        node->index = njs_value_index(vm, &node->u.value, generator->runtime);
-        if (njs_fast_path(node->index != NJS_INDEX_NONE)) {
-            return NJS_OK;
+        node->index = njs_scope_global_index(vm, &node->u.value,
+                                             generator->runtime);
+        if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
+            return NJS_ERROR;
         }
 
-        return NJS_ERROR;
+        return NJS_OK;
 
     case NJS_TOKEN_OBJECT_VALUE:
         node->index = node->u.object->index;
@@ -414,6 +421,9 @@
         return njs_generate_array(vm, generator, node);
 
     case NJS_TOKEN_FUNCTION_EXPRESSION:
+        return njs_generate_function_expression(vm, generator, node);
+
+    case NJS_TOKEN_FUNCTION:
         return njs_generate_function(vm, generator, node);
 
     case NJS_TOKEN_REGEXP:
@@ -422,32 +432,16 @@
     case NJS_TOKEN_TEMPLATE_LITERAL:
         return njs_generate_template_literal(vm, generator, node);
 
-    case NJS_TOKEN_THIS:
     case NJS_TOKEN_EXTERNAL:
         return NJS_OK;
 
     case NJS_TOKEN_NAME:
     case NJS_TOKEN_ARGUMENTS:
     case NJS_TOKEN_EVAL:
-    case NJS_TOKEN_NON_LOCAL_THIS:
+    case NJS_TOKEN_THIS:
         return njs_generate_name(vm, generator, node);
 
-    case NJS_TOKEN_GLOBAL_OBJECT:
-        if (vm->options.module) {
-            node->index = njs_value_index(vm, &njs_value_undefined,
-                                          generator->runtime);
-            if (njs_fast_path(node->index != NJS_INDEX_NONE)) {
-                return NJS_OK;
-            }
-
-            return NJS_ERROR;
-        }
-
-        node->index = NJS_INDEX_GLOBAL_OBJECT;
-
-        return NJS_OK;
-
-    case NJS_TOKEN_FUNCTION:
+    case NJS_TOKEN_FUNCTION_DECLARATION:
         return njs_generate_function_declaration(vm, generator, node);
 
     case NJS_TOKEN_FUNCTION_CALL:
@@ -596,39 +590,39 @@
 njs_generate_name(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
-    njs_variable_t            *var;
-    njs_vmcode_object_copy_t  *copy;
+    njs_variable_t              *var;
+    njs_vmcode_function_copy_t  *copy;
 
-    var = njs_variable_resolve(vm, node);
-
-    if (var != NULL && var->type == NJS_VARIABLE_FUNCTION) {
-
-        node->index = njs_generate_dest_index(vm, generator, node);
-        if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
-            return node->index;
-        }
-
-        njs_generate_code(generator, njs_vmcode_object_copy_t, copy,
-                          NJS_VMCODE_OBJECT_COPY, 2, node);
-        copy->retval = node->index;
-        copy->object = var->index;
-
-        return NJS_OK;
+    var = njs_variable_reference(vm, node);
+    if (njs_slow_path(var == NULL)) {
+        return njs_generate_global_reference(vm, generator, node, 1);
     }
 
-    return njs_generate_variable(vm, generator, node, NJS_REFERENCE);
+    if (var->function && var->type == NJS_VARIABLE_FUNCTION) {
+        njs_generate_code(generator, njs_vmcode_function_copy_t, copy,
+                          NJS_VMCODE_FUNCTION_COPY, 0, node);
+        copy->function = &var->value;
+        copy->retval = node->index;
+    }
+
+    return NJS_OK;
 }
 
 
 static njs_int_t
 njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator,
-    njs_parser_node_t *node, njs_reference_type_t type)
+    njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar)
 {
-    njs_index_t  index;
+    njs_variable_t              *var;
+    njs_vmcode_function_copy_t  *copy;
 
-    index = njs_variable_index(vm, node);
-    if (njs_slow_path(index == NJS_INDEX_NONE)) {
+    var = njs_variable_reference(vm, node);
 
+    if (retvar != NULL) {
+        *retvar = var;
+    }
+
+    if (njs_slow_path(var == NULL)) {
         switch (type) {
         case NJS_DECLARATION:
             return njs_generate_reference_error(vm, generator, node);
@@ -640,7 +634,12 @@
         }
     }
 
-    node->index = index;
+    if (var->function && var->type == NJS_VARIABLE_FUNCTION) {
+        njs_generate_code(generator, njs_vmcode_function_copy_t, copy,
+                          NJS_VMCODE_FUNCTION_COPY, 0, node);
+        copy->function = &var->value;
+        copy->retval = node->index;
+    }
 
     return NJS_OK;
 }
@@ -651,19 +650,18 @@
     njs_parser_node_t *node)
 {
     njs_int_t          ret;
-    njs_index_t        index;
+    njs_variable_t     *var;
     njs_parser_node_t  *lvalue, *expr;
     njs_vmcode_move_t  *move;
 
     lvalue = node->left;
 
-    index = njs_variable_index(vm, lvalue);
-    if (njs_slow_path(index == NJS_INDEX_NONE)) {
+    ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION, &var);
+    if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
 
-    lvalue->index = index;
-
+    lvalue->index = var->index;
     expr = node->right;
 
     if (expr == NULL) {
@@ -1208,6 +1206,11 @@
 
     foreach = node->left;
 
+    ret = njs_generator(vm, generator, foreach->left);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
     ret = njs_generator(vm, generator, foreach->right);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
@@ -1240,11 +1243,6 @@
 
     njs_code_set_jump_offset(generator, njs_vmcode_prop_foreach_t, prop_offset);
 
-    ret = njs_generator(vm, generator, node->left->left);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
-    }
-
     njs_generate_code(generator, njs_vmcode_prop_next_t, prop_next,
                       NJS_VMCODE_PROPERTY_NEXT, 3, node->left->left);
     prop_offset = njs_code_offset(generator, prop_next);
@@ -1635,16 +1633,16 @@
         njs_generate_code(generator, njs_vmcode_stop_t, stop,
                           NJS_VMCODE_STOP, 1, node);
 
-        index = NJS_INDEX_NONE;
+        index = njs_scope_undefined_index(vm, 0);
         node = node->right;
 
-        if (node != NULL && node->token_type != NJS_TOKEN_FUNCTION) {
-            index = node->index;
-        }
-
-        if (index == NJS_INDEX_NONE) {
-            index = njs_value_index(vm, &njs_value_undefined,
-                                    generator->runtime);
+        if (node != NULL) {
+            if ((node->index != NJS_INDEX_NONE
+                 && node->token_type != NJS_TOKEN_FUNCTION_DECLARATION)
+                || node->token_type == NJS_TOKEN_THIS)
+            {
+                index = node->index;
+            }
         }
 
         stop->retval = index;
@@ -1686,7 +1684,8 @@
 
     if (lvalue->token_type == NJS_TOKEN_NAME) {
 
-        ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION);
+        ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION,
+                                    NULL);
         if (njs_slow_path(ret != NJS_OK)) {
             return ret;
         }
@@ -1810,7 +1809,8 @@
 
     if (lvalue->token_type == NJS_TOKEN_NAME) {
 
-        ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION);
+        ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION,
+                                    NULL);
         if (njs_slow_path(ret != NJS_OK)) {
             return ret;
         }
@@ -2029,6 +2029,48 @@
 
 
 static njs_int_t
+njs_generate_function_expression(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node)
+{
+    njs_int_t                ret;
+    njs_variable_t           *var;
+    njs_function_lambda_t    *lambda;
+    njs_vmcode_function_t    *function;
+    const njs_lexer_entry_t  *lex_entry;
+
+    var = njs_variable_reference(vm, node->left);
+    if (njs_slow_path(var == NULL)) {
+        return njs_generate_reference_error(vm, generator, node->left);
+    }
+
+    lambda = node->u.value.data.u.lambda;
+
+    lex_entry = njs_lexer_entry(var->unique_id);
+    if (njs_slow_path(lex_entry == NULL)) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_generate_function_scope(vm, lambda, node, &lex_entry->name);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    njs_generate_code(generator, njs_vmcode_function_t, function,
+                      NJS_VMCODE_FUNCTION, 1, node);
+    function->lambda = lambda;
+
+    node->index = njs_generate_object_dest_index(vm, generator, node);
+    if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
+        return NJS_ERROR;
+    }
+
+    function->retval = node->index;
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
 njs_generate_function(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
@@ -2263,7 +2305,7 @@
     expr = node->left;
 
     if (expr->token_type == NJS_TOKEN_NAME) {
-        ret = njs_generate_variable(vm, generator, expr, NJS_TYPEOF);
+        ret = njs_generate_variable(vm, generator, expr, NJS_TYPEOF, NULL);
         if (njs_slow_path(ret != NJS_OK)) {
             return NJS_ERROR;
         }
@@ -2307,7 +2349,8 @@
 
     if (lvalue->token_type == NJS_TOKEN_NAME) {
 
-        ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION);
+        ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION,
+                                    NULL);
         if (njs_slow_path(ret != NJS_OK)) {
             return ret;
         }
@@ -2402,21 +2445,21 @@
 {
     njs_int_t                ret;
     njs_variable_t           *var;
+    njs_function_t           *function;
     njs_function_lambda_t    *lambda;
     const njs_lexer_entry_t  *lex_entry;
 
-    var = njs_variable_resolve(vm, node);
+    var = njs_variable_reference(vm, node);
     if (njs_slow_path(var == NULL)) {
         return njs_generate_reference_error(vm, generator, node);
     }
 
-    if (!njs_is_function(&var->value)) {
-        /* A variable was declared with the same name. */
-        return NJS_OK;
+    if (njs_is_function(&var->value)) {
+        lambda = njs_function(&var->value)->u.lambda;
+    } else {
+        lambda = var->value.data.u.lambda;
     }
 
-    lambda = njs_function_lambda(&var->value);
-
     lex_entry = njs_lexer_entry(node->u.reference.unique_id);
     if (njs_slow_path(lex_entry == NULL)) {
         return NJS_ERROR;
@@ -2427,6 +2470,17 @@
         return ret;
     }
 
+    function = njs_function_alloc(vm, lambda);
+    if (njs_slow_path(function == NULL)) {
+        return NJS_ERROR;
+    }
+
+    function->global = njs_function_scope(var->scope)->type == NJS_SCOPE_GLOBAL;
+    function->object.shared = 1;
+    function->args_count = lambda->nargs - lambda->rest_parameters;
+
+    njs_set_function(&var->value, function);
+
     return ret;
 }
 
@@ -2435,8 +2489,7 @@
 njs_generate_function_scope(njs_vm_t *vm, njs_function_lambda_t *lambda,
     njs_parser_node_t *node, const njs_str_t *name)
 {
-    size_t             size;
-    njs_arr_t          *closure;
+    njs_arr_t          *arr;
     njs_bool_t         module;
     njs_vm_code_t      *code;
     njs_generator_t    generator;
@@ -2444,9 +2497,6 @@
 
     njs_memzero(&generator, sizeof(njs_generator_t));
 
-    module = node->right->scope->module;
-    file_node = module ? node->right : node;
-
     node = node->right;
 
     code = njs_generate_scope(vm, &generator, node->scope, name);
@@ -2458,24 +2508,22 @@
         return NJS_ERROR;
     }
 
+    module = node->right->scope->module;
+    file_node = module ? node->right : node;
+
     code->file = file_node->scope->file;
 
-    size = 0;
-    closure = node->scope->values[1];
-
-    if (closure != NULL) {
-        lambda->block_closures = 1;
-        lambda->closure_scope = closure->start;
-        size = (1 + closure->items) * sizeof(njs_value_t);
-    }
-
-    lambda->closure_size = size;
-
-    lambda->nesting = node->scope->nesting;
-
     lambda->start = generator.code_start;
-    lambda->local_size = generator.scope_size;
-    lambda->local_scope = generator.local_scope;
+    lambda->closures = generator.closures->start;
+    lambda->nclosures = generator.closures->items;
+    lambda->nlocal = node->scope->items;
+    lambda->temp = node->scope->temp;
+
+    if (node->scope->declarations != NULL) {
+        arr = node->scope->declarations;
+        lambda->declarations = arr->start;
+        lambda->ndeclarations = arr->items;
+    }
 
     return NJS_OK;
 }
@@ -2486,11 +2534,8 @@
     njs_parser_scope_t *scope, const njs_str_t *name)
 {
     u_char         *p;
-    size_t          size;
-    uintptr_t       scope_size;
-    njs_int_t       ret;
-    njs_uint_t      n, index;
-    njs_value_t    *value;
+    int64_t        nargs;
+    njs_uint_t     index;
     njs_vm_code_t  *code;
 
     generator->code_size = 128;
@@ -2504,8 +2549,8 @@
     generator->code_start = p;
     generator->code_end = p;
 
-    ret = njs_generate_lambda_variables(vm, generator, scope->top);
-    if (njs_slow_path(ret != NJS_OK)) {
+    nargs = njs_generate_lambda_variables(vm, generator, scope->top);
+    if (njs_slow_path(nargs < NJS_OK)) {
         return NULL;
     }
 
@@ -2536,6 +2581,13 @@
         generator->lines = code->lines;
     }
 
+    generator->closures = njs_arr_create(vm->mem_pool, 4, sizeof(njs_index_t));
+    if (njs_slow_path(generator->closures == NULL)) {
+        return NULL;
+    }
+
+    scope->closures = generator->closures;
+
     if (njs_slow_path(njs_generator(vm, generator, scope->top) != NJS_OK)) {
         return NULL;
     }
@@ -2548,46 +2600,22 @@
 
     generator->code_size = generator->code_end - generator->code_start;
 
-    scope_size = njs_scope_offset(scope->next_index[0]);
-
-    if (scope->type == NJS_SCOPE_GLOBAL) {
-        scope_size -= NJS_INDEX_GLOBAL_OFFSET;
-    }
-
-    generator->local_scope = njs_mp_alloc(vm->mem_pool, scope_size);
-    if (njs_slow_path(generator->local_scope == NULL)) {
-        return NULL;
-    }
-
-    generator->scope_size = scope_size;
-
-    size = scope->values[0]->items * sizeof(njs_value_t);
-
-    njs_thread_log_debug("SCOPE SIZE: %uz %uz", size, scope_size);
-
-    p = memcpy(generator->local_scope, scope->values[0]->start, size);
-    value = (njs_value_t *) (p + size);
-
-    for (n = scope_size - size; n != 0; n -= sizeof(njs_value_t)) {
-        njs_set_undefined(value++);
-    }
-
     return code;
 }
 
 
-static njs_int_t
+static int64_t
 njs_generate_lambda_variables(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
-    njs_index_t             index;
+    int64_t                 nargs;
     njs_variable_t          *var;
     njs_rbtree_node_t       *rb_node;
-    njs_vmcode_move_t       *move;
-    njs_vmcode_this_t       *this;
     njs_variable_node_t     *var_node;
     njs_vmcode_arguments_t  *arguments;
 
+    nargs = 0;
+
     rb_node = njs_rbtree_min(&node->scope->variables);
 
     while (njs_rbtree_is_there_successor(&node->scope->variables, rb_node)) {
@@ -2598,16 +2626,8 @@
             break;
         }
 
-        if (var->argument != 0) {
-            index = njs_scope_index((var->argument - 1), NJS_SCOPE_ARGUMENTS);
-
-            njs_generate_code_move(generator, move, var->index, index, node);
-        }
-
-        if (var->this_object) {
-            njs_generate_code(generator, njs_vmcode_this_t, this,
-                              NJS_VMCODE_THIS, 1, NULL);
-            this->dst = var->index;
+        if (var->argument) {
+            nargs++;
         }
 
         if (var->arguments_object) {
@@ -2619,7 +2639,7 @@
         rb_node = njs_rbtree_node_successor(&node->scope->variables, rb_node);
     }
 
-    return NJS_OK;
+    return nargs;
 }
 
 
@@ -2644,7 +2664,12 @@
         index = node->right->index;
 
     } else {
-        index = njs_value_index(vm, &njs_value_undefined, generator->runtime);
+        index = njs_scope_global_index(vm, &njs_value_undefined,
+                                       generator->runtime);
+    }
+
+    if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+        return NJS_ERROR;
     }
 
     immediate = njs_generate_lookup_block(generator->block, NJS_GENERATOR_TRY,
@@ -2703,11 +2728,14 @@
 njs_generate_function_call(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
-    njs_int_t                    ret;
+    njs_int_t                    ret, nargs;
     njs_jump_off_t               func_offset;
+    njs_variable_t               *var;
     njs_parser_node_t            *name;
     njs_vmcode_function_frame_t  *func;
 
+    var = NULL;
+
     if (node->left != NULL) {
         /* Generate function code in function expression. */
         ret = njs_generator(vm, generator, node->left);
@@ -2718,7 +2746,7 @@
         name = node->left;
 
     } else {
-        ret = njs_generate_variable(vm, generator, node, NJS_REFERENCE);
+        ret = njs_generate_variable(vm, generator, node, NJS_REFERENCE, &var);
         if (njs_slow_path(ret != NJS_OK)) {
             return ret;
         }
@@ -2732,16 +2760,20 @@
     func->ctor = node->ctor;
     func->name = name->index;
 
-    ret = njs_generate_call(vm, generator, node);
-
-    if (njs_fast_path(ret >= 0)) {
-        func = njs_code_ptr(generator, njs_vmcode_function_frame_t,
-                            func_offset);
-        func->nargs = ret;
-        return NJS_OK;
+    nargs = njs_generate_move_arguments(vm, generator, node);
+    if (njs_slow_path(nargs < 0)) {
+        return nargs;
     }
 
-    return ret;
+    func = njs_code_ptr(generator, njs_vmcode_function_frame_t, func_offset);
+    func->nargs = nargs;
+
+    ret = njs_generate_call(vm, generator, node);
+    if (njs_fast_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    return NJS_OK;
 }
 
 
@@ -2749,7 +2781,7 @@
 njs_generate_method_call(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
-    njs_int_t                  ret;
+    njs_int_t                  ret, nargs;
     njs_jump_off_t             method_offset;
     njs_parser_node_t          *prop;
     njs_vmcode_method_frame_t  *method;
@@ -2777,21 +2809,20 @@
     method->object = prop->left->index;
     method->method = prop->right->index;
 
-    ret = njs_generate_children_indexes_release(vm, generator, prop);
-    if (njs_slow_path(ret != NJS_OK)) {
+    nargs = njs_generate_move_arguments(vm, generator, node);
+    if (njs_slow_path(nargs < 0)) {
+        return nargs;
+    }
+
+    method = njs_code_ptr(generator, njs_vmcode_method_frame_t, method_offset);
+    method->nargs = nargs;
+
+    ret = njs_generate_call(vm, generator, node);
+    if (njs_fast_path(ret != NJS_OK)) {
         return ret;
     }
 
-    ret = njs_generate_call(vm, generator, node);
-
-    if (njs_fast_path(ret >= 0)) {
-        method = njs_code_ptr(generator, njs_vmcode_method_frame_t,
-                              method_offset);
-        method->nargs = ret;
-        return NJS_OK;
-    }
-
-    return ret;
+    return NJS_OK;
 }
 
 
@@ -2799,29 +2830,9 @@
 njs_generate_call(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
-    njs_int_t                   ret;
-    njs_uint_t                  nargs;
     njs_index_t                 retval;
-    njs_parser_node_t           *arg;
-    njs_vmcode_move_t           *move;
     njs_vmcode_function_call_t  *call;
 
-    nargs = 0;
-
-    for (arg = node->right; arg != NULL; arg = arg->right) {
-        nargs++;
-
-        ret = njs_generator(vm, generator, arg->left);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return ret;
-        }
-
-        if (arg->index != arg->left->index) {
-            njs_generate_code_move(generator, move, arg->index,
-                                   arg->left->index, node);
-        }
-    }
-
     retval = njs_generate_dest_index(vm, generator, node);
     if (njs_slow_path(retval == NJS_INDEX_ERROR)) {
         return retval;
@@ -2833,6 +2844,35 @@
                       NJS_VMCODE_FUNCTION_CALL, 1, node);
     call->retval = retval;
 
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_generate_move_arguments(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node)
+{
+    njs_int_t              ret;
+    njs_uint_t             nargs;
+    njs_parser_node_t      *arg;
+    njs_vmcode_move_arg_t  *move_arg;
+
+    nargs = 0;
+
+    for (arg = node->right; arg != NULL; arg = arg->right) {
+        ret = njs_generator(vm, generator, arg->left);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+
+        njs_generate_code(generator, njs_vmcode_move_arg_t, move_arg,
+                          NJS_VMCODE_MOVE_ARG, 0, node);
+        move_arg->src = arg->left->index;
+        move_arg->dst = nargs;
+
+        nargs++;
+    }
+
     return nargs;
 }
 
@@ -2849,7 +2889,7 @@
 #define njs_generate_code_finally(generator, _code, _retval, _exit, node)     \
     do {                                                                      \
             njs_generate_code(generator, njs_vmcode_finally_t, _code,         \
-                              NJS_VMCODE_FINALLY, 2, node);                   \
+                              NJS_VMCODE_FINALLY, 1, node);                   \
             _code->retval = _retval;                                          \
             _code->exit_value = _exit;                                        \
             _code->continue_offset = offsetof(njs_vmcode_finally_t,           \
@@ -2869,6 +2909,7 @@
     njs_index_t                  exception_index, exit_index, catch_index;
     njs_jump_off_t               try_offset, try_end_offset, catch_offset,
                                  catch_end_offset;
+    njs_variable_t               *var;
     const njs_str_t              *dest_label;
     njs_vmcode_catch_t           *catch;
     njs_vmcode_finally_t         *finally;
@@ -2928,7 +2969,7 @@
         njs_generate_patch_block(vm, generator, try_block->exit);
 
         njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_break,
-                          NJS_VMCODE_TRY_BREAK, 2, NULL);
+                          NJS_VMCODE_TRY_BREAK, 1, NULL);
         try_break->exit_value = exit_index;
 
         try_break->offset = -sizeof(njs_vmcode_try_end_t);
@@ -2943,7 +2984,7 @@
         njs_generate_patch_block(vm, generator, try_block->continuation);
 
         njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_continue,
-                          NJS_VMCODE_TRY_CONTINUE, 2, NULL);
+                          NJS_VMCODE_TRY_CONTINUE, 1, NULL);
         try_continue->exit_value = exit_index;
 
         try_continue->offset = -sizeof(njs_vmcode_try_end_t);
@@ -2966,11 +3007,13 @@
     if (node->token_type == NJS_TOKEN_CATCH) {
         /* A "try/catch" case. */
 
-        catch_index = njs_variable_index(vm, node->left);
-        if (njs_slow_path(catch_index == NJS_INDEX_NONE)) {
+        var = njs_variable_reference(vm, node->left);
+        if (njs_slow_path(var == NULL)) {
             return NJS_ERROR;
         }
 
+        catch_index = node->left->index;
+
         njs_generate_code_catch(generator, catch, catch_index, node);
 
         ret = njs_generator(vm, generator, node->right);
@@ -3025,11 +3068,13 @@
         if (node->left != NULL) {
             /* A try/catch/finally case. */
 
-            catch_index = njs_variable_index(vm, node->left->left);
-            if (njs_slow_path(catch_index == NJS_INDEX_NONE)) {
+            var = njs_variable_reference(vm, node->left->left);
+            if (njs_slow_path(var == NULL)) {
                 return NJS_ERROR;
             }
 
+            catch_index = node->left->left->index;
+
             njs_generate_code_catch(generator, catch, catch_index, node);
             catch_offset = njs_code_offset(generator, catch);
 
@@ -3057,7 +3102,7 @@
                 njs_generate_patch_block(vm, generator, catch_block->exit);
 
                 njs_generate_code(generator, njs_vmcode_try_trampoline_t,
-                                  try_break, NJS_VMCODE_TRY_BREAK, 2, NULL);
+                                  try_break, NJS_VMCODE_TRY_BREAK, 1, NULL);
 
                 try_break->exit_value = exit_index;
 
@@ -3074,7 +3119,7 @@
                                          catch_block->continuation);
 
                 njs_generate_code(generator, njs_vmcode_try_trampoline_t,
-                                  try_continue, NJS_VMCODE_TRY_CONTINUE, 2,
+                                  try_continue, NJS_VMCODE_TRY_CONTINUE, 1,
                                   NULL);
 
                 try_continue->exit_value = exit_index;
@@ -3206,17 +3251,20 @@
     njs_int_t                 ret;
     njs_index_t               index;
     njs_module_t              *module;
+    njs_variable_t            *var;
     njs_parser_node_t         *lvalue, *expr;
     njs_vmcode_object_copy_t  *copy;
 
     lvalue = node->left;
     expr = node->right;
 
-    index = njs_variable_index(vm, lvalue);
-    if (njs_slow_path(index == NJS_INDEX_NONE)) {
+    var = njs_variable_reference(vm, lvalue);
+    if (njs_slow_path(var == NULL)) {
         return NJS_ERROR;
     }
 
+    index = lvalue->index;
+
     if (expr->left != NULL) {
         ret = njs_generator(vm, generator, expr->left);
         if (njs_slow_path(ret != NJS_OK)) {
@@ -3293,11 +3341,6 @@
     if (dest != NULL && dest->index != NJS_INDEX_NONE) {
         index = dest->index;
 
-        if (njs_is_callee_argument_index(index)) {
-            /* Assgin object directly to a callee argument. */
-            return index;
-        }
-
         if (node->left == NULL) {
             /* Assign empty object directly to variable */
             return index;
@@ -3338,14 +3381,12 @@
         return *last;
     }
 
-    scope = node->scope;
-
-    while (scope->type == NJS_SCOPE_BLOCK) {
-         scope = scope->parent;
+    scope = njs_function_scope(node->scope);
+    if (njs_slow_path(scope == NULL)) {
+        return NJS_ERROR;
     }
 
-    return njs_scope_next_index(vm, scope, NJS_SCOPE_INDEX_LOCAL,
-                                &njs_value_invalid);
+    return njs_scope_index(scope->type, scope->temp++, NJS_LEVEL_TEMP);
 }
 
 
@@ -3427,7 +3468,11 @@
                  3, node);
 
     prop_get->value = index;
-    prop_get->object = NJS_INDEX_GLOBAL_OBJECT;
+
+    prop_get->object = njs_scope_global_this_index();
+    if (njs_slow_path(prop_get->object == NJS_INDEX_ERROR)) {
+        return NJS_ERROR;
+    }
 
     lex_entry = njs_lexer_entry(node->u.reference.unique_id);
     if (njs_slow_path(lex_entry == NULL)) {
@@ -3440,8 +3485,9 @@
         return NJS_ERROR;
     }
 
-    prop_get->property = njs_value_index(vm, &property, generator->runtime);
-    if (njs_slow_path(prop_get->property == NJS_INDEX_NONE)) {
+    prop_get->property = njs_scope_global_index(vm, &property,
+                                                generator->runtime);
+    if (njs_slow_path(prop_get->property == NJS_INDEX_ERROR)) {
         return NJS_ERROR;
     }
 
diff --git a/src/njs_generator.h b/src/njs_generator.h
index 7b7b0c7..74d7a5c 100644
--- a/src/njs_generator.h
+++ b/src/njs_generator.h
@@ -13,10 +13,9 @@
 struct njs_generator_s {
     njs_value_t                     *local_scope;
 
-    size_t                          scope_size;
-
     njs_generator_block_t           *block;
     njs_arr_t                       *index_cache;
+    njs_arr_t                       *closures;
 
     njs_arr_t                       *lines;
 
diff --git a/src/njs_lexer.h b/src/njs_lexer.h
index fddc30a..32c0dda 100644
--- a/src/njs_lexer.h
+++ b/src/njs_lexer.h
@@ -141,6 +141,7 @@
     NJS_TOKEN_TEMPLATE_LITERAL,
 
     NJS_TOKEN_FUNCTION,
+    NJS_TOKEN_FUNCTION_DECLARATION,
     NJS_TOKEN_FUNCTION_EXPRESSION,
     NJS_TOKEN_FUNCTION_CALL,
     NJS_TOKEN_METHOD_CALL,
@@ -173,8 +174,6 @@
     NJS_TOKEN_THROW,
 
     NJS_TOKEN_THIS,
-    NJS_TOKEN_GLOBAL_OBJECT,
-    NJS_TOKEN_NON_LOCAL_THIS,
     NJS_TOKEN_ARGUMENTS,
     NJS_TOKEN_EVAL,
 
diff --git a/src/njs_main.h b/src/njs_main.h
index 6e007b8..5b23d37 100644
--- a/src/njs_main.h
+++ b/src/njs_main.h
@@ -57,6 +57,7 @@
 #include <njs_lexer.h>
 #include <njs_parser.h>
 #include <njs_generator.h>
+#include <njs_scope.h>
 
 #include <njs_boolean.h>
 #include <njs_symbol.h>
diff --git a/src/njs_module.c b/src/njs_module.c
index 92edbb9..fa3212a 100644
--- a/src/njs_module.c
+++ b/src/njs_module.c
@@ -61,11 +61,12 @@
         module = *item;
 
         if (module->function.native) {
-            value = njs_vmcode_operand(vm, module->index);
+            value = njs_scope_valid_value(vm, module->index);
             njs_set_object(value, &module->object);
 
         } else {
-            ret = njs_vm_invoke(vm, &module->function, NULL, 0, module->index);
+            ret = njs_vm_invoke(vm, &module->function, NULL, 0,
+                                njs_scope_valid_value(vm, module->index));
             if (ret == NJS_ERROR) {
                 return ret;
             }
@@ -573,11 +574,8 @@
     scope = njs_parser_global_scope(parser);
     vm = parser->vm;
 
-    module->index = njs_scope_next_index(vm, scope, NJS_SCOPE_INDEX_LOCAL,
-                                         &njs_value_undefined);
-    if (njs_slow_path(module->index == NJS_INDEX_ERROR)) {
-        return NJS_ERROR;
-    }
+    module->index = njs_scope_index(scope->type, scope->items, NJS_LEVEL_LOCAL);
+    scope->items++;
 
     if (vm->modules == NULL) {
         vm->modules = njs_arr_create(vm->mem_pool, 4, sizeof(njs_module_t *));
diff --git a/src/njs_parser.c b/src/njs_parser.c
index 65347c5..259e0dc 100644
--- a/src/njs_parser.c
+++ b/src/njs_parser.c
@@ -9,7 +9,8 @@
 #include <njs_main.h>
 
 
-static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type);
+static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type,
+    njs_bool_t init_this);
 static void njs_parser_scope_end(njs_parser_t *parser);
 
 static njs_int_t njs_parser_check_error_state(njs_parser_t *parser,
@@ -426,7 +427,7 @@
 static njs_parser_node_t *njs_parser_return_set(njs_parser_t *parser,
     njs_parser_node_t *expr);
 static njs_parser_node_t *njs_parser_variable_node(njs_parser_t *parser,
-    uintptr_t unique_id, njs_variable_type_t type);
+    uintptr_t unique_id, njs_variable_type_t type, njs_variable_t  **retvar);
 
 static njs_parser_node_t *njs_parser_reference(njs_parser_t *parser,
     njs_lexer_token_t *token);
@@ -503,7 +504,7 @@
 
 
 njs_int_t
-njs_parser(njs_vm_t *vm, njs_parser_t *parser, njs_rbtree_t *prev_vars)
+njs_parser(njs_vm_t *vm, njs_parser_t *parser)
 {
     njs_int_t          ret;
     njs_lexer_token_t  *token;
@@ -512,20 +513,17 @@
 
     njs_set_undefined(&vm->retval);
 
-    ret = njs_parser_scope_begin(parser, NJS_SCOPE_GLOBAL);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
-    }
-
-    if (prev_vars != NULL) {
-        /*
-         * Copy the global scope variables from the previous
-         * iteration of the accumulative mode.
-         */
-        ret = njs_variables_copy(vm, &parser->scope->variables, prev_vars);
-        if (ret != NJS_OK) {
-            return ret;
+    if (parser->scope == NULL) {
+        ret = njs_parser_scope_begin(parser, NJS_SCOPE_GLOBAL, 1);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
         }
+
+    } else {
+        parser->scope->temp = 0;
+        parser->scope->top = NULL;
+        parser->node = NULL;
+        parser->ret = NJS_OK;
     }
 
     njs_queue_init(&parser->stack);
@@ -600,33 +598,15 @@
 
 
 static njs_int_t
-njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type)
+njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type,
+    njs_bool_t init_this)
 {
-    njs_arr_t           *values;
-    njs_uint_t          nesting;
-    njs_lexer_t         *lexer;
-    njs_parser_scope_t  *scope, *parent;
+    njs_lexer_t                      *lexer;
+    njs_variable_t                   *var;
+    njs_parser_scope_t               *scope, *parent;
+    const njs_lexer_keyword_entry_t  *keyword;
 
-    nesting = 0;
-
-    if (type == NJS_SCOPE_FUNCTION) {
-
-        for (scope = parser->scope; scope != NULL; scope = scope->parent) {
-
-            if (scope->type == NJS_SCOPE_FUNCTION) {
-                nesting = scope->nesting + 1;
-
-                if (nesting < NJS_MAX_NESTING) {
-                    break;
-                }
-
-                njs_parser_syntax_error(parser, "The maximum function nesting "
-                                        "level is \"%d\"", NJS_MAX_NESTING);
-
-                return NJS_ERROR;
-            }
-        }
-    }
+    static const njs_str_t  njs_this_str = njs_str("this");
 
     scope = njs_mp_zalloc(parser->vm->mem_pool, sizeof(njs_parser_scope_t));
     if (njs_slow_path(scope == NULL)) {
@@ -635,40 +615,10 @@
 
     scope->type = type;
 
-    if (type == NJS_SCOPE_FUNCTION) {
-        scope->next_index[0] = type;
-        scope->next_index[1] = NJS_SCOPE_CLOSURE + nesting
-                               + sizeof(njs_value_t);
-
-    } else {
-        if (type == NJS_SCOPE_GLOBAL) {
-            type += NJS_INDEX_GLOBAL_OFFSET;
-        }
-
-        scope->next_index[0] = type;
-        scope->next_index[1] = 0;
-    }
-
-    scope->nesting = nesting;
-    scope->argument_closures = 0;
-
-    njs_queue_init(&scope->nested);
     njs_rbtree_init(&scope->variables, njs_parser_scope_rbtree_compare);
     njs_rbtree_init(&scope->labels, njs_parser_scope_rbtree_compare);
     njs_rbtree_init(&scope->references, njs_parser_scope_rbtree_compare);
 
-    values = NULL;
-
-    if (scope->type < NJS_SCOPE_BLOCK) {
-        values = njs_arr_create(parser->vm->mem_pool, 4, sizeof(njs_value_t));
-        if (njs_slow_path(values == NULL)) {
-            return NJS_ERROR;
-        }
-    }
-
-    scope->values[0] = values;
-    scope->values[1] = NULL;
-
     lexer = parser->lexer;
 
     if (lexer->file.length != 0) {
@@ -680,15 +630,27 @@
     scope->parent = parent;
     parser->scope = scope;
 
-    if (parent != NULL) {
-        njs_queue_insert_tail(&parent->nested, &scope->link);
+    if (type == NJS_SCOPE_FUNCTION || type == NJS_SCOPE_GLOBAL) {
+        if (init_this) {
+            /* Add this as first variable. */
+            keyword = njs_lexer_keyword(njs_this_str.start,
+                                        njs_this_str.length);
+            if (njs_slow_path(keyword == NULL)) {
+                return NJS_ERROR;
+            }
 
-        if (nesting == 0) {
-            /* Inherit function nesting in blocks. */
-            scope->nesting = parent->nesting;
+            var = njs_variable_add(parser, scope, (uintptr_t) keyword->value,
+                                   NJS_VARIABLE_VAR);
+            if (njs_slow_path(var == NULL)) {
+                return NJS_ERROR;
+            }
+
+            var->index = njs_scope_index(type, 0, NJS_LEVEL_LOCAL);
         }
     }
 
+    scope->items = 1;
+
     return NJS_OK;
 }
 
@@ -799,7 +761,7 @@
         njs_parser_next(parser, njs_parser_generator_declaration);
 
     } else {
-        node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION);
+        node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_DECLARATION);
         if (node == NULL) {
             return NJS_ERROR;
         }
@@ -1324,7 +1286,11 @@
     array->token_line = token->line;
 
     template = parser->node;
-    index = NJS_SCOPE_CALLEE_ARGUMENTS;
+
+    index = njs_scope_temp_index(template->scope);
+    if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+        return NJS_ERROR;
+    }
 
     if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) {
         node = njs_parser_argument(parser, array, index);
@@ -1335,7 +1301,10 @@
         template->right = node;
         temp->right = node;
 
-        index += sizeof(njs_value_t);
+        index = njs_scope_temp_index(template->scope);
+        if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+            return NJS_ERROR;
+        }
 
     } else {
         template->left = array;
@@ -1431,7 +1400,10 @@
         parent->right = node;
         parent = node;
 
-        parser->target->index += sizeof(njs_value_t);
+        parser->target->index = njs_scope_temp_index(node->scope);
+        if (njs_slow_path(parser->target->index == NJS_INDEX_ERROR)) {
+            return NJS_ERROR;
+        }
 
     } else {
         ret = njs_parser_array_item(parser, template->left, parser->node);
@@ -2068,7 +2040,7 @@
 
     /* MethodDefinition */
     } else if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) {
-        expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+        expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION);
         if (expr == NULL) {
             return NJS_ERROR;
         }
@@ -2796,11 +2768,9 @@
         return NJS_ERROR;
     }
 
-    if (parser->target->index == 0) {
-        node->index = NJS_SCOPE_CALLEE_ARGUMENTS;
-
-    } else {
-        node->index = parser->target->index + sizeof(njs_value_t);
+    node->index = njs_scope_temp_index(node->scope);
+    if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
+        return NJS_ERROR;
     }
 
     node->token_line = token->line;
@@ -4628,7 +4598,7 @@
     void       *target;
     njs_int_t  ret;
 
-    ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK);
+    ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0);
     if (ret != NJS_OK) {
         return NJS_ERROR;
     }
@@ -4812,6 +4782,7 @@
     njs_lexer_token_t *token, njs_queue_link_t *current)
 {
     njs_int_t          ret;
+    njs_variable_t     *var;
     njs_parser_node_t  *name;
 
     ret = njs_parser_binding_pattern(parser, token, current);
@@ -4830,11 +4801,17 @@
         return NJS_DONE;
     }
 
-    name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR);
+    name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR,
+                                    &var);
     if (name == NULL) {
         return NJS_ERROR;
     }
 
+    if (var->self) {
+        var->type = NJS_VARIABLE_VAR;
+        var->self = 0;
+    }
+
     name->token_line = token->line;
 
     parser->node = name;
@@ -5340,7 +5317,7 @@
             }
 
             var = njs_parser_variable_node(parser, token->unique_id,
-                                            NJS_VARIABLE_VAR);
+                                            NJS_VARIABLE_VAR, NULL);
             if (var == NULL) {
                 return NJS_ERROR;
             }
@@ -6259,7 +6236,7 @@
         return NJS_ERROR;
     }
 
-    ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK);
+    ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0);
     if (ret != NJS_OK) {
         return NJS_ERROR;
     }
@@ -6292,7 +6269,7 @@
 
     if (njs_lexer_token_is_binding_identifier(token)) {
         node = njs_parser_variable_node(parser, token->unique_id,
-                                        NJS_VARIABLE_CATCH);
+                                        NJS_VARIABLE_CATCH, NULL);
         if (node == NULL) {
             return NJS_ERROR;
         }
@@ -6405,43 +6382,6 @@
 /*
  * 14.1 Function Definitions.
  */
-static njs_function_t *
-njs_parser_function_alloc(njs_parser_t *parser, njs_variable_t *var)
-{
-    njs_value_t            *value;
-    njs_function_t         *function;
-    njs_function_lambda_t  *lambda;
-
-    lambda = njs_function_lambda_alloc(parser->vm, 1);
-    if (lambda == NULL) {
-        njs_memory_error(parser->vm);
-        return NULL;
-    }
-
-    /* TODO:
-     *  njs_function_t is used to pass lambda to
-     *  njs_generate_function_declaration() and is not actually needed.
-     *  real njs_function_t is created by njs_vmcode_function() in runtime.
-     */
-
-    function = njs_function_alloc(parser->vm, lambda, NULL, 1);
-    if (function == NULL) {
-        return NULL;
-    }
-
-    njs_set_function(&var->value, function);
-
-    if (var->index != NJS_INDEX_NONE
-        && njs_scope_accumulative(parser->vm, parser->scope))
-    {
-        value = (njs_value_t *) var->index;
-        *value = var->value;
-    }
-
-    return function;
-}
-
-
 static njs_int_t
 njs_parser_function_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
     njs_queue_link_t *current)
@@ -6449,8 +6389,7 @@
     njs_int_t          ret;
     uintptr_t          unique_id;
     njs_variable_t     *var;
-    njs_function_t     *function;
-    njs_parser_node_t  *node, *temp;
+    njs_parser_node_t  *node;
 
     if (!njs_lexer_token_is_binding_identifier(token)) {
         return njs_parser_failed(parser);
@@ -6478,38 +6417,26 @@
 
     njs_lexer_consume_token(parser->lexer, 1);
 
-    var = njs_variable_add(parser, parser->scope, unique_id,
-                           NJS_VARIABLE_FUNCTION);
+    var = njs_variable_function_add(parser, parser->scope, unique_id,
+                                    NJS_VARIABLE_FUNCTION);
     if (var == NULL) {
         return NJS_ERROR;
     }
 
-    ret = njs_variable_reference(parser->vm, parser->scope, node,
-                                 unique_id, NJS_DECLARATION);
+    node->u.value.data.u.lambda = var->value.data.u.lambda;
+
+    node->left = (njs_parser_node_t *) unique_id;
+
+    parser->node = node;
+
+    ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1);
     if (ret != NJS_OK) {
         return NJS_ERROR;
     }
 
-    function = njs_parser_function_alloc(parser, var);
-    if (function == NULL) {
-        return NJS_ERROR;
-    }
-
-    temp = njs_parser_node_new(parser, 0);
-    if (temp == NULL) {
-        return NJS_ERROR;
-    }
-
-    temp->left = node;
-    temp->u.value.data.u.lambda = function->u.lambda;
-
-    node->left = (njs_parser_node_t *) function;
-
-    parser->node = temp;
-
     njs_parser_next(parser, njs_parser_function_parse);
 
-    return njs_parser_after(parser, current, temp, 1,
+    return njs_parser_after(parser, current, node, 1,
                             njs_parser_function_declaration_after);
 }
 
@@ -6518,18 +6445,21 @@
 njs_parser_function_declaration_after(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current)
 {
-    njs_function_t  *function;
+    njs_int_t  ret;
+    uintptr_t  unique_id;
 
-    parser->node = parser->target->left;
+    unique_id = (uintptr_t) parser->node->left;
 
-    function = (njs_function_t *) parser->node->left;
-
-    function->args_count = function->u.lambda->nargs
-                           - function->u.lambda->rest_parameters;
-
-    parser->node->right = parser->target->right;
     parser->node->left = NULL;
 
+    njs_value_null_set(&parser->node->u.value);
+
+    ret = njs_parser_variable_reference(parser, parser->scope, parser->node,
+                                        unique_id, NJS_DECLARATION);
+    if (ret != NJS_OK) {
+        return NJS_ERROR;
+    }
+
     return njs_parser_stack_pop(parser);
 }
 
@@ -6538,13 +6468,6 @@
 njs_parser_function_parse(njs_parser_t *parser, njs_lexer_token_t *token,
     njs_queue_link_t *current)
 {
-    njs_int_t  ret;
-
-    ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
-    if (ret != NJS_OK) {
-        return NJS_ERROR;
-    }
-
     parser->target = parser->node;
     parser->node = NULL;
 
@@ -6555,26 +6478,30 @@
 }
 
 
+static const njs_lexer_entry_t njs_parser_anonymous_entry =
+{
+    .name = njs_str("anonymous")
+};
+
+
 static njs_int_t
 njs_parser_function_expression(njs_parser_t *parser, njs_lexer_token_t *token,
     njs_queue_link_t *current)
 {
     njs_int_t              ret;
+    uintptr_t              unique_id;
     njs_variable_t         *var;
-    njs_function_t         *function;
     njs_function_lambda_t  *lambda;
 
-    ret = njs_parser_scope_begin(parser, NJS_SCOPE_SHIM);
+    ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1);
     if (ret != NJS_OK) {
         return NJS_ERROR;
     }
 
+    var = NULL;
+
     if (njs_lexer_token_is_binding_identifier(token)) {
-        var = njs_variable_add(parser, parser->scope, token->unique_id,
-                               NJS_VARIABLE_SHIM);
-        if (var == NULL) {
-            return NJS_ERROR;
-        }
+        unique_id = token->unique_id;
 
         njs_lexer_consume_token(parser->lexer, 1);
 
@@ -6583,19 +6510,8 @@
             return NJS_ERROR;
         }
 
-        function = njs_parser_function_alloc(parser, var);
-        if (function == NULL) {
-            return NJS_ERROR;
-        }
-
-        lambda = function->u.lambda;
-
     } else {
-        /* Anonymous function. */
-        lambda = njs_function_lambda_alloc(parser->vm, 1);
-        if (lambda == NULL) {
-            return NJS_ERROR;
-        }
+        unique_id = (uintptr_t) &njs_parser_anonymous_entry;
     }
 
     if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) {
@@ -6604,11 +6520,36 @@
 
     njs_lexer_consume_token(parser->lexer, 1);
 
+    parser->node->left = njs_parser_node_new(parser, NJS_TOKEN_NAME);
+    if (parser->node->left == NULL) {
+        return NJS_ERROR;
+    }
+
+    var = njs_variable_scope_add(parser, parser->scope, parser->scope,
+                                 unique_id, NJS_VARIABLE_FUNCTION, 1);
+    if (var == NULL) {
+        return NJS_ERROR;
+    }
+
+    var->self = 1;
+
+    ret = njs_parser_variable_reference(parser, parser->scope,
+                                        parser->node->left, unique_id,
+                                        NJS_DECLARATION);
+    if (ret != NJS_OK) {
+        return NJS_ERROR;
+    }
+
+    lambda = njs_function_lambda_alloc(parser->vm, 1);
+    if (lambda == NULL) {
+        return NJS_ERROR;
+    }
+
     parser->node->u.value.data.u.lambda = lambda;
 
     njs_parser_next(parser, njs_parser_function_parse);
 
-    return njs_parser_after(parser, current, NULL, 1,
+    return njs_parser_after(parser, current, var, 1,
                             njs_parser_function_expression_after);
 }
 
@@ -6617,7 +6558,17 @@
 njs_parser_function_expression_after(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current)
 {
-    njs_parser_scope_end(parser);
+    njs_variable_t  *var;
+
+    var = (njs_variable_t *) parser->target;
+
+    var->index = njs_scope_index(var->scope->type, var->scope->items,
+                                 NJS_LEVEL_LOCAL);
+    var->scope->items++;
+
+    if (var->self) {
+        parser->node->u.value.data.u.lambda->self = var->index;
+    }
 
     return njs_parser_stack_pop(parser);
 }
@@ -6639,7 +6590,9 @@
 njs_parser_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token,
     njs_queue_link_t *current)
 {
-    njs_variable_t         *arg, *cur_arg;
+    njs_variable_t         *arg;
+    njs_rbtree_node_t      *rb_node;
+    njs_variable_node_t    var_node;
     njs_function_lambda_t  *lambda;
 
     lambda = parser->target->u.value.data.u.lambda;
@@ -6667,28 +6620,31 @@
     default:
         /* SingleNameBinding */
         if (njs_lexer_token_is_binding_identifier(token)) {
-            arg = njs_variable_add(parser, parser->scope,
-                                   token->unique_id, NJS_VARIABLE_VAR);
+            var_node.key = token->unique_id;
+
+            rb_node = njs_rbtree_find(&parser->scope->variables,
+                                      &var_node.node);
+            if (rb_node != NULL) {
+                arg = ((njs_variable_node_t *) rb_node)->variable;
+
+                if (!arg->self) {
+                    njs_parser_syntax_error(parser,
+                                            "Duplicate parameter names");
+                    return NJS_DONE;
+                }
+
+                arg->self = 0;
+
+            } else {
+                arg = njs_variable_add(parser, parser->scope,
+                                       token->unique_id, NJS_VARIABLE_VAR);
+            }
+
             if (arg == NULL) {
                 return NJS_ERROR;
             }
 
-            if (arg->index > 0) {
-                njs_parser_syntax_error(parser, "Duplicate parameter names");
-                return NJS_DONE;
-            }
-
-            cur_arg = (njs_variable_t *) parser->node;
-
-            if (cur_arg == NULL) {
-                arg->index = NJS_SCOPE_ARGUMENTS;
-
-                /* A "this" reservation. */
-                arg->index += sizeof(njs_value_t);
-
-            } else {
-                arg->index = cur_arg->index + sizeof(njs_value_t);
-            }
+            arg->argument = 1;
 
             lambda->nargs++;
 
@@ -6744,8 +6700,9 @@
     njs_queue_link_t *current)
 {
     njs_int_t              ret;
-    njs_variable_t         *arg;
-    njs_parser_node_t      *node;
+    uintptr_t              unique_id;
+    njs_variable_t         *arg, *var;
+    njs_parser_node_t      *node, *name;
     njs_function_lambda_t  *lambda;
 
     node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
@@ -6756,6 +6713,34 @@
     node->token_line = token->line;
     parser->node = node;
 
+    ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 0);
+    if (ret != NJS_OK) {
+        return NJS_ERROR;
+    }
+
+    name = njs_parser_node_new(parser, NJS_TOKEN_NAME);
+    if (name == NULL) {
+        return NJS_ERROR;
+    }
+
+    node->left = name;
+
+    unique_id = (uintptr_t) &njs_parser_anonymous_entry;
+
+    var = njs_variable_scope_add(parser, parser->scope, parser->scope,
+                                 unique_id, NJS_VARIABLE_FUNCTION, 1);
+    if (var == NULL) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_parser_variable_reference(parser, parser->scope, node->left,
+                                        unique_id, NJS_DECLARATION);
+    if (ret != NJS_OK) {
+        return NJS_ERROR;
+    }
+
+    node->left->u.reference.variable = var;
+
     lambda = njs_function_lambda_alloc(parser->vm, 0);
     if (lambda == NULL) {
         return NJS_ERROR;
@@ -6763,11 +6748,6 @@
 
     node->u.value.data.u.lambda = lambda;
 
-    ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
-    if (ret != NJS_OK) {
-        return NJS_ERROR;
-    }
-
     parser->scope->arrow_function = 1;
 
     if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) {
@@ -6782,18 +6762,20 @@
                                 njs_parser_arrow_function_args_after);
 
     } else if (njs_lexer_token_is_binding_identifier(token)) {
-        arg = njs_variable_add(parser, parser->scope,
-                               token->unique_id, NJS_VARIABLE_VAR);
+        arg = njs_variable_add(parser, parser->scope, token->unique_id,
+                               NJS_VARIABLE_VAR);
         if (arg == NULL) {
             return NJS_ERROR;
         }
 
-        arg->index = NJS_SCOPE_ARGUMENTS;
+        arg->argument = 1;
 
-        /* A "this" reservation. */
-        arg->index += sizeof(njs_value_t);
+        var->index = njs_scope_index(parser->scope->type, parser->scope->items,
+                                     NJS_LEVEL_LOCAL);
+        parser->scope->items++;
 
-        lambda->nargs = 1;
+        lambda->self = var->index;
+        lambda->nargs++;
 
         njs_lexer_consume_token(parser->lexer, 1);
 
@@ -6813,12 +6795,25 @@
 njs_parser_arrow_function_args_after(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current)
 {
+    njs_variable_t  *var, **vv;
+
     if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) {
         return njs_parser_failed(parser);
     }
 
     njs_lexer_consume_token(parser->lexer, 1);
 
+    vv = &parser->target->left->u.reference.variable;
+
+    var = *vv;
+    *vv = NULL;
+
+    var->index = njs_scope_index(var->scope->type, var->scope->items,
+                                 NJS_LEVEL_LOCAL);
+    var->scope->items++;
+
+    parser->target->u.value.data.u.lambda->self = var->index;
+
     njs_parser_next(parser, njs_parser_arrow_function_arrow);
 
     return NJS_OK;
@@ -6929,7 +6924,7 @@
         return njs_parser_failed(parser);
     }
 
-    expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+    expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION);
     if (expr == NULL) {
         return NJS_ERROR;
     }
@@ -7003,7 +6998,7 @@
         return njs_parser_failed(parser);
     }
 
-    expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+    expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION);
     if (expression == NULL) {
         return NJS_ERROR;
     }
@@ -7047,7 +7042,7 @@
         return njs_parser_failed(parser);
     }
 
-    expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+    expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION);
     if (expression == NULL) {
         return NJS_ERROR;
     }
@@ -7152,7 +7147,7 @@
     expr = parser->node;
     expr->u.value.data.u.lambda = lambda;
 
-    ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
+    ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1);
     if (ret != NJS_OK) {
         return NJS_ERROR;
     }
@@ -7321,7 +7316,8 @@
         return NJS_DONE;
     }
 
-    name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR);
+    name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR,
+                                    NULL);
     if (name == NULL) {
         return NJS_ERROR;
     }
@@ -7376,17 +7372,24 @@
     parser->target->right = parser->node;
 
     parser->node = parser->target;
-    parser->node->hoist = 1;
 
     return njs_parser_stack_pop(parser);
 }
 
 
+static const njs_lexer_entry_t njs_parser_module_entry =
+{
+    .name = njs_str("module")
+};
+
+
 njs_int_t
 njs_parser_module_lambda(njs_parser_t *parser, njs_lexer_token_t *token,
     njs_queue_link_t *current)
 {
     njs_int_t              ret;
+    uintptr_t              unique_id;
+    njs_variable_t         *var;
     njs_parser_node_t      *node, *parent;
     njs_function_lambda_t  *lambda;
 
@@ -7395,6 +7398,32 @@
         return NJS_ERROR;
     }
 
+    ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 0);
+    if (ret != NJS_OK) {
+        return NJS_ERROR;
+    }
+
+    node->left = njs_parser_node_new(parser, NJS_TOKEN_NAME);
+    if (node->left == NULL) {
+        return NJS_ERROR;
+    }
+
+    unique_id = (uintptr_t) &njs_parser_module_entry;
+
+    var = njs_variable_scope_add(parser, parser->scope, parser->scope,
+                                 unique_id, NJS_VARIABLE_FUNCTION, 1);
+    if (var == NULL) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_parser_variable_reference(parser, parser->scope, node->left,
+                                        unique_id, NJS_DECLARATION);
+    if (ret != NJS_OK) {
+        return NJS_ERROR;
+    }
+
+    node->left->u.reference.variable = var;
+
     lambda = njs_function_lambda_alloc(parser->vm, 1);
     if (lambda == NULL) {
         return NJS_ERROR;
@@ -7405,11 +7434,6 @@
 
     parser->node = node;
 
-    ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
-    if (ret != NJS_OK) {
-        return NJS_ERROR;
-    }
-
     parser->scope->module = 1;
 
     parent = parser->node;
@@ -7426,7 +7450,8 @@
 njs_parser_module_lambda_after(njs_parser_t *parser, njs_lexer_token_t *token,
     njs_queue_link_t *current)
 {
-    njs_int_t  ret;
+    njs_int_t       ret;
+    njs_variable_t  *var, **vv;
 
     ret = njs_parser_export_sink(parser);
     if (ret != NJS_OK) {
@@ -7437,6 +7462,17 @@
 
     parser->node = parser->target;
 
+    vv = &parser->target->left->u.reference.variable;
+
+    var = *vv;
+    *vv = NULL;
+
+    var->index = njs_scope_index(var->scope->type, var->scope->items,
+                                 NJS_LEVEL_LOCAL);
+    var->scope->items++;
+
+    parser->node->u.value.data.u.lambda->self = var->index;
+
     njs_parser_scope_end(parser);
 
     return njs_parser_stack_pop(parser);
@@ -7529,7 +7565,7 @@
 
 static njs_parser_node_t *
 njs_parser_variable_node(njs_parser_t *parser, uintptr_t unique_id,
-    njs_variable_type_t type)
+    njs_variable_type_t type, njs_variable_t  **retvar)
 {
     njs_int_t          ret;
     njs_variable_t     *var;
@@ -7540,17 +7576,8 @@
         return NULL;
     }
 
-    if (njs_is_null(&var->value)) {
-
-        switch (type) {
-
-        case NJS_VARIABLE_VAR:
-            njs_set_undefined(&var->value);
-            break;
-
-        default:
-            break;
-        }
+    if (retvar != NULL) {
+        *retvar = var;
     }
 
     node = njs_parser_node_new(parser, NJS_TOKEN_NAME);
@@ -7558,8 +7585,8 @@
         return NULL;
     }
 
-    ret = njs_variable_reference(parser->vm, parser->scope, node, unique_id,
-                                 NJS_DECLARATION);
+    ret = njs_parser_variable_reference(parser, parser->scope, node, unique_id,
+                                        NJS_DECLARATION);
     if (njs_slow_path(ret != NJS_OK)) {
         return NULL;
     }
@@ -7571,10 +7598,14 @@
 static njs_parser_node_t *
 njs_parser_reference(njs_parser_t *parser, njs_lexer_token_t *token)
 {
-    njs_int_t           ret;
-    njs_variable_t      *var;
-    njs_parser_node_t   *node;
-    njs_parser_scope_t  *scope;
+    njs_int_t                        ret;
+    njs_index_t                      index;
+    njs_variable_t                   *var;
+    njs_parser_node_t                *node;
+    njs_parser_scope_t               *scope;
+    const njs_lexer_keyword_entry_t  *keyword;
+
+    static const njs_str_t  njs_undefined_str = njs_str("undefined");
 
     node = njs_parser_node_new(parser, token->type);
     if (njs_slow_path(node == NULL)) {
@@ -7585,51 +7616,58 @@
 
     case NJS_TOKEN_NULL:
         njs_thread_log_debug("JS: null");
-
-        node->u.value = njs_value_null;
         break;
 
     case NJS_TOKEN_THIS:
         njs_thread_log_debug("JS: this");
 
-        scope = njs_function_scope(parser->scope, 0);
-
-        if (scope != NULL) {
-            if (scope == njs_function_scope(parser->scope, 1)) {
-                node->index = NJS_INDEX_THIS;
-
-            } else {
-                node->token_type = NJS_TOKEN_NON_LOCAL_THIS;
-                node->token_line = token->line;
-
-                ret = njs_variable_reference(parser->vm, scope, node,
-                                             token->unique_id, NJS_REFERENCE);
-                if (njs_slow_path(ret != NJS_OK)) {
-                    return NULL;
-                }
-
-                var = njs_variable_add(parser, scope, token->unique_id,
-                                       NJS_VARIABLE_VAR);
-                if (njs_slow_path(var == NULL)) {
-                    return NULL;
-                }
-
-                var->this_object = 1;
-            }
-
-            break;
+        scope = njs_function_scope(parser->scope);
+        if (njs_slow_path(scope == NULL)) {
+            njs_parser_syntax_error(parser,
+                                    "function or global scope not found");
+            return NULL;
         }
 
-        node->token_type = NJS_TOKEN_GLOBAL_OBJECT;
+        if (parser->vm->options.module) {
+            keyword = njs_lexer_keyword(njs_undefined_str.start,
+                                        njs_undefined_str.length);
+            if (njs_slow_path(keyword == NULL)) {
+                return NULL;
+            }
+
+            token->unique_id = (uintptr_t) keyword->value;
+
+        } else if (!scope->arrow_function) {
+            index = njs_scope_index(scope->type, 0, NJS_LEVEL_LOCAL);
+
+            var = njs_variable_scope_add(parser, scope, scope, token->unique_id,
+                                         NJS_VARIABLE_VAR, index);
+            if (njs_slow_path(var == NULL)) {
+                return NULL;
+            }
+        }
+
+        node->token_type = NJS_TOKEN_THIS;
+        node->token_line = token->line;
+
+        ret = njs_parser_variable_reference(parser, parser->scope, node,
+                                            token->unique_id, NJS_REFERENCE);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NULL;
+        }
 
         break;
 
     case NJS_TOKEN_ARGUMENTS:
         njs_thread_log_debug("JS: arguments");
 
-        scope = njs_function_scope(parser->scope, 0);
+        scope = njs_function_scope(parser->scope);
 
-        if (scope == NULL) {
+        while (scope->arrow_function) {
+            scope = njs_function_scope(scope->parent);
+        }
+
+        if (scope == NULL || scope->type == NJS_SCOPE_GLOBAL) {
             njs_parser_syntax_error(parser, "\"%V\" object in global scope",
                                     &token->text);
             return NULL;
@@ -7637,8 +7675,8 @@
 
         node->token_line = token->line;
 
-        ret = njs_variable_reference(parser->vm, scope, node, token->unique_id,
-                                     NJS_REFERENCE);
+        ret = njs_parser_variable_reference(parser, parser->scope, node,
+                                            token->unique_id, NJS_REFERENCE);
         if (njs_slow_path(ret != NJS_OK)) {
             return NULL;
         }
@@ -7665,8 +7703,8 @@
 
             node->token_line = token->line;
 
-            ret = njs_variable_reference(parser->vm, parser->scope, node,
-                                         token->unique_id, NJS_REFERENCE);
+            ret = njs_parser_variable_reference(parser, parser->scope, node,
+                                               token->unique_id, NJS_REFERENCE);
             if (njs_slow_path(ret != NJS_OK)) {
                 return NULL;
             }
@@ -8322,6 +8360,41 @@
 }
 
 
+njs_int_t
+njs_parser_variable_reference(njs_parser_t *parser, njs_parser_scope_t *scope,
+    njs_parser_node_t *node, uintptr_t unique_id, njs_reference_type_t type)
+{
+    njs_rbtree_node_t         *rb_node;
+    njs_variable_reference_t  *vr;
+    njs_parser_rbtree_node_t  parse_node, *rb_parse_node;
+
+    vr = &node->u.reference;
+
+    vr->unique_id = unique_id;
+    vr->type = type;
+
+    parse_node.key = unique_id;
+
+    rb_node = njs_rbtree_find(&scope->references, &parse_node.node);
+    if (rb_node != NULL) {
+        return NJS_OK;
+    }
+
+    rb_parse_node = njs_mp_alloc(parser->vm->mem_pool,
+                                 sizeof(njs_parser_rbtree_node_t));
+    if (njs_slow_path(rb_parse_node == NULL)) {
+        return NJS_ERROR;
+    }
+
+    rb_parse_node->key = unique_id;
+    rb_parse_node->index = NJS_INDEX_NONE;
+
+    njs_rbtree_insert(&scope->references, &rb_parse_node->node);
+
+    return NJS_OK;
+}
+
+
 njs_token_type_t
 njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser,
     njs_str_t *name, njs_token_type_t type)
@@ -8632,8 +8705,6 @@
     njs_token_serialize(NJS_TOKEN_FINALLY);
     njs_token_serialize(NJS_TOKEN_THROW);
     njs_token_serialize(NJS_TOKEN_THIS);
-    njs_token_serialize(NJS_TOKEN_GLOBAL_OBJECT);
-    njs_token_serialize(NJS_TOKEN_NON_LOCAL_THIS);
     njs_token_serialize(NJS_TOKEN_ARGUMENTS);
     njs_token_serialize(NJS_TOKEN_EVAL);
     njs_token_serialize(NJS_TOKEN_IMPORT);
diff --git a/src/njs_parser.h b/src/njs_parser.h
index 4fe8ea5..854ac9b 100644
--- a/src/njs_parser.h
+++ b/src/njs_parser.h
@@ -12,26 +12,21 @@
 struct njs_parser_scope_s {
     njs_parser_node_t               *top;
 
-    njs_queue_link_t                link;
-    njs_queue_t                     nested;
-
     njs_parser_scope_t              *parent;
     njs_rbtree_t                    variables;
     njs_rbtree_t                    labels;
     njs_rbtree_t                    references;
 
-#define NJS_SCOPE_INDEX_LOCAL       0
-#define NJS_SCOPE_INDEX_CLOSURE     1
+    njs_arr_t                       *closures;
+    njs_arr_t                       *declarations;
 
-    njs_arr_t                       *values[2];  /* Array of njs_value_t. */
-    njs_index_t                     next_index[2];
+    uint32_t                        temp;
+    uint32_t                        items;
 
     njs_str_t                       cwd;
     njs_str_t                       file;
 
     njs_scope_t                     type:8;
-    uint8_t                         nesting;     /* 4 bits */
-    uint8_t                         argument_closures;
     uint8_t                         module;
     uint8_t                         arrow_function;
 };
@@ -41,7 +36,6 @@
     njs_token_type_t                token_type:16;
     uint8_t                         ctor:1;
     uint8_t                         temporary;    /* 1 bit  */
-    uint8_t                         hoist;        /* 1 bit  */
     uint32_t                        token_line;
 
     union {
@@ -101,7 +95,7 @@
 typedef struct {
     NJS_RBTREE_NODE                 (node);
     uintptr_t                       key;
-    njs_parser_node_t               *parser_node;
+    njs_index_t                     index;
 } njs_parser_rbtree_node_t;
 
 
@@ -110,14 +104,16 @@
 
 intptr_t njs_parser_scope_rbtree_compare(njs_rbtree_node_t *node1,
     njs_rbtree_node_t *node2);
-njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser,
-    njs_rbtree_t *prev_vars);
+njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser);
 
 njs_int_t njs_parser_module_lambda(njs_parser_t *parser,
     njs_lexer_token_t *token, njs_queue_link_t *current);
 njs_variable_t *njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node);
 njs_index_t njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node);
 njs_bool_t njs_parser_has_side_effect(njs_parser_node_t *node);
+njs_int_t njs_parser_variable_reference(njs_parser_t *parser,
+    njs_parser_scope_t *scope, njs_parser_node_t *node, uintptr_t unique_id,
+    njs_reference_type_t type);
 njs_token_type_t njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser,
     njs_str_t *name, njs_token_type_t type);
 njs_int_t njs_parser_string_create(njs_vm_t *vm, njs_lexer_token_t *token,
@@ -139,10 +135,6 @@
      || (node)->token_type == NJS_TOKEN_PROPERTY)
 
 
-#define njs_scope_accumulative(vm, scope)                                     \
-    ((vm)->options.accumulative && (scope)->type == NJS_SCOPE_GLOBAL)
-
-
 #define njs_parser_syntax_error(parser, fmt, ...)                             \
     njs_parser_lexer_error(parser, NJS_OBJ_TYPE_SYNTAX_ERROR, fmt,            \
                            ##__VA_ARGS__)
@@ -215,17 +207,18 @@
 
 
 njs_inline njs_parser_scope_t *
-njs_function_scope(njs_parser_scope_t *scope, njs_bool_t any)
+njs_function_scope(njs_parser_scope_t *scope)
 {
-    while (scope->type != NJS_SCOPE_GLOBAL) {
-        if (scope->type == NJS_SCOPE_FUNCTION
-            && (any || !scope->arrow_function))
+    do {
+        if (scope->type == NJS_SCOPE_GLOBAL
+            || scope->type == NJS_SCOPE_FUNCTION)
         {
             return scope;
         }
 
         scope = scope->parent;
-    }
+
+    } while (scope != NULL);
 
     return NULL;
 }
diff --git a/src/njs_scope.c b/src/njs_scope.c
new file mode 100644
index 0000000..4d903cd
--- /dev/null
+++ b/src/njs_scope.c
@@ -0,0 +1,259 @@
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs_main.h>
+
+
+static njs_value_t *njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src,
+    njs_uint_t runtime, njs_index_t **index);
+
+
+njs_index_t
+njs_scope_temp_index(njs_parser_scope_t *scope)
+{
+    scope = njs_function_scope(scope);
+    if (njs_slow_path(scope == NULL)) {
+        return NJS_INDEX_ERROR;
+    }
+
+    return njs_scope_index(NJS_SCOPE_GLOBAL, scope->temp++, NJS_LEVEL_TEMP);
+}
+
+
+njs_value_t *
+njs_scope_create_index_value(njs_vm_t *vm, njs_index_t index)
+{
+    njs_value_t  *value;
+
+    value = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+    if (njs_slow_path(value == NULL)) {
+        return NULL;
+    }
+
+    njs_scope_value_set(vm, index, value);
+
+    return value;
+}
+
+
+njs_value_t **
+njs_scope_make(njs_vm_t *vm, uint32_t count)
+{
+    size_t       size;
+    njs_value_t  **refs, *values;
+
+    size = (count * sizeof(njs_value_t *)) + (count * sizeof(njs_value_t));
+
+    refs = njs_mp_alloc(vm->mem_pool, size);
+    if (njs_slow_path(refs == NULL)) {
+        njs_memory_error(vm);
+        return NULL;
+    }
+
+    values = (njs_value_t *) ((u_char *) refs
+                              + (count * sizeof(njs_value_t *)));
+
+    while (count != 0) {
+        count--;
+
+        refs[count] = &values[count];
+
+        njs_set_invalid(refs[count]);
+    }
+
+    return refs;
+}
+
+
+njs_index_t
+njs_scope_global_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime)
+{
+    njs_index_t  index, *retval;
+    njs_value_t  **values, *value;
+
+    value = njs_scope_value_index(vm, src, runtime, &retval);
+    if (njs_slow_path(value == NULL)) {
+        return NJS_INDEX_ERROR;
+    }
+
+    if (*retval != NJS_INDEX_ERROR) {
+        return *retval;
+    }
+
+    if (vm->scope_absolute == NULL) {
+        vm->scope_absolute = njs_arr_create(vm->mem_pool, 8,
+                                            sizeof(njs_value_t *));
+        if (njs_slow_path(vm->scope_absolute == NULL)) {
+            return NJS_INDEX_ERROR;
+        }
+    }
+
+    index = vm->scope_absolute->items;
+
+    values = njs_arr_add(vm->scope_absolute);
+    if (njs_slow_path(values == NULL)) {
+        return NJS_INDEX_ERROR;
+    }
+
+    *values = value;
+
+    vm->levels[NJS_LEVEL_STATIC] = vm->scope_absolute->start;
+
+    *retval = njs_scope_index(NJS_SCOPE_GLOBAL, index, NJS_LEVEL_STATIC);
+
+    return *retval;
+}
+
+
+static njs_int_t
+njs_scope_values_hash_test(njs_lvlhsh_query_t *lhq, void *data)
+{
+    njs_str_t    string;
+    njs_value_t  *value;
+
+    value = data;
+
+    if (njs_is_string(value)) {
+        njs_string_get(value, &string);
+
+    } else {
+        string.start = (u_char *) value;
+        string.length = sizeof(njs_value_t);
+    }
+
+    if (lhq->key.length == string.length
+        && memcmp(lhq->key.start, string.start, string.length) == 0)
+    {
+        return NJS_OK;
+    }
+
+    return NJS_DECLINED;
+}
+
+
+static const njs_lvlhsh_proto_t  njs_values_hash_proto
+    njs_aligned(64) =
+{
+    NJS_LVLHSH_DEFAULT,
+    njs_scope_values_hash_test,
+    njs_lvlhsh_alloc,
+    njs_lvlhsh_free,
+};
+
+
+/*
+ * Constant values such as njs_value_true are copied to values_hash during
+ * code generation when they are used as operands to guarantee aligned value.
+ */
+
+static njs_value_t *
+njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime,
+    njs_index_t **index)
+{
+    u_char              *start;
+    uint32_t            value_size, size, length;
+    njs_int_t           ret;
+    njs_str_t           str;
+    njs_bool_t          long_string;
+    njs_value_t         *value;
+    njs_string_t        *string;
+    njs_lvlhsh_t        *values_hash;
+    njs_lvlhsh_query_t  lhq;
+
+    long_string = 0;
+    value_size = sizeof(njs_value_t);
+
+    if (njs_is_string(src)) {
+        njs_string_get(src, &str);
+
+        size = (uint32_t) str.length;
+        start = str.start;
+
+        if (src->short_string.size == NJS_STRING_LONG) {
+            long_string = 1;
+        }
+
+    } else {
+        size = value_size;
+        start = (u_char *) src;
+    }
+
+    lhq.key_hash = njs_djb_hash(start, size);
+    lhq.key.length = size;
+    lhq.key.start = start;
+    lhq.proto = &njs_values_hash_proto;
+
+    if (njs_lvlhsh_find(&vm->shared->values_hash, &lhq) == NJS_OK) {
+        value = lhq.value;
+
+        *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
+
+    } else if (runtime && njs_lvlhsh_find(&vm->values_hash, &lhq) == NJS_OK) {
+        value = lhq.value;
+
+        *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
+
+    } else {
+        if (long_string) {
+            length = src->long_string.data->length;
+
+            if (size != length && length > NJS_STRING_MAP_STRIDE) {
+                size = njs_string_map_offset(size)
+                       + njs_string_map_size(length);
+            }
+
+            value_size += sizeof(njs_string_t) + size;
+        }
+
+        value_size += sizeof(njs_index_t);
+
+        value = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), value_size);
+        if (njs_slow_path(value == NULL)) {
+            return NULL;
+        }
+
+        *value = *src;
+
+        if (long_string) {
+            string = (njs_string_t *) ((u_char *) value + sizeof(njs_value_t)
+                                       + sizeof(njs_index_t));
+
+            value->long_string.data = string;
+
+            string->start = (u_char *) string + sizeof(njs_string_t);
+            string->length = src->long_string.data->length;
+            string->retain = 0xffff;
+
+            memcpy(string->start, start, size);
+        }
+
+        *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
+        **index = NJS_INDEX_ERROR;
+
+        lhq.replace = 0;
+        lhq.value = value;
+        lhq.pool = vm->mem_pool;
+
+        values_hash = runtime ? &vm->values_hash : &vm->shared->values_hash;
+
+        ret = njs_lvlhsh_insert(values_hash, &lhq);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NULL;
+        }
+    }
+
+    if (start != (u_char *) src) {
+        /*
+         * The source node value must be updated with the shared value
+         * allocated from the permanent memory pool because the node
+         * value can be used as a variable initial value.
+         */
+        *(njs_value_t *) src = *value;
+    }
+
+    return value;
+}
diff --git a/src/njs_scope.h b/src/njs_scope.h
new file mode 100644
index 0000000..0303da4
--- /dev/null
+++ b/src/njs_scope.h
@@ -0,0 +1,122 @@
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_SCOPE_H_INCLUDED_
+#define _NJS_SCOPE_H_INCLUDED_
+
+
+#define NJS_SCOPE_TYPE_SIZE     4
+#define NJS_SCOPE_VALUE_OFFSET  (NJS_SCOPE_TYPE_SIZE + 1)
+#define NJS_SCOPE_VALUE_MAX     ((1 << (32 - NJS_SCOPE_VALUE_OFFSET)) - 1)
+#define NJS_SCOPE_TYPE_MASK     ((NJS_SCOPE_VALUE_MAX) << NJS_SCOPE_TYPE_SIZE)
+
+#define NJS_INDEX_NONE          ((njs_index_t) 0)
+#define NJS_INDEX_ERROR         ((njs_index_t) -1)
+
+
+njs_index_t njs_scope_temp_index(njs_parser_scope_t *scope);
+njs_value_t *njs_scope_create_index_value(njs_vm_t *vm, njs_index_t index);
+njs_value_t **njs_scope_make(njs_vm_t *vm, uint32_t count);
+njs_index_t njs_scope_global_index(njs_vm_t *vm, const njs_value_t *src,
+    njs_uint_t runtime);
+
+
+njs_inline njs_index_t
+njs_scope_index(njs_scope_t scope, njs_index_t index, njs_level_type_t type)
+{
+    if (index > NJS_SCOPE_VALUE_MAX || type >= NJS_LEVEL_MAX
+        || (scope != NJS_SCOPE_GLOBAL && scope != NJS_SCOPE_FUNCTION))
+    {
+        return NJS_INDEX_ERROR;
+    }
+
+    if (scope == NJS_SCOPE_GLOBAL && type == NJS_LEVEL_LOCAL) {
+        type = NJS_LEVEL_GLOBAL;
+    }
+
+    return (index << NJS_SCOPE_VALUE_OFFSET) | type;
+}
+
+
+njs_inline njs_level_type_t
+njs_scope_index_type(njs_index_t index)
+{
+    return (njs_level_type_t) (index & ~NJS_SCOPE_TYPE_MASK);
+}
+
+
+njs_inline uint32_t
+njs_scope_index_value(njs_index_t index)
+{
+    return (uint32_t) (index >> NJS_SCOPE_VALUE_OFFSET);
+}
+
+
+njs_inline njs_value_t *
+njs_scope_value(njs_vm_t *vm, njs_index_t index)
+{
+    return vm->levels[njs_scope_index_type(index)]
+                     [njs_scope_index_value(index)];
+}
+
+
+njs_inline njs_value_t *
+njs_scope_valid_value(njs_vm_t *vm, njs_index_t index)
+{
+    njs_value_t  *value;
+
+    value = njs_scope_value(vm, index);
+
+    if (!njs_is_valid(value)) {
+        njs_set_undefined(value);
+    }
+
+    return value;
+}
+
+
+njs_inline void
+njs_scope_value_set(njs_vm_t *vm, njs_index_t index, njs_value_t *value)
+{
+    vm->levels[njs_scope_index_type(index)]
+              [njs_scope_index_value(index)] = value;
+}
+
+
+njs_inline njs_value_t *
+njs_scope_value_clone(njs_vm_t *vm, njs_index_t index, njs_value_t *value)
+{
+    njs_value_t  *newval;
+
+    newval = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+    if (njs_slow_path(newval == NULL)) {
+        njs_memory_error(vm);
+        return NULL;
+    }
+
+    *newval = *value;
+
+    njs_scope_value_set(vm, index, newval);
+
+    return newval;
+}
+
+
+njs_inline njs_index_t
+njs_scope_undefined_index(njs_vm_t *vm, njs_uint_t runtime)
+{
+    return njs_scope_global_index(vm, &njs_value_undefined, runtime);
+}
+
+
+njs_inline njs_index_t
+njs_scope_global_this_index()
+{
+    return njs_scope_index(NJS_SCOPE_GLOBAL, 0, NJS_LEVEL_LOCAL);
+}
+
+
+#endif /* _NJS_PARSER_H_INCLUDED_ */
diff --git a/src/njs_shell.c b/src/njs_shell.c
index 1a49f04..51d9633 100644
--- a/src/njs_shell.c
+++ b/src/njs_shell.c
@@ -258,7 +258,7 @@
     vm_options.file.length = njs_strlen(opts.file);
 
     vm_options.init = 1;
-    vm_options.accumulative = opts.interactive;
+    vm_options.interactive = opts.interactive;
     vm_options.disassemble = opts.disassemble;
     vm_options.backtrace = 1;
     vm_options.quiet = opts.quiet;
@@ -797,7 +797,7 @@
             return;
         }
 
-        if (vm->options.accumulative) {
+        if (vm->options.interactive) {
             njs_print(out.start, out.length);
             njs_print("\n", 1);
         }
diff --git a/src/njs_string.c b/src/njs_string.c
index f0159cd..d865939 100644
--- a/src/njs_string.c
+++ b/src/njs_string.c
@@ -4788,145 +4788,6 @@
 }
 
 
-static njs_int_t
-njs_values_hash_test(njs_lvlhsh_query_t *lhq, void *data)
-{
-    njs_str_t    string;
-    njs_value_t  *value;
-
-    value = data;
-
-    if (njs_is_string(value)) {
-        njs_string_get(value, &string);
-
-    } else {
-        string.start = (u_char *) value;
-        string.length = sizeof(njs_value_t);
-    }
-
-    if (lhq->key.length == string.length
-        && memcmp(lhq->key.start, string.start, string.length) == 0)
-    {
-        return NJS_OK;
-    }
-
-    return NJS_DECLINED;
-}
-
-
-static const njs_lvlhsh_proto_t  njs_values_hash_proto
-    njs_aligned(64) =
-{
-    NJS_LVLHSH_DEFAULT,
-    njs_values_hash_test,
-    njs_lvlhsh_alloc,
-    njs_lvlhsh_free,
-};
-
-
-/*
- * Constant values such as njs_value_true are copied to values_hash during
- * code generation when they are used as operands to guarantee aligned value.
- */
-
-njs_index_t
-njs_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime)
-{
-    u_char              *start;
-    uint32_t            value_size, size, length;
-    njs_int_t           ret;
-    njs_str_t           str;
-    njs_bool_t          long_string;
-    njs_value_t         *value;
-    njs_string_t        *string;
-    njs_lvlhsh_t        *values_hash;
-    njs_lvlhsh_query_t  lhq;
-
-    long_string = 0;
-    value_size = sizeof(njs_value_t);
-
-    if (njs_is_string(src)) {
-        njs_string_get(src, &str);
-
-        size = (uint32_t) str.length;
-        start = str.start;
-
-        if (src->short_string.size == NJS_STRING_LONG) {
-            long_string = 1;
-        }
-
-    } else {
-        size = value_size;
-        start = (u_char *) src;
-    }
-
-    lhq.key_hash = njs_djb_hash(start, size);
-    lhq.key.length = size;
-    lhq.key.start = start;
-    lhq.proto = &njs_values_hash_proto;
-
-    if (njs_lvlhsh_find(&vm->shared->values_hash, &lhq) == NJS_OK) {
-        value = lhq.value;
-
-    } else if (runtime && njs_lvlhsh_find(&vm->values_hash, &lhq) == NJS_OK) {
-        value = lhq.value;
-
-    } else {
-        if (long_string) {
-            length = src->long_string.data->length;
-
-            if (size != length && length > NJS_STRING_MAP_STRIDE) {
-                size = njs_string_map_offset(size)
-                       + njs_string_map_size(length);
-            }
-
-            value_size += sizeof(njs_string_t) + size;
-        }
-
-        value = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), value_size);
-        if (njs_slow_path(value == NULL)) {
-            return NJS_INDEX_NONE;
-        }
-
-        *value = *src;
-
-        if (long_string) {
-            string = (njs_string_t *) ((u_char *) value + sizeof(njs_value_t));
-            value->long_string.data = string;
-
-            string->start = (u_char *) string + sizeof(njs_string_t);
-            string->length = src->long_string.data->length;
-            string->retain = 0xffff;
-
-            memcpy(string->start, start, size);
-        }
-
-        lhq.replace = 0;
-        lhq.value = value;
-        lhq.pool = vm->mem_pool;
-
-        values_hash = runtime ? &vm->values_hash : &vm->shared->values_hash;
-
-        ret = njs_lvlhsh_insert(values_hash, &lhq);
-
-        if (njs_slow_path(ret != NJS_OK)) {
-            return NJS_INDEX_NONE;
-        }
-    }
-
-    if (start != (u_char *) src) {
-        /*
-         * The source node value must be updated with the shared value
-         * allocated from the permanent memory pool because the node
-         * value can be used as a variable initial value.
-         */
-        *(njs_value_t *) src = *value;
-    }
-
-    return (njs_index_t) value;
-}
-
-
 const njs_object_type_init_t  njs_string_type_init = {
     .constructor = njs_native_ctor(njs_string_constructor, 1, 0),
     .constructor_props = &njs_string_constructor_init,
diff --git a/src/njs_string.h b/src/njs_string.h
index 8ac10ad..2f43d09 100644
--- a/src/njs_string.h
+++ b/src/njs_string.h
@@ -237,9 +237,6 @@
 njs_int_t njs_string_decode_uri(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t component);
 
-njs_index_t njs_value_index(njs_vm_t *vm, const njs_value_t *src,
-    njs_uint_t runtime);
-
 njs_int_t njs_string_prototype_concat(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
 njs_int_t njs_string_get_substitution(njs_vm_t *vm, njs_value_t *matched,
diff --git a/src/njs_value.h b/src/njs_value.h
index 06e4fd7..28aed7e 100644
--- a/src/njs_value.h
+++ b/src/njs_value.h
@@ -266,16 +266,6 @@
 };
 
 
-typedef struct {
-    union {
-        uint32_t                      count;
-        njs_value_t                   values;
-    } u;
-
-    njs_value_t                       values[1];
-} njs_closure_t;
-
-
 struct njs_function_s {
     njs_object_t                      object;
 
@@ -283,18 +273,11 @@
 
     uint8_t                           args_count:4;
 
-    /*
-     * If "closure" is true njs_closure_t[] is available right after the
-     * njs_function_t and njs_function_closures() may be used to access it.
-     */
-
-#define njs_function_closures(function)                                      \
-    ((njs_closure_t **) ((u_char *) function + sizeof(njs_function_t)))
-
-    uint8_t                           closure:1;
+    uint8_t                           closure_copied:1;
     uint8_t                           native:1;
     uint8_t                           ctor:1;
     uint8_t                           global_this:1;
+    uint8_t                           global:1;
 
     uint8_t                           magic8;
 
diff --git a/src/njs_variable.c b/src/njs_variable.c
index 6d485f7..ee796cb 100644
--- a/src/njs_variable.c
+++ b/src/njs_variable.c
@@ -9,10 +9,10 @@
 #include <njs_main.h>
 
 
-static njs_variable_t *njs_variable_scope_add(njs_parser_t *parser,
-    njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type);
-static njs_int_t njs_variable_reference_resolve(njs_vm_t *vm,
-    njs_variable_reference_t *vr, njs_parser_scope_t *node_scope);
+static njs_value_t **njs_variable_scope_function_add(njs_parser_t *parser,
+    njs_parser_scope_t *scope);
+static njs_parser_scope_t *njs_variable_scope_find(njs_parser_t *parser,
+     njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type);
 static njs_variable_t *njs_variable_alloc(njs_vm_t *vm, uintptr_t unique_id,
     njs_variable_type_t type);
 
@@ -21,34 +21,87 @@
 njs_variable_add(njs_parser_t *parser, njs_parser_scope_t *scope,
     uintptr_t unique_id, njs_variable_type_t type)
 {
-    njs_variable_t  *var;
+    njs_parser_scope_t  *root;
 
-    var = njs_variable_scope_add(parser, scope, unique_id, type);
+    root = njs_variable_scope_find(parser, scope, unique_id, type);
+    if (njs_slow_path(root == NULL)) {
+        njs_parser_ref_error(parser, "scope not found");
+        return NULL;
+    }
+
+    return njs_variable_scope_add(parser, root, scope, unique_id, type,
+                                  NJS_INDEX_NONE);
+}
+
+
+njs_variable_t *
+njs_variable_function_add(njs_parser_t *parser, njs_parser_scope_t *scope,
+    uintptr_t unique_id, njs_variable_type_t type)
+{
+    njs_value_t            **declr;
+    njs_variable_t         *var;
+    njs_parser_scope_t     *root;
+    njs_function_lambda_t  *lambda;
+
+    root = njs_variable_scope_find(parser, scope, unique_id, type);
+    if (njs_slow_path(root == NULL)) {
+        njs_parser_ref_error(parser, "scope not found");
+        return NULL;
+    }
+
+    var = njs_variable_scope_add(parser, root, scope, unique_id, type,
+                                 NJS_INDEX_ERROR);
     if (njs_slow_path(var == NULL)) {
         return NULL;
     }
 
-    if (type == NJS_VARIABLE_VAR && scope->type == NJS_SCOPE_BLOCK) {
-        /* A "var" declaration is stored in function or global scope. */
-        do {
-            scope = scope->parent;
+    if (var->index == NJS_INDEX_ERROR || !var->function) {
+        root = njs_function_scope(scope);
+        if (njs_slow_path(scope == NULL)) {
+            return NULL;
+        }
 
-            var = njs_variable_scope_add(parser, scope, unique_id, type);
-            if (njs_slow_path(var == NULL)) {
-                return NULL;
-            }
+        lambda = njs_function_lambda_alloc(parser->vm, 1);
+        if (lambda == NULL) {
+            return NULL;
+        }
 
-        } while (scope->type == NJS_SCOPE_BLOCK);
+        var->value.data.u.lambda = lambda;
+
+        declr = njs_variable_scope_function_add(parser, root);
+        if (njs_slow_path(declr == NULL)) {
+            return NULL;
+        }
+
+        *declr = &var->value;
+
+        var->index = njs_scope_index(root->type, root->items, NJS_LEVEL_LOCAL);
+        root->items++;
     }
 
-    if (type == NJS_VARIABLE_FUNCTION) {
-        var->type = type;
-    }
+    var->type = NJS_VARIABLE_FUNCTION;
+    var->function = 1;
 
     return var;
 }
 
 
+static njs_value_t **
+njs_variable_scope_function_add(njs_parser_t *parser, njs_parser_scope_t *scope)
+{
+    if (scope->declarations == NULL) {
+        scope->declarations = njs_arr_create(parser->vm->mem_pool, 1,
+                                             sizeof(njs_value_t *));
+        if (njs_slow_path(scope->declarations == NULL)) {
+            return NULL;
+        }
+    }
+
+    return njs_arr_add(scope->declarations);
+}
+
+
+
 njs_int_t
 njs_variables_copy(njs_vm_t *vm, njs_rbtree_t *variables,
     njs_rbtree_t *prev_variables)
@@ -77,43 +130,128 @@
 }
 
 
-static njs_variable_t *
-njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope,
-    uintptr_t unique_id, njs_variable_type_t type)
+static njs_parser_scope_t *
+njs_variable_scope(njs_parser_scope_t *scope, uintptr_t unique_id,
+    njs_variable_t **retvar, njs_variable_type_t type)
 {
+    njs_variable_t       *var;
+    njs_rbtree_node_t    *node;
+    njs_variable_node_t  var_node;
+
+    *retvar = NULL;
+
+    var_node.key = unique_id;
+
+    do {
+        node = njs_rbtree_find(&scope->variables, &var_node.node);
+
+        if (node != NULL) {
+            var = ((njs_variable_node_t *) node)->variable;
+
+            if (var->type != NJS_VARIABLE_CATCH || type != NJS_VARIABLE_VAR) {
+                *retvar = var;
+                return scope;
+            }
+        }
+
+        if (scope->type == NJS_SCOPE_GLOBAL
+            || scope->type == NJS_SCOPE_FUNCTION)
+        {
+            return scope;
+        }
+
+        scope = scope->parent;
+
+    } while (scope != NULL);
+
+    return NULL;
+}
+
+
+static njs_parser_scope_t *
+njs_variable_scope_find(njs_parser_t *parser, njs_parser_scope_t *scope,
+     uintptr_t unique_id, njs_variable_type_t type)
+{
+    njs_bool_t               module;
     njs_variable_t           *var;
-    njs_rbtree_node_t        *node;
-    njs_variable_node_t      var_node, *var_node_new;
+    njs_parser_scope_t       *root;
     const njs_lexer_entry_t  *entry;
 
+    if (type != NJS_VARIABLE_VAR && type != NJS_VARIABLE_FUNCTION) {
+        return scope;
+    }
+
+    root = njs_variable_scope(scope, unique_id, &var, type);
+    if (njs_slow_path(root == NULL)) {
+        return NULL;
+    }
+
+    if (type == NJS_VARIABLE_FUNCTION) {
+        root = scope;
+    }
+
+    if (var == NULL) {
+        return root;
+    }
+
+    if (var->original->type == NJS_SCOPE_BLOCK) {
+        if (type == NJS_VARIABLE_FUNCTION
+            || var->type == NJS_VARIABLE_FUNCTION)
+        {
+            if (var->original == root) {
+                goto failed;
+            }
+        }
+    }
+
+    if (type != NJS_VARIABLE_FUNCTION
+        && var->type != NJS_VARIABLE_FUNCTION)
+    {
+        return var->scope;
+    }
+
+    if (root != scope) {
+        return root;
+    }
+
+    module = parser->vm->options.module || scope->module;
+
+    if (module) {
+        if (type == NJS_VARIABLE_FUNCTION
+            || var->type == NJS_VARIABLE_FUNCTION)
+        {
+            goto failed;
+        }
+    }
+
+    return root;
+
+failed:
+
+    entry = njs_lexer_entry(unique_id);
+
+    njs_parser_syntax_error(parser, "\"%V\" has already been declared",
+                            &entry->name);
+    return NULL;
+}
+
+
+njs_variable_t *
+njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope,
+    njs_parser_scope_t *original, uintptr_t unique_id,
+    njs_variable_type_t type, njs_index_t index)
+{
+    njs_variable_t       *var;
+    njs_rbtree_node_t    *node;
+    njs_parser_scope_t   *root;
+    njs_variable_node_t  var_node, *var_node_new;
+
     var_node.key = unique_id;
 
     node = njs_rbtree_find(&scope->variables, &var_node.node);
 
     if (node != NULL) {
-        var = ((njs_variable_node_t *) node)->variable;
-
-        if (scope->module || scope->type == NJS_SCOPE_BLOCK) {
-
-            if (type == NJS_VARIABLE_FUNCTION
-                || var->type == NJS_VARIABLE_FUNCTION)
-            {
-                goto fail;
-            }
-        }
-
-        if (scope->type == NJS_SCOPE_GLOBAL) {
-
-            if (parser->vm->options.module) {
-                if (type == NJS_VARIABLE_FUNCTION
-                    || var->type == NJS_VARIABLE_FUNCTION)
-                {
-                    goto fail;
-                }
-            }
-        }
-
-        return var;
+        return ((njs_variable_node_t *) node)->variable;
     }
 
     var = njs_variable_alloc(parser->vm, unique_id, type);
@@ -121,6 +259,20 @@
         goto memory_error;
     }
 
+    var->scope = scope;
+    var->index = index;
+    var->original = original;
+
+    if (index == NJS_INDEX_NONE) {
+        root = njs_function_scope(scope);
+        if (njs_slow_path(scope == NULL)) {
+            return NULL;
+        }
+
+        var->index = njs_scope_index(root->type, root->items, NJS_LEVEL_LOCAL);
+        root->items++;
+    }
+
     var_node_new = njs_variable_node_alloc(parser->vm, var, unique_id);
     if (njs_slow_path(var_node_new == NULL)) {
         goto memory_error;
@@ -135,14 +287,6 @@
     njs_memory_error(parser->vm);
 
     return NULL;
-
-fail:
-
-    entry = njs_lexer_entry(unique_id);
-
-    njs_parser_syntax_error(parser, "\"%V\" has already been declared",
-                            &entry->name);
-    return NULL;
 }
 
 
@@ -204,191 +348,198 @@
 }
 
 
-njs_int_t
-njs_variable_reference(njs_vm_t *vm, njs_parser_scope_t *scope,
-    njs_parser_node_t *node, uintptr_t unique_id, njs_reference_type_t type)
+static njs_bool_t
+njs_variable_closure_test(njs_parser_scope_t *root, njs_parser_scope_t *scope)
 {
-    njs_variable_reference_t  *vr;
-    njs_parser_rbtree_node_t  *rb_node;
-
-    vr = &node->u.reference;
-
-    vr->unique_id = unique_id;
-    vr->type = type;
-
-    rb_node = njs_mp_alloc(vm->mem_pool, sizeof(njs_parser_rbtree_node_t));
-    if (njs_slow_path(rb_node == NULL)) {
-        return NJS_ERROR;
+    if (root == scope) {
+        return 0;
     }
 
-    rb_node->key = unique_id;
-    rb_node->parser_node = node;
-
-    njs_rbtree_insert(&scope->references, &rb_node->node);
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
-njs_variables_scope_resolve(njs_vm_t *vm, njs_parser_scope_t *scope,
-    njs_bool_t closure)
-{
-    njs_int_t                 ret;
-    njs_queue_t               *nested;
-    njs_queue_link_t          *lnk;
-    njs_rbtree_node_t         *rb_node;
-    njs_parser_node_t         *node;
-    njs_parser_rbtree_node_t  *parser_rb_node;
-    njs_variable_reference_t  *vr;
-
-    nested = &scope->nested;
-
-    for (lnk = njs_queue_first(nested);
-         lnk != njs_queue_tail(nested);
-         lnk = njs_queue_next(lnk))
-    {
-        scope = njs_queue_link_data(lnk, njs_parser_scope_t, link);
-
-        ret = njs_variables_scope_resolve(vm, scope, closure);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return NJS_ERROR;
+    do {
+        if (root->type == NJS_SCOPE_FUNCTION) {
+            return 1;
         }
 
-        rb_node = njs_rbtree_min(&scope->references);
+        root = root->parent;
 
-        while (njs_rbtree_is_there_successor(&scope->references, rb_node)) {
-            parser_rb_node = (njs_parser_rbtree_node_t *) rb_node;
-            node = parser_rb_node->parser_node;
+    } while (root != scope);
 
-            if (node == NULL) {
-                break;
-            }
-
-            vr = &node->u.reference;
-
-            if (closure) {
-                ret = njs_variable_reference_resolve(vm, vr, node->scope);
-                if (njs_slow_path(ret != NJS_OK)) {
-                    goto next;
-                }
-
-                if (vr->scope_index == NJS_SCOPE_INDEX_LOCAL) {
-                    goto next;
-                }
-            }
-
-            (void) njs_variable_resolve(vm, node);
-
-        next:
-
-            rb_node = njs_rbtree_node_successor(&scope->references, rb_node);
-        }
-    }
-
-    return NJS_OK;
-}
-
-
-njs_int_t
-njs_variables_scope_reference(njs_vm_t *vm, njs_parser_scope_t *scope)
-{
-    njs_int_t  ret;
-
-    /*
-     * Calculating proper scope types for variables.
-     * A variable is considered to be local variable if it is referenced
-     * only in the local scope (reference and definition nestings are the same).
-     */
-
-    ret = njs_variables_scope_resolve(vm, scope, 1);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
-    }
-
-    ret = njs_variables_scope_resolve(vm, scope, 0);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
-    }
-
-    return NJS_OK;
-}
-
-
-njs_index_t
-njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node)
-{
-    njs_variable_t  *var;
-
-    if (node->index != NJS_INDEX_NONE) {
-        return node->index;
-    }
-
-    var = njs_variable_resolve(vm, node);
-
-    if (njs_fast_path(var != NULL)) {
-        return var->index;
-    }
-
-    return NJS_INDEX_NONE;
+    return 0;
 }
 
 
 njs_variable_t *
 njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node)
 {
-    njs_int_t                 ret;
-    njs_uint_t                scope_index;
-    njs_index_t               index;
-    njs_variable_t            *var;
-    njs_variable_reference_t  *vr;
+    njs_rbtree_node_t         *rb_node;
+    njs_parser_scope_t        *scope;
+    njs_variable_node_t       var_node;
+    njs_variable_reference_t  *ref;
 
-    vr = &node->u.reference;
+    ref = &node->u.reference;
+    scope = node->scope;
 
-    ret = njs_variable_reference_resolve(vm, vr, node->scope);
+    var_node.key = ref->unique_id;
 
-    if (njs_slow_path(ret != NJS_OK)) {
-        node->u.reference.not_defined = 1;
-        return NULL;
-    }
+    do {
+        rb_node = njs_rbtree_find(&scope->variables, &var_node.node);
 
-    scope_index = vr->scope_index;
-
-    var = vr->variable;
-    index = var->index;
-
-    if (index != NJS_INDEX_NONE) {
-
-        if (scope_index == NJS_SCOPE_INDEX_LOCAL
-            || njs_scope_type(index) != NJS_SCOPE_ARGUMENTS)
-        {
-            node->index = index;
-
-            return var;
+        if (rb_node != NULL) {
+            return ((njs_variable_node_t *) rb_node)->variable;
         }
 
-        vr->scope->argument_closures++;
-        index = (index >> NJS_SCOPE_SHIFT) + 1;
+        scope = scope->parent;
 
-        if (index > 255 || vr->scope->argument_closures == 0) {
-            njs_internal_error(vm, "too many argument closures");
+    } while (scope != NULL);
+
+    return NULL;
+}
+
+
+static njs_index_t
+njs_variable_closure(njs_vm_t *vm, njs_variable_t *var,
+    njs_parser_scope_t *scope)
+{
+    njs_index_t               index, prev_index, *idx;
+    njs_level_type_t          type;
+    njs_rbtree_node_t         *rb_node;
+    njs_parser_scope_t        **p, *root;
+    njs_parser_rbtree_node_t  *parse_node, ref_node;
+#define NJS_VAR_MAX_DEPTH     32
+    njs_parser_scope_t        *list[NJS_VAR_MAX_DEPTH];
+
+    ref_node.key = var->unique_id;
+
+    p = list;
+
+    do {
+        if (njs_slow_path(p == &list[NJS_VAR_MAX_DEPTH - 1])) {
+            njs_error(vm, "maximum depth of nested functions is reached");
+            return NJS_INDEX_ERROR;
+        }
+
+        if (scope->type == NJS_SCOPE_FUNCTION) {
+            *p++ = scope;
+        }
+
+        scope = scope->parent;
+
+    } while (scope != var->scope && scope->type != NJS_SCOPE_GLOBAL);
+
+    prev_index = var->index;
+
+    while (p != list) {
+        p--;
+
+        scope = *p;
+
+        rb_node = njs_rbtree_find(&scope->references, &ref_node.node);
+
+        parse_node = ((njs_parser_rbtree_node_t *) rb_node);
+
+        type = NJS_LEVEL_LOCAL;
+
+        if (parse_node != NULL) {
+            type = njs_scope_index_type(parse_node->index);
+
+            if (p != list && parse_node->index != 0) {
+                prev_index = parse_node->index;
+                continue;
+            }
+        }
+
+        root = njs_function_scope(scope);
+
+        if (type != NJS_LEVEL_CLOSURE && root == scope) {
+            /* Create new closure for scope. */
+
+            index = njs_scope_index(root->type, root->closures->items,
+                                    NJS_LEVEL_CLOSURE);
+            if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+                return NJS_INDEX_ERROR;
+            }
+
+            idx = njs_arr_add(root->closures);
+            if (njs_slow_path(idx == NULL)) {
+                return NJS_INDEX_ERROR;
+            }
+
+            *idx = prev_index;
+
+            if (parse_node == NULL) {
+                /* Create new reference for closure. */
+
+                parse_node = njs_mp_alloc(vm->mem_pool,
+                                          sizeof(njs_parser_rbtree_node_t));
+                if (njs_slow_path(parse_node == NULL)) {
+                    return NJS_INDEX_ERROR;
+                }
+
+                parse_node->key = var->unique_id;
+
+                njs_rbtree_insert(&scope->references, &parse_node->node);
+            }
+
+            parse_node->index = index;
+        }
+
+        prev_index = parse_node->index;
+    }
+
+    return prev_index;
+}
+
+
+njs_variable_t *
+njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node)
+{
+    njs_rbtree_node_t         *rb_node;
+    njs_parser_scope_t        *scope;
+    njs_parser_rbtree_node_t  *parse_node, ref_node;
+    njs_variable_reference_t  *ref;
+
+    ref = &node->u.reference;
+    scope = node->scope;
+
+    if (ref->variable == NULL) {
+        ref->variable = njs_variable_resolve(vm, node);
+        if (njs_slow_path(ref->variable == NULL)) {
+            ref->not_defined = 1;
 
             return NULL;
         }
-
-        var->argument = index;
     }
 
-    index = njs_scope_next_index(vm, vr->scope, scope_index, &var->value);
+    ref->closure = njs_variable_closure_test(node->scope, ref->variable->scope);
+    ref->scope = node->scope;
 
-    if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+    ref_node.key = ref->unique_id;
+
+    rb_node = njs_rbtree_find(&scope->references, &ref_node.node);
+    if (njs_slow_path(rb_node == NULL)) {
         return NULL;
     }
 
-    var->index = index;
-    node->index = index;
+    parse_node = ((njs_parser_rbtree_node_t *) rb_node);
 
-    return var;
+    if (parse_node->index != NJS_INDEX_NONE) {
+        node->index = parse_node->index;
+
+        return ref->variable;
+    }
+
+    if (!ref->closure) {
+        node->index = ref->variable->index;
+
+        return ref->variable;
+    }
+
+    node->index = njs_variable_closure(vm, ref->variable, scope);
+    if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
+        return NULL;
+    }
+
+    return ref->variable;
 }
 
 
@@ -415,120 +566,6 @@
 }
 
 
-static njs_int_t
-njs_variable_reference_resolve(njs_vm_t *vm, njs_variable_reference_t *vr,
-    njs_parser_scope_t *node_scope)
-{
-    njs_rbtree_node_t    *node;
-    njs_parser_scope_t   *scope, *previous;
-    njs_variable_node_t  var_node;
-
-    var_node.key = vr->unique_id;
-
-    scope = node_scope;
-    previous = NULL;
-
-    for ( ;; ) {
-        node = njs_rbtree_find(&scope->variables, &var_node.node);
-
-        if (node != NULL) {
-            vr->variable = ((njs_variable_node_t *) node)->variable;
-
-            if (scope->type == NJS_SCOPE_BLOCK
-                && vr->variable->type == NJS_VARIABLE_VAR)
-            {
-                scope = scope->parent;
-                continue;
-            }
-
-            if (scope->type == NJS_SCOPE_SHIM) {
-                scope = previous;
-
-            } else {
-                /*
-                 * Variables declared in a block with "let" or "const"
-                 * keywords are actually stored in function or global scope.
-                 */
-                while (scope->type == NJS_SCOPE_BLOCK) {
-                    scope = scope->parent;
-                }
-            }
-
-            vr->scope = scope;
-
-            vr->scope_index = NJS_SCOPE_INDEX_LOCAL;
-
-            if (vr->scope->type > NJS_SCOPE_GLOBAL
-                && node_scope->nesting != vr->scope->nesting)
-            {
-                vr->scope_index = NJS_SCOPE_INDEX_CLOSURE;
-            }
-
-            return NJS_OK;
-        }
-
-        if (scope->parent == NULL) {
-            /* A global scope. */
-            vr->scope = scope;
-
-            return NJS_DECLINED;
-        }
-
-        previous = scope;
-        scope = scope->parent;
-    }
-}
-
-
-njs_index_t
-njs_scope_next_index(njs_vm_t *vm, njs_parser_scope_t *scope,
-    njs_uint_t scope_index, const njs_value_t *default_value)
-{
-    njs_arr_t    *values;
-    njs_index_t  index;
-    njs_value_t  *value;
-
-    if (njs_scope_accumulative(vm, scope)) {
-        /*
-         * When non-clonable VM runs in accumulative mode all
-         * global variables should be allocated in absolute scope
-         * to share them among consecutive VM invocations.
-         */
-        value = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
-                             sizeof(njs_value_t));
-        if (njs_slow_path(value == NULL)) {
-            return NJS_INDEX_ERROR;
-        }
-
-        index = (njs_index_t) value;
-
-    } else {
-        values = scope->values[scope_index];
-
-        if (values == NULL) {
-            values = njs_arr_create(vm->mem_pool, 4, sizeof(njs_value_t));
-            if (njs_slow_path(values == NULL)) {
-                return NJS_INDEX_ERROR;
-            }
-
-            scope->values[scope_index] = values;
-        }
-
-        value = njs_arr_add(values);
-        if (njs_slow_path(value == NULL)) {
-            return NJS_INDEX_ERROR;
-        }
-
-        index = scope->next_index[scope_index];
-        scope->next_index[scope_index] += sizeof(njs_value_t);
-    }
-
-    *value = *default_value;
-
-    return index;
-}
-
-
 static njs_variable_t *
 njs_variable_alloc(njs_vm_t *vm, uintptr_t unique_id, njs_variable_type_t type)
 {
diff --git a/src/njs_variable.h b/src/njs_variable.h
index 0334881..8759e04 100644
--- a/src/njs_variable.h
+++ b/src/njs_variable.h
@@ -12,7 +12,6 @@
     NJS_VARIABLE_CONST = 0,
     NJS_VARIABLE_LET,
     NJS_VARIABLE_CATCH,
-    NJS_VARIABLE_SHIM,
     NJS_VARIABLE_VAR,
     NJS_VARIABLE_FUNCTION,
 } njs_variable_type_t;
@@ -22,9 +21,13 @@
     uintptr_t             unique_id;
 
     njs_variable_type_t   type:8;    /* 3 bits */
-    uint8_t               argument;
-    uint8_t               this_object;
-    uint8_t               arguments_object;
+    njs_bool_t            argument;
+    njs_bool_t            arguments_object;
+    njs_bool_t            self;
+    njs_bool_t            function;
+
+    njs_parser_scope_t    *scope;
+    njs_parser_scope_t    *original;
 
     njs_index_t           index;
     njs_value_t           value;
@@ -43,8 +46,8 @@
     uintptr_t             unique_id;
     njs_variable_t        *variable;
     njs_parser_scope_t    *scope;
-    njs_uint_t            scope_index;  /* NJS_SCOPE_INDEX_... */
     njs_bool_t            not_defined;
+    njs_bool_t            closure;
 } njs_variable_reference_t;
 
 
@@ -57,6 +60,8 @@
 
 njs_variable_t *njs_variable_add(njs_parser_t *parser,
     njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type);
+njs_variable_t *njs_variable_function_add(njs_parser_t *parser,
+    njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type);
 njs_int_t njs_variables_copy(njs_vm_t *vm, njs_rbtree_t *variables,
     njs_rbtree_t *prev_variables);
 njs_variable_t * njs_label_add(njs_vm_t *vm, njs_parser_scope_t *scope,
@@ -65,12 +70,10 @@
     uintptr_t unique_id);
 njs_int_t njs_label_remove(njs_vm_t *vm, njs_parser_scope_t *scope,
     uintptr_t unique_id);
-njs_int_t njs_variable_reference(njs_vm_t *vm, njs_parser_scope_t *scope,
-    njs_parser_node_t *node, uintptr_t unique_id, njs_reference_type_t type);
-njs_int_t njs_variables_scope_reference(njs_vm_t *vm,
-    njs_parser_scope_t *scope);
-njs_index_t njs_scope_next_index(njs_vm_t *vm, njs_parser_scope_t *scope,
-    njs_uint_t scope_index, const njs_value_t *default_value);
+njs_variable_t *njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node);
+njs_variable_t *njs_variable_scope_add(njs_parser_t *parser,
+    njs_parser_scope_t *scope, njs_parser_scope_t *original,
+    uintptr_t unique_id, njs_variable_type_t type, njs_index_t index);
 njs_int_t njs_name_copy(njs_vm_t *vm, njs_str_t *dst, const njs_str_t *src);
 
 
diff --git a/src/njs_vm.c b/src/njs_vm.c
index 22e4ee7..144e3d9 100644
--- a/src/njs_vm.c
+++ b/src/njs_vm.c
@@ -81,6 +81,10 @@
 
     vm->symbol_generator = NJS_SYMBOL_KNOWN_MAX;
 
+    if (njs_scope_undefined_index(vm, 0) == NJS_INDEX_ERROR) {
+        return NULL;
+    }
+
     return vm;
 }
 
@@ -116,15 +120,21 @@
 njs_int_t
 njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
 {
-    njs_int_t        ret;
-    njs_str_t        ast;
-    njs_chb_t        chain;
-    njs_lexer_t      lexer;
-    njs_parser_t     parser;
-    njs_vm_code_t    *code;
-    njs_generator_t  generator;
+    njs_int_t           ret;
+    njs_str_t           ast;
+    njs_chb_t           chain;
+    njs_value_t         **global, **new;
+    njs_lexer_t         lexer;
+    njs_parser_t        parser;
+    njs_vm_code_t       *code;
+    njs_generator_t     generator;
+    njs_parser_scope_t  *scope;
 
-    if (vm->modules != NULL && vm->options.accumulative) {
+    njs_memzero(&parser, sizeof(njs_parser_t));
+
+    parser.scope = vm->global_scope;
+
+    if (parser.scope != NULL && vm->modules != NULL) {
         njs_module_reset(vm);
     }
 
@@ -133,25 +143,19 @@
         return NJS_ERROR;
     }
 
-    njs_memzero(&parser, sizeof(njs_parser_t));
-
     parser.lexer = &lexer;
 
-    ret = njs_parser(vm, &parser, vm->variables_hash);
+    ret = njs_parser(vm, &parser);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
 
     *start = lexer.start;
-
-    ret = njs_variables_scope_reference(vm, parser.scope);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
-    }
+    scope = parser.scope;
 
     njs_memzero(&generator, sizeof(njs_generator_t));
 
-    code = njs_generate_scope(vm, &generator, parser.scope, &njs_entry_main);
+    code = njs_generate_scope(vm, &generator, scope, &njs_entry_main);
     if (njs_slow_path(code == NULL)) {
         if (!njs_is_error(&vm->retval)) {
             njs_internal_error(vm, "njs_generate_scope() failed");
@@ -160,17 +164,45 @@
         return NJS_ERROR;
     }
 
-    vm->start = generator.code_start;
-    vm->global_scope = generator.local_scope;
-    vm->scope_size = generator.scope_size;
+    vm->global_scope = scope;
 
-    vm->variables_hash = &parser.scope->variables;
+    if (scope->items > vm->global_items) {
+        global = vm->levels[NJS_LEVEL_GLOBAL];
 
-    if (vm->options.init && !vm->options.accumulative) {
-        ret = njs_vm_init(vm);
-        if (njs_slow_path(ret != NJS_OK)) {
+        new = njs_scope_make(vm, scope->items);
+        if (njs_slow_path(new == NULL)) {
             return ret;
         }
+
+        vm->levels[NJS_LEVEL_GLOBAL] = new;
+
+        if (global != NULL) {
+            while (vm->global_items != 0) {
+                vm->global_items--;
+
+                *new++ = *global++;
+            }
+
+            njs_mp_free(vm->mem_pool, global);
+        }
+    }
+
+    /* globalThis and this */
+    njs_scope_value_set(vm, njs_scope_global_this_index(), &vm->global_value);
+
+    vm->start = generator.code_start;
+    vm->variables_hash = &scope->variables;
+    vm->global_items = scope->items;
+
+    vm->levels[NJS_LEVEL_TEMP] = NULL;
+
+    if (scope->temp != 0) {
+        new = njs_scope_make(vm, scope->temp);
+        if (njs_slow_path(new == NULL)) {
+            return ret;
+        }
+
+        vm->levels[NJS_LEVEL_TEMP] = new;
     }
 
     if (vm->options.disassemble) {
@@ -201,13 +233,14 @@
 njs_vm_t *
 njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external)
 {
-    njs_mp_t   *nmp;
-    njs_vm_t   *nvm;
-    njs_int_t  ret;
+    njs_mp_t     *nmp;
+    njs_vm_t     *nvm;
+    njs_int_t    ret;
+    njs_value_t  **global;
 
     njs_thread_log_debug("CLONE:");
 
-    if (vm->options.accumulative) {
+    if (vm->options.interactive) {
         return NULL;
     }
 
@@ -232,6 +265,20 @@
         goto fail;
     }
 
+    global = njs_scope_make(nvm, nvm->global_items);
+    if (njs_slow_path(global == NULL)) {
+        goto fail;
+    }
+
+    nvm->levels[NJS_LEVEL_GLOBAL] = global;
+
+    njs_set_object(&nvm->global_value, &nvm->global_object);
+
+    /* globalThis and this */
+    njs_scope_value_set(nvm, njs_scope_global_this_index(), &nvm->global_value);
+
+    nvm->levels[NJS_LEVEL_LOCAL] = NULL;
+
     return nvm;
 
 fail:
@@ -245,46 +292,27 @@
 static njs_int_t
 njs_vm_init(njs_vm_t *vm)
 {
-    size_t       size, scope_size;
-    u_char       *values;
     njs_int_t    ret;
-    njs_value_t  *global;
     njs_frame_t  *frame;
 
-    scope_size = vm->scope_size + NJS_INDEX_GLOBAL_OFFSET;
-
-    size = njs_frame_size(0) + scope_size + NJS_FRAME_SPARE_SIZE;
-    size = njs_align_size(size, NJS_FRAME_SPARE_SIZE);
-
-    frame = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), size);
+    frame = (njs_frame_t *) njs_function_frame_alloc(vm, NJS_FRAME_SIZE);
     if (njs_slow_path(frame == NULL)) {
+        njs_memory_error(vm);
         return NJS_ERROR;
     }
 
-    njs_memzero(frame, njs_frame_size(0));
+    frame->exception.catch = NULL;
+    frame->exception.next = NULL;
+    frame->previous_active_frame = NULL;
 
-    vm->top_frame = &frame->native;
     vm->active_frame = frame;
 
-    frame->native.size = size;
-    frame->native.free_size = size - (njs_frame_size(0) + scope_size);
-
-    values = (u_char *) frame + njs_frame_size(0);
-
-    frame->native.free = values + scope_size;
-
-    vm->scopes[NJS_SCOPE_GLOBAL] = (njs_value_t *) values;
-
-    memcpy(values + NJS_INDEX_GLOBAL_OFFSET, vm->global_scope, vm->scope_size);
-
     ret = njs_regexp_init(vm);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
 
-    global = (njs_value_t *) (values + NJS_INDEX_GLOBAL_OBJECT_OFFSET);
-
-    ret = njs_builtin_objects_clone(vm, global);
+    ret = njs_builtin_objects_clone(vm, &vm->global_value);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
@@ -303,13 +331,13 @@
 njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
     njs_uint_t nargs)
 {
-    return njs_vm_invoke(vm, function, args, nargs, (njs_index_t) &vm->retval);
+    return njs_vm_invoke(vm, function, args, nargs, &vm->retval);
 }
 
 
 njs_int_t
 njs_vm_invoke(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
-    njs_uint_t nargs, njs_index_t retval)
+    njs_uint_t nargs, njs_value_t *retval)
 {
     njs_int_t  ret;
 
@@ -327,55 +355,17 @@
 njs_vm_scopes_restore(njs_vm_t *vm, njs_native_frame_t *native,
     njs_native_frame_t *previous)
 {
-    njs_uint_t      n, nesting;
-    njs_value_t     *args;
-    njs_frame_t     *frame;
-    njs_closure_t   **closures;
-    njs_function_t  *function;
+    njs_frame_t  *frame;
 
     vm->top_frame = previous;
 
-    args = previous->arguments;
-    function = previous->function;
-
-    if (function != NULL) {
-        args += function->args_offset;
-    }
-
-    vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = args;
-
-    function = native->function;
-
-    if (function->native) {
+    if (native->function->native) {
         return;
     }
 
-    if (function->closure) {
-        /* GC: release function closures. */
-    }
-
     frame = (njs_frame_t *) native;
     frame = frame->previous_active_frame;
     vm->active_frame = frame;
-
-    /* GC: arguments, local, and local block closures. */
-
-    vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments;
-    vm->scopes[NJS_SCOPE_LOCAL] = frame->local;
-
-    function = frame->native.function;
-
-    nesting = (function != NULL) ? function->u.lambda->nesting : 0;
-    closures = njs_frame_closures(frame);
-
-    for (n = 0; n <= nesting; n++) {
-        vm->scopes[NJS_SCOPE_CLOSURE + n] = &closures[n]->u.values;
-    }
-
-    while (n < NJS_MAX_NESTING) {
-        vm->scopes[NJS_SCOPE_CLOSURE + n] = NULL;
-        n++;
-    }
 }
 
 
diff --git a/src/njs_vm.h b/src/njs_vm.h
index d6ac1b8..5883b5d 100644
--- a/src/njs_vm.h
+++ b/src/njs_vm.h
@@ -29,49 +29,12 @@
 
 
 typedef enum {
-    NJS_SCOPE_ABSOLUTE = 0,
-    NJS_SCOPE_GLOBAL = 1,
-    NJS_SCOPE_CALLEE_ARGUMENTS = 2,
-    /*
-     * The argument and local VM scopes should be separated because a
-     * function may be called with any number of arguments.
-     */
-    NJS_SCOPE_ARGUMENTS = 3,
-    NJS_SCOPE_LOCAL = 4,
-    NJS_SCOPE_FUNCTION = NJS_SCOPE_LOCAL,
-
-    NJS_SCOPE_CLOSURE = 5,
-    /*
-     * The block and shim scopes are not really VM scopes.
-     * They are used only on parsing phase.
-     */
-    NJS_SCOPE_BLOCK = 16,
-    NJS_SCOPE_SHIM = 17,
+    NJS_SCOPE_GLOBAL = 0,
+    NJS_SCOPE_FUNCTION,
+    NJS_SCOPE_BLOCK
 } njs_scope_t;
 
 
-/*
- * The maximum possible function nesting level is (16 - NJS_SCOPE_CLOSURE),
- * that is 11.  The 8 is reasonable limit.
- */
-#define NJS_MAX_NESTING        8
-
-#define NJS_SCOPES             (NJS_SCOPE_CLOSURE + NJS_MAX_NESTING)
-
-#define NJS_SCOPE_SHIFT        4
-#define NJS_SCOPE_MASK         ((uintptr_t) ((1 << NJS_SCOPE_SHIFT) - 1))
-
-#define NJS_INDEX_NONE         ((njs_index_t) 0)
-#define NJS_INDEX_ERROR        ((njs_index_t) -1)
-#define NJS_INDEX_THIS         ((njs_index_t) (0 | NJS_SCOPE_ARGUMENTS))
-
-#define njs_scope_type(index)                                                 \
-     ((uintptr_t) (index) & NJS_SCOPE_MASK)
-
-#define njs_is_callee_argument_index(index)                                   \
-    (((index) & NJS_SCOPE_CALLEE_ARGUMENTS) == NJS_SCOPE_CALLEE_ARGUMENTS)
-
-
 typedef enum {
     NJS_OBJ_TYPE_OBJECT = 0,
     NJS_OBJ_TYPE_ARRAY,
@@ -149,38 +112,22 @@
 };
 
 
-#define njs_scope_index(value, type)                                          \
-    ((njs_index_t) (((value) << NJS_SCOPE_SHIFT) | (type)))
-
-
-#define njs_global_scope_index(value)                                         \
-    (njs_scope_index(value, NJS_SCOPE_GLOBAL))
-
-
-#define NJS_INDEX_GLOBAL_OBJECT  njs_global_scope_index(0)
-#define NJS_INDEX_GLOBAL_OBJECT_OFFSET  njs_scope_index(0, 0)
-
-
-#define NJS_INDEX_GLOBAL_RETVAL  njs_global_scope_index(1)
-#define NJS_INDEX_GLOBAL_OFFSET  njs_scope_index(1, 0)
-
-
-#define njs_scope_offset(index)                                               \
-    ((uintptr_t) (index) & ~NJS_SCOPE_MASK)
-
-
-#define njs_vmcode_operand(vm, index)                                         \
-    ((njs_value_t *)                                                          \
-     ((u_char *) vm->scopes[(uintptr_t) (index) & NJS_SCOPE_MASK]             \
-      + njs_scope_offset(index)))
-
-
 enum njs_hook_e {
     NJS_HOOK_EXIT = 0,
     NJS_HOOK_MAX
 };
 
 
+typedef enum {
+    NJS_LEVEL_LOCAL = 0,
+    NJS_LEVEL_CLOSURE,
+    NJS_LEVEL_GLOBAL,
+    NJS_LEVEL_STATIC,
+    NJS_LEVEL_TEMP,
+    NJS_LEVEL_MAX
+} njs_level_type_t;
+
+
 struct njs_vm_s {
     /* njs_vm_t must be aligned to njs_value_t due to scratch value. */
     njs_value_t              retval;
@@ -188,7 +135,9 @@
     njs_arr_t                *paths;
     njs_arr_t                *protos;
 
-    njs_value_t              *scopes[NJS_SCOPES];
+    njs_arr_t                *scope_absolute;
+    njs_value_t              **levels[NJS_LEVEL_MAX];
+    size_t                   global_items;
 
     njs_external_ptr_t       external;
 
@@ -221,8 +170,6 @@
     njs_mp_t                 *mem_pool;
 
     u_char                   *start;
-    njs_value_t              *global_scope;
-    size_t                   scope_size;
     size_t                   stack_size;
 
     njs_vm_shared_t          *shared;
@@ -232,6 +179,8 @@
 
     njs_array_t              *promise_reason;
 
+    njs_parser_scope_t       *global_scope;
+
     /*
      * MemoryError is statically allocated immutable Error object
      * with the InternalError prototype.
@@ -240,6 +189,7 @@
 
     njs_object_t             string_object;
     njs_object_t             global_object;
+    njs_value_t              global_value;
 
     njs_arr_t                *codes;  /* of njs_vm_code_t */
     njs_arr_t                *functions_name_cache;
diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c
index 31b4239..8eda085 100644
--- a/src/njs_vmcode.c
+++ b/src/njs_vmcode.c
@@ -22,6 +22,8 @@
     njs_value_t *inlvd1, njs_value_t *inlvd2);
 static njs_jump_off_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *invld);
+static njs_jump_off_t njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value,
+    njs_index_t retval);
 
 static njs_jump_off_t njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *key, njs_value_t *retval);
@@ -51,12 +53,6 @@
     njs_value_t *retval, u_char *pc);
 static void njs_vmcode_error(njs_vm_t *vm, u_char *pc);
 
-/*
- * These functions are forbidden to inline to minimize JavaScript VM
- * interpreter memory footprint.  The size is less than 8K on AMD64
- * and should fit in CPU L1 instruction cache.
- */
-
 static njs_jump_off_t njs_string_concat(njs_vm_t *vm, njs_value_t *val1,
     njs_value_t *val2);
 static njs_jump_off_t njs_values_equal(njs_vm_t *vm, njs_value_t *val1,
@@ -67,11 +63,9 @@
     njs_value_t *value, const njs_value_t *this, uintptr_t nargs,
     njs_bool_t ctor);
 
-/*
- * The nJSVM is optimized for an ABIs where the first several arguments
- * are passed in registers (AMD64, ARM32/64): two pointers to the operand
- * values is passed as arguments although they are not always used.
- */
+
+#define njs_vmcode_operand(vm, index)  njs_scope_valid_value(vm, index)
+
 
 njs_int_t
 njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc)
@@ -89,10 +83,11 @@
     njs_value_t                  numeric1, numeric2, primitive1, primitive2;
     njs_frame_t                  *frame;
     njs_jump_off_t               ret;
-    njs_vmcode_this_t            *this;
     njs_native_frame_t           *previous, *native;
     njs_property_next_t          *next;
+    njs_vmcode_finally_t         *finally;
     njs_vmcode_generic_t         *vmcode;
+    njs_vmcode_move_arg_t        *move_arg;
     njs_vmcode_prop_get_t        *get;
     njs_vmcode_prop_set_t        *set;
     njs_vmcode_operation_t       op;
@@ -101,7 +96,9 @@
     njs_vmcode_equal_jump_t      *equal;
     njs_vmcode_try_return_t      *try_return;
     njs_vmcode_method_frame_t    *method_frame;
+    njs_vmcode_function_copy_t   *fcopy;
     njs_vmcode_prop_accessor_t   *accessor;
+    njs_vmcode_try_trampoline_t  *try_trampoline;
     njs_vmcode_function_frame_t  *function_frame;
 
 next:
@@ -613,9 +610,24 @@
             *retval = vm->retval;
 
         } else {
+
             switch (op) {
+            case NJS_VMCODE_MOVE_ARG:
+                move_arg = (njs_vmcode_move_arg_t *) pc;
+                native = vm->top_frame;
+
+                hint = move_arg->dst;
+
+                value1 = &native->arguments_offset[hint];
+                value2 = njs_vmcode_operand(vm, move_arg->src);
+
+                *value1 = *value2;
+
+                ret = sizeof(njs_vmcode_move_arg_t);
+                break;
+
             case NJS_VMCODE_STOP:
-                value2 = njs_vmcode_operand(vm, value2);
+                value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
                 vm->retval = *value2;
 
                 return NJS_OK;
@@ -690,38 +702,14 @@
                 break;
 
             case NJS_VMCODE_RETURN:
-                value2 = njs_vmcode_operand(vm, value2);
+                value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
+                return njs_vmcode_return(vm, NULL, value2);
 
-                frame = (njs_frame_t *) vm->top_frame;
-
-                if (frame->native.ctor) {
-                    if (njs_is_object(value2)) {
-                        njs_release(vm, vm->scopes[NJS_SCOPE_ARGUMENTS]);
-
-                    } else {
-                        value2 = vm->scopes[NJS_SCOPE_ARGUMENTS];
-                    }
-                }
-
-                previous = njs_function_previous_frame(&frame->native);
-
-                njs_vm_scopes_restore(vm, &frame->native, previous);
-
-                /*
-                 * If a retval is in a callee arguments scope it
-                 * must be in the previous callee arguments scope.
-                 */
-                retval = njs_vmcode_operand(vm, frame->native.retval);
-
-                /*
-                 * GC: value external/internal++ depending on
-                 * value and retval type
-                 */
-                *retval = *value2;
-
-                njs_function_frame_free(vm, &frame->native);
-
-                return NJS_OK;
+            case NJS_VMCODE_FUNCTION_COPY:
+                fcopy = (njs_vmcode_function_copy_t *) pc;
+                ret = njs_vmcode_function_copy(vm, fcopy->function,
+                                               fcopy->retval);
+                break;
 
             case NJS_VMCODE_FUNCTION_FRAME:
                 function_frame = (njs_vmcode_function_frame_t *) pc;
@@ -775,7 +763,9 @@
             case NJS_VMCODE_FUNCTION_CALL:
                 vm->active_frame->native.pc = pc;
 
-                ret = njs_function_frame_invoke(vm, (njs_index_t) value2);
+                value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
+
+                ret = njs_function_frame_invoke(vm, value2);
                 if (njs_slow_path(ret == NJS_ERROR)) {
                     goto error;
                 }
@@ -801,16 +791,6 @@
                 ret = sizeof(njs_vmcode_prop_next_t);
                 break;
 
-            case NJS_VMCODE_THIS:
-                frame = vm->active_frame;
-                this = (njs_vmcode_this_t *) pc;
-
-                retval = njs_vmcode_operand(vm, this->dst);
-                *retval = frame->native.arguments[0];
-
-                ret = sizeof(njs_vmcode_this_t);
-                break;
-
             case NJS_VMCODE_ARGUMENTS:
                 ret = njs_vmcode_arguments(vm, pc);
                 if (njs_slow_path(ret == NJS_ERROR)) {
@@ -838,15 +818,21 @@
                 break;
 
             case NJS_VMCODE_THROW:
-                value2 = njs_vmcode_operand(vm, value2);
+                value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
                 vm->retval = *value2;
                 goto error;
 
             case NJS_VMCODE_TRY_BREAK:
+                try_trampoline = (njs_vmcode_try_trampoline_t *) pc;
+                value1 = njs_scope_value(vm, try_trampoline->exit_value);
+
                 ret = njs_vmcode_try_break(vm, value1, value2);
                 break;
 
             case NJS_VMCODE_TRY_CONTINUE:
+                try_trampoline = (njs_vmcode_try_trampoline_t *) pc;
+                value1 = njs_scope_value(vm, try_trampoline->exit_value);
+
                 ret = njs_vmcode_try_continue(vm, value1, value2);
                 break;
 
@@ -877,6 +863,9 @@
                 break;
 
             case NJS_VMCODE_FINALLY:
+                finally = (njs_vmcode_finally_t *) pc;
+                value1 = njs_scope_value(vm, finally->exit_value);
+
                 ret = njs_vmcode_finally(vm, value1, value2, pc);
 
                 switch (ret) {
@@ -1005,18 +994,21 @@
 njs_vmcode_function(njs_vm_t *vm, u_char *pc)
 {
     njs_function_t         *function;
-    njs_function_lambda_t  *lambda;
     njs_vmcode_function_t  *code;
+    njs_function_lambda_t  *lambda;
 
     code = (njs_vmcode_function_t *) pc;
     lambda = code->lambda;
 
-    function = njs_function_alloc(vm, lambda,
-                                  njs_frame_closures(vm->active_frame), 0);
+    function = njs_function_alloc(vm, lambda);
     if (njs_slow_path(function == NULL)) {
         return NJS_ERROR;
     }
 
+    if (njs_function_capture_closure(vm, function, lambda) != NJS_OK) {
+        return NJS_ERROR;
+    }
+
     function->args_count = lambda->nargs - lambda->rest_parameters;
 
     njs_set_function(&vm->retval, function);
@@ -1085,7 +1077,7 @@
           .u.native = njs_string_prototype_concat
     };
 
-    value = njs_vmcode_operand(vm, retval);
+    value = njs_vmcode_operand(vm, (njs_index_t) retval);
 
     if (!njs_is_primitive(value)) {
         array = njs_array(value);
@@ -1097,7 +1089,7 @@
             return ret;
         }
 
-        ret = njs_function_frame_invoke(vm, (njs_index_t) retval);
+        ret = njs_function_frame_invoke(vm, value);
         if (njs_slow_path(ret != NJS_OK)) {
             return ret;
         }
@@ -1144,6 +1136,27 @@
 
 
 static njs_jump_off_t
+njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value, njs_index_t retidx)
+{
+    njs_value_t     *retval;
+    njs_function_t  *function;
+
+    retval = njs_scope_valid_value(vm, retidx);
+
+    if (njs_is_undefined(retval)) {
+        *retval = *value;
+
+        function = njs_function_value_copy(vm, retval);
+        if (njs_slow_path(function == NULL)) {
+            return NJS_ERROR;
+        }
+    }
+
+    return sizeof(njs_vmcode_function_copy_t);
+}
+
+
+static njs_jump_off_t
 njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
     njs_value_t *init)
 {
@@ -1674,20 +1687,17 @@
 static njs_jump_off_t
 njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
 {
-    njs_value_t         *value;
     njs_frame_t         *frame;
     njs_native_frame_t  *previous;
 
-    value = njs_vmcode_operand(vm, retval);
-
     frame = (njs_frame_t *) vm->top_frame;
 
     if (frame->native.ctor) {
-        if (njs_is_object(value)) {
-            njs_release(vm, vm->scopes[NJS_SCOPE_ARGUMENTS]);
+        if (njs_is_object(retval)) {
+            njs_release(vm, frame->native.local[0]);
 
         } else {
-            value = vm->scopes[NJS_SCOPE_ARGUMENTS];
+            retval = frame->native.local[0];
         }
     }
 
@@ -1695,14 +1705,7 @@
 
     njs_vm_scopes_restore(vm, &frame->native, previous);
 
-    /*
-     * If a retval is in a callee arguments scope it
-     * must be in the previous callee arguments scope.
-     */
-    retval = njs_vmcode_operand(vm, frame->native.retval);
-
-    /* GC: value external/internal++ depending on value and retval type */
-    *retval = *value;
+    *frame->native.retval = *retval;
 
     njs_function_frame_free(vm, &frame->native);
 
@@ -1743,7 +1746,7 @@
     njs_set_invalid(exception_value);
 
     try_start = (njs_vmcode_try_start_t *) pc;
-    exit_value = njs_vmcode_operand(vm, try_start->exit_value);
+    exit_value = njs_scope_value(vm, try_start->exit_value);
 
     njs_set_invalid(exit_value);
     njs_number(exit_value) = 0;
@@ -1826,7 +1829,7 @@
     njs_value_t           *exception_value, *exit_value;
     njs_vmcode_finally_t  *finally;
 
-    exception_value = njs_vmcode_operand(vm, retval);
+    exception_value = njs_scope_value(vm, (njs_index_t) retval);
 
     if (njs_is_valid(exception_value)) {
         vm->retval = *exception_value;
@@ -1835,7 +1838,8 @@
     }
 
     finally = (njs_vmcode_finally_t *) pc;
-    exit_value = njs_vmcode_operand(vm, finally->exit_value);
+
+    exit_value = njs_scope_value(vm, finally->exit_value);
 
     /*
      * exit_value is set by:
diff --git a/src/njs_vmcode.h b/src/njs_vmcode.h
index 9f1dd20..76d1394 100644
--- a/src/njs_vmcode.h
+++ b/src/njs_vmcode.h
@@ -28,100 +28,100 @@
 
 #define NJS_VMCODE_3OPERANDS            0
 #define NJS_VMCODE_2OPERANDS            1
-#define NJS_VMCODE_1OPERAND             2
-#define NJS_VMCODE_NO_OPERAND           3
 
-#define NJS_VMCODE_NO_RETVAL            0
-#define NJS_VMCODE_RETVAL               1
 
-#define VMCODE0(n)                      (n)
-#define VMCODE1(n)                      ((n) + 128)
+enum {
+    NJS_VMCODE_MOVE_ARG = 0,
+    NJS_VMCODE_STOP,
+    NJS_VMCODE_JUMP,
+    NJS_VMCODE_PROPERTY_SET,
+    NJS_VMCODE_PROPERTY_ACCESSOR,
+    NJS_VMCODE_IF_TRUE_JUMP,
+    NJS_VMCODE_IF_FALSE_JUMP,
+    NJS_VMCODE_IF_EQUAL_JUMP,
+    NJS_VMCODE_PROPERTY_INIT,
+    NJS_VMCODE_RETURN,
+    NJS_VMCODE_FUNCTION_COPY,
+    NJS_VMCODE_FUNCTION_FRAME,
+    NJS_VMCODE_METHOD_FRAME,
+    NJS_VMCODE_FUNCTION_CALL,
+    NJS_VMCODE_PROPERTY_NEXT,
+    NJS_VMCODE_THIS,
+    NJS_VMCODE_ARGUMENTS,
+    NJS_VMCODE_PROTO_INIT,
 
-#define NJS_VMCODE_STOP                 VMCODE0(0)
-#define NJS_VMCODE_JUMP                 VMCODE0(1)
-#define NJS_VMCODE_PROPERTY_SET         VMCODE0(2)
-#define NJS_VMCODE_PROPERTY_ACCESSOR    VMCODE0(3)
-#define NJS_VMCODE_IF_TRUE_JUMP         VMCODE0(4)
-#define NJS_VMCODE_IF_FALSE_JUMP        VMCODE0(5)
-#define NJS_VMCODE_IF_EQUAL_JUMP        VMCODE0(6)
-#define NJS_VMCODE_PROPERTY_INIT        VMCODE0(7)
-#define NJS_VMCODE_RETURN               VMCODE0(8)
-#define NJS_VMCODE_FUNCTION_FRAME       VMCODE0(9)
-#define NJS_VMCODE_METHOD_FRAME         VMCODE0(10)
-#define NJS_VMCODE_FUNCTION_CALL        VMCODE0(11)
-#define NJS_VMCODE_PROPERTY_NEXT        VMCODE0(16)
-#define NJS_VMCODE_THIS                 VMCODE0(17)
-#define NJS_VMCODE_ARGUMENTS            VMCODE0(18)
-#define NJS_VMCODE_PROTO_INIT           VMCODE0(19)
+    NJS_VMCODE_TRY_START,
+    NJS_VMCODE_THROW,
+    NJS_VMCODE_TRY_BREAK,
+    NJS_VMCODE_TRY_CONTINUE,
+    NJS_VMCODE_TRY_END,
+    NJS_VMCODE_CATCH,
+    NJS_VMCODE_FINALLY,
+    NJS_VMCODE_ERROR,
 
-#define NJS_VMCODE_TRY_START            VMCODE0(32)
-#define NJS_VMCODE_THROW                VMCODE0(33)
-#define NJS_VMCODE_TRY_BREAK            VMCODE0(34)
-#define NJS_VMCODE_TRY_CONTINUE         VMCODE0(35)
-#define NJS_VMCODE_TRY_END              VMCODE0(37)
-#define NJS_VMCODE_CATCH                VMCODE0(38)
-#define NJS_VMCODE_FINALLY              VMCODE0(39)
-#define NJS_VMCODE_ERROR                VMCODE0(40)
+    NJS_VMCODE_NORET = 127
+};
 
-#define NJS_VMCODE_NORET                127
 
-#define NJS_VMCODE_MOVE                 VMCODE1(0)
-#define NJS_VMCODE_PROPERTY_GET         VMCODE1(1)
-#define NJS_VMCODE_INCREMENT            VMCODE1(2)
-#define NJS_VMCODE_POST_INCREMENT       VMCODE1(3)
-#define NJS_VMCODE_DECREMENT            VMCODE1(4)
-#define NJS_VMCODE_POST_DECREMENT       VMCODE1(5)
-#define NJS_VMCODE_TRY_RETURN           VMCODE1(6)
-#define NJS_VMCODE_GLOBAL_GET           VMCODE1(7)
+enum {
+    NJS_VMCODE_MOVE = NJS_VMCODE_NORET + 1,
+    NJS_VMCODE_PROPERTY_GET,
+    NJS_VMCODE_INCREMENT,
+    NJS_VMCODE_POST_INCREMENT,
+    NJS_VMCODE_DECREMENT,
+    NJS_VMCODE_POST_DECREMENT,
+    NJS_VMCODE_TRY_RETURN,
+    NJS_VMCODE_GLOBAL_GET,
 
-#define NJS_VMCODE_LESS                 VMCODE1(8)
-#define NJS_VMCODE_GREATER              VMCODE1(9)
-#define NJS_VMCODE_LESS_OR_EQUAL        VMCODE1(10)
-#define NJS_VMCODE_GREATER_OR_EQUAL     VMCODE1(11)
-#define NJS_VMCODE_ADDITION             VMCODE1(12)
-#define NJS_VMCODE_EQUAL                VMCODE1(13)
-#define NJS_VMCODE_NOT_EQUAL            VMCODE1(14)
+    NJS_VMCODE_LESS,
+    NJS_VMCODE_GREATER,
+    NJS_VMCODE_LESS_OR_EQUAL,
+    NJS_VMCODE_GREATER_OR_EQUAL,
+    NJS_VMCODE_ADDITION,
+    NJS_VMCODE_EQUAL,
+    NJS_VMCODE_NOT_EQUAL,
 
-#define NJS_VMCODE_SUBSTRACTION         VMCODE1(16)
-#define NJS_VMCODE_MULTIPLICATION       VMCODE1(17)
-#define NJS_VMCODE_EXPONENTIATION       VMCODE1(18)
-#define NJS_VMCODE_DIVISION             VMCODE1(19)
-#define NJS_VMCODE_REMAINDER            VMCODE1(20)
-#define NJS_VMCODE_BITWISE_AND          VMCODE1(21)
-#define NJS_VMCODE_BITWISE_OR           VMCODE1(22)
-#define NJS_VMCODE_BITWISE_XOR          VMCODE1(23)
-#define NJS_VMCODE_LEFT_SHIFT           VMCODE1(24)
-#define NJS_VMCODE_RIGHT_SHIFT          VMCODE1(25)
-#define NJS_VMCODE_UNSIGNED_RIGHT_SHIFT VMCODE1(26)
-#define NJS_VMCODE_OBJECT_COPY          VMCODE1(27)
-#define NJS_VMCODE_TEMPLATE_LITERAL     VMCODE1(28)
-#define NJS_VMCODE_PROPERTY_IN          VMCODE1(29)
-#define NJS_VMCODE_PROPERTY_DELETE      VMCODE1(30)
-#define NJS_VMCODE_PROPERTY_FOREACH     VMCODE1(31)
+    NJS_VMCODE_SUBSTRACTION,
+    NJS_VMCODE_MULTIPLICATION,
+    NJS_VMCODE_EXPONENTIATION,
+    NJS_VMCODE_DIVISION,
+    NJS_VMCODE_REMAINDER,
+    NJS_VMCODE_BITWISE_AND,
+    NJS_VMCODE_BITWISE_OR,
+    NJS_VMCODE_BITWISE_XOR,
+    NJS_VMCODE_LEFT_SHIFT,
+    NJS_VMCODE_RIGHT_SHIFT,
+    NJS_VMCODE_UNSIGNED_RIGHT_SHIFT,
+    NJS_VMCODE_OBJECT_COPY,
+    NJS_VMCODE_TEMPLATE_LITERAL,
+    NJS_VMCODE_PROPERTY_IN,
+    NJS_VMCODE_PROPERTY_DELETE,
+    NJS_VMCODE_PROPERTY_FOREACH,
 
-#define NJS_VMCODE_STRICT_EQUAL         VMCODE1(32)
-#define NJS_VMCODE_STRICT_NOT_EQUAL     VMCODE1(33)
+    NJS_VMCODE_STRICT_EQUAL,
+    NJS_VMCODE_STRICT_NOT_EQUAL,
 
-#define NJS_VMCODE_TEST_IF_TRUE         VMCODE1(34)
-#define NJS_VMCODE_TEST_IF_FALSE        VMCODE1(35)
+    NJS_VMCODE_TEST_IF_TRUE,
+    NJS_VMCODE_TEST_IF_FALSE,
 
-#define NJS_VMCODE_COALESCE             VMCODE1(36)
+    NJS_VMCODE_COALESCE,
 
-#define NJS_VMCODE_UNARY_PLUS           VMCODE1(37)
-#define NJS_VMCODE_UNARY_NEGATION       VMCODE1(38)
-#define NJS_VMCODE_BITWISE_NOT          VMCODE1(39)
-#define NJS_VMCODE_LOGICAL_NOT          VMCODE1(40)
-#define NJS_VMCODE_OBJECT               VMCODE1(41)
-#define NJS_VMCODE_ARRAY                VMCODE1(42)
-#define NJS_VMCODE_FUNCTION             VMCODE1(43)
-#define NJS_VMCODE_REGEXP               VMCODE1(44)
+    NJS_VMCODE_UNARY_PLUS,
+    NJS_VMCODE_UNARY_NEGATION,
+    NJS_VMCODE_BITWISE_NOT,
+    NJS_VMCODE_LOGICAL_NOT,
+    NJS_VMCODE_OBJECT,
+    NJS_VMCODE_ARRAY,
+    NJS_VMCODE_FUNCTION,
+    NJS_VMCODE_REGEXP,
 
-#define NJS_VMCODE_INSTANCE_OF          VMCODE1(45)
-#define NJS_VMCODE_TYPEOF               VMCODE1(46)
-#define NJS_VMCODE_VOID                 VMCODE1(47)
-#define NJS_VMCODE_DELETE               VMCODE1(48)
+    NJS_VMCODE_INSTANCE_OF,
+    NJS_VMCODE_TYPEOF,
+    NJS_VMCODE_VOID,
+    NJS_VMCODE_DELETE,
 
-#define NJS_VMCODE_NOP                  255
+    NJS_VMCODE_NOP = 255
+};
 
 
 typedef struct {
@@ -394,6 +394,20 @@
 } njs_vmcode_error_t;
 
 
+typedef struct {
+    njs_vmcode_t               code;
+    njs_index_t                src;
+    njs_uint_t                 dst;
+} njs_vmcode_move_arg_t;
+
+
+typedef struct {
+    njs_vmcode_t               code;
+    njs_value_t                *function;
+    njs_index_t                retval;
+} njs_vmcode_function_copy_t;
+
+
 njs_int_t njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc);
 
 njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor);
diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c
index e49cc08..5aa43f9 100644
--- a/src/test/njs_unit_test.c
+++ b/src/test/njs_unit_test.c
@@ -146,6 +146,47 @@
     { njs_str("var f = 1; function f() {}; f"),
       njs_str("1") },
 
+    { njs_str("var f = 1; function f() {}; f"),
+      njs_str("1") },
+
+    { njs_str("function f(a) {return function (x) {return a(x)}} f(1)(0)"),
+      njs_str("TypeError: number is not a function") },
+
+    { njs_str("var x = 0;"
+              ""
+              "function f1() {"
+              "    function f2() {"
+              "        return x;"
+              "    };"
+              ""
+              "    return f2();"
+              ""
+              "    var x = 1;"
+              "}"
+              ""
+              "f1() === undefined"),
+      njs_str("true") },
+
+    { njs_str("var fn = function fn() {return fn.test}; fn.test = 'test'; fn()"),
+      njs_str("test") },
+
+    { njs_str("var body;"
+              "var n = 'outside';"
+              "var before = function() {return n};"
+              ""
+              "var func = function n() {"
+              "    var n;"
+              "    body = function() {return n};"
+              "};"
+              ""
+              "func();"
+              ""
+              "[before(), body()]"),
+      njs_str("outside,") },
+
+    { njs_str("var func = function x(x) {return x}; func()"),
+      njs_str("undefined") },
+
 #if 0 /* TODO */
     { njs_str("var a; Object.getOwnPropertyDescriptor(this, 'a').value"),
       njs_str("undefined") },
@@ -3660,6 +3701,9 @@
     { njs_str("delete undefined"),
       njs_str("SyntaxError: Delete of an unqualified identifier in 1") },
 
+    { njs_str("delete this !== true"),
+      njs_str("false") },
+
     /* Object shorthand methods. */
 
     { njs_str("var o = {m(){}}; new o.m();"),
@@ -9305,7 +9349,7 @@
               "})"
               "})"
               "})"),
-      njs_str("SyntaxError: The maximum function nesting level is \"8\" in 1") },
+      njs_str("[object Function]") },
 
     { njs_str("Function.prototype.toString = function () {return 'X'};"
                  "eval"),
@@ -9365,6 +9409,72 @@
     { njs_str("(function (Object, Array, Boolean){ return Object + Array + Boolean})('x', 'y', 'z')"),
       njs_str("xyz") },
 
+    { njs_str("var n = 11, res;"
+              "function a() {return b}"
+              "res = a()(2);"
+              "function b(k) {var x = b; return 1 + k + n} res"),
+      njs_str("14") },
+
+    { njs_str("var y = 9, res;"
+              "function a(n) {function b() {return c(n + 2)} return b()}"
+              "res = a(1);"
+              "function c(m) {var x = c; return m + 3 + y} res"),
+      njs_str("15") },
+
+    { njs_str("var res;"
+              "closure();"
+              "res = globalThis.funcall(1);"
+              "function closure() {"
+              "    var y = 9, res;"
+              "    globalThis.funcall = a;"
+              "    function a(n) { function b() {return c(2)} return b() }"
+              "    function c(m) {var x = c; return m + 3 + y}"
+              "} res"),
+      njs_str("14") },
+
+    { njs_str("function a() {"
+              "    var x = 1;"
+              "    function b() {var n = x; x = undefined; return n}"
+              "    return b;"
+              "}"
+              "[a()(), a()()];"),
+      njs_str("1,1") },
+
+    { njs_str("function a(obj, name) {!Object.prototype.hasOwnProperty.call(obj, name)}"
+              "a(this, 'b');"
+              "function b() {}"),
+      njs_str("undefined") },
+
+    { njs_str("function abc() {"
+              "function x() {var a = x.arr; x.arr = 123; return a}"
+              "return x;"
+              "} [abc()(),abc()()]"),
+      njs_str(",") },
+
+    { njs_str("function x() {var a = x.arr; x.arr = 123; return a} [x(),x()]"),
+      njs_str(",123") },
+
+    { njs_str("var obj;"
+              "function make(desc) {obj = {'a': 123}}"
+              "function a(desc) {make()}"
+              "a(); obj.a"),
+      njs_str("123") },
+
+    { njs_str("var res;"
+              "function cls() {"
+              "    var obj = {'a': 123};"
+              "    Object.defineProperty(obj, \"length\", {"
+              "        get: function() {res = obj}"
+              "    });"
+              "    return obj;"
+              "}"
+              "var obj = cls();"
+              "[].includes.call(obj); res.a"),
+      njs_str("123") },
+
+    { njs_str("function f(){} typeof(f)"),
+      njs_str("function") },
+
     /* Recursive factorial. */
 
     { njs_str("function f(a) {"
@@ -11283,6 +11393,38 @@
     { njs_str("function f(){}; (function(){try {f(f((new RegExp('a**'))))} catch (e) { return 1}})()"),
       njs_str("1") },
 
+    { njs_str("var before, during, after;"
+              ""
+              "try {"
+              "    throw 'exception';"
+              "} catch (err) {"
+              "    before = err;"
+              ""
+              "    for (var err in { name: null }) {"
+              "        during = err;"
+              "    }"
+              ""
+              "    after = err;"
+              "}"
+              ""
+              "[before === 'exception', during === 'name', after === 'name']"),
+      njs_str("true,true,true") },
+
+    { njs_str("var arr = [];"
+              "foo = \"outside\";"
+              ""
+              "try {"
+              "    throw new Error();"
+              "}"
+              "catch (foo) {"
+              "    var foo = \"inside\";"
+              "    arr.push(foo);"
+              "}"
+              ""
+              "arr.push(foo);"
+              "arr"),
+      njs_str("inside,outside") },
+
     { njs_str("var o = { valueOf: function() { return '3' } }; --o"),
       njs_str("2") },
 
@@ -20330,7 +20472,7 @@
     { njs_str("/abc/i.test('ABC')" ENTER),
       njs_str("true") },
 
-    /* Accumulative mode. */
+    /* Interactive mode. */
 
     { njs_str("var a = 1" ENTER
               "a" ENTER),
@@ -20775,7 +20917,7 @@
         njs_vm_opt_init(&options);
 
         options.init = 1;
-        options.accumulative = 1;
+        options.interactive = 1;
         options.backtrace = 1;
 
         vm = njs_vm_create(&options);