From: Igor Sysoev Date: Mon, 2 Jan 2017 19:59:24 +0000 (+0300) Subject: Global and function scopes have been fixed. Implicitly declared X-Git-Tag: 0.1.8~11 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=7447df08bbb9f6259f9e8a687960c06517c08f5a;p=njs.git Global and function scopes have been fixed. Implicitly declared variables are not supported anymore. --- diff --git a/Makefile b/Makefile index ca486c41..deb9de66 100644 --- 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 \ diff --git a/njs/njs_disassembler.c b/njs/njs_disassembler.c index 4b2c488e..3a406c29 100644 --- a/njs/njs_disassembler.c +++ b/njs/njs_disassembler.c @@ -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 ") }, diff --git a/njs/njs_function.c b/njs/njs_function.c index 2153f020..bbba2cb1 100644 --- a/njs/njs_function.c +++ b/njs/njs_function.c @@ -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; } diff --git a/njs/njs_generator.c b/njs/njs_generator.c index caa9bae6..19642a92 100644 --- a/njs/njs_generator.c +++ b/njs/njs_generator.c @@ -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); -} diff --git a/njs/njs_parser.c b/njs/njs_parser.c index 868da57f..2a346e96 100644 --- a/njs/njs_parser.c +++ b/njs/njs_parser.c @@ -38,11 +38,15 @@ * 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); diff --git a/njs/njs_parser.h b/njs/njs_parser.h index 902124ab..7a87bf2c 100644 --- a/njs/njs_parser.h +++ b/njs/njs_parser.h @@ -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); diff --git a/njs/njs_parser_expression.c b/njs/njs_parser_expression.c index 64b1fe77..2b6bd186 100644 --- a/njs/njs_parser_expression.c +++ b/njs/njs_parser_expression.c @@ -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) { diff --git a/njs/njs_variable.c b/njs/njs_variable.c index 971d078d..0a61f26e 100644 --- a/njs/njs_variable.c +++ b/njs/njs_variable.c @@ -23,8 +23,8 @@ #include -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)); -} diff --git a/njs/njs_variable.h b/njs/njs_variable.h index 7fdd3067..60294b37 100644 --- a/njs/njs_variable.h +++ b/njs/njs_variable.h @@ -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); diff --git a/njs/njs_vm.c b/njs/njs_vm.c index d8a5236e..0257926b 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -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; diff --git a/njs/njs_vm.h b/njs/njs_vm.h index ba65a43c..772526af 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -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; diff --git a/njs/njscript.c b/njs/njscript.c index 049fbce2..a5501182 100644 --- a/njs/njscript.c +++ b/njs/njscript.c @@ -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; diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 6e84d1d9..476e0061 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -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