[njs] Fixed the calculation of the scope indices for variables.

Dmitry Volyntsev xeioex at nginx.com
Fri Feb 9 17:14:05 UTC 2018


details:   http://hg.nginx.org/njs/rev/03eebf0e08cc
branches:  
changeset: 438:03eebf0e08cc
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Fri Feb 09 20:11:17 2018 +0300
description:
Fixed the calculation of the scope indices for variables.

diffstat:

 njs/njs_parser.h         |  14 +++++---
 njs/njs_variable.c       |  80 +++++++++++++++++++++++++++++++++++++----------
 njs/test/njs_unit_test.c |  21 ++++++++++++
 3 files changed, 93 insertions(+), 22 deletions(-)

diffs (221 lines):

diff -r 05605a9f0ad7 -r 03eebf0e08cc njs/njs_parser.h
--- a/njs/njs_parser.h	Fri Feb 09 19:16:19 2018 +0300
+++ b/njs/njs_parser.h	Fri Feb 09 20:11:17 2018 +0300
@@ -235,11 +235,15 @@ struct njs_parser_scope_s {
     nxt_lvlhsh_t                    variables;
     nxt_lvlhsh_t                    references;
 
-    nxt_array_t                     *values[2]; /* Array of njs_value_t. */
+    /*
+     * 0: local scope index;
+     * 1: closure scope index.
+     */
+    nxt_array_t                     *values[2];  /* Array of njs_value_t. */
     njs_index_t                     next_index[2];
 
     njs_scope_t                     type:8;
-    uint8_t                         nesting;    /* 4 bits */
+    uint8_t                         nesting;     /* 4 bits */
     uint8_t                         argument_closures;
 };
 
@@ -248,9 +252,9 @@ typedef struct njs_parser_node_s    njs_
 
 struct njs_parser_node_s {
     njs_token_t                     token:16;
-    uint8_t                         ctor:1;     /* 1 bit  */
-    uint8_t                         temporary;  /* 1 bit  */
-    uint8_t                         reference;  /* 1 bit  */
+    uint8_t                         ctor:1;      /* 1 bit  */
+    uint8_t                         temporary;   /* 1 bit  */
+    uint8_t                         reference;   /* 1 bit  */
     uint32_t                        token_line;
     uint32_t                        variable_name_hash;
 
diff -r 05605a9f0ad7 -r 03eebf0e08cc njs/njs_variable.c
--- a/njs/njs_variable.c	Fri Feb 09 19:16:19 2018 +0300
+++ b/njs/njs_variable.c	Fri Feb 09 20:11:17 2018 +0300
@@ -223,15 +223,17 @@ njs_variable_reference(njs_vm_t *vm, njs
 }
 
 
-njs_ret_t
-njs_variables_scope_reference(njs_vm_t *vm, njs_parser_scope_t *scope)
+static njs_ret_t
+njs_variables_scope_resolve(njs_vm_t *vm, njs_parser_scope_t *scope,
+    nxt_bool_t local_scope)
 {
-    njs_ret_t           ret;
-    nxt_queue_t         *nested;
-    njs_variable_t      *var;
-    nxt_queue_link_t    *lnk;
-    njs_parser_node_t   *node;
-    nxt_lvlhsh_each_t   lhe;
+    njs_ret_t             ret;
+    nxt_queue_t           *nested;
+    njs_variable_t        *var;
+    nxt_queue_link_t      *lnk;
+    njs_parser_node_t     *node;
+    nxt_lvlhsh_each_t     lhe;
+    njs_variable_scope_t  vs;
 
     nested = &scope->nested;
 
@@ -241,7 +243,7 @@ njs_variables_scope_reference(njs_vm_t *
     {
         scope = nxt_queue_link_data(lnk, njs_parser_scope_t, link);
 
-        ret = njs_variables_scope_reference(vm, scope);
+        ret = njs_variables_scope_resolve(vm, scope, local_scope);
         if (nxt_slow_path(ret != NXT_OK)) {
             return NXT_ERROR;
         }
@@ -255,6 +257,25 @@ njs_variables_scope_reference(njs_vm_t *
                 break;
             }
 
+            if (!local_scope) {
+                ret = njs_variable_find(vm, node, &vs);
+                if (nxt_slow_path(ret != NXT_OK)) {
+                    continue;
+                }
+
+                if (vs.scope->type == NJS_SCOPE_GLOBAL) {
+                    continue;
+                }
+
+                if (node->scope->nesting == vs.scope->nesting) {
+                    /*
+                     * A variable is referenced locally here, but may be
+                     * referenced non-locally in other places, skipping.
+                     */
+                    continue;
+                }
+            }
+
             var = njs_variable_get(vm, node);
             if (nxt_slow_path(var == NULL)) {
                 return NXT_ERROR;
@@ -266,6 +287,31 @@ njs_variables_scope_reference(njs_vm_t *
 }
 
 
+njs_ret_t
+njs_variables_scope_reference(njs_vm_t *vm, njs_parser_scope_t *scope)
+{
+    njs_ret_t  ret;
+
+    /*
+     * Calculating proper scope types for variables.
+     * A variable is considered to be local variable if it is referenced
+     * only in the local scope (reference and definition nestings are the same).
+     */
+
+    ret = njs_variables_scope_resolve(vm, scope, 0);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NXT_ERROR;
+    }
+
+    ret = njs_variables_scope_resolve(vm, scope, 1);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NXT_ERROR;
+    }
+
+    return NXT_OK;
+}
+
+
 njs_index_t
 njs_variable_typeof(njs_vm_t *vm, njs_parser_node_t *node)
 {
@@ -309,7 +355,7 @@ njs_variable_t *
 njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node)
 {
     nxt_int_t             ret;
-    nxt_uint_t            n;
+    nxt_uint_t            scope_index;
     nxt_array_t           *values;
     njs_index_t           index;
     njs_value_t           *value;
@@ -322,10 +368,10 @@ njs_variable_get(njs_vm_t *vm, njs_parse
         goto not_found;
     }
 
-    n = 0;
+    scope_index = 0;
 
     if (vs.scope->type > NJS_SCOPE_GLOBAL) {
-        n = (node->scope->nesting != vs.scope->nesting);
+        scope_index = (node->scope->nesting != vs.scope->nesting);
     }
 
     var = vs.variable;
@@ -333,7 +379,7 @@ njs_variable_get(njs_vm_t *vm, njs_parse
 
     if (index != NJS_INDEX_NONE) {
 
-        if (n == 0 || njs_scope_type(index) != NJS_SCOPE_ARGUMENTS) {
+        if (scope_index == 0 || njs_scope_type(index) != NJS_SCOPE_ARGUMENTS) {
             node->index = index;
 
             return var;
@@ -371,7 +417,7 @@ njs_variable_get(njs_vm_t *vm, njs_parse
         index = (njs_index_t) value;
 
     } else {
-        values = vs.scope->values[n];
+        values = vs.scope->values[scope_index];
 
         if (values == NULL) {
             values = nxt_array_create(4, sizeof(njs_value_t),
@@ -380,7 +426,7 @@ njs_variable_get(njs_vm_t *vm, njs_parse
                 return NULL;
             }
 
-            vs.scope->values[n] = values;
+            vs.scope->values[scope_index] = values;
         }
 
         value = nxt_array_add(values, &njs_array_mem_proto, vm->mem_cache_pool);
@@ -388,8 +434,8 @@ njs_variable_get(njs_vm_t *vm, njs_parse
             return NULL;
         }
 
-        index = vs.scope->next_index[n];
-        vs.scope->next_index[n] += sizeof(njs_value_t);
+        index = vs.scope->next_index[scope_index];
+        vs.scope->next_index[scope_index] += sizeof(njs_value_t);
     }
 
     if (njs_is_object(&var->value)) {
diff -r 05605a9f0ad7 -r 03eebf0e08cc njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Fri Feb 09 19:16:19 2018 +0300
+++ b/njs/test/njs_unit_test.c	Fri Feb 09 20:11:17 2018 +0300
@@ -4591,6 +4591,27 @@ static njs_unit_test_t  njs_test[] =
                  "function f() { function h() { x = 3; return y; } }"),
       nxt_string("undefined") },
 
+    { nxt_string("function f() {"
+                 "    var a = 'a';"
+                 "    if (0) { a = 'b' };"
+                 "    function f2() { return a };"
+                 "    return f2"
+                 "};"
+                 "f()()"),
+      nxt_string("a") },
+
+    { nxt_string("function f() {"
+                 "    var a = 'a'; "
+                 "    if (0) { if (0) {a = 'b'} };"
+                 "    function f2() { return a };"
+                 "    return f2"
+                 "};"
+                 "f()()"),
+      nxt_string("a") },
+
+    { nxt_string("function f() { var a = f2(); }"),
+      nxt_string("ReferenceError: \"f2\" is not defined in 1") },
+
     /* Recursive fibonacci. */
 
     { nxt_string("function fibo(n) {"


More information about the nginx-devel mailing list