]> git.kaiwu.me - njs.git/commitdiff
Parser: carry the declaration kind across nested parsing
authorDmitry Volyntsev <xeioex@nginx.com>
Tue, 1 Sep 2026 13:46:16 +0000 (06:46 -0700)
committerDmitry Volyntsev <xeioexception@gmail.com>
Fri, 4 Sep 2026 20:56:37 +0000 (13:56 -0700)
Previously, the kind of a declaration being parsed lived in a mutable
parser->var_type, written once when "var", "let" or "const" was seen and read
for every declarator in the list.  A declaration nested in a declarator
initializer overwrote it, so the declarators after it were recorded with the
wrong kind and lexical ones lost their per-iteration binding:

    let out = [];
    for (let a = function() { var z; }, b = 0; b < 2; b++) out.push(() => b);
    out[0]() + ',' + out[1]()

gave "2,2" instead of "0,1", because "b" was recorded as a var.

The fix is to carry the kind in njs_parser_stack_entry_t, as the [In]
parameter already is, so a continuation restores the kind in effect when it
was pushed.  njs_parser_stack_entry_t does not grow.

src/njs_parser.c
src/njs_parser.h
src/test/njs_unit_test.c

index 370b4626b67c42c15124b9659bb6cfa486da53be..9328cea442efe746581a80c99c63304a56df930e 100644 (file)
@@ -530,6 +530,7 @@ njs_parser_reject(njs_parser_t *parser)
             njs_parser_next(parser, entry->state);
             parser->target = entry->node;
             parser->allow_in = entry->allow_in;
+            parser->var_type = entry->var_type;
 
             return NJS_DECLINED;
         }
index d3ae85bbae797d4fd8f213b376f3e56ecf5a60c0..3fa821d6059f8bc36794f60a25c9d9d761209b43 100644 (file)
@@ -95,6 +95,7 @@ typedef struct {
 
     njs_parser_node_t               *node;
 
+    njs_variable_type_t             var_type;
     uint8_t                         allow_in;
     uint8_t                         optional;
 } njs_parser_stack_entry_t;
@@ -304,6 +305,7 @@ njs_parser_stack_pop(njs_parser_t *parser)
 
     parser->target = entry->node;
     parser->allow_in = entry->allow_in;
+    parser->var_type = entry->var_type;
 
     njs_mp_free(parser->vm->mem_pool, entry);
 
@@ -349,6 +351,7 @@ _njs_parser_after(njs_parser_t *parser, njs_queue_link_t *link, void *node,
     entry->state = state;
     entry->node = node;
     entry->allow_in = parser->allow_in;
+    entry->var_type = parser->var_type;
     entry->optional = is_optional;
 
     njs_queue_insert_before(link, &entry->link);
index 2a801b3822944b9c286534b5d7c9ace87a6b1d13..cf5274f728cd880121ab682e24c621248be9a5e7 100644 (file)
@@ -20691,6 +20691,49 @@ static njs_unit_test_t  njs_test[] =
               "} res"),
       njs_str("0,0,0,0,0") },
 
+    /*
+     * A declaration nested in a declarator initializer must not change the
+     * kind of the declarators that follow it.
+     */
+
+    { njs_str("let out = [];"
+              "for (let a = function() { var z; }, b = 0; b < 2; b++) {"
+              "    out.push(() => b);"
+              "} out[0]() + ',' + out[1]()"),
+      njs_str("0,1") },
+
+    { njs_str("let out = [];"
+              "for (let a = 1, b = function() { var z; }, c = 0; c < 2; c++) {"
+              "    out.push(() => c);"
+              "} out[0]() + ',' + out[1]()"),
+      njs_str("0,1") },
+
+    { njs_str("let out = [];"
+              "for (var a = function() { let z; }, b = 0; b < 2; b++) {"
+              "    out.push(() => b);"
+              "} out[0]() + ',' + out[1]()"),
+      njs_str("2,2") },
+
+    { njs_str("var r;"
+              "for (const a = function() { var z; }, b = 2; b > 0;) {"
+              "    try { b = 3 } catch (e) { r = e.name } break"
+              "} r"),
+      njs_str("TypeError") },
+
+    /* The same state serves an ordinary declaration list. */
+
+    { njs_str("var r;"
+              "const a = function() { var z; }, b = 2;"
+              "try { b = 3 } catch (e) { r = e.name } r"),
+      njs_str("TypeError") },
+
+    { njs_str("let a = function() { var z; }, b = 0;"
+              "for (; b < 2; b++) {} b"),
+      njs_str("2") },
+
+    { njs_str("let a = function() { var ; }, b = 0;"),
+      njs_str("SyntaxError: Unexpected token \";\"") },
+
     { njs_str("let arr = [], res = [];"
               "for (let i = 0; arr.push(() => i), i < 10; i++) {}"
               "for (let k = 0; k < 10; k++) {res.push(arr[k]())}"