]> git.kaiwu.me - njs.git/commitdiff
Global and function scopes have been fixed. Implicitly declared
authorIgor Sysoev <igor@sysoev.ru>
Mon, 2 Jan 2017 19:59:24 +0000 (22:59 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Mon, 2 Jan 2017 19:59:24 +0000 (22:59 +0300)
variables are not supported anymore.

13 files changed:
Makefile
njs/njs_disassembler.c
njs/njs_function.c
njs/njs_generator.c
njs/njs_parser.c
njs/njs_parser.h
njs/njs_parser_expression.c
njs/njs_variable.c
njs/njs_variable.h
njs/njs_vm.c
njs/njs_vm.h
njs/njscript.c
njs/test/njs_unit_test.c

index ca486c415d08fd1684a4394bd4a18db924f6157e..deb9de662eb3ba50aae26003544200ce83fadb50 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -334,6 +334,7 @@ $(NXT_BUILDDIR)/njs_parser.o: \
        njs/njs_string.h \
        njs/njs_object.h \
        njs/njs_function.h \
+       njs/njs_variable.h \
        njs/njs_parser.h \
        njs/njs_parser.c \
 
@@ -348,6 +349,7 @@ $(NXT_BUILDDIR)/njs_parser_expression.o: \
        njs/njs_number.h \
        njs/njs_object.h \
        njs/njs_function.h \
+       njs/njs_variable.h \
        njs/njs_parser.h \
        njs/njs_parser_expression.c \
 
@@ -363,6 +365,7 @@ $(NXT_BUILDDIR)/njs_generator.o: \
        njs/njs_string.h \
        njs/njs_object.h \
        njs/njs_function.h \
+       njs/njs_variable.h \
        njs/njs_parser.h \
        njs/njs_generator.c \
 
index 4b2c488ef6c084ab7882c306d0825a50a26196c3..3a406c29c0201f6dc7f8725fb34f38caeed97a2e 100644 (file)
@@ -133,8 +133,6 @@ static njs_code_name_t  code_names[] = {
 
     { njs_vmcode_move, sizeof(njs_vmcode_move_t),
           nxt_string("MOVE            ") },
-    { njs_vmcode_validate, sizeof(njs_vmcode_validate_t),
-          nxt_string("VALIDATE        ") },
 
     { njs_vmcode_throw, sizeof(njs_vmcode_throw_t),
           nxt_string("THROW           ") },
index 2153f0205353456a6957afbab528c0359df1375d..bbba2cb1a14c9a5147e0f35fe55b28765297d82c 100644 (file)
@@ -307,8 +307,8 @@ njs_function_call(njs_vm_t *vm, njs_index_t retval, size_t advance)
 #if (NXT_DEBUG)
     vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = NULL;
 #endif
-    frame->prev_local = vm->scopes[NJS_SCOPE_LOCAL];
-    vm->scopes[NJS_SCOPE_LOCAL] = frame->local;
+    frame->prev_local = vm->scopes[NJS_SCOPE_FUNCTION];
+    vm->scopes[NJS_SCOPE_FUNCTION] = frame->local;
 
     return NJS_APPLIED;
 }
index caa9bae6ccb517735d16d28158992728c3b63e68..19642a927782fba7dae97580066722ea96926d61 100644 (file)
@@ -30,7 +30,9 @@ static nxt_int_t njs_generate_name(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
 static nxt_int_t njs_generate_builtin_object(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
-static nxt_int_t njs_generate_variable(njs_parser_t *parser,
+static nxt_int_t njs_generate_variable(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node);
+static nxt_int_t njs_generate_var_statement(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
 static nxt_int_t njs_generate_if_statement(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
@@ -82,6 +84,8 @@ static nxt_int_t njs_generate_3addr_operation(njs_vm_t *vm,
     njs_parser_t *parser, njs_parser_node_t *node);
 static nxt_int_t njs_generate_2addr_operation(njs_vm_t *vm,
     njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_typeof_operation(njs_vm_t *vm,
+    njs_parser_t *parser, njs_parser_node_t *node);
 static nxt_int_t njs_generate_inc_dec_operation(njs_vm_t *vm,
     njs_parser_t *parser, njs_parser_node_t *node, nxt_bool_t post);
 static nxt_int_t njs_generate_function_declaration(njs_vm_t *vm,
@@ -103,12 +107,12 @@ static nxt_int_t njs_generate_throw_statement(njs_vm_t *vm,
 static nxt_noinline njs_index_t njs_generator_dest_index(njs_vm_t *vm,
     njs_parser_t *parser, njs_parser_node_t *node);
 static nxt_noinline njs_index_t
-    njs_generator_object_dest_index(njs_parser_t *parser,
+    njs_generator_object_dest_index(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
-static njs_index_t njs_generator_node_temp_index_get(njs_parser_t *parser,
-    njs_parser_node_t *node);
-static nxt_noinline njs_index_t
-    njs_generator_temp_index_get(njs_parser_t *parser);
+static njs_index_t njs_generator_node_temp_index_get(njs_vm_t *vm,
+    njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_noinline njs_index_t njs_generator_temp_index_get(njs_vm_t *vm,
+    njs_parser_t *parser, njs_parser_node_t *node);
 static nxt_noinline nxt_int_t
     njs_generator_children_indexes_release(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
@@ -116,7 +120,6 @@ static nxt_noinline nxt_int_t njs_generator_node_index_release(njs_vm_t *vm,
     njs_parser_t *parser, njs_parser_node_t *node);
 static nxt_noinline nxt_int_t njs_generator_index_release(njs_vm_t *vm,
     njs_parser_t *parser, njs_index_t index);
-nxt_inline nxt_bool_t njs_generator_is_constant(njs_parser_node_t *node);
 
 
 static const nxt_str_t  no_label = { 0, NULL };
@@ -133,6 +136,9 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 
     switch (node->token) {
 
+    case NJS_TOKEN_VAR:
+        return njs_generate_var_statement(vm, parser, node);
+
     case NJS_TOKEN_IF:
         return njs_generate_if_statement(vm, parser, node);
 
@@ -233,13 +239,15 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 
     case NJS_TOKEN_DELETE:
     case NJS_TOKEN_VOID:
-    case NJS_TOKEN_TYPEOF:
     case NJS_TOKEN_UNARY_PLUS:
     case NJS_TOKEN_UNARY_NEGATION:
     case NJS_TOKEN_LOGICAL_NOT:
     case NJS_TOKEN_BITWISE_NOT:
         return njs_generate_2addr_operation(vm, parser, node);
 
+    case NJS_TOKEN_TYPEOF:
+        return njs_generate_typeof_operation(vm, parser, node);
+
     case NJS_TOKEN_INCREMENT:
     case NJS_TOKEN_DECREMENT:
         return njs_generate_inc_dec_operation(vm, parser, node, 0);
@@ -336,9 +344,15 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 static nxt_int_t
 njs_generate_name(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 {
+    njs_variable_t            *var;
     njs_vmcode_object_copy_t  *copy;
 
-    if (node->u.variable->function) {
+    var = njs_variable_get(vm, node, NJS_NAME_REFERENCE);
+    if (nxt_slow_path(var == NULL)) {
+        return NXT_ERROR;
+    }
+
+    if (var->type == NJS_VARIABLE_FUNCTION) {
 
         node->index = njs_generator_dest_index(vm, parser, node);
         if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
@@ -350,12 +364,12 @@ njs_generate_name(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
         copy->code.operands = NJS_VMCODE_2OPERANDS;
         copy->code.retval = NJS_VMCODE_RETVAL;
         copy->retval = node->index;
-        copy->object = node->u.variable->index;
+        copy->object = var->index;
 
         return NXT_OK;
     }
 
-    return njs_generate_variable(parser, node);
+    return njs_generate_variable(vm, parser, node);
 }
 
 
@@ -366,7 +380,10 @@ njs_generate_builtin_object(njs_vm_t *vm, njs_parser_t *parser,
     njs_index_t               index;
     njs_vmcode_object_copy_t  *copy;
 
-    index = node->index;
+    index = njs_variable_index(vm, node, NJS_NAME_REFERENCE);
+    if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+        return NXT_ERROR;
+    }
 
     node->index = njs_generator_dest_index(vm, parser, node);
     if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
@@ -385,26 +402,70 @@ njs_generate_builtin_object(njs_vm_t *vm, njs_parser_t *parser,
 
 
 static nxt_int_t
-njs_generate_variable(njs_parser_t *parser, njs_parser_node_t *node)
+njs_generate_variable(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node)
+{
+    njs_index_t  index;
+
+    index = njs_variable_index(vm, node, NJS_NAME_REFERENCE);
+    if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+        return NXT_ERROR;
+    }
+
+    node->index = index;
+
+    return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_var_statement(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node)
 {
-    njs_value_t            *value;
-    njs_vmcode_validate_t  *validate;
+    nxt_int_t          ret;
+    njs_index_t        index;
+    njs_parser_node_t  *lvalue, *expr;
+    njs_vmcode_move_t  *move;
 
-    node->index = node->u.variable->index;
+    lvalue = node->left;
 
-    if (node->state == NJS_VARIABLE_NORMAL
-        && node->u.variable->state < NJS_VARIABLE_SET)
-    {
-        njs_generate_code(parser, njs_vmcode_validate_t, validate);
-        validate->code.operation = njs_vmcode_validate;
-        validate->code.operands = NJS_VMCODE_NO_OPERAND;
-        validate->code.retval = NJS_VMCODE_NO_RETVAL;
-        validate->index = node->index;
+    index = njs_variable_index(vm, lvalue, NJS_NAME_DECLARATION);
+    if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+        return NXT_ERROR;
+    }
+
+    lvalue->index = index;
+
+    expr = node->right;
 
-        value = njs_variable_value(parser, node->index);
-        njs_set_invalid(value);
+    if (expr == NULL) {
+        /* Variable is only declared. */
+        return NXT_OK;
+    }
+
+    expr->dest = lvalue;
+
+    ret = njs_generator(vm, parser, expr);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    /*
+     * lvalue and expression indexes are equal if the expression is an
+     * empty object or expression result is stored directly in variable.
+     */
+    if (lvalue->index != expr->index) {
+        njs_generate_code(parser, njs_vmcode_move_t, move);
+        move->code.operation = njs_vmcode_move;
+        move->code.operands = NJS_VMCODE_2OPERANDS;
+        move->code.retval = NJS_VMCODE_RETVAL;
+        move->dst = lvalue->index;
+        move->src = expr->index;
     }
 
+    node->index = expr->index;
+    node->temporary = expr->temporary;
+
     return NXT_OK;
 }
 
@@ -602,7 +663,10 @@ njs_generate_switch_statement(njs_vm_t *vm, njs_parser_t *parser,
     index = expr->index;
 
     if (!expr->temporary) {
-        index = njs_generator_temp_index_get(parser);
+        index = njs_generator_temp_index_get(vm, parser, swtch);
+        if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+            return NXT_ERROR;
+        }
 
         njs_generate_code(parser, njs_vmcode_move_t, move);
         move->code.operation = njs_vmcode_move;
@@ -953,7 +1017,11 @@ njs_generate_for_in_statement(njs_vm_t *vm, njs_parser_t *parser,
     prop_foreach->code.retval = NJS_VMCODE_RETVAL;
     prop_foreach->object = foreach->right->index;
 
-    index = njs_generator_temp_index_get(parser);
+    index = njs_generator_temp_index_get(vm, parser, foreach->right);
+    if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+        return NXT_ERROR;
+    }
+
     prop_foreach->next = index;
 
     /* The loop body. */
@@ -1236,11 +1304,11 @@ static nxt_int_t
 njs_generate_assignment(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node)
 {
-    nxt_int_t                   ret;
-    njs_value_t                 *value;
-    njs_parser_node_t           *lvalue, *expr, *object, *property;
-    njs_vmcode_move_t           *move;
-    njs_vmcode_prop_set_t       *prop_set;
+    nxt_int_t              ret;
+    njs_index_t            index;
+    njs_parser_node_t      *lvalue, *expr, *object, *property;
+    njs_vmcode_move_t      *move;
+    njs_vmcode_prop_set_t  *prop_set;
 
     lvalue = node->left;
     expr = node->right;
@@ -1248,26 +1316,13 @@ njs_generate_assignment(njs_vm_t *vm, njs_parser_t *parser,
 
     if (lvalue->token == NJS_TOKEN_NAME) {
 
-        lvalue->index = lvalue->u.variable->index;
-
-        /* Use a constant value is stored as variable initial value. */
-
-        if (njs_generator_is_constant(expr)) {
-
-            ret = njs_generator(vm, parser, expr);
-            if (nxt_slow_path(ret != NXT_OK)) {
-                return ret;
-            }
-
-            if (lvalue->state == NJS_VARIABLE_FIRST_ASSIGNMENT) {
-                value = njs_variable_value(parser, lvalue->index);
-                *value = expr->u.value;
-                node->index = expr->index;
-
-                return NXT_OK;
-            }
+        index = njs_variable_index(vm, lvalue, NJS_NAME_REFERENCE);
+        if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+            return NXT_ERROR;
         }
 
+        lvalue->index = index;
+
         expr->dest = lvalue;
 
         ret = njs_generator(vm, parser, expr);
@@ -1325,7 +1380,13 @@ njs_generate_assignment(njs_vm_t *vm, njs_parser_t *parser,
             move->code.operands = NJS_VMCODE_2OPERANDS;
             move->code.retval = NJS_VMCODE_RETVAL;
             move->src = object->index;
-            move->dst = njs_generator_node_temp_index_get(parser, object);
+
+            index = njs_generator_node_temp_index_get(vm, parser, object);
+            if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+                return NXT_ERROR;
+            }
+
+            move->dst = index;
         }
 
         if (property->token == NJS_TOKEN_NAME) {
@@ -1334,7 +1395,13 @@ njs_generate_assignment(njs_vm_t *vm, njs_parser_t *parser,
             move->code.operands = NJS_VMCODE_2OPERANDS;
             move->code.retval = NJS_VMCODE_RETVAL;
             move->src = property->index;
-            move->dst = njs_generator_node_temp_index_get(parser, property);
+
+            index = njs_generator_node_temp_index_get(vm, parser, property);
+            if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+                return NXT_ERROR;
+            }
+
+            move->dst = index;
         }
     }
 
@@ -1373,7 +1440,7 @@ njs_generate_operation_assignment(njs_vm_t *vm, njs_parser_t *parser,
     lvalue = node->left;
 
     if (lvalue->token == NJS_TOKEN_NAME) {
-        ret = njs_generate_variable(parser, lvalue);
+        ret = njs_generate_variable(vm, parser, lvalue);
         if (nxt_slow_path(ret != NXT_OK)) {
             return ret;
         }
@@ -1390,7 +1457,11 @@ njs_generate_operation_assignment(njs_vm_t *vm, njs_parser_t *parser,
             move->code.retval = NJS_VMCODE_RETVAL;
             move->src = lvalue->index;
 
-            index = njs_generator_temp_index_get(parser);
+            index = njs_generator_temp_index_get(vm, parser, expr);
+            if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+                return NXT_ERROR;
+            }
+
             move->dst = index;
         }
 
@@ -1439,11 +1510,16 @@ njs_generate_operation_assignment(njs_vm_t *vm, njs_parser_t *parser,
         return ret;
     }
 
+    index = njs_generator_node_temp_index_get(vm, parser, node);
+    if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+        return NXT_ERROR;
+    }
+
     njs_generate_code(parser, njs_vmcode_prop_get_t, prop_get);
     prop_get->code.operation = njs_vmcode_property_get;
     prop_get->code.operands = NJS_VMCODE_3OPERANDS;
     prop_get->code.retval = NJS_VMCODE_RETVAL;
-    prop_get->value = njs_generator_node_temp_index_get(parser, node);
+    prop_get->value = index;
     prop_get->object = object->index;
     prop_get->property = property->index;
 
@@ -1484,7 +1560,10 @@ njs_generate_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 {
     njs_vmcode_object_t  *object;
 
-    node->index = njs_generator_object_dest_index(parser, node);
+    node->index = njs_generator_object_dest_index(vm, parser, node);
+    if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
+        return NXT_ERROR;
+    }
 
     njs_generate_code(parser, njs_vmcode_object_t, object);
     object->code.operation = njs_vmcode_object;
@@ -1492,12 +1571,7 @@ njs_generate_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
     object->code.retval = NJS_VMCODE_RETVAL;
     object->retval = node->index;
 
-    if (node->left == NULL) {
-        return NXT_OK;
-    }
-
     /* Initialize object. */
-
     return njs_generator(vm, parser, node->left);
 }
 
@@ -1507,7 +1581,10 @@ njs_generate_array(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 {
     njs_vmcode_array_t  *array;
 
-    node->index = njs_generator_object_dest_index(parser, node);
+    node->index = njs_generator_object_dest_index(vm, parser, node);
+    if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
+        return NXT_ERROR;
+    }
 
     njs_generate_code(parser, njs_vmcode_array_t, array);
     array->code.operation = njs_vmcode_array;
@@ -1534,18 +1611,24 @@ njs_generate_function(njs_vm_t *vm, njs_parser_t *parser,
 
     ret = njs_generate_function_scope(vm, lambda, node);
 
-    if (nxt_fast_path(ret == NXT_OK)) {
-        njs_generate_code(parser, njs_vmcode_function_t, function);
-        function->code.operation = njs_vmcode_function;
-        function->code.operands = NJS_VMCODE_1OPERAND;
-        function->code.retval = NJS_VMCODE_RETVAL;
-        function->lambda = lambda;
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    njs_generate_code(parser, njs_vmcode_function_t, function);
+    function->code.operation = njs_vmcode_function;
+    function->code.operands = NJS_VMCODE_1OPERAND;
+    function->code.retval = NJS_VMCODE_RETVAL;
+    function->lambda = lambda;
 
-        node->index = njs_generator_object_dest_index(parser, node);
-        function->retval = node->index;
+    node->index = njs_generator_object_dest_index(vm, parser, node);
+    if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
+        return NXT_ERROR;
     }
 
-    return ret;
+    function->retval = node->index;
+
+    return NXT_OK;
 }
 
 
@@ -1554,7 +1637,10 @@ njs_generate_regexp(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 {
     njs_vmcode_regexp_t  *regexp;
 
-    node->index = njs_generator_object_dest_index(parser, node);
+    node->index = njs_generator_object_dest_index(vm, parser, node);
+    if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
+        return NXT_ERROR;
+    }
 
     njs_generate_code(parser, njs_vmcode_regexp_t, regexp);
     regexp->code.operation = njs_vmcode_regexp;
@@ -1624,6 +1710,7 @@ njs_generate_3addr_operation(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node)
 {
     nxt_int_t           ret;
+    njs_index_t         index;
     njs_parser_node_t   *left, *right;
     njs_vmcode_move_t   *move;
     njs_vmcode_3addr_t  *code;
@@ -1645,7 +1732,13 @@ njs_generate_3addr_operation(njs_vm_t *vm, njs_parser_t *parser,
             move->code.operands = NJS_VMCODE_2OPERANDS;
             move->code.retval = NJS_VMCODE_RETVAL;
             move->src = left->index;
-            move->dst = njs_generator_node_temp_index_get(parser, left);
+
+            index = njs_generator_node_temp_index_get(vm, parser, left);
+            if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+                return NXT_ERROR;
+            }
+
+            move->dst = index;
         }
     }
 
@@ -1710,6 +1803,51 @@ njs_generate_2addr_operation(njs_vm_t *vm, njs_parser_t *parser,
 }
 
 
+static nxt_int_t
+njs_generate_typeof_operation(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node)
+{
+    nxt_int_t           ret;
+    njs_index_t         index;
+    njs_parser_node_t   *expr;
+    njs_vmcode_2addr_t  *code;
+
+    expr = node->left;
+
+    if (expr->token == NJS_TOKEN_NAME) {
+        index = njs_variable_index(vm, expr, NJS_NAME_TYPEOF);
+        if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+            return NXT_ERROR;
+        }
+
+        expr->index = index;
+
+    } else {
+        ret = njs_generator(vm, parser, node->left);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
+    }
+
+    njs_generate_code(parser, njs_vmcode_2addr_t, code);
+    code->code.operation = node->u.operation;
+    code->code.operands = NJS_VMCODE_2OPERANDS;
+    code->code.retval = NJS_VMCODE_RETVAL;
+    code->src = node->left->index;
+
+    node->index = njs_generator_dest_index(vm, parser, node);
+    if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
+        return node->index;
+    }
+
+    code->dst = node->index;
+
+    nxt_thread_log_debug("CODE2  %p, %p", code->dst, code->src);
+
+    return NXT_OK;
+}
+
+
 static nxt_int_t
 njs_generate_inc_dec_operation(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node, nxt_bool_t post)
@@ -1725,7 +1863,7 @@ njs_generate_inc_dec_operation(njs_vm_t *vm, njs_parser_t *parser,
 
     if (lvalue->token == NJS_TOKEN_NAME) {
 
-        ret = njs_generate_variable(parser, lvalue);
+        ret = njs_generate_variable(vm, parser, lvalue);
         if (nxt_slow_path(ret != NXT_OK)) {
             return ret;
         }
@@ -1776,11 +1914,15 @@ njs_generate_inc_dec_operation(njs_vm_t *vm, njs_parser_t *parser,
         }
     }
 
-    dest_index = njs_generator_node_temp_index_get(parser, node);
+    dest_index = njs_generator_node_temp_index_get(vm, parser, node);
 
 found:
 
-    index = post ? njs_generator_temp_index_get(parser) : dest_index;
+    index = post ? njs_generator_temp_index_get(vm, parser, node) : dest_index;
+
+    if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+        return NXT_ERROR;
+    }
 
     njs_generate_code(parser, njs_vmcode_prop_get_t, prop_get);
     prop_get->code.operation = njs_vmcode_property_get;
@@ -1821,19 +1963,11 @@ static nxt_int_t
 njs_generate_function_declaration(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node)
 {
-    nxt_int_t    ret;
-    njs_value_t  *value;
-
-    value = njs_variable_value(parser, node->index);
-
-    ret = njs_generate_function_scope(vm, value->data.u.function->u.lambda,
-                                      node);
+    njs_function_lambda_t  *lambda;
 
-    if (nxt_fast_path(ret == NXT_OK)) {
-        node->u.value = *value;
-    }
+    lambda = node->u.value.data.u.function->u.lambda;
 
-    return ret;
+    return njs_generate_function_scope(vm, lambda, node);
 }
 
 
@@ -1858,12 +1992,13 @@ njs_generate_function_scope(njs_vm_t *vm, njs_function_lambda_t *lambda,
 nxt_int_t
 njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 {
-    u_char         *p;
-    size_t         code_size, size;
-    uintptr_t      scope_size;
-    nxt_uint_t     n;
-    njs_value_t    *value;
-    njs_vm_code_t  *code;
+    u_char              *p;
+    size_t              code_size, size;
+    uintptr_t           scope_size;
+    nxt_uint_t          n;
+    njs_value_t         *value;
+    njs_vm_code_t       *code;
+    njs_parser_scope_t  *scope;
 
     p = nxt_mem_cache_alloc(vm->mem_cache_pool, parser->code_size);
     if (nxt_slow_path(p == NULL)) {
@@ -1877,6 +2012,8 @@ njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
         return NXT_ERROR;
     }
 
+    scope = node->scope;
+
     code_size = parser->code_end - parser->code_start;
 
     nxt_thread_log_debug("SCOPE CODE SIZE: %uz %uz",
@@ -1887,8 +2024,11 @@ njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
         return NXT_ERROR;
     }
 
-    scope_size = parser->index[parser->scope - NJS_INDEX_CACHE]
-                 - parser->scope_offset;
+    scope_size = njs_offset(scope->next_index);
+
+    if (scope->type == NJS_SCOPE_GLOBAL) {
+        scope_size -= NJS_INDEX_GLOBAL_OFFSET;
+    }
 
     parser->local_scope = nxt_mem_cache_alloc(vm->mem_cache_pool, scope_size);
     if (nxt_slow_path(parser->local_scope == NULL)) {
@@ -1897,11 +2037,11 @@ njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 
     parser->scope_size = scope_size;
 
-    size = parser->scope_values->items * sizeof(njs_value_t);
+    size = scope->values->items * sizeof(njs_value_t);
 
     nxt_thread_log_debug("SCOPE SIZE: %uz %uz", size, scope_size);
 
-    p = memcpy(parser->local_scope, parser->scope_values->start, size);
+    p = memcpy(parser->local_scope, scope->values->start, size);
     value = (njs_value_t *) (p + size);
 
     for (n = scope_size - size; n != 0; n -= sizeof(njs_value_t)) {
@@ -1963,7 +2103,7 @@ static nxt_int_t
 njs_generate_function_call(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node)
 {
-    nxt_int_t                    ret;
+    njs_ret_t                    ret;
     njs_parser_node_t            *name;
     njs_vmcode_function_frame_t  *func;
 
@@ -1977,8 +2117,10 @@ njs_generate_function_call(njs_vm_t *vm, njs_parser_t *parser,
         name = node->left;
 
     } else {
-        /* njs_generate_variable() always returns NXT_OK. */
-        (void) njs_generate_variable(parser, node);
+        ret = njs_generate_variable(vm, parser, node);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
         name = node;
     }
 
@@ -2100,7 +2242,7 @@ njs_generate_try_statement(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node)
 {
     nxt_int_t               ret;
-    njs_index_t             index;
+    njs_index_t             index, catch_index;
     njs_vmcode_catch_t      *catch;
     njs_vmcode_finally_t    *finally;
     njs_vmcode_try_end_t    *try_end, *catch_end;
@@ -2111,7 +2253,11 @@ njs_generate_try_statement(njs_vm_t *vm, njs_parser_t *parser,
     try_start->code.operands = NJS_VMCODE_2OPERANDS;
     try_start->code.retval = NJS_VMCODE_NO_RETVAL;
 
-    index = njs_generator_temp_index_get(parser);
+    index = njs_generator_temp_index_get(vm, parser, node);
+    if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+        return NXT_ERROR;
+    }
+
     try_start->value = index;
 
     ret = njs_generator(vm, parser, node->left);
@@ -2131,9 +2277,9 @@ njs_generate_try_statement(njs_vm_t *vm, njs_parser_t *parser,
     if (node->token == NJS_TOKEN_CATCH) {
         /* A "try/catch" case. */
 
-        ret = njs_generator(vm, parser, node->left);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return ret;
+        catch_index = njs_variable_index(vm, node->left, NJS_NAME_DECLARATION);
+        if (nxt_slow_path(catch_index == NJS_INDEX_ERROR)) {
+            return NXT_ERROR;
         }
 
         njs_generate_code(parser, njs_vmcode_catch_t, catch);
@@ -2141,7 +2287,7 @@ njs_generate_try_statement(njs_vm_t *vm, njs_parser_t *parser,
         catch->code.operands = NJS_VMCODE_2OPERANDS;
         catch->code.retval = NJS_VMCODE_NO_RETVAL;
         catch->offset = sizeof(njs_vmcode_catch_t);
-        catch->exception = node->left->index;
+        catch->exception = catch_index;
 
         ret = njs_generator(vm, parser, node->right);
         if (nxt_slow_path(ret != NXT_OK)) {
@@ -2156,16 +2302,17 @@ njs_generate_try_statement(njs_vm_t *vm, njs_parser_t *parser,
         if (node->left != NULL) {
             /* A try/catch/finally case. */
 
-            ret = njs_generator(vm, parser, node->left->left);
-            if (nxt_slow_path(ret != NXT_OK)) {
-                return ret;
+            catch_index = njs_variable_index(vm, node->left->left,
+                                             NJS_NAME_DECLARATION);
+            if (nxt_slow_path(catch_index == NJS_INDEX_ERROR)) {
+                return NXT_ERROR;
             }
 
             njs_generate_code(parser, njs_vmcode_catch_t, catch);
             catch->code.operation = njs_vmcode_catch;
             catch->code.operands = NJS_VMCODE_2OPERANDS;
             catch->code.retval = NJS_VMCODE_NO_RETVAL;
-            catch->exception = node->left->left->index;
+            catch->exception = catch_index;
 
             ret = njs_generator(vm, parser, node->left->right);
             if (nxt_slow_path(ret != NXT_OK)) {
@@ -2260,12 +2407,13 @@ njs_generator_dest_index(njs_vm_t *vm, njs_parser_t *parser,
         return dest->index;
     }
 
-    return njs_generator_node_temp_index_get(parser, node);
+    return njs_generator_node_temp_index_get(vm, parser, node);
 }
 
 
 static nxt_noinline njs_index_t
-njs_generator_object_dest_index(njs_parser_t *parser, njs_parser_node_t *node)
+njs_generator_object_dest_index(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node)
 {
     njs_index_t        index;
     njs_parser_node_t  *dest;
@@ -2286,27 +2434,30 @@ njs_generator_object_dest_index(njs_parser_t *parser, njs_parser_node_t *node)
         }
     }
 
-    return njs_generator_node_temp_index_get(parser, node);
+    return njs_generator_node_temp_index_get(vm, parser, node);
 }
 
 
 static njs_index_t
-njs_generator_node_temp_index_get(njs_parser_t *parser, njs_parser_node_t *node)
+njs_generator_node_temp_index_get(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node)
 {
     node->temporary = 1;
 
-    node->index = njs_generator_temp_index_get(parser);
+    node->index = njs_generator_temp_index_get(vm, parser, node);
 
     return node->index;
 }
 
 
 static nxt_noinline njs_index_t
-njs_generator_temp_index_get(njs_parser_t *parser)
+njs_generator_temp_index_get(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node)
 {
-    nxt_uint_t   n;
-    njs_index_t  index, *last;
-    nxt_array_t  *cache;
+    nxt_array_t         *cache;
+    njs_value_t         *value;
+    njs_index_t         index, *last;
+    njs_parser_scope_t  *scope;
 
     cache = parser->index_cache;
 
@@ -2318,15 +2469,22 @@ njs_generator_temp_index_get(njs_parser_t *parser)
         return *last;
     }
 
-    /* Skip absolute and propery scopes. */
-    n = parser->scope - NJS_INDEX_CACHE;
+    scope = node->scope;
+
+    while (scope->type == NJS_SCOPE_BLOCK) {
+         scope = scope->parent;
+    }
 
-    index = parser->index[n];
-    parser->index[n] += sizeof(njs_value_t);
+    value = nxt_array_add(scope->values, &njs_array_mem_proto,
+                          vm->mem_cache_pool);
+    if (nxt_slow_path(value == NULL)) {
+        return NJS_INDEX_ERROR;
+    }
 
-    index |= parser->scope;
+    *value = njs_value_invalid;
 
-    nxt_thread_log_debug("GET %p", index);
+    index = scope->next_index;
+    scope->next_index += sizeof(njs_value_t);
 
     return index;
 }
@@ -2389,11 +2547,3 @@ njs_generator_index_release(njs_vm_t *vm, njs_parser_t *parser,
 
     return NXT_ERROR;
 }
-
-
-nxt_inline nxt_bool_t
-njs_generator_is_constant(njs_parser_node_t *node)
-{
-    return (node->token >= NJS_TOKEN_FIRST_CONST
-            && node->token <= NJS_TOKEN_LAST_CONST);
-}
index 868da57f6faeb9e8d33af36e02b1b41f66728889..2a346e96f449120b89e59f269f1fae7647021bde 100644 (file)
  * is treated as a single expiression.
  */
 
+static njs_ret_t njs_parser_scope_begin(njs_vm_t *vm, njs_parser_t *parser,
+    njs_scope_t type);
+static void njs_parser_scope_end(njs_vm_t *vm, njs_parser_t *parser);
 static njs_token_t njs_parser_statement_chain(njs_vm_t *vm,
     njs_parser_t *parser, njs_token_t token);
 static njs_token_t njs_parser_statement(njs_vm_t *vm, njs_parser_t *parser,
     njs_token_t token);
-static njs_token_t njs_parser_block(njs_vm_t *vm, njs_parser_t *parser);
+static njs_token_t njs_parser_block_statement(njs_vm_t *vm,
+    njs_parser_t *parser);
 static njs_token_t njs_parser_function_declaration(njs_vm_t *vm,
     njs_parser_t *parser);
 static njs_parser_t *njs_parser_function_create(njs_vm_t *vm,
@@ -89,9 +93,15 @@ static njs_token_t njs_parser_unexpected_token(njs_vm_t *vm,
 njs_parser_node_t *
 njs_parser(njs_vm_t *vm, njs_parser_t *parser)
 {
+    njs_ret_t          ret;
     njs_token_t        token;
     njs_parser_node_t  *node;
 
+    ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_GLOBAL);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NULL;
+    }
+
     token = njs_parser_token(parser);
 
     while (token != NJS_TOKEN_END) {
@@ -112,6 +122,7 @@ njs_parser(njs_vm_t *vm, njs_parser_t *parser)
     if (node != NULL && node->right != NULL) {
         if (node->right->token == NJS_TOKEN_FUNCTION) {
             node->token = NJS_TOKEN_CALL;
+            node->scope = parser->scope;
             return node;
         }
 
@@ -125,11 +136,83 @@ njs_parser(njs_vm_t *vm, njs_parser_t *parser)
     }
 
     node->token = NJS_TOKEN_END;
+    node->scope = parser->scope;
 
     return node;
 }
 
 
+static njs_ret_t
+njs_parser_scope_begin(njs_vm_t *vm, njs_parser_t *parser, njs_scope_t type)
+{
+    nxt_array_t         *values;
+    njs_parser_scope_t  *scope, *parent;
+
+    scope = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_parser_scope_t));
+    if (nxt_slow_path(scope == NULL)) {
+        return NXT_ERROR;
+    }
+
+    scope->type = type;
+
+    if (type == NJS_SCOPE_GLOBAL) {
+        type += NJS_INDEX_GLOBAL_OFFSET;
+    }
+
+    scope->next_index = type;
+
+    scope->inclusive = 0;
+    nxt_lvlhsh_init(&scope->variables);
+
+    values = NULL;
+
+    if (scope->type < NJS_SCOPE_BLOCK) {
+        values = nxt_array_create(4, sizeof(njs_value_t), &njs_array_mem_proto,
+                                  vm->mem_cache_pool);
+        if (nxt_slow_path(values == NULL)) {
+            return NXT_ERROR;
+        }
+    }
+
+    scope->values = values;
+
+    parent = parser->scope;
+
+    if (parent != NULL) {
+        parent->inclusive++;
+    }
+
+    scope->parent = parent;
+    parser->scope = scope;
+
+    return NXT_OK;
+}
+
+
+static void
+njs_parser_scope_end(njs_vm_t *vm, njs_parser_t *parser)
+{
+    njs_parser_scope_t  *scope, *parent;
+
+    scope = parser->scope;
+
+    parent = scope->parent;
+
+#if 0
+    if (scope->inclusive == 0
+        && scope->type == NJS_SCOPE_BLOCK
+        && nxt_lvlhsh_is_empty(&scope->variables))
+    {
+        parent->inclusive--;
+
+        nxt_mem_cache_free(vm->mem_cache_pool, scope);
+    }
+#endif
+
+    parser->scope = parent;
+}
+
+
 static njs_token_t
 njs_parser_statement_chain(njs_vm_t *vm, njs_parser_t *parser,
     njs_token_t token)
@@ -213,7 +296,7 @@ njs_parser_statement(njs_vm_t *vm, njs_parser_t *parser,
         return njs_parser_token(parser);
 
     case NJS_TOKEN_OPEN_BRACE:
-        return njs_parser_block(vm, parser);
+        return njs_parser_block_statement(vm, parser);
 
     case NJS_TOKEN_CLOSE_BRACE:
         parser->node = NULL;
@@ -251,8 +334,9 @@ njs_parser_statement(njs_vm_t *vm, njs_parser_t *parser,
 
 
 static njs_token_t
-njs_parser_block(njs_vm_t *vm, njs_parser_t *parser)
+njs_parser_block_statement(njs_vm_t *vm, njs_parser_t *parser)
 {
+    njs_ret_t    ret;
     njs_token_t  token;
 
     token = njs_parser_token(parser);
@@ -262,6 +346,11 @@ njs_parser_block(njs_vm_t *vm, njs_parser_t *parser)
 
     parser->node = NULL;
 
+    ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_BLOCK);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NJS_TOKEN_ERROR;
+    }
+
     while (token != NJS_TOKEN_CLOSE_BRACE) {
         token = njs_parser_statement_chain(vm, parser, token);
         if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
@@ -269,6 +358,8 @@ njs_parser_block(njs_vm_t *vm, njs_parser_t *parser)
         }
     }
 
+    njs_parser_scope_end(vm, parser);
+
     return njs_parser_token(parser);
 }
 
@@ -289,7 +380,6 @@ static njs_token_t
 njs_parser_function_declaration(njs_vm_t *vm, njs_parser_t *parser)
 {
     njs_token_t        token;
-    njs_value_t        *value;
     njs_variable_t     *var;
     njs_function_t     *function;
     njs_parser_node_t  *node;
@@ -310,15 +400,11 @@ njs_parser_function_declaration(njs_vm_t *vm, njs_parser_t *parser)
         return NJS_TOKEN_ILLEGAL;
     }
 
-    var = njs_parser_name_alloc(vm, parser);
+    var = njs_variable_add(vm, parser, NJS_VARIABLE_FUNCTION);
     if (nxt_slow_path(var == NULL)) {
         return NJS_TOKEN_ERROR;
     }
 
-    var->state = NJS_VARIABLE_DECLARED;
-    var->function = 1;
-    node->index = var->index;
-
     token = njs_parser_token(parser);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
         return token;
@@ -331,10 +417,11 @@ njs_parser_function_declaration(njs_vm_t *vm, njs_parser_t *parser)
         return NJS_TOKEN_ERROR;
     }
 
-    value = njs_variable_value(parser, node->index);
-    value->data.u.function = function;
-    value->type = NJS_FUNCTION;
-    value->data.truth = 1;
+    var->value.data.u.function = function;
+    var->value.type = NJS_FUNCTION;
+    var->value.data.truth = 1;
+
+    node->u.value = var->value;
 
     parser = njs_parser_function_create(vm, parser);
     if (nxt_slow_path(parser == NULL)) {
@@ -343,17 +430,19 @@ njs_parser_function_declaration(njs_vm_t *vm, njs_parser_t *parser)
 
     function->u.lambda->u.parser = parser;
 
-    return njs_parser_function_lambda(vm, function->u.lambda, token);
+    token = njs_parser_function_lambda(vm, function->u.lambda, token);
+
+    vm->parser = parser->parent;
+
+    return token;
 }
 
 
 static njs_token_t
 njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser)
 {
-    nxt_uint_t             level;
+    njs_ret_t              ret;
     njs_token_t            token;
-    njs_index_t            index;
-    njs_value_t            *value;
     njs_variable_t         *var;
     njs_function_t         *function;
     njs_parser_node_t      *node;
@@ -365,6 +454,7 @@ njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser)
     }
 
     node->token = NJS_TOKEN_FUNCTION_EXPRESSION;
+    node->scope = parser->scope;
     parser->node = node;
     parser->code_size += sizeof(njs_vmcode_function_t);
 
@@ -378,30 +468,35 @@ njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser)
         return token;
     }
 
+    /*
+     * An optional function expression name is stored
+     * in intermediate shim scope.
+     */
+    ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_SHIM);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NJS_TOKEN_ERROR;
+    }
+
     if (token == NJS_TOKEN_NAME) {
-        var = njs_parser_variable(vm, parser, &level);
+        var = njs_variable_add(vm, parser, NJS_VARIABLE_SHIM);
         if (nxt_slow_path(var == NULL)) {
             return NJS_TOKEN_ERROR;
         }
 
-        var->state = NJS_VARIABLE_DECLARED;
-
         token = njs_parser_token(parser);
         if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
             return token;
         }
 
-        index = var->index;
-        value = njs_variable_value(parser, index);
-
         function = njs_function_alloc(vm);
         if (nxt_slow_path(function == NULL)) {
             return NJS_TOKEN_ERROR;
         }
 
-        value->data.u.function = function;
-        value->type = NJS_FUNCTION;
-        value->data.truth = 1;
+        var->value.data.u.function = function;
+        var->value.type = NJS_FUNCTION;
+        var->value.data.truth = 1;
+
         lambda = function->u.lambda;
 
     } else {
@@ -416,14 +511,19 @@ njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser)
     node->u.value.data.u.lambda = lambda;
     lambda->u.parser = parser;
 
-    return njs_parser_function_lambda(vm, lambda, token);
+    token = njs_parser_function_lambda(vm, lambda, token);
+
+    njs_parser_scope_end(vm, parser);
+
+    vm->parser = parser->parent;
+
+    return token;
 }
 
 
 static njs_parser_t *
 njs_parser_function_create(njs_vm_t *vm, njs_parser_t *parent)
 {
-    nxt_array_t   *values, *arguments;
     njs_parser_t  *parser;
 
     parser = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_parser_t));
@@ -432,26 +532,10 @@ njs_parser_function_create(njs_vm_t *vm, njs_parser_t *parent)
     }
 
     parser->parent = parent;
+    parser->scope = parent->scope;
     parser->lexer = parent->lexer;
     vm->parser = parser;
 
-    arguments = nxt_array_create(4, sizeof(njs_variable_t),
-                                 &njs_array_mem_proto, vm->mem_cache_pool);
-    if (nxt_slow_path(arguments == NULL)) {
-        return NULL;
-    }
-
-    parser->arguments = arguments;
-
-    values = nxt_array_create(4, sizeof(njs_value_t), &njs_array_mem_proto,
-                              vm->mem_cache_pool);
-    if (nxt_slow_path(values == NULL)) {
-        return NULL;
-    }
-
-    parser->scope_values = values;
-    parser->scope = NJS_SCOPE_LOCAL;
-
     return parser;
 }
 
@@ -460,7 +544,7 @@ static njs_token_t
 njs_parser_function_lambda(njs_vm_t *vm, njs_function_lambda_t *lambda,
     njs_token_t token)
 {
-    nxt_str_t          *name;
+    njs_ret_t          ret;
     njs_index_t        index;
     njs_parser_t       *parser;
     njs_variable_t     *arg;
@@ -468,6 +552,11 @@ njs_parser_function_lambda(njs_vm_t *vm, njs_function_lambda_t *lambda,
 
     parser = lambda->u.parser;
 
+    ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_FUNCTION);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NJS_TOKEN_ERROR;
+    }
+
     token = njs_parser_match(vm, parser, token, NJS_TOKEN_OPEN_PARENTHESIS);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
         return token;
@@ -484,26 +573,19 @@ njs_parser_function_lambda(njs_vm_t *vm, njs_function_lambda_t *lambda,
             return NJS_TOKEN_ERROR;
         }
 
-        arg = nxt_array_add(parser->arguments, &njs_array_mem_proto,
-                            vm->mem_cache_pool);
+        arg = njs_variable_add(vm, parser, NJS_VARIABLE_VAR);
         if (nxt_slow_path(arg == NULL)) {
             return NJS_TOKEN_ERROR;
         }
 
-        name = &parser->lexer->text;
+        arg->index = index;
+        index += sizeof(njs_value_t);
 
-        arg->name_start = nxt_mem_cache_alloc(vm->mem_cache_pool, name->length);
-        if (nxt_slow_path(arg->name_start == NULL)) {
+        ret = njs_name_copy(vm, &arg->name, &parser->lexer->text);
+        if (nxt_slow_path(ret != NXT_OK)) {
             return NJS_TOKEN_ERROR;
         }
 
-        memcpy(arg->name_start, name->start, name->length);
-        arg->name_len = name->length;
-
-        arg->state = NJS_VARIABLE_DECLARED;
-        arg->index = index;
-        index += sizeof(njs_value_t);
-
         token = njs_parser_token(parser);
         if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
             return token;
@@ -528,7 +610,21 @@ njs_parser_function_lambda(njs_vm_t *vm, njs_function_lambda_t *lambda,
         return NJS_TOKEN_ERROR;
     }
 
-    token = njs_parser_block(vm, parser);
+    token = njs_parser_token(parser);
+    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+        return token;
+    }
+
+    parser->node = NULL;
+
+    while (token != NJS_TOKEN_CLOSE_BRACE) {
+        token = njs_parser_statement_chain(vm, parser, token);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+    }
+
+    token =  njs_parser_token(parser);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
         return token;
     }
@@ -574,7 +670,9 @@ njs_parser_function_lambda(njs_vm_t *vm, njs_function_lambda_t *lambda,
     }
 
     parser->parent->node->right = parser->node;
-    vm->parser = parser->parent;
+    parser->node->scope = parser->scope;
+
+    njs_parser_scope_end(vm, parser);
 
     return token;
 }
@@ -586,7 +684,7 @@ njs_parser_return_statement(njs_vm_t *vm, njs_parser_t *parser)
     njs_token_t        token;
     njs_parser_node_t  *node;
 
-    if (parser->scope == NJS_SCOPE_GLOBAL) {
+    if (parser->scope->type == NJS_SCOPE_GLOBAL) {
         nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
                   "SyntaxError: Illegal return statement");
 
@@ -641,11 +739,10 @@ njs_parser_return_statement(njs_vm_t *vm, njs_parser_t *parser)
 static njs_token_t
 njs_parser_var_statement(njs_vm_t *vm, njs_parser_t *parser)
 {
-    nxt_bool_t         first;
-    nxt_uint_t         level;
+    njs_ret_t          ret;
     njs_token_t        token;
     njs_variable_t     *var;
-    njs_parser_node_t  *left, *stmt, *name, *assign;
+    njs_parser_node_t  *left, *stmt, *name, *assign, *expr;
 
     parser->node = NULL;
     left = NULL;
@@ -660,20 +757,30 @@ njs_parser_var_statement(njs_vm_t *vm, njs_parser_t *parser)
             return NJS_TOKEN_ILLEGAL;
         }
 
-        var = njs_parser_variable(vm, parser, &level);
+        var = njs_variable_add(vm, parser, NJS_VARIABLE_VAR);
         if (nxt_slow_path(var == NULL)) {
             return NJS_TOKEN_ERROR;
         }
 
-        first = (var->state == NJS_VARIABLE_CREATED);
+        name = njs_parser_node_alloc(vm);
+        if (nxt_slow_path(name == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        name->token = NJS_TOKEN_NAME;
 
-        var->state = NJS_VARIABLE_DECLARED;
+        ret = njs_variable_reference(vm, parser, name);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NJS_TOKEN_ERROR;
+        }
 
         token = njs_parser_token(parser);
         if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
             return token;
         }
 
+        expr = NULL;
+
         if (token == NJS_TOKEN_ASSIGNMENT) {
 
             token = njs_parser_token(parser);
@@ -686,41 +793,31 @@ njs_parser_var_statement(njs_vm_t *vm, njs_parser_t *parser)
                 return token;
             }
 
-            name = njs_parser_node_alloc(vm);
-            if (nxt_slow_path(name == NULL)) {
-                return NJS_TOKEN_ERROR;
-            }
-
-            name->token = NJS_TOKEN_NAME;
-            name->u.variable = var;
-
-            if (first) {
-                name->state = NJS_VARIABLE_FIRST_ASSIGNMENT;
-            }
+            expr = parser->node;
+        }
 
-            assign = njs_parser_node_alloc(vm);
-            if (nxt_slow_path(assign == NULL)) {
-                return NJS_TOKEN_ERROR;
-            }
+        assign = njs_parser_node_alloc(vm);
+        if (nxt_slow_path(assign == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
 
-            assign->token = NJS_TOKEN_ASSIGNMENT;
-            assign->u.operation = njs_vmcode_move;
-            assign->left = name;
-            assign->right = parser->node;
+        assign->token = NJS_TOKEN_VAR;
+        assign->u.operation = njs_vmcode_move;
+        assign->left = name;
+        assign->right = expr;
 
-            stmt = njs_parser_node_alloc(vm);
-            if (nxt_slow_path(stmt == NULL)) {
-                return NJS_TOKEN_ERROR;
-            }
+        stmt = njs_parser_node_alloc(vm);
+        if (nxt_slow_path(stmt == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
 
-            stmt->token = NJS_TOKEN_STATEMENT;
-            stmt->left = left;
-            stmt->right = assign;
-            parser->node = stmt;
-            parser->code_size += sizeof(njs_vmcode_2addr_t);
+        stmt->token = NJS_TOKEN_STATEMENT;
+        stmt->left = left;
+        stmt->right = assign;
+        parser->node = stmt;
+        parser->code_size += sizeof(njs_vmcode_2addr_t);
 
-            left = stmt;
-        }
+        left = stmt;
 
     } while (token == NJS_TOKEN_COMMA);
 
@@ -829,6 +926,7 @@ njs_parser_switch_statement(njs_vm_t *vm, njs_parser_t *parser)
     }
 
     swtch->token = NJS_TOKEN_SWITCH;
+    swtch->scope = parser->scope;
     swtch->left = parser->node;
     last = &swtch->right;
 
@@ -1126,8 +1224,6 @@ njs_parser_for_in_statement(njs_vm_t *vm, njs_parser_t *parser, nxt_str_t *name,
         return NJS_TOKEN_ILLEGAL;
     }
 
-    node->u.variable->state = NJS_VARIABLE_DECLARED;
-
     node = njs_parser_node_alloc(vm);
     if (nxt_slow_path(node == NULL)) {
         return NJS_TOKEN_ERROR;
@@ -1227,7 +1323,7 @@ njs_parser_break_statement(njs_vm_t *vm, njs_parser_t *parser)
 static njs_token_t
 njs_parser_try_statement(njs_vm_t *vm, njs_parser_t *parser)
 {
-    nxt_uint_t         level;
+    njs_ret_t          ret;
     njs_token_t        token;
     njs_variable_t     *var;
     njs_parser_node_t  *node, *try, *catch;
@@ -1245,6 +1341,7 @@ njs_parser_try_statement(njs_vm_t *vm, njs_parser_t *parser)
     }
 
     try->token = NJS_TOKEN_TRY;
+    try->scope = parser->scope;
     try->left = parser->node;
     parser->code_size += sizeof(njs_vmcode_try_start_t)
                          + sizeof(njs_vmcode_try_end_t);
@@ -1272,12 +1369,19 @@ njs_parser_try_statement(njs_vm_t *vm, njs_parser_t *parser)
         catch->token = NJS_TOKEN_CATCH;
         try->right = catch;
 
-        var = njs_parser_variable(vm, parser, &level);
-        if (nxt_slow_path(var == NULL)) {
+        /*
+         * The "catch" clause creates a block scope for single variable
+         * which receives exception value.
+         */
+        ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_BLOCK);
+        if (nxt_slow_path(ret != NXT_OK)) {
             return NJS_TOKEN_ERROR;
         }
 
-        var->state = NJS_VARIABLE_DECLARED;
+        var = njs_variable_add(vm, parser, NJS_VARIABLE_LET);
+        if (nxt_slow_path(var == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
 
         node = njs_parser_node_alloc(vm);
         if (nxt_slow_path(node == NULL)) {
@@ -1285,7 +1389,11 @@ njs_parser_try_statement(njs_vm_t *vm, njs_parser_t *parser)
         }
 
         node->token = NJS_TOKEN_NAME;
-        node->u.variable = var;
+
+        ret = njs_variable_reference(vm, parser, node);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NJS_TOKEN_ERROR;
+        }
 
         catch->left = node;
 
@@ -1307,7 +1415,7 @@ njs_parser_try_statement(njs_vm_t *vm, njs_parser_t *parser)
 
         catch->right = parser->node;
 
-        /* TODO: remove variable from scope. */
+        njs_parser_scope_end(vm, parser);
     }
 
     if (token == NJS_TOKEN_FINALLY) {
@@ -1354,13 +1462,11 @@ njs_parser_try_block(njs_vm_t *vm, njs_parser_t *parser)
     njs_token_t  token;
 
     token = njs_parser_token(parser);
-
-    if (nxt_fast_path(token == NJS_TOKEN_OPEN_BRACE)) {
-        parser->node = NULL;
-        return njs_parser_block(vm, parser);
+    if (nxt_slow_path(token != NJS_TOKEN_OPEN_BRACE)) {
+        return NJS_TOKEN_ILLEGAL;
     }
 
-    return NJS_TOKEN_ILLEGAL;
+    return njs_parser_block_statement(vm, parser);
 }
 
 
@@ -1457,10 +1563,8 @@ njs_token_t
 njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
 {
     double             num;
-    nxt_int_t          ret;
-    nxt_uint_t         level;
+    njs_ret_t          ret;
     njs_extern_t       *ext;
-    njs_variable_t     *var;
     njs_parser_node_t  *node;
 
     if (token == NJS_TOKEN_OPEN_PARENTHESIS) {
@@ -1504,38 +1608,17 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
             break;
         }
 
-        var = njs_parser_variable(vm, parser, &level);
-        if (nxt_slow_path(var == NULL)) {
+        ret = njs_variable_reference(vm, parser, node);
+        if (nxt_slow_path(ret != NXT_OK)) {
             return NJS_TOKEN_ERROR;
         }
 
-        switch (var->state) {
-
-        case NJS_VARIABLE_CREATED:
-            var->state = NJS_VARIABLE_PENDING;
-            parser->code_size += sizeof(njs_vmcode_1addr_t);
-            break;
-
-        case NJS_VARIABLE_PENDING:
-            var->state = NJS_VARIABLE_USED;
-            parser->code_size += sizeof(njs_vmcode_1addr_t);
-            break;
-
-        case NJS_VARIABLE_USED:
-            parser->code_size += sizeof(njs_vmcode_1addr_t);
-            break;
-
-        case NJS_VARIABLE_SET:
-        case NJS_VARIABLE_DECLARED:
-            break;
-        }
-
         parser->code_size += sizeof(njs_vmcode_object_copy_t);
-        node->u.variable = var;
         break;
 
     case NJS_TOKEN_OPEN_BRACE:
         node->token = NJS_TOKEN_OBJECT;
+        node->scope = parser->scope;
 
         nxt_thread_log_debug("JS: OBJECT");
 
@@ -1555,6 +1638,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
 
     case NJS_TOKEN_OPEN_BRACKET:
         node->token = NJS_TOKEN_ARRAY;
+        node->scope = parser->scope;
 
         nxt_thread_log_debug("JS: ARRAY");
 
@@ -1581,6 +1665,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
         nxt_thread_log_debug("REGEX: '%V'", &parser->lexer->text);
 
         node->token = NJS_TOKEN_REGEXP;
+        node->scope = parser->scope;
         parser->code_size += sizeof(njs_vmcode_regexp_t);
 
         break;
@@ -1651,7 +1736,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
     case NJS_TOKEN_THIS:
         nxt_thread_log_debug("JS: this");
 
-        if (parser->scope != NJS_SCOPE_GLOBAL) {
+        if (parser->scope->type != NJS_SCOPE_GLOBAL) {
             node->index = NJS_INDEX_THIS;
             break;
         }
@@ -1721,24 +1806,27 @@ static njs_token_t
 njs_parser_builtin_object(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node)
 {
-    nxt_uint_t      index, level;
-    njs_value_t     *value;
+    njs_ret_t       ret;
+    nxt_uint_t      index;
     njs_variable_t  *var;
 
-    var = njs_parser_variable(vm, parser, &level);
+    var = njs_builtin_add(vm, parser);
     if (nxt_slow_path(var == NULL)) {
         return NJS_TOKEN_ERROR;
     }
 
-    var->state = NJS_VARIABLE_DECLARED;
-    node->index = var->index;
+    /* TODO: once */
+    index = node->token - NJS_TOKEN_FIRST_OBJECT;
+    var->value.data.u.object = &vm->shared->objects[index];
+    var->value.type = NJS_OBJECT;
+    var->value.data.truth = 1;
 
-    value = njs_variable_value(parser, node->index);
+    ret = njs_variable_reference(vm, parser, node);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NJS_TOKEN_ERROR;
+    }
 
-    index = node->token - NJS_TOKEN_FIRST_OBJECT;
-    value->data.u.object = &vm->shared->objects[index];
-    value->type = NJS_OBJECT;
-    value->data.truth = 1;
+    node->scope = parser->scope;
 
     parser->node = node;
     parser->code_size += sizeof(njs_vmcode_object_copy_t);
@@ -1751,24 +1839,27 @@ static njs_token_t
 njs_parser_builtin_function(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node)
 {
-    nxt_uint_t      index, level;
-    njs_value_t     *value;
+    njs_ret_t       ret;
+    nxt_uint_t      index;
     njs_variable_t  *var;
 
-    var = njs_parser_variable(vm, parser, &level);
+    var = njs_builtin_add(vm, parser);
     if (nxt_slow_path(var == NULL)) {
         return NJS_TOKEN_ERROR;
     }
 
-    var->state = NJS_VARIABLE_DECLARED;
-    node->index = var->index;
+    /* TODO: once */
+    index = node->token - NJS_TOKEN_FIRST_FUNCTION;
+    var->value.data.u.function = &vm->shared->functions[index];
+    var->value.type = NJS_FUNCTION;
+    var->value.data.truth = 1;
 
-    value = njs_variable_value(parser, node->index);
+    ret = njs_variable_reference(vm, parser, node);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NJS_TOKEN_ERROR;
+    }
 
-    index = node->token - NJS_TOKEN_FIRST_FUNCTION;
-    value->data.u.function = &vm->shared->functions[index];
-    value->type = NJS_FUNCTION;
-    value->data.truth = 1;
+    node->scope = parser->scope;
 
     parser->node = node;
     parser->code_size += sizeof(njs_vmcode_object_copy_t);
index 902124aba3d89ef57b58365ce2994189af04d1b6..7a87bf2c288c7f354f6225772da5727d36bba15d 100644 (file)
@@ -213,38 +213,51 @@ typedef struct {
 } njs_lexer_t;
 
 
-typedef enum {
-    NJS_VARIABLE_NORMAL = 0,
-    NJS_VARIABLE_FIRST_ASSIGNMENT,
-    NJS_VARIABLE_ASSIGNMENT,
-    NJS_VARIABLE_TYPEOF,
-} njs_variable_node_state_t;
-
-
 #define njs_parser_is_lvalue(node)                                            \
     ((node)->token == NJS_TOKEN_NAME || (node)->token == NJS_TOKEN_PROPERTY)
 
 
+typedef struct njs_parser_scope_s   njs_parser_scope_t;
+
+struct njs_parser_scope_s {
+    nxt_array_t                     *values;   /* Array of njs_value_t. */
+
+    nxt_lvlhsh_t                    variables;
+    njs_parser_scope_t              *parent;
+    njs_index_t                     next_index;
+    uint32_t                        inclusive;
+    njs_scope_t                     type:8;
+};
+
+
 typedef struct njs_parser_node_s    njs_parser_node_t;
 
 struct njs_parser_node_s {
     njs_token_t                     token:16;
-    njs_variable_node_state_t       state:2;    /* 2 bits */
     uint8_t                         ctor:1;     /* 1 bit  */
     uint8_t                         temporary;  /* 1 bit  */
     uint32_t                        token_line;
+    uint32_t                        variable_name_hash;
 
     union {
         uint32_t                    length;
-        njs_vmcode_operation_t      operation;
+        nxt_str_t                   variable_name;
         njs_value_t                 value;
-        njs_variable_t              *variable;
+        njs_vmcode_operation_t      operation;
         njs_parser_node_t           *object;
         njs_extern_t                *external;
     } u;
 
     njs_index_t                     index;
 
+    /*
+     * The scope points to
+     *   in global and function node: global or function scopes;
+     *   in variable node: a scope where variable was referenced;
+     *   in operation node: a scope to allocate indexes for temporary values.
+     */
+    njs_parser_scope_t              *scope;
+
     njs_parser_node_t               *left;
     njs_parser_node_t               *right;
     njs_parser_node_t               *dest;
@@ -275,6 +288,7 @@ typedef enum {
     NJS_PARSER_SWITCH,
 } njs_parser_block_type_t;
 
+
 typedef struct njs_parser_block_s   njs_parser_block_t;
 
 struct njs_parser_block_s {
@@ -290,18 +304,13 @@ struct njs_parser_s {
     njs_lexer_t                     *lexer;
     njs_parser_node_t               *node;
 
-    /* Vector of njs_variable_t. */
-    nxt_array_t                     *arguments;
     njs_parser_block_t              *block;
 
-    nxt_lvlhsh_t                    variables_hash;
+    njs_parser_scope_t              *scope;
 
     nxt_array_t                     *index_cache;
     njs_index_t                     index[NJS_SCOPES - NJS_INDEX_CACHE];
 
-    nxt_array_t                     *scope_values;
-
-    uint8_t                         scope;        /* 4 bits */
     uint8_t                         branch;       /* 1 bit */
 
     /* Parsing Function() or eval(). */
@@ -346,6 +355,12 @@ njs_token_t njs_parser_property_token(njs_parser_t *parser);
 njs_token_t njs_parser_token(njs_parser_t *parser);
 nxt_int_t njs_parser_string_create(njs_vm_t *vm, njs_value_t *value);
 njs_index_t njs_parser_index(njs_parser_t *parser, uint32_t scope);
+njs_ret_t njs_variable_reference(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node);
+njs_variable_t *njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node,
+    njs_name_reference_t reference);
+njs_index_t njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node,
+    njs_name_reference_t reference);
 nxt_bool_t njs_parser_has_side_effect(njs_parser_node_t *node);
 u_char *njs_parser_trace_handler(nxt_trace_t *trace, nxt_trace_data_t *td,
     u_char *start);
index 64b1fe77cdb9fc29fe60b04edcf82a9e5adba9cf..2b6bd1864d7a4db00a0464ffd0dd06297ba59276 100644 (file)
@@ -260,7 +260,7 @@ njs_token_t
 njs_parser_var_expression(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
 {
     size_t                  size;
-    njs_parser_node_t       *node, *pending;
+    njs_parser_node_t       *node;
     njs_vmcode_operation_t  operation;
 
     token = njs_parser_conditional_expression(vm, parser, token);
@@ -297,22 +297,12 @@ njs_parser_var_expression(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
 
         node = parser->node;
 
-        if (parser->node->token != NJS_TOKEN_NAME) {
+        if (!njs_parser_is_lvalue(parser->node)) {
             nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
                       "ReferenceError: Invalid left-hand side in assignment");
             return NJS_TOKEN_ILLEGAL;
         }
 
-        pending = NULL;
-
-        if (node->token == NJS_TOKEN_NAME) {
-            node->state = NJS_VARIABLE_ASSIGNMENT;
-
-            if (node->u.variable->state == NJS_VARIABLE_PENDING) {
-                pending = node;
-            }
-        }
-
         node = njs_parser_node_alloc(vm);
         if (nxt_slow_path(node == NULL)) {
             return NJS_TOKEN_ERROR;
@@ -320,6 +310,7 @@ njs_parser_var_expression(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
 
         node->token = token;
         node->u.operation = operation;
+        node->scope = parser->scope;
         node->left = parser->node;
         parser->code_size += size;
 
@@ -335,17 +326,6 @@ njs_parser_var_expression(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
 
         node->right = parser->node;
         parser->node = node;
-
-        if (pending != NULL
-            && pending->u.variable->state == NJS_VARIABLE_PENDING)
-        {
-            pending->u.variable->state = NJS_VARIABLE_SET;
-            parser->code_size -= sizeof(njs_vmcode_1addr_t);
-
-            if (!parser->branch) {
-                pending->state = NJS_VARIABLE_FIRST_ASSIGNMENT;
-            }
-        }
     }
 }
 
@@ -363,7 +343,7 @@ njs_parser_assignment_expression(njs_vm_t *vm, njs_parser_t *parser,
     njs_token_t token)
 {
     size_t                  size;
-    njs_parser_node_t       *node, *pending;
+    njs_parser_node_t       *node;
     njs_vmcode_operation_t  operation;
 
     token = njs_parser_conditional_expression(vm, parser, token);
@@ -463,22 +443,6 @@ njs_parser_assignment_expression(njs_vm_t *vm, njs_parser_t *parser,
             return NJS_TOKEN_ILLEGAL;
         }
 
-        pending = NULL;
-
-        if (node->token == NJS_TOKEN_NAME) {
-
-            if (token == NJS_TOKEN_ASSIGNMENT) {
-                node->state = NJS_VARIABLE_ASSIGNMENT;
-
-                if (node->u.variable->state == NJS_VARIABLE_PENDING) {
-                    pending = node;
-                }
-
-            } else if (node->u.variable->state == NJS_VARIABLE_PENDING) {
-                node->u.variable->state = NJS_VARIABLE_USED;
-            }
-        }
-
         node = njs_parser_node_alloc(vm);
         if (nxt_slow_path(node == NULL)) {
             return NJS_TOKEN_ERROR;
@@ -486,6 +450,7 @@ njs_parser_assignment_expression(njs_vm_t *vm, njs_parser_t *parser,
 
         node->token = token;
         node->u.operation = operation;
+        node->scope = parser->scope;
         node->left = parser->node;
 
         token = njs_parser_token(parser);
@@ -531,16 +496,6 @@ njs_parser_assignment_expression(njs_vm_t *vm, njs_parser_t *parser,
         }
 
         parser->code_size += size;
-
-        if (pending != NULL
-            && pending->u.variable->state == NJS_VARIABLE_PENDING)
-        {
-            pending->u.variable->state = NJS_VARIABLE_SET;
-
-            if (!parser->branch) {
-                pending->state = NJS_VARIABLE_FIRST_ASSIGNMENT;
-            }
-        }
     }
 }
 
@@ -576,6 +531,7 @@ njs_parser_conditional_expression(njs_vm_t *vm, njs_parser_t *parser,
         }
 
         cond->token = NJS_TOKEN_CONDITIONAL;
+        cond->scope = parser->scope;
         cond->left = parser->node;
 
         node = njs_parser_node_alloc(vm);
@@ -670,6 +626,7 @@ njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser,
 
         node->token = token;
         node->u.operation = op->operation;
+        node->scope = parser->scope;
         node->left = parser->node;
         node->left->dest = node;
 
@@ -713,6 +670,7 @@ njs_parser_exponential_expression(njs_vm_t *vm, njs_parser_t *parser,
 
             node->token = token;
             node->u.operation = njs_vmcode_exponentiation;
+            node->scope = parser->scope;
             node->left = parser->node;
             node->left->dest = node;
 
@@ -856,10 +814,6 @@ njs_parser_unary_expression(njs_vm_t *vm, njs_parser_t *parser,
         }
     }
 
-    if (token == NJS_TOKEN_TYPEOF && parser->node->token == NJS_TOKEN_NAME) {
-        parser->node->state = NJS_VARIABLE_TYPEOF;
-    }
-
     node = njs_parser_node_alloc(vm);
     if (nxt_slow_path(node == NULL)) {
         return NJS_TOKEN_ERROR;
@@ -867,6 +821,7 @@ njs_parser_unary_expression(njs_vm_t *vm, njs_parser_t *parser,
 
     node->token = token;
     node->u.operation = operation;
+    node->scope = parser->scope;
     node->left = parser->node;
     node->left->dest = node;
     parser->node = node;
@@ -921,6 +876,7 @@ njs_parser_inc_dec_expression(njs_vm_t *vm, njs_parser_t *parser,
 
     node->token = token;
     node->u.operation = operation;
+    node->scope = parser->scope;
     node->left = parser->node;
     parser->node = node;
 
@@ -975,6 +931,7 @@ njs_parser_post_inc_dec_expression(njs_vm_t *vm, njs_parser_t *parser,
 
     node->token = token;
     node->u.operation = operation;
+    node->scope = parser->scope;
     node->left = parser->node;
     parser->node = node;
 
@@ -1022,6 +979,7 @@ njs_parser_call_expression(njs_vm_t *vm, njs_parser_t *parser,
         case NJS_TOKEN_NAME:
             func = node;
             func->token = NJS_TOKEN_FUNCTION_CALL;
+            func->scope = parser->scope;
             parser->code_size += sizeof(njs_vmcode_function_frame_t)
                                  + sizeof(njs_vmcode_function_call_t);
             break;
@@ -1033,6 +991,7 @@ njs_parser_call_expression(njs_vm_t *vm, njs_parser_t *parser,
             }
 
             func->token = NJS_TOKEN_METHOD_CALL;
+            func->scope = parser->scope;
             func->left = node;
             parser->code_size += sizeof(njs_vmcode_method_frame_t)
                                  + sizeof(njs_vmcode_function_call_t);
@@ -1059,6 +1018,7 @@ njs_parser_call_expression(njs_vm_t *vm, njs_parser_t *parser,
             }
 
             func->token = NJS_TOKEN_FUNCTION_CALL;
+            func->scope = parser->scope;
             func->left = node;
             parser->code_size += sizeof(njs_vmcode_function_frame_t)
                                  + sizeof(njs_vmcode_function_call_t);
@@ -1127,6 +1087,7 @@ njs_parser_new_expression(njs_vm_t *vm, njs_parser_t *parser,
         }
 
         func->token = NJS_TOKEN_METHOD_CALL;
+        func->scope = parser->scope;
         func->left = node;
         parser->code_size += sizeof(njs_vmcode_method_frame_t)
                              + sizeof(njs_vmcode_function_call_t);
@@ -1153,6 +1114,7 @@ njs_parser_new_expression(njs_vm_t *vm, njs_parser_t *parser,
         }
 
         func->token = NJS_TOKEN_FUNCTION_CALL;
+        func->scope = parser->scope;
         func->left = node;
         parser->code_size += sizeof(njs_vmcode_function_frame_t)
                              + sizeof(njs_vmcode_function_call_t);
@@ -1197,6 +1159,7 @@ njs_parser_property_expression(njs_vm_t *vm, njs_parser_t *parser,
 
         node->token = NJS_TOKEN_PROPERTY;
         node->u.operation = njs_vmcode_property_get;
+        node->scope = parser->scope;
         node->left = parser->node;
 
         if (token == NJS_TOKEN_DOT) {
index 971d078d09993e2bc73919f373c8e327ec3c1778..0a61f26ee2f9dfea8253ad2b58d9f07d9bb89db0 100644 (file)
@@ -23,8 +23,8 @@
 #include <string.h>
 
 
-static njs_variable_t *njs_variable_alloc(njs_vm_t *vm,
-    njs_parser_t *parser, nxt_str_t *name);
+static njs_variable_t *njs_variable_alloc(njs_vm_t *vm, nxt_str_t *name,
+    njs_variable_type_t type);
 
 
 static nxt_int_t
@@ -34,9 +34,7 @@ njs_variables_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
 
     var = data;
 
-    if (lhq->key.length == var->name_len
-        && memcmp(var->name_start, lhq->key.start, lhq->key.length) == 0)
-    {
+    if (nxt_strstr_eq(&lhq->key, &var->name)) {
         return NXT_OK;
     }
 
@@ -56,86 +54,184 @@ static const nxt_lvlhsh_proto_t  njs_variables_hash_proto
 
 
 njs_variable_t *
-njs_parser_name_alloc(njs_vm_t *vm, njs_parser_t *parser)
+njs_builtin_add(njs_vm_t *vm, njs_parser_t *parser)
 {
     nxt_int_t           ret;
     njs_variable_t      *var;
+    njs_parser_scope_t  *scope;
     nxt_lvlhsh_query_t  lhq;
 
-    var = njs_variable_alloc(vm, parser, &parser->lexer->text);
+    lhq.key_hash = parser->lexer->key_hash;
+    lhq.key = parser->lexer->text;
+    lhq.proto = &njs_variables_hash_proto;
+
+    scope = parser->scope;
+
+    while (scope->type != NJS_SCOPE_GLOBAL) {
+        scope = scope->parent;
+    }
+
+    if (nxt_lvlhsh_find(&scope->variables, &lhq) == NXT_OK) {
+        var = lhq.value;
+
+        return var;
+    }
+
+    var = njs_variable_alloc(vm, &lhq.key, NJS_VARIABLE_VAR);
     if (nxt_slow_path(var == NULL)) {
-        return NULL;
+        return var;
     }
 
-    lhq.key_hash = parser->lexer->key_hash;
-    lhq.key = parser->lexer->text;
     lhq.replace = 0;
     lhq.value = var;
-    lhq.proto = &njs_variables_hash_proto;
     lhq.pool = vm->mem_cache_pool;
 
-    ret = nxt_lvlhsh_insert(&parser->variables_hash, &lhq);
+    ret = nxt_lvlhsh_insert(&scope->variables, &lhq);
 
     if (nxt_fast_path(ret == NXT_OK)) {
         return var;
     }
 
-    nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-              "SyntaxError: Duplicate declaration \"%.*s\"",
-              (int) parser->lexer->text.length, parser->lexer->text.start);
+    nxt_mem_cache_free(vm->mem_cache_pool, var->name.start);
+    nxt_mem_cache_free(vm->mem_cache_pool, var);
 
     return NULL;
 }
 
 
 njs_variable_t *
-njs_parser_variable(njs_vm_t *vm, njs_parser_t *parser, nxt_uint_t *level)
+njs_variable_add(njs_vm_t *vm, njs_parser_t *parser, njs_variable_type_t type)
 {
     nxt_int_t           ret;
-    nxt_uint_t          n;
-    njs_parser_t        *scope;
     njs_variable_t      *var;
+    njs_parser_scope_t  *scope;
     nxt_lvlhsh_query_t  lhq;
 
-    *level = 0;
-
     lhq.key_hash = parser->lexer->key_hash;
     lhq.key = parser->lexer->text;
     lhq.proto = &njs_variables_hash_proto;
 
-    scope = parser;
+    scope = parser->scope;
 
-    do {
-        var = scope->arguments->start;
-        n = scope->arguments->items;
+    if (type >= NJS_VARIABLE_VAR) {
+        /*
+         * A "var" and "function" declarations are
+         * stored in function or global scope.
+         */
+        while (scope->type == NJS_SCOPE_BLOCK) {
+            scope = scope->parent;
+        }
+    }
+
+    var = njs_variable_alloc(vm, &lhq.key, type);
+    if (nxt_slow_path(var == NULL)) {
+        return var;
+    }
+
+    lhq.replace = 0;
+    lhq.value = var;
+    lhq.pool = vm->mem_cache_pool;
+
+    ret = nxt_lvlhsh_insert(&scope->variables, &lhq);
+
+    if (nxt_fast_path(ret == NXT_OK)) {
+        return var;
+    }
+
+    nxt_mem_cache_free(vm->mem_cache_pool, var->name.start);
+    nxt_mem_cache_free(vm->mem_cache_pool, var);
+
+    if (ret == NXT_ERROR) {
+        return NULL;
+    }
+
+    /* ret == NXT_DECLINED. */
+
+    nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
+              "SyntaxError: Identifier \"%.*s\" has already been declared",
+              (int) lhq.key.length, lhq.key.start);
 
-        while (n != 0) {
-            if (lhq.key.length == var->name_len
-                && memcmp(var->name_start, lhq.key.start, lhq.key.length) == 0)
-            {
-                return var;
+    return NULL;
+}
+
+
+njs_ret_t
+njs_variable_reference(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node)
+{
+    njs_ret_t  ret;
+
+    ret = njs_name_copy(vm, &node->u.variable_name, &parser->lexer->text);
+
+    if (nxt_fast_path(ret == NXT_OK)) {
+        node->variable_name_hash = parser->lexer->key_hash;
+        node->scope = parser->scope;
+    }
+
+    return ret;
+}
+
+
+njs_variable_t *
+njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node,
+    njs_name_reference_t reference)
+{
+    nxt_int_t           ret;
+    nxt_array_t         *values;
+    njs_index_t         index;
+    njs_value_t         *value;
+    njs_variable_t      *var;
+    njs_parser_scope_t  *scope, *parent, *inclusive;
+    nxt_lvlhsh_query_t  lhq;
+    const njs_value_t   *initial;
+
+    lhq.key_hash = node->variable_name_hash;
+    lhq.key = node->u.variable_name;
+    lhq.proto = &njs_variables_hash_proto;
+
+    inclusive = NULL;
+    scope = node->scope;
+
+    for ( ;; ) {
+        if (nxt_lvlhsh_find(&scope->variables, &lhq) == NXT_OK) {
+            var = lhq.value;
+
+            if (scope->type == NJS_SCOPE_SHIM) {
+                scope = inclusive;
+
+            } 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;
+                }
             }
 
-            var++;
-            n--;
-        }
+            initial = &njs_value_void;
 
-        if (nxt_lvlhsh_find(&scope->variables_hash, &lhq) == NXT_OK) {
-            return lhq.value;
+            goto found;
         }
 
-        scope = scope->parent;
-        (*level)++;
+        parent = scope->parent;
 
-    } while (scope != NULL);
+        if (parent == NULL) {
+            /* A global scope. */
+            break;
+        }
 
-    *level = 0;
+        inclusive = scope;
+        scope = parent;
+    }
 
-    if (nxt_lvlhsh_find(&vm->variables_hash, &lhq) == NXT_OK) {
-        return lhq.value;
+    if (reference != NJS_NAME_TYPEOF) {
+        goto not_found;
     }
 
-    var = njs_variable_alloc(vm, parser, &parser->lexer->text);
+    /* Add variable referenced by typeof to the global scope. */
+
+    var = njs_variable_alloc(vm, &lhq.key, NJS_VARIABLE_TYPEOF);
     if (nxt_slow_path(var == NULL)) {
         return NULL;
     }
@@ -144,16 +240,130 @@ njs_parser_variable(njs_vm_t *vm, njs_parser_t *parser, nxt_uint_t *level)
     lhq.value = var;
     lhq.pool = vm->mem_cache_pool;
 
-    ret = nxt_lvlhsh_insert(&parser->variables_hash, &lhq);
+    ret = nxt_lvlhsh_insert(&scope->variables, &lhq);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NULL;
+    }
+
+    initial = &njs_value_invalid;
+
+found:
+
+    if (reference == NJS_NAME_REFERENCE && var->type == NJS_VARIABLE_TYPEOF) {
+        goto not_found;
+    }
+
+    index = var->index;
+
+    if (index != NJS_INDEX_NONE) {
+        node->index = index;
+        return var;
+    }
+
+    if (reference == NJS_NAME_REFERENCE && var->type <= NJS_VARIABLE_LET) {
+        goto not_found;
+    }
+
+    values = scope->values;
+
+    if (values == NULL) {
+        values = nxt_array_create(4, sizeof(njs_value_t), &njs_array_mem_proto,
+                                  vm->mem_cache_pool);
+        if (nxt_slow_path(values == NULL)) {
+            return NULL;
+        }
+
+        scope->values = values;
+    }
+
+    value = nxt_array_add(values, &njs_array_mem_proto, vm->mem_cache_pool);
+    if (nxt_slow_path(value == NULL)) {
+        return NULL;
+    }
+
+    if (njs_is_object(&var->value)) {
+        *value = var->value;
+
+    } else {
+        *value = *initial;
+    }
+
+    index = scope->next_index;
+    scope->next_index += sizeof(njs_value_t);
+
+    var->index = index;
+    node->index = index;
+
+    return var;
+
+not_found:
+
+    nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
+              "ReferenceError: \"%.*s\" is not defined",
+              (int) lhq.key.length, lhq.key.start);
+
+    return NULL;
+}
+
+
+njs_index_t
+njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node,
+    njs_name_reference_t reference)
+{
+    njs_variable_t  *var;
+
+    var = njs_variable_get(vm, node, reference);
+
+    if (nxt_fast_path(var != NULL)) {
+        return var->index;
+    }
+
+    return NJS_INDEX_ERROR;
+}
+
+
+static njs_variable_t *
+njs_variable_alloc(njs_vm_t *vm, nxt_str_t *name, njs_variable_type_t type)
+{
+    njs_ret_t       ret;
+    njs_variable_t  *var;
+
+    var = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_variable_t));
+    if (nxt_slow_path(var == NULL)) {
+        return NULL;
+    }
+
+    var->type = type;
+
+    ret = njs_name_copy(vm, &var->name, name);
 
     if (nxt_fast_path(ret == NXT_OK)) {
         return var;
     }
 
+    nxt_mem_cache_free(vm->mem_cache_pool, var);
+
     return NULL;
 }
 
 
+njs_ret_t
+njs_name_copy(njs_vm_t *vm, nxt_str_t *dst, nxt_str_t *src)
+{
+    dst->length = src->length;
+
+    dst->start = nxt_mem_cache_alloc(vm->mem_cache_pool, src->length);
+
+    if (nxt_slow_path(dst->start != NULL)) {
+        (void) memcpy(dst->start, src->start, src->length);
+
+        return NXT_OK;
+    }
+
+    return NXT_ERROR;
+}
+
+
 nxt_str_t *
 njs_vm_export_functions(njs_vm_t *vm)
 {
@@ -200,8 +410,7 @@ njs_vm_export_functions(njs_vm_t *vm)
         value = njs_global_variable_value(vm, var);
 
         if (njs_is_function(value) && !value->data.u.function->native) {
-            ex->length = var->name_len;
-            ex->start = var->name_start;
+            *ex = var->name;
             ex++;
         }
     }
@@ -239,44 +448,3 @@ njs_vm_function(njs_vm_t *vm, nxt_str_t *name)
 
     return NULL;
 }
-
-
-static njs_variable_t *
-njs_variable_alloc(njs_vm_t *vm, njs_parser_t *parser, nxt_str_t *name)
-{
-    njs_value_t     *value;
-    njs_variable_t  *var;
-
-    var = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_variable_t));
-
-    if (nxt_fast_path(var != NULL)) {
-        var->name_start = nxt_mem_cache_alloc(vm->mem_cache_pool, name->length);
-
-        if (nxt_fast_path(var->name_start != NULL)) {
-
-            memcpy(var->name_start, name->start, name->length);
-            var->name_len = name->length;
-
-            value = nxt_array_add(parser->scope_values, &njs_array_mem_proto,
-                                  vm->mem_cache_pool);
-            if (nxt_fast_path(value != NULL)) {
-                 *value = njs_value_void;
-                 var->index = njs_parser_index(parser, parser->scope);
-                 return var;
-            }
-        }
-    }
-
-    return NULL;
-}
-
-
-njs_value_t *
-njs_variable_value(njs_parser_t *parser, njs_index_t index)
-{
-    u_char  *scope;
-
-    scope = parser->scope_values->start;
-
-    return (njs_value_t *) (scope + (njs_offset(index) - parser->scope_offset));
-}
index 7fdd30671409890382ccc2587b319873115fe273..60294b37c5a103f404a4971c2a392f530fe958d4 100644 (file)
@@ -9,21 +9,28 @@
 
 
 typedef enum {
-    NJS_VARIABLE_CREATED = 0,
-    NJS_VARIABLE_PENDING,
-    NJS_VARIABLE_USED,
-    NJS_VARIABLE_SET,
-    NJS_VARIABLE_DECLARED,
-} njs_variable_state_t;
+    NJS_VARIABLE_CONST = 0,
+    NJS_VARIABLE_LET,
+    NJS_VARIABLE_TYPEOF,
+    NJS_VARIABLE_SHIM,
+    NJS_VARIABLE_VAR,
+    NJS_VARIABLE_FUNCTION,
+} njs_variable_type_t;
+
+
+typedef enum {
+    NJS_NAME_REFERENCE = 0,
+    NJS_NAME_DECLARATION,
+    NJS_NAME_TYPEOF,
+} njs_name_reference_t;
 
 
 typedef struct {
-    u_char                *name_start;
-    uint16_t              name_len;
-    njs_variable_state_t  state:8;   /* 3 bits */
-    uint8_t               function;  /* 1 bit */
+    nxt_str_t             name;
+    njs_variable_type_t   type:8;    /* 3 bits */
 
     njs_index_t           index;
+    njs_value_t           value;
 } njs_variable_t;
 
 
@@ -32,11 +39,11 @@ typedef struct {
                       + njs_offset((var)->index) - NJS_INDEX_GLOBAL_OFFSET)
 
 
+njs_variable_t *njs_builtin_add(njs_vm_t *vm, njs_parser_t *parser);
+njs_variable_t *njs_variable_add(njs_vm_t *vm, njs_parser_t *parser,
+    njs_variable_type_t type);
+njs_ret_t njs_name_copy(njs_vm_t *vm, nxt_str_t *dst, nxt_str_t *src);
 
-njs_variable_t *njs_parser_name_alloc(njs_vm_t *vm, njs_parser_t *parser);
-njs_variable_t *njs_parser_variable(njs_vm_t *vm, njs_parser_t *parser,
-    nxt_uint_t *level);
-njs_value_t *njs_variable_value(njs_parser_t *parser, njs_index_t index);
 nxt_str_t *njs_vm_export_functions(njs_vm_t *vm);
 
 
index d8a5236e4163d58cc05398937011035b1301fa71..0257926ba9ba51aa39ed71808a18cf09220c85e2 100644 (file)
@@ -120,6 +120,7 @@ const njs_value_t  njs_value_false =        njs_value(NJS_BOOLEAN, 0, 0.0);
 const njs_value_t  njs_value_true =         njs_value(NJS_BOOLEAN, 1, 1.0);
 const njs_value_t  njs_value_zero =         njs_value(NJS_NUMBER, 0, 0.0);
 const njs_value_t  njs_value_nan =          njs_value(NJS_NUMBER, 0, NAN);
+const njs_value_t  njs_value_invalid =      njs_value(NJS_INVALID, 0, 0.0);
 
 
 const njs_value_t  njs_string_empty =       njs_string("");
@@ -271,10 +272,10 @@ start:
 
             vm->frame = previous;
 
-            /* GC: NJS_SCOPE_ARGUMENTS and NJS_SCOPE_LOCAL. */
+            /* GC: NJS_SCOPE_ARGUMENTS and NJS_SCOPE_FUNCTION. */
 
             vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = previous->arguments;
-            vm->scopes[NJS_SCOPE_LOCAL] = frame->prev_local;
+            vm->scopes[NJS_SCOPE_FUNCTION] = frame->prev_local;
             vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->prev_arguments;
 
             if (frame->native.size != 0) {
@@ -2097,23 +2098,6 @@ njs_vmcode_move(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
 }
 
 
-njs_ret_t
-njs_vmcode_validate(njs_vm_t *vm, njs_value_t *invld, njs_value_t *index)
-{
-    njs_value_t  *value;
-
-    value = njs_vmcode_operand(vm, index);
-
-    if (nxt_fast_path(njs_is_valid(value))) {
-        return sizeof(njs_vmcode_validate_t);
-    }
-
-    vm->exception = &njs_exception_reference_error;
-
-    return NXT_ERROR;
-}
-
-
 njs_ret_t
 njs_vmcode_jump(njs_vm_t *vm, njs_value_t *invld, njs_value_t *offset)
 {
@@ -2566,7 +2550,7 @@ njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
     vm->frame = previous;
 
     vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = previous->arguments;
-    vm->scopes[NJS_SCOPE_LOCAL] = frame->prev_local;
+    vm->scopes[NJS_SCOPE_FUNCTION] = frame->prev_local;
     args = vm->scopes[NJS_SCOPE_ARGUMENTS];
     vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->prev_arguments;
 
index ba65a43c62645a7518c9c2975b96738cd0df3941..772526af23f96ee0fc22731463aa99397f12c969 100644 (file)
@@ -489,12 +489,6 @@ typedef struct {
 } njs_vmcode_3addr_t;
 
 
-typedef struct {
-    njs_vmcode_t               code;
-    njs_index_t                index;
-} njs_vmcode_validate_t;
-
-
 typedef struct {
     njs_vmcode_t               code;
     njs_index_t                dst;
@@ -673,14 +667,20 @@ typedef struct {
 
 typedef enum {
     NJS_SCOPE_ABSOLUTE = 0,
-    NJS_SCOPE_LOCAL,
     NJS_SCOPE_GLOBAL,
+    NJS_SCOPE_FUNCTION,
     NJS_SCOPE_CALLEE_ARGUMENTS,
     NJS_SCOPE_ARGUMENTS,
     NJS_SCOPE_CLOSURE,
     NJS_SCOPE_PARENT_LOCAL,
     NJS_SCOPE_PARENT_ARGUMENTS,
     NJS_SCOPE_PARENT_CLOSURE,
+    /*
+     * The block and shim scopes are not really VM scopes.
+     * They used only on parsing phase.
+     */
+    NJS_SCOPE_BLOCK = 16,
+    NJS_SCOPE_SHIM = 17,
 } njs_scope_t;
 
 
@@ -689,7 +689,7 @@ typedef enum {
 #define NJS_SCOPE_SHIFT        4
 #define NJS_SCOPE_MASK         ((uintptr_t) ((1 << NJS_SCOPE_SHIFT) - 1))
 
-#define NJS_INDEX_CACHE        NJS_SCOPE_LOCAL
+#define NJS_INDEX_CACHE        NJS_SCOPE_GLOBAL
 
 #define NJS_INDEX_NONE         ((njs_index_t) 0)
 #define NJS_INDEX_ERROR        ((njs_index_t) -1)
@@ -969,8 +969,6 @@ njs_ret_t njs_vmcode_strict_not_equal(njs_vm_t *vm, njs_value_t *val1,
     njs_value_t *val2);
 
 njs_ret_t njs_vmcode_move(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld);
-njs_ret_t njs_vmcode_validate(njs_vm_t *vm, njs_value_t *invld,
-    njs_value_t *index);
 
 njs_ret_t njs_vmcode_jump(njs_vm_t *vm, njs_value_t *invld,
     njs_value_t *offset);
@@ -1032,6 +1030,7 @@ extern const njs_value_t  njs_value_false;
 extern const njs_value_t  njs_value_true;
 extern const njs_value_t  njs_value_zero;
 extern const njs_value_t  njs_value_nan;
+extern const njs_value_t  njs_value_invalid;
 
 extern const njs_value_t  njs_string_empty;
 extern const njs_value_t  njs_string_comma;
index 049fbce22ac6dfdccc509e894452a2afe2c6ee67..a550118254ef861be61145b9d66d47d16b4fd2e7 100644 (file)
@@ -189,7 +189,6 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end,
 {
     nxt_int_t          ret;
     njs_lexer_t        *lexer;
-    njs_value_t        *value;
     njs_parser_t       *parser;
     njs_parser_node_t  *node;
 
@@ -212,25 +211,9 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end,
     lexer->keywords_hash = vm->shared->keywords_hash;
 
     parser->code_size = sizeof(njs_vmcode_stop_t);
-    parser->scope = NJS_SCOPE_GLOBAL;
     parser->scope_offset = NJS_INDEX_GLOBAL_OFFSET;
     parser->index[NJS_SCOPE_GLOBAL - NJS_INDEX_CACHE] = NJS_INDEX_GLOBAL_OFFSET;
 
-    parser->scope_values = nxt_array_create(4, sizeof(njs_value_t),
-                                            &njs_array_mem_proto,
-                                            vm->mem_cache_pool);
-    if (nxt_slow_path(parser->scope_values == NULL)) {
-        return NJS_ERROR;
-    }
-
-    /* Empty array to minimize tests in njs_parser_variable(). */
-    parser->arguments = nxt_array_create(0, sizeof(njs_variable_t),
-                                         &njs_array_mem_proto,
-                                         vm->mem_cache_pool);
-    if (nxt_slow_path(parser->arguments == NULL)) {
-        return NJS_TOKEN_ERROR;
-    }
-
     node = njs_parser(vm, parser);
     if (nxt_slow_path(node == NULL)) {
         return NJS_ERROR;
@@ -238,8 +221,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end,
 
     if (function != NULL) {
         if (node->token == NJS_TOKEN_CALL) {
-            value = njs_variable_value(parser, node->right->index);
-            *function = value->data.u.function;
+            *function = node->right->u.value.data.u.function;
 
         } else {
             *function = NULL;
@@ -257,7 +239,6 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end,
 
     vm->global_scope = parser->local_scope;
     vm->scope_size = parser->scope_size;
-    vm->variables_hash = parser->variables_hash;
 
     vm->parser = NULL;
 
index 6e84d1d9bb2298e14b601d4797c3135c536de5a4..476e0061063bd5c833eae5a1d218a596c490b4d9 100644 (file)
@@ -76,10 +76,10 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("undefined") },
 
     { nxt_string("function f(){} function f(){}"),
-      nxt_string("SyntaxError: Duplicate declaration \"f\" in 1") },
+      nxt_string("SyntaxError: Identifier \"f\" has already been declared in 1") },
 
     { nxt_string("var f = 1; function f() {}"),
-      nxt_string("SyntaxError: Duplicate declaration \"f\" in 1") },
+      nxt_string("SyntaxError: Identifier \"f\" has already been declared in 1") },
 
     { nxt_string("f() = 1"),
       nxt_string("ReferenceError: Invalid left-hand side in assignment in 1") },
@@ -586,7 +586,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("false && (true || true)"),
       nxt_string("false") },
 
-    { nxt_string("a = true; a = -~!a"),
+    { nxt_string("var a = true; a = -~!a"),
       nxt_string("1") },
 
     { nxt_string("12 & 6"),
@@ -623,7 +623,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("NaN ^ 65536"),
       nxt_string("65536") },
 
-    { nxt_string("x = '1'; +x + 2"),
+    { nxt_string("var x = '1'; +x + 2"),
       nxt_string("3") },
 
     /* Weird things. */
@@ -1166,13 +1166,13 @@ static njs_unit_test_t  njs_test[] =
 
     /**/
 
-    { nxt_string("a = 1 ? 2 : 3"),
+    { nxt_string("var a; a = 1 ? 2 : 3"),
       nxt_string("2") },
 
-    { nxt_string("a = 1 ? 2 : 3 ? 4 : 5"),
+    { nxt_string("var a; a = 1 ? 2 : 3 ? 4 : 5"),
       nxt_string("2") },
 
-    { nxt_string("a = 0 ? 2 : 3 ? 4 : 5"),
+    { nxt_string("var a; a = 0 ? 2 : 3 ? 4 : 5"),
       nxt_string("4") },
 
     { nxt_string("0 ? 2 ? 3 : 4 : 5"),
@@ -1187,16 +1187,16 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("(1 ? 0 : 3) ? 4 : 5"),
       nxt_string("5") },
 
-    { nxt_string("a = (1 + 2) ? 2 ? 3 + 4 : 5 : 6"),
+    { nxt_string("var a; a = (1 + 2) ? 2 ? 3 + 4 : 5 : 6"),
       nxt_string("7") },
 
-    { nxt_string("a = (1 ? 2 : 3) + 4"),
+    { nxt_string("var a; a = (1 ? 2 : 3) + 4"),
       nxt_string("6") },
 
-    { nxt_string("a = 1 ? b = 2 + 4 : b = 3"),
+    { nxt_string("var a, b; a = 1 ? b = 2 + 4 : b = 3"),
       nxt_string("6") },
 
-    { nxt_string("a = 1 ? [1,2] : []"),
+    { nxt_string("var a; a = 1 ? [1,2] : []"),
       nxt_string("1,2") },
 
     /**/
@@ -1550,43 +1550,44 @@ static njs_unit_test_t  njs_test[] =
 
     /**/
 
-    { nxt_string("a = 2; b = ++a + ++a; a + ' ' + b"),
+    { nxt_string("var a, b; a = 2; b = ++a + ++a; a + ' ' + b"),
       nxt_string("4 7") },
 
-    { nxt_string("a = 2; b = a++ + a++; a + ' ' + b"),
+    { nxt_string("var a, b; a = 2; b = a++ + a++; a + ' ' + b"),
       nxt_string("4 5") },
 
-    { nxt_string("a = b = 7; a +' '+ b"),
+    { nxt_string("var a, b; a = b = 7; a +' '+ b"),
       nxt_string("7 7") },
 
-    { nxt_string("a = b = c = 5; a +' '+ b +' '+ c"),
+    { nxt_string("var a, b, c; a = b = c = 5; a +' '+ b +' '+ c"),
       nxt_string("5 5 5") },
 
-    { nxt_string("a = b = (c = 5) + 2; a +' '+ b +' '+ c"),
+    { nxt_string("var a, b, c; a = b = (c = 5) + 2; a +' '+ b +' '+ c"),
       nxt_string("7 7 5") },
 
     { nxt_string("1, 2 + 5, 3"),
       nxt_string("3") },
 
-    { nxt_string("a = 1 /* YES */\n b = a + 2 \n \n + 1 \n + 3"),
+    { nxt_string("var a, b; a = 1 /* YES */\n b = a + 2 \n \n + 1 \n + 3"),
       nxt_string("7") },
 
-    { nxt_string("a = 1 // YES \n b = a + 2 \n \n + 1 \n + 3"),
+    { nxt_string("var a, b; a = 1 // YES \n b = a + 2 \n \n + 1 \n + 3"),
       nxt_string("7") },
 
-    { nxt_string("a = 0; ++ \n a"),
+    { nxt_string("var a; a = 0; ++ \n a"),
       nxt_string("1") },
 
     { nxt_string("a = 0; a \n ++"),
       nxt_string("SyntaxError: Unexpected end of input in 2") },
 
-    { nxt_string("a = 1 ? 2 \n : 3"),
+    { nxt_string("var a; a = 1 ? 2 \n : 3"),
       nxt_string("2") },
 
-    { nxt_string("a = 0 / 0; b = 1 / 0; c = -1 / 0; a +' '+ b +' '+ c"),
+    { nxt_string("var a, b, c;"
+                 "a = 0 / 0; b = 1 / 0; c = -1 / 0; a +' '+ b +' '+ c"),
       nxt_string("NaN Infinity -Infinity") },
 
-    { nxt_string("a = (b = 7) + 5; var c; a +' '+ b +' '+ c"),
+    { nxt_string("var a, b; a = (b = 7) + 5; var c; a +' '+ b +' '+ c"),
       nxt_string("12 7 undefined") },
 
     { nxt_string("var a, b = 1, c; a +' '+ b +' '+ c"),
@@ -1595,13 +1596,13 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var a = 1, b = a + 1; a +' '+ b"),
       nxt_string("1 2") },
 
-    { nxt_string("a = a = 1"),
+    { nxt_string("var a; a = a = 1"),
       nxt_string("1") },
 
     { nxt_string("var a = 1, \n b; a +' '+ b"),
       nxt_string("1 undefined") },
 
-    { nxt_string("a = b + 1; var b; a +' '+ b"),
+    { nxt_string("var a; a = b + 1; var b; a +' '+ b"),
       nxt_string("NaN undefined") },
 
     { nxt_string("var a += 1"),
@@ -1610,26 +1611,26 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var a = a + 1"),
       nxt_string("undefined") },
 
-    { nxt_string("a = b + 1; var b = 1; a +' '+ b"),
+    { nxt_string("var a; a = b + 1; var b = 1; a +' '+ b"),
       nxt_string("NaN 1") },
 
-    { nxt_string("(a) = 1"),
+    { nxt_string("var a; (a) = 1"),
       nxt_string("1") },
 
     { nxt_string("a"),
-      nxt_string("ReferenceError") },
+      nxt_string("ReferenceError: \"a\" is not defined in 1") },
 
     { nxt_string("a + a"),
-      nxt_string("ReferenceError") },
+      nxt_string("ReferenceError: \"a\" is not defined in 1") },
 
     { nxt_string("a = b + 1"),
-      nxt_string("ReferenceError") },
+      nxt_string("ReferenceError: \"a\" is not defined in 1") },
 
     { nxt_string("a = a + 1"),
-      nxt_string("ReferenceError") },
+      nxt_string("ReferenceError: \"a\" is not defined in 1") },
 
     { nxt_string("a += 1"),
-      nxt_string("ReferenceError") },
+      nxt_string("ReferenceError: \"a\" is not defined in 1") },
 
     { nxt_string("a += 1; var a = 2"),
       nxt_string("undefined") },
@@ -1646,7 +1647,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var a = 1; a += (a = 2)"),
       nxt_string("3") },
 
-    { nxt_string("var a = b = 1; a +' '+ b"),
+    { nxt_string("var a = b = 1; var b; a +' '+ b"),
       nxt_string("1 1") },
 
     { nxt_string("var a \n if (!a) a = 3; a"),
@@ -1820,23 +1821,23 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("for ( ;null; ) { continue }"),
       nxt_string("undefined") },
 
-    { nxt_string("for (i = 0; i < 100; i++) if (i > 9) continue; i"),
+    { nxt_string("var i; for (i = 0; i < 100; i++) if (i > 9) continue; i"),
       nxt_string("100") },
 
-    { nxt_string("var a = []; for (i in a) continue"),
+    { nxt_string("var a = [], i; for (i in a) continue"),
       nxt_string("undefined") },
 
-    { nxt_string("var a = []; for (i in a) continue;"),
+    { nxt_string("var a = [], i; for (i in a) continue;"),
       nxt_string("undefined") },
 
-    { nxt_string("var a = []; for (i in a) { continue }"),
+    { nxt_string("var a = [], i; for (i in a) { continue }"),
       nxt_string("undefined") },
 
-    { nxt_string("var a = [1,2,3,4,5]; var s = 0;"
+    { nxt_string("var a = [1,2,3,4,5]; var s = 0, i;"
                  "for (i in a) { if (a[i] > 4) continue; else s += a[i] } s"),
       nxt_string("10") },
 
-    { nxt_string("var a = [1,2,3,4,5]; var s = 0;"
+    { nxt_string("var a = [1,2,3,4,5]; var s = 0, i;"
                  "for (i in a) { if (a[i] > 4) continue; s += a[i] } s"),
       nxt_string("10") },
 
@@ -1881,53 +1882,53 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("for ( ;; ) { break }"),
       nxt_string("undefined") },
 
-    { nxt_string("for (i = 0; i < 100; i++) if (i > 9) break; i"),
+    { nxt_string("var i; for (i = 0; i < 100; i++) if (i > 9) break; i"),
       nxt_string("10") },
 
-    { nxt_string("var a = []; for (i in a) break"),
+    { nxt_string("var a = [], i; for (i in a) break"),
       nxt_string("undefined") },
 
-    { nxt_string("var a = []; for (i in a) break;"),
+    { nxt_string("var a = [], i; for (i in a) break;"),
       nxt_string("undefined") },
 
-    { nxt_string("var a = []; for (i in a) { break }"),
+    { nxt_string("var a = [], i; for (i in a) { break }"),
       nxt_string("undefined") },
 
-    { nxt_string("var a = [1,2,3,4,5]; var s = 0;"
+    { nxt_string("var a = [1,2,3,4,5]; var s = 0, i;"
                  "for (i in a) { if (a[i] > 4) break; else s += a[i] } s"),
       nxt_string("10") },
 
-    { nxt_string("var a = [1,2,3,4,5]; var s = 0;"
+    { nxt_string("var a = [1,2,3,4,5]; var s = 0, i;"
                  "for (i in a) { if (a[i] > 4) break; s += a[i] } s"),
       nxt_string("10") },
 
-    { nxt_string("var a = [1,2,3,4,5]; var s = 0;"
+    { nxt_string("var a = [1,2,3,4,5]; var s = 0, i;"
                  "for (i in a) if (a[i] > 4) break; s += a[i] } s"),
       nxt_string("5") },
 
     /**/
 
-    { nxt_string("for (i = 0; i < 10; i++) { i += 1 } i"),
+    { nxt_string("var i; for (i = 0; i < 10; i++) { i += 1 } i"),
       nxt_string("10") },
 
     /* Factorial. */
 
-    { nxt_string("n = 5; f = 1; while (n--) f *= n + 1; f"),
+    { nxt_string("var n = 5, f = 1; while (n--) f *= n + 1; f"),
       nxt_string("120") },
 
-    { nxt_string("n = 5; f = 1; while (n) { f *= n; n-- } f"),
+    { nxt_string("var n = 5, f = 1; while (n) { f *= n; n-- } f"),
       nxt_string("120") },
 
     /* Fibonacci. */
 
-    { nxt_string("var n = 50, x;"
+    { nxt_string("var n = 50, x, i, j, k;"
                  "for(i=0,j=1,k=0; k<n; i=j,j=x,k++ ){ x=i+j } x"),
       nxt_string("20365011074") },
 
     { nxt_string("3 + 'abc' + 'def' + null + true + false + undefined"),
       nxt_string("3abcdefnulltruefalseundefined") },
 
-    { nxt_string("a = 0; do a++; while (a < 5) if (a == 5) a = 7.33 \n"
+    { nxt_string("var a = 0; do a++; while (a < 5) if (a == 5) a = 7.33 \n"
                  "else a = 8; while (a < 10) a++; a"),
       nxt_string("10.33") },
 
@@ -1984,11 +1985,20 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("typeof a"),
       nxt_string("undefined") },
 
+    { nxt_string("typeof a; var a"),
+      nxt_string("undefined") },
+
+    { nxt_string("typeof a; var a; a"),
+      nxt_string("undefined") },
+
+    { nxt_string("var a = 5; typeof a"),
+      nxt_string("number") },
+
     { nxt_string("typeof a; a"),
-      nxt_string("ReferenceError") },
+      nxt_string("ReferenceError: \"a\" is not defined in 1") },
 
     { nxt_string("typeof a; a = 1"),
-      nxt_string("1") },
+      nxt_string("ReferenceError: \"a\" is not defined in 1") },
 
     /**/
 
@@ -2007,142 +2017,148 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("++null"),
       nxt_string("ReferenceError: Invalid left-hand side in prefix operation in 1") },
 
-    { nxt_string("var a; b = a; a = 1; a +' '+ b"),
+    { nxt_string("var a, b; b = a; a = 1; a +' '+ b"),
       nxt_string("1 undefined") },
 
     { nxt_string("a = 1"),
-      nxt_string("1") },
+      nxt_string("ReferenceError: \"a\" is not defined in 1") },
 
-    { nxt_string("a; a = 1; a"),
-      nxt_string("ReferenceError") },
+    { nxt_string("var a; a = 1; a"),
+      nxt_string("1") },
 
-    { nxt_string("a = {}; typeof a +' '+ a"),
+    { nxt_string("var a = {}; typeof a +' '+ a"),
       nxt_string("object [object Object]") },
 
-    { nxt_string("a = {}; a.b"),
+    { nxt_string("var a = {}; a.b"),
       nxt_string("undefined") },
 
-    { nxt_string("a = {}; a.b = 1 + 2; a.b"),
+    { nxt_string("var a = {}; a.b = 1 + 2; a.b"),
       nxt_string("3") },
 
-    { nxt_string("a = {}; a['b']"),
+    { nxt_string("var a = {}; a['b']"),
       nxt_string("undefined") },
 
-    { nxt_string("a = {}; a.b.c"),
+    { nxt_string("var a = {}; a.b.c"),
       nxt_string("TypeError") },
 
     { nxt_string("'a'.b = 1"),
       nxt_string("TypeError") },
 
-    { nxt_string("a = {}; a.b = 1; a.b"),
+    { nxt_string("var a = {}; a.b = 1; a.b"),
       nxt_string("1") },
 
-    { nxt_string("a = {}; a.b = 1; a.b += 2"),
+    { nxt_string("var a = {}; a.b = 1; a.b += 2"),
       nxt_string("3") },
 
-    { nxt_string("a = {}; a.b = 1; a.b += a.b"),
+    { nxt_string("var a = {}; a.b = 1; a.b += a.b"),
       nxt_string("2") },
 
-    { nxt_string("a = {}; a.b = 1; x = {}; x.b = 3; a.b += (x.b = 2)"),
+    { nxt_string("var a = {}; a.b = 1; var x = {}; x.b = 3; a.b += (x.b = 2)"),
       nxt_string("3") },
 
-    { nxt_string("a = {}; a.b = 1; a.b += (a.b = 2)"),
+    { nxt_string("var a = {}; a.b = 1; a.b += (a.b = 2)"),
       nxt_string("3") },
 
-    { nxt_string("a = {}; a.b += 1"),
+    { nxt_string("var a = {}; a.b += 1"),
       nxt_string("NaN") },
 
-    { nxt_string("a = 1; b = 2; a = b += 1"),
+    { nxt_string("var a = 1; var b = 2; a = b += 1"),
       nxt_string("3") },
 
-    { nxt_string("a = 1; b = { x:2 }; a = b.x += 1"),
+    { nxt_string("var a = 1; var b = { x:2 }; a = b.x += 1"),
       nxt_string("3") },
 
-    { nxt_string("a = 1; b = { x:2 }; a = b.x += (a = 1)"),
+    { nxt_string("var a = 1; var b = { x:2 }; a = b.x += (a = 1)"),
       nxt_string("3") },
 
-    { nxt_string("a = undefined; a.b++; a.b"),
+    { nxt_string("var a = undefined; a.b++; a.b"),
       nxt_string("TypeError") },
 
-    { nxt_string("a = null; a.b++; a.b"),
+    { nxt_string("var a = null; a.b++; a.b"),
       nxt_string("TypeError") },
 
-    { nxt_string("a = true; a.b++; a.b"),
+    { nxt_string("var a = true; a.b++; a.b"),
       nxt_string("TypeError") },
 
-    { nxt_string("a = 1; a.b++; a.b"),
+    { nxt_string("var a = 1; a.b++; a.b"),
       nxt_string("TypeError") },
 
     { nxt_string("var n = 1, o = { p: n += 1 }; o.p"),
       nxt_string("2") },
 
-    { nxt_string("a = {}; a.b = {}; a.b.c = 1; a.b['c']"),
+    { nxt_string("var a = {}; a.b = {}; a.b.c = 1; a.b['c']"),
       nxt_string("1") },
 
-    { nxt_string("a = {}; a.b = {}; a.b.c = 1; a['b']['c']"),
+    { nxt_string("var a = {}; a.b = {}; a.b.c = 1; a['b']['c']"),
       nxt_string("1") },
 
-    { nxt_string("a = {}; a.b = {}; c = 'd'; a.b.d = 1; a['b'][c]"),
+    { nxt_string("var a = {}; a.b = {}; var c = 'd'; a.b.d = 1; a['b'][c]"),
       nxt_string("1") },
 
-    { nxt_string("a = {}; a.b = 1; c = a.b++; a.b +' '+ c"),
+    { nxt_string("var a = {}; a.b = 1; var c = a.b++; a.b +' '+ c"),
       nxt_string("2 1") },
 
-    { nxt_string("a = 2; a.b = 1; c = a.b++; a +' '+ a.b +' '+ c"),
+    { nxt_string("var a = 2; a.b = 1; var c = a.b++; a +' '+ a.b +' '+ c"),
       nxt_string("TypeError") },
 
-    { nxt_string("x = { a: 1 }; x.a"),
+    { nxt_string("var x = { a: 1 }; x.a"),
       nxt_string("1") },
 
-    { nxt_string("a = { x:1 }; b = { y:2 }; a.x = b.y; a.x"),
+    { nxt_string("var a = { x:1 }; var b = { y:2 }; a.x = b.y; a.x"),
       nxt_string("2") },
 
-    { nxt_string("a = { x:1 }; b = { y:2 }; c = a.x = b.y; c"),
+    { nxt_string("var a = { x:1 }; var b = { y:2 }; var c; c = a.x = b.y"),
       nxt_string("2") },
 
-    { nxt_string("a = { x:1 }; b = { y:2 }; a.x = b.y"),
+    { nxt_string("var a = { x:1 }; var b = { y:2 }; var c = a.x = b.y; c"),
       nxt_string("2") },
 
-    { nxt_string("a = { x:1 }; b = a.x = 1 + 2; a.x +' '+ b"),
+    { nxt_string("var a = { x:1 }; var b = { y:2 }; a.x = b.y"),
+      nxt_string("2") },
+
+    { nxt_string("var a = { x:1 }; var b = a.x = 1 + 2; a.x +' '+ b"),
       nxt_string("3 3") },
 
-    { nxt_string("a = { x:1 }; b = { y:2 }; c = {}; c.x = a.x = b.y; c.x"),
+    { nxt_string("var a = { x:1 }; var b = { y:2 }; var c = {};"
+                 "c.x = a.x = b.y; c.x"),
       nxt_string("2") },
 
-    { nxt_string("y = 2; x = { a:1, b: y + 5, c:3 }; x.a +' '+ x.b +' '+ x.c"),
+    { nxt_string("var y = 2, x = { a:1, b: y + 5, c:3 };"
+                 "x.a +' '+ x.b +' '+ x.c"),
       nxt_string("1 7 3") },
 
-    { nxt_string("x = { a: 1, b: { a:2, c:5 } }; x.a +' '+ x.b.a +' '+ x.b.c"),
+    { nxt_string("var x = { a: 1, b: { a:2, c:5 } };"
+                 "x.a +' '+ x.b.a +' '+ x.b.c"),
       nxt_string("1 2 5") },
 
-    { nxt_string("var y = 5; x = { a:y }; x.a"),
+    { nxt_string("var y = 5, x = { a:y }; x.a"),
       nxt_string("5") },
 
-    { nxt_string("x = { a: 1; b: 2 }"),
+    { nxt_string("var x = { a: 1; b: 2 }"),
       nxt_string("SyntaxError: Unexpected token \";\" in 1") },
 
-    { nxt_string("x = { a: 1, b: x.a }"),
-      nxt_string("ReferenceError") },
+    { nxt_string("var x = { a: 1, b: x.a }"),
+      nxt_string("TypeError") },
 
-    { nxt_string("a = { b: 2 }; a.b += 1"),
+    { nxt_string("var a = { b: 2 }; a.b += 1"),
       nxt_string("3") },
 
-    { nxt_string("o = {a:1}; c = o; o.a = o = {b:5};"
+    { nxt_string("var o = {a:1}, c = o; o.a = o = {b:5};"
                  "o.a +' '+ o.b +' '+ c.a.b"),
       nxt_string("undefined 5 5") },
 
-    { nxt_string("y = { a: 2 }; x = { a: 1, b: y.a }; x.a +' '+ x.b"),
+    { nxt_string("var y = { a: 2 }, x = { a: 1, b: y.a }; x.a +' '+ x.b"),
       nxt_string("1 2") },
 
-    { nxt_string("y = { a: 1 }; x = { a: y.a++, b: y.a++ }\n"
+    { nxt_string("var y = { a: 1 }, x = { a: y.a++, b: y.a++ }\n"
                  "x.a +' '+ x.b +' '+ y.a"),
       nxt_string("1 2 3") },
 
-    { nxt_string("var a=''; var o = {a:1, b:2};"
+    { nxt_string("var a='', o = {a:1, b:2}, p;"
                  "for (p in o) { a += p +':'+ o[p] +',' } a"),
       nxt_string("a:1,b:2,") },
 
-    { nxt_string("x = { a: 1 }; b = delete x.a; x.a +' '+ b"),
+    { nxt_string("var x = { a: 1 }, b = delete x.a; x.a +' '+ b"),
       nxt_string("undefined true") },
 
     { nxt_string("delete null"),
@@ -2179,25 +2195,25 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("delete a"),
       nxt_string("SyntaxError: Delete of an unqualified identifier in 1") },
 
-    { nxt_string("a = 1; delete a"),
+    { nxt_string("var a = 1; delete a"),
       nxt_string("SyntaxError: Delete of an unqualified identifier in 1") },
 
     { nxt_string("function f(){} delete f"),
       nxt_string("SyntaxError: Delete of an unqualified identifier in 1") },
 
-    { nxt_string("a = { x:1 }; ('x' in a) +' '+ (1 in a)"),
+    { nxt_string("var a = { x:1 }; ('x' in a) +' '+ (1 in a)"),
       nxt_string("true false") },
 
     { nxt_string("delete --[][1]"),
       nxt_string("true") },
 
-    { nxt_string("a = {}; 1 in a"),
+    { nxt_string("var a = {}; 1 in a"),
       nxt_string("false") },
 
-    { nxt_string("a = 1; 1 in a"),
+    { nxt_string("var a = 1; 1 in a"),
       nxt_string("TypeError") },
 
-    { nxt_string("a = true; 1 in a"),
+    { nxt_string("var a = true; 1 in a"),
       nxt_string("TypeError") },
 
     { nxt_string("var n = { toString: function() { return 'a' } };"
@@ -2240,16 +2256,16 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("3 * [5,7]"),
       nxt_string("NaN") },
 
-    { nxt_string("a = [ 1, 2, 3 ]; a[0] + a[1] + a[2]"),
+    { nxt_string("var a = [ 1, 2, 3 ]; a[0] + a[1] + a[2]"),
       nxt_string("6") },
 
     { nxt_string("var n = 1, a = [ n += 1 ]; a"),
       nxt_string("2") },
 
-    { nxt_string("a = [ 1, 2; 3 ]; a[0] + a[1] + a[2]"),
+    { nxt_string("var a = [ 1, 2; 3 ]; a[0] + a[1] + a[2]"),
       nxt_string("SyntaxError: Unexpected token \";\" in 1") },
 
-    { nxt_string("a = [ 1, 2, 3 ]; a[0] +' '+ a[1] +' '+ a[2] +' '+ a[3]"),
+    { nxt_string("var a = [ 1, 2, 3 ]; a[0] +' '+ a[1] +' '+ a[2] +' '+ a[3]"),
       nxt_string("1 2 3 undefined") },
 
     { nxt_string("[] - 2"),
@@ -2264,13 +2280,13 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("[[[1]]] - 2"),
       nxt_string("-1") },
 
-    { nxt_string("a = []; a - 2"),
+    { nxt_string("var a = []; a - 2"),
       nxt_string("-2") },
 
-    { nxt_string("a = [1]; a - 2"),
+    { nxt_string("var a = [1]; a - 2"),
       nxt_string("-1") },
 
-    { nxt_string("a = []; a[0] = 1; a - 2"),
+    { nxt_string("var a = []; a[0] = 1; a - 2"),
       nxt_string("-1") },
 
     { nxt_string("[] + 2 + 3"),
@@ -2279,37 +2295,38 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("[1] + 2 + 3"),
       nxt_string("123") },
 
-    { nxt_string("a = []; a + 2 + 3"),
+    { nxt_string("var a = []; a + 2 + 3"),
       nxt_string("23") },
 
-    { nxt_string("a = [1]; a + 2 + 3"),
+    { nxt_string("var a = [1]; a + 2 + 3"),
       nxt_string("123") },
 
-    { nxt_string("a = [1,2]; i = 0; a[i++] += a[0] = 5 + i; a[0] +' '+ a[1]"),
+    { nxt_string("var a = [1,2], i = 0; a[i++] += a[0] = 5 + i;"
+                 "a[0] +' '+ a[1]"),
       nxt_string("7 2") },
 
-    { nxt_string("a = []; a[0] = 1; a + 2 + 3"),
+    { nxt_string("var a = []; a[0] = 1; a + 2 + 3"),
       nxt_string("123") },
 
-    { nxt_string("a = []; a['0'] = 1; a + 2 + 3"),
+    { nxt_string("var a = []; a['0'] = 1; a + 2 + 3"),
       nxt_string("123") },
 
-    { nxt_string("a = []; a[2] = 1; a[2]"),
+    { nxt_string("var a = []; a[2] = 1; a[2]"),
       nxt_string("1") },
 
-    { nxt_string("a = [1, 2]; 1 in a"),
+    { nxt_string("var a = [1, 2]; 1 in a"),
       nxt_string("true") },
 
-    { nxt_string("a = [1, 2]; 2 in a"),
+    { nxt_string("var a = [1, 2]; 2 in a"),
       nxt_string("false") },
 
-    { nxt_string("a = [1, 2]; delete a[0]; 0 in a"),
+    { nxt_string("var a = [1, 2]; delete a[0]; 0 in a"),
       nxt_string("false") },
 
     { nxt_string("var a = [ function(a) {return a + 1} ]; a[0](5)"),
       nxt_string("6") },
 
-    { nxt_string("var s = '', a = [5,1,2];"
+    { nxt_string("var s = '', a = [5,1,2], i;"
                  "a[null] = null;"
                  "a[undefined] = 'defined';"
                  "a[false] = false;"
@@ -2329,16 +2346,16 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("[1,2].length"),
       nxt_string("2") },
 
-    { nxt_string("a = [1,2]; a.length"),
+    { nxt_string("var a = [1,2]; a.length"),
       nxt_string("2") },
 
-    { nxt_string("a = [1,2,3]; a.join()"),
+    { nxt_string("var a = [1,2,3]; a.join()"),
       nxt_string("1,2,3") },
 
-    { nxt_string("a = [1,2,3]; a.join(':')"),
+    { nxt_string("var a = [1,2,3]; a.join(':')"),
       nxt_string("1:2:3") },
 
-    { nxt_string("a = []; a[5] = 5; a.join()"),
+    { nxt_string("var a = []; a[5] = 5; a.join()"),
       nxt_string(",,,,,5") },
 
     { nxt_string("var a = [,null,undefined,false,true,0,1]; a.join()"),
@@ -2352,10 +2369,10 @@ static njs_unit_test_t  njs_test[] =
                  "[o].join()"),
       nxt_string("undefined") },
 
-    { nxt_string("a = []; a[5] = 5; a"),
+    { nxt_string("var a = []; a[5] = 5; a"),
       nxt_string(",,,,,5") },
 
-    { nxt_string("a = []; a.concat([])"),
+    { nxt_string("var a = []; a.concat([])"),
       nxt_string("") },
 
     { nxt_string("var s = { toString: function() { return 'S' } };"
@@ -2372,7 +2389,7 @@ static njs_unit_test_t  njs_test[] =
 
     /* Array.toString(). */
 
-    { nxt_string("a = [1,2,3]; a.join = 'NO';"
+    { nxt_string("var a = [1,2,3]; a.join = 'NO';"
                  "Object.prototype.toString = function () { return 'A' }; a"),
       nxt_string("[object Array]") },
 
@@ -2452,13 +2469,13 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("Array.isArray([])"),
       nxt_string("true") },
 
-    { nxt_string("a = [1,2,3]; a.concat(4, [5, 6, 7], 8)"),
+    { nxt_string("var a = [1,2,3]; a.concat(4, [5, 6, 7], 8)"),
       nxt_string("1,2,3,4,5,6,7,8") },
 
-    { nxt_string("a = []; a[100] = a.length; a[100] +' '+ a.length"),
+    { nxt_string("var a = []; a[100] = a.length; a[100] +' '+ a.length"),
       nxt_string("0 101") },
 
-    { nxt_string("a = [1,2]; a[100] = 100; a[100] +' '+ a.length"),
+    { nxt_string("var a = [1,2]; a[100] = 100; a[100] +' '+ a.length"),
       nxt_string("100 101") },
 
     { nxt_string("Array.prototype.slice(1)"),
@@ -2479,26 +2496,28 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("[0,1,2,3,4].slice(6,7)"),
       nxt_string("") },
 
-    { nxt_string("a = [1,2,3,4,5]; b = a.slice(3); b[0] +' '+ b[1] +' '+ b[2]"),
+    { nxt_string("var a = [1,2,3,4,5], b = a.slice(3);"
+                 "b[0] +' '+ b[1] +' '+ b[2]"),
       nxt_string("4 5 undefined") },
 
-    { nxt_string("a = [1,2]; a.pop() +' '+ a.length +' '+ a"),
+    { nxt_string("var a = [1,2]; a.pop() +' '+ a.length +' '+ a"),
       nxt_string("2 1 1") },
 
-    { nxt_string("a = [1,2]; len = a.push(3); len +' '+ a.pop() +' '+ a"),
+    { nxt_string("var a = [1,2], len = a.push(3); len +' '+ a.pop() +' '+ a"),
       nxt_string("3 3 1,2") },
 
-    { nxt_string("a = [1,2]; len = a.push(3,4,5); len +' '+ a.pop() +' '+ a"),
+    { nxt_string("var a = [1,2], len = a.push(3,4,5);"
+                 "len +' '+ a.pop() +' '+ a"),
       nxt_string("5 5 1,2,3,4") },
 
-    { nxt_string("a = [1,2,3]; a.shift() +' '+ a[0] +' '+ a.length"),
+    { nxt_string("var a = [1,2,3]; a.shift() +' '+ a[0] +' '+ a.length"),
       nxt_string("1 2 2") },
 
-    { nxt_string("a = [1,2]; len = a.unshift(3);"
+    { nxt_string("var a = [1,2], len = a.unshift(3);"
                  "len +' '+ a +' '+ a.shift()"),
       nxt_string("3 3,1,2 3") },
 
-    { nxt_string("a = [1,2]; len = a.unshift(3,4,5);"
+    { nxt_string("var a = [1,2], len = a.unshift(3,4,5);"
                  "len +' '+ a +' '+ a.shift()"),
       nxt_string("5 3,4,5,1,2 3") },
 
@@ -2960,25 +2979,25 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'囲碁織'.toUTF8().length"),
       nxt_string("9") },
 
-    { nxt_string("a = 'abc'; a.length"),
+    { nxt_string("var a = 'abc'; a.length"),
       nxt_string("3") },
 
-    { nxt_string("a = 'abc'; a['length']"),
+    { nxt_string("var a = 'abc'; a['length']"),
       nxt_string("3") },
 
-    { nxt_string("a = 'абв'; a.length"),
+    { nxt_string("var a = 'абв'; a.length"),
       nxt_string("3") },
 
-    { nxt_string("a = 'abc' + 'абв'; a.length"),
+    { nxt_string("var a = 'abc' + 'абв'; a.length"),
       nxt_string("6") },
 
-    { nxt_string("a = 'abc' + 1 + 'абв'; a +' '+ a.length"),
+    { nxt_string("var a = 'abc' + 1 + 'абв'; a +' '+ a.length"),
       nxt_string("abc1абв 7") },
 
-    { nxt_string("a = 1; a.length"),
+    { nxt_string("var a = 1; a.length"),
       nxt_string("undefined") },
 
-    { nxt_string("a = 'abc'; a.concat('абв', 123)"),
+    { nxt_string("var a = 'abc'; a.concat('абв', 123)"),
       nxt_string("abcабв123") },
 
     { nxt_string("''.concat.call(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)"),
@@ -3039,7 +3058,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'\\u00CE\\u00B1'.toBytes() === 'α'"),
       nxt_string("false") },
 
-    { nxt_string("b = '\\u00C2\\u00B6'.toBytes(); u = b.fromUTF8();"
+    { nxt_string("var b = '\\u00C2\\u00B6'.toBytes(), u = b.fromUTF8();"
                  "b.length +' '+ b +' '+ u.length +' '+ u"),
       nxt_string("2 ¶ 1 ¶") },
 
@@ -3049,28 +3068,28 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'α'.toUTF8()[0]"),
       nxt_string("\xCE") },
 
-    { nxt_string("a = 'a'.toBytes() + 'α'; a + a.length"),
+    { nxt_string("var a = 'a'.toBytes() + 'α'; a + a.length"),
       nxt_string("aα3") },
 
-    { nxt_string("a = 'µ§±®'.toBytes(); a"),
+    { nxt_string("var a = 'µ§±®'.toBytes(); a"),
       nxt_string("\xB5\xA7\xB1\xAE") },
 
-    { nxt_string("a = 'µ§±®'.toBytes(2); a"),
+    { nxt_string("var a = 'µ§±®'.toBytes(2); a"),
       nxt_string("\xB1\xAE") },
 
-    { nxt_string("a = 'µ§±®'.toBytes(1,3); a"),
+    { nxt_string("var a = 'µ§±®'.toBytes(1,3); a"),
       nxt_string("\xA7\xB1") },
 
-    { nxt_string("a = '\\xB5\\xA7\\xB1\\xAE'.toBytes(); a.fromBytes()"),
+    { nxt_string("var a = '\\xB5\\xA7\\xB1\\xAE'.toBytes(); a.fromBytes()"),
       nxt_string("µ§±®") },
 
-    { nxt_string("a = '\\xB5\\xA7\\xB1\\xAE'.toBytes(); a.fromBytes(2)"),
+    { nxt_string("var a = '\\xB5\\xA7\\xB1\\xAE'.toBytes(); a.fromBytes(2)"),
       nxt_string("±®") },
 
-    { nxt_string("a = '\\xB5\\xA7\\xB1\\xAE'.toBytes(); a.fromBytes(1, 3)"),
+    { nxt_string("var a = '\\xB5\\xA7\\xB1\\xAE'.toBytes(); a.fromBytes(1, 3)"),
       nxt_string("§±") },
 
-    { nxt_string("a = 'abcdefgh'; a.substr(3, 15)"),
+    { nxt_string("var a = 'abcdefgh'; a.substr(3, 15)"),
       nxt_string("defgh") },
 
     { nxt_string("'abcdefgh'.substr(3, 15)"),
@@ -3183,7 +3202,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'abc'.charCodeAt(3)"),
       nxt_string("NaN") },
 
-    { nxt_string("a = 'abcdef'; a.3"),
+    { nxt_string("var a = 'abcdef'; a.3"),
       nxt_string("SyntaxError: Unexpected token \"3\" in 1") },
 
     { nxt_string("'abcdef'[3]"),
@@ -3204,7 +3223,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'abcdef'[8]"),
       nxt_string("undefined") },
 
-    { nxt_string("a = 'abcdef'; b = 1 + 2; a[b]"),
+    { nxt_string("var a = 'abcdef', b = 1 + 2; a[b]"),
       nxt_string("d") },
 
     /**/
@@ -3244,19 +3263,19 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("typeof $r"),
       nxt_string("undefined") },
 
-    { nxt_string("a = $r.uri; s = a.fromUTF8(); s.length +' '+ s"),
+    { nxt_string("var a = $r.uri, s = a.fromUTF8(); s.length +' '+ s"),
       nxt_string("3 АБВ") },
 
-    { nxt_string("a = $r.uri; s = a.fromUTF8(2); s.length +' '+ s"),
+    { nxt_string("var a = $r.uri, s = a.fromUTF8(2); s.length +' '+ s"),
       nxt_string("2 БВ") },
 
-    { nxt_string("a = $r.uri; s = a.fromUTF8(2, 4); s.length +' '+ s"),
+    { nxt_string("var a = $r.uri, s = a.fromUTF8(2, 4); s.length +' '+ s"),
       nxt_string("1 Б") },
 
-    { nxt_string("a = $r.uri; a +' '+ a.length +' '+ a"),
+    { nxt_string("var a = $r.uri; a +' '+ a.length +' '+ a"),
       nxt_string("АБВ 6 АБВ") },
 
-    { nxt_string("$r.uri = 'αβγ'; a = $r.uri; a.length +' '+ a"),
+    { nxt_string("$r.uri = 'αβγ'; var a = $r.uri; a.length +' '+ a"),
       nxt_string("6 αβγ") },
 
     { nxt_string("$r.uri.length +' '+ $r.uri"),
@@ -3265,16 +3284,16 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("$r.uri = $r.uri.substr(2); $r.uri.length +' '+ $r.uri"),
       nxt_string("4 БВ") },
 
-    { nxt_string("a = $r.host; a +' '+ a.length +' '+ a"),
+    { nxt_string("var a = $r.host; a +' '+ a.length +' '+ a"),
       nxt_string("АБВГДЕЁЖЗИЙ 22 АБВГДЕЁЖЗИЙ") },
 
-    { nxt_string("a = $r.host; a.substr(2, 2)"),
+    { nxt_string("var a = $r.host; a.substr(2, 2)"),
       nxt_string("Б") },
 
-    { nxt_string("a = $r.header['User-Agent']; a +' '+ a.length +' '+ a"),
+    { nxt_string("var a = $r.header['User-Agent']; a +' '+ a.length +' '+ a"),
       nxt_string("User-Agent|АБВ 17 User-Agent|АБВ") },
 
-    { nxt_string("var a='';"
+    { nxt_string("var a='', p;"
                  "for (p in $r.header) { a += p +':'+ $r.header[p] +',' }"
                  "a"),
       nxt_string("01:01|АБВ,02:02|АБВ,03:03|АБВ,") },
@@ -3282,7 +3301,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("$r.some_method('YES')"),
       nxt_string("АБВ") },
 
-    { nxt_string("for (p in $r.some_method);"),
+    { nxt_string("var p; for (p in $r.some_method);"),
       nxt_string("undefined") },
 
     { nxt_string("'uri' in $r"),
@@ -3342,14 +3361,14 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'abcdef'.substr(2, 4).charAt(2)"),
       nxt_string("e") },
 
-    { nxt_string("a = 'abcdef'.substr(2, 4).charAt(2).length"),
+    { nxt_string("var a = 'abcdef'.substr(2, 4).charAt(2).length; a"),
       nxt_string("1") },
 
-    { nxt_string("a = 'abcdef'.substr(2, 4).charAt(2) + '1234'"),
+    { nxt_string("var a = 'abcdef'.substr(2, 4).charAt(2) + '1234'; a"),
       nxt_string("e1234") },
 
-    { nxt_string("a = ('abcdef'.substr(2, 5 * 2 - 6).charAt(2) + '1234')"
-                 "    .length"),
+    { nxt_string("var a = ('abcdef'.substr(2, 5 * 2 - 6).charAt(2) + '1234')"
+                 "         .length; a"),
       nxt_string("5") },
 
     { nxt_string("String.fromCharCode('_')"),
@@ -3365,6 +3384,7 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("αβγ") },
 
     { nxt_string("(function() {"
+                 "    var n;"
                  "    for (n = 0; n <= 1114111; n++) {"
                  "        if (String.fromCharCode(n).charCodeAt(0) !== n)"
                  "            return n;"
@@ -3373,11 +3393,11 @@ static njs_unit_test_t  njs_test[] =
                  "})()"),
       nxt_string("-1") },
 
-    { nxt_string("a = 'abcdef'; function f(a) {"
+    { nxt_string("var a = 'abcdef'; function f(a) {"
                  "return a.slice(a.indexOf('cd')) } f(a)"),
       nxt_string("cdef") },
 
-    { nxt_string("a = 'abcdef'; a.slice(a.indexOf('cd'))"),
+    { nxt_string("var a = 'abcdef'; a.slice(a.indexOf('cd'))"),
       nxt_string("cdef") },
 
     { nxt_string("'abcdef'.indexOf('de', 2)"),
@@ -3542,7 +3562,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'абв'.toUpperCase()"),
       nxt_string("АБВ") },
 
-    { nxt_string("var a = [];"
+    { nxt_string("var a = [], code;"
                  "for (code = 0; code <= 1114111; code++) {"
                  "    var s = String.fromCharCode(code);"
                  "    var n = s.toUpperCase();"
@@ -3551,7 +3571,7 @@ static njs_unit_test_t  njs_test[] =
                  "} a"),
       nxt_string("181,305,383,453,456,459,498,837,962,976,977,981,982,1008,1009,1013,7835,8126") },
 
-    { nxt_string("var a = [];"
+    { nxt_string("var a = [], code;"
                  "for (code = 0; code <= 1114111; code++) {"
                  "    var s = String.fromCharCode(code);"
                  "    var n = s.toLowerCase();"
@@ -4099,31 +4119,28 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("function f(){return\n1} f()"),
       nxt_string("undefined") },
 
-    { nxt_string("function f(a) { return a + 1 } b = f(2)"),
+    { nxt_string("function f(a) { return a + 1 } var b = f(2); b"),
       nxt_string("3") },
 
-    { nxt_string("f = function(a) { a *= 2; return a }; f(10)"),
+    { nxt_string("var f = function(a) { a *= 2; return a }; f(10)"),
       nxt_string("20") },
 
     { nxt_string("var f = function b(a) { a *= 2; return a }; f(10)"),
       nxt_string("20") },
 
     { nxt_string("var f = function b(a) { a *= 2; return a }; b(10)"),
-      nxt_string("ReferenceError") },
+      nxt_string("ReferenceError: \"b\" is not defined in 1") },
 
-    { nxt_string("var f = function(a) { a *= 2; return a }; f(10)"),
+    { nxt_string("var f; f = function(a) { a *= 2; return a }; f(10)"),
       nxt_string("20") },
 
-    { nxt_string("f = function b(a) { a *= 2; return a }; b(10)"),
-      nxt_string("ReferenceError") },
-
-    { nxt_string("f = function b(a) { a *= 2; return a }; f(10)"),
+    { nxt_string("var f; f = function b(a) { a *= 2; return a }; f(10)"),
       nxt_string("20") },
 
-    { nxt_string("f = a = function(a) { a *= 2; return a }; f(10)"),
+    { nxt_string("var a, f = a = function(a) { a *= 2; return a }; f(10)"),
       nxt_string("20") },
 
-    { nxt_string("f = a = function(a) { a *= 2; return a }; a(10)"),
+    { nxt_string("var a, f = a = function(a) { a *= 2; return a }; a(10)"),
       nxt_string("20") },
 
     { nxt_string("var f = function b(a) { a *= 2; return a } = 5"),
@@ -4132,7 +4149,7 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("function a() { return { x:2} }; var b = a(); b.x"),
       nxt_string("2") },
 
-    { nxt_string("a = {}; function f(a) { return a + 1 } a.b = f(2); a.b"),
+    { nxt_string("var a = {}; function f(a) { return a + 1 } a.b = f(2); a.b"),
       nxt_string("3") },
 
     { nxt_string("(function(x) { return x + 1 })(2)"),
@@ -4141,40 +4158,40 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("(function(x) { return x + 1 }(2))"),
       nxt_string("3") },
 
-    { nxt_string("a = function() { return 1 }(); a"),
+    { nxt_string("var a = function() { return 1 }(); a"),
       nxt_string("1") },
 
-    { nxt_string("a = (function() { return 1 })(); a"),
+    { nxt_string("var a = (function() { return 1 })(); a"),
       nxt_string("1") },
 
-    { nxt_string("a = (function(a) { return a + 1 })(2); a"),
+    { nxt_string("var a = (function(a) { return a + 1 })(2); a"),
       nxt_string("3") },
 
-    { nxt_string("a = (function(a) { return a + 1 }(2)); a"),
+    { nxt_string("var a = (function(a) { return a + 1 }(2)); a"),
       nxt_string("3") },
 
-    { nxt_string("a = +function(a) { return a + 1 }(2); a"),
+    { nxt_string("var a = +function(a) { return a + 1 }(2); a"),
       nxt_string("3") },
 
-    { nxt_string("a = -function(a) { return a + 1 }(2); a"),
+    { nxt_string("var a = -function(a) { return a + 1 }(2); a"),
       nxt_string("-3") },
 
-    { nxt_string("a = !function(a) { return a + 1 }(2); a"),
+    { nxt_string("var a = !function(a) { return a + 1 }(2); a"),
       nxt_string("false") },
 
-    { nxt_string("a = ~function(a) { return a + 1 }(2); a"),
+    { nxt_string("var a = ~function(a) { return a + 1 }(2); a"),
       nxt_string("-4") },
 
-    { nxt_string("a = void function(a) { return a + 1 }(2); a"),
+    { nxt_string("var a = void function(a) { return a + 1 }(2); a"),
       nxt_string("undefined") },
 
-    { nxt_string("a = true && function(a) { return a + 1 }(2); a"),
+    { nxt_string("var a = true && function(a) { return a + 1 }(2); a"),
       nxt_string("3") },
 
-    { nxt_string("a = 0, function(a) { return a + 1 }(2); a"),
+    { nxt_string("var a; a = 0, function(a) { return a + 1 }(2); a"),
       nxt_string("0") },
 
-    { nxt_string("a = (0, function(a) { return a + 1 }(2)); a"),
+    { nxt_string("var a = (0, function(a) { return a + 1 }(2)); a"),
       nxt_string("3") },
 
     { nxt_string("var a = 0, function(a) { return a + 1 }(2); a"),
@@ -4185,7 +4202,7 @@ static njs_unit_test_t  njs_test[] =
 
     { nxt_string("var a = +function f(a) { return a + 1 }(2);"
                  "var b = f(5); a"),
-      nxt_string("ReferenceError") },
+      nxt_string("ReferenceError: \"f\" is not defined in 1") },
 
     { nxt_string("var o = { f: function(a) { return a * 2 } }; o.f(5)"),
       nxt_string("10") },
@@ -4317,7 +4334,7 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("01") },
 
     { nxt_string("function f(a) { return this+a };"
-                 "o = { g: function (f, a, b) { return f.call(a, b) } };"
+                 "var o = { g: function (f, a, b) { return f.call(a, b) } };"
                  "o.g(f, '0', 1)"),
       nxt_string("01") },
 
@@ -4408,7 +4425,7 @@ static njs_unit_test_t  njs_test[] =
                  "o.__proto__ === F.prototype"),
       nxt_string("true") },
 
-    { nxt_string("f = { F: function(){} }; o = new f.F();"
+    { nxt_string("var f = { F: function(){} }; var o = new f.F();"
                  "o.__proto__ === f.F.prototype"),
       nxt_string("true") },
 
@@ -4475,9 +4492,20 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("3 5") },
 
     { nxt_string("function a() { return function(x) { return x + 1 } }"
-                 "b = a(); b(2)"),
+                 "var b = a(); b(2)"),
       nxt_string("3") },
 
+    /* Scopes. */
+
+    { nxt_string("function f(x) { a = x } var a; f(5); a"),
+      nxt_string("5") },
+
+    { nxt_string("function f(x) { var a = x } var a = 2; f(5); a"),
+      nxt_string("2") },
+
+    { nxt_string("function f(a) { return a } var a = '2'; a + f(5)"),
+      nxt_string("25") },
+
     /* RegExp. */
 
     { nxt_string("/./x"),
@@ -7033,6 +7061,7 @@ njs_unit_test(nxt_bool_t disassemble)
 
         printf("\"%.*s\"\n",
                (int) njs_test[i].script.length, njs_test[i].script.start);
+        fflush(stdout);
 
         vm = njs_vm_create(mcp, &shared, &externals);
         if (vm == NULL) {