#include <string.h>
+static njs_variable_t *njs_variable_scope_add(njs_vm_t *vm,
+ njs_parser_scope_t *scope, nxt_lvlhsh_query_t *lhq,
+ njs_variable_type_t type);
static njs_ret_t njs_variable_reference_resolve(njs_vm_t *vm,
njs_variable_reference_t *vr, njs_parser_scope_t *node_scope);
static njs_variable_t *njs_variable_alloc(njs_vm_t *vm, nxt_str_t *name,
njs_variable_add(njs_vm_t *vm, njs_parser_scope_t *scope, nxt_str_t *name,
uint32_t hash, njs_variable_type_t type)
{
- nxt_int_t ret;
njs_variable_t *var;
nxt_lvlhsh_query_t lhq;
lhq.key = *name;
lhq.proto = &njs_variables_hash_proto;
- if (type >= NJS_VARIABLE_VAR) {
- /*
- * A "var" and "function" declarations are
- * stored in function or global scope.
- */
- while (scope->type == NJS_SCOPE_BLOCK) {
+ var = njs_variable_scope_add(vm, scope, &lhq, type);
+ if (nxt_slow_path(var == NULL)) {
+ return NULL;
+ }
+
+ if (type == NJS_VARIABLE_VAR && scope->type == NJS_SCOPE_BLOCK) {
+ /* A "var" declaration is stored in function or global scope. */
+ do {
scope = scope->parent;
- }
+
+ var = njs_variable_scope_add(vm, scope, &lhq, type);
+ if (nxt_slow_path(var == NULL)) {
+ return NULL;
+ }
+
+ } while (scope->type == NJS_SCOPE_BLOCK);
}
- if (nxt_lvlhsh_find(&scope->variables, &lhq) == NXT_OK) {
- var = lhq.value;
+ if (type == NJS_VARIABLE_FUNCTION) {
+ var->type = type;
+ }
+
+ return var;
+}
+
- if (type == NJS_VARIABLE_FUNCTION) {
- var->type = type;
+static njs_variable_t *
+njs_variable_scope_add(njs_vm_t *vm, njs_parser_scope_t *scope,
+ nxt_lvlhsh_query_t *lhq, njs_variable_type_t type)
+{
+ nxt_int_t ret;
+ njs_variable_t *var;
+
+ if (nxt_lvlhsh_find(&scope->variables, lhq) == NXT_OK) {
+ var = lhq->value;
+
+ if (!scope->module && scope->type != NJS_SCOPE_BLOCK) {
+ return var;
+ }
+
+ if (type == NJS_VARIABLE_FUNCTION
+ || var->type == NJS_VARIABLE_FUNCTION)
+ {
+ njs_parser_syntax_error(vm, vm->parser,
+ "\"%V\" has already been declared",
+ &lhq->key);
+ return NULL;
}
return var;
}
- var = njs_variable_alloc(vm, &lhq.key, type);
+ var = njs_variable_alloc(vm, &lhq->key, type);
if (nxt_slow_path(var == NULL)) {
- return var;
+ return NULL;
}
- lhq.replace = 0;
- lhq.value = var;
- lhq.pool = vm->mem_pool;
+ lhq->replace = 0;
+ lhq->value = var;
+ lhq->pool = vm->mem_pool;
- ret = nxt_lvlhsh_insert(&scope->variables, &lhq);
+ ret = nxt_lvlhsh_insert(&scope->variables, lhq);
if (nxt_fast_path(ret == NXT_OK)) {
return var;
if (nxt_lvlhsh_find(&scope->variables, &lhq) == NXT_OK) {
vr->variable = lhq.value;
+ if (scope->type == NJS_SCOPE_BLOCK
+ && vr->variable->type == NJS_VARIABLE_VAR)
+ {
+ scope = scope->parent;
+ continue;
+ }
+
if (scope->type == NJS_SCOPE_SHIM) {
scope = previous;
{ nxt_string("do function f() { } while (0)"),
nxt_string("SyntaxError: Functions can only be declared at top level or inside a block in 1") },
+ { nxt_string("function f() { return 1; } { function f() { return 2; } } f()"),
+ nxt_string("1") },
+
+ { nxt_string("function f() { return 1; } { function f() { return 2; } { function f() { return 3; } }} f()"),
+ nxt_string("1") },
+
+ { nxt_string("{ var f; function f() {} }"),
+ nxt_string("SyntaxError: \"f\" has already been declared in 1") },
+
+ { nxt_string("{ function f() {} var f; }"),
+ nxt_string("SyntaxError: \"f\" has already been declared in 1") },
+
+ { nxt_string("{ function f() {} { var f }}"),
+ nxt_string("SyntaxError: \"f\" has already been declared in 1") },
+
{ nxt_string("function f() { return f() } f()"),
nxt_string("RangeError: Maximum call stack size exceeded") },
};
+static njs_unit_test_t njs_module_test[] =
+{
+ { nxt_string("function f(){return 2}; var f; f()"),
+ nxt_string("SyntaxError: \"f\" has already been declared in 1") },
+
+ { nxt_string("function f(){return 2}; var f = 1; f()"),
+ nxt_string("SyntaxError: \"f\" has already been declared in 1") },
+
+ { nxt_string("function f(){return 1}; function f(){return 2}; f()"),
+ nxt_string("SyntaxError: \"f\" has already been declared in 1") },
+
+ { nxt_string("var f = 1; function f() {};"),
+ nxt_string("SyntaxError: \"f\" has already been declared in 1") },
+
+ { nxt_string("{ var f = 1; } function f() {};"),
+ nxt_string("SyntaxError: \"f\" has already been declared in 1") },
+};
+
+
static njs_unit_test_t njs_tz_test[] =
{
{ nxt_string("var d = new Date(1); d = d + ''; d.slice(0, 33)"),
(void) putenv((char *) "TZ=UTC");
tzset();
- ret = njs_unit_test(njs_test, nxt_nitems(njs_test), 0,
+ /* script tests. */
+
+ ret = njs_unit_test(njs_test, nxt_nitems(njs_test), 0, disassemble,
+ verbose);
+ if (ret != NXT_OK) {
+ return ret;
+ }
+
+ /* module tests. */
+
+ ret = njs_unit_test(njs_module_test, nxt_nitems(njs_module_test), 1,
disassemble, verbose);
if (ret != NXT_OK) {
return ret;