]> git.kaiwu.me - njs.git/commitdiff
Parser: release non-global semantic state
authorDmitry Volyntsev <xeioex@nginx.com>
Thu, 3 Sep 2026 15:53:14 +0000 (08:53 -0700)
committerDmitry Volyntsev <xeioexception@gmail.com>
Thu, 10 Sep 2026 03:11:04 +0000 (20:11 -0700)
Allocate non-global scopes, local variables, variable tree nodes, references,
and declaration arrays from the compilation pool.  The global scope and its
symbol table remain persistent for runtime lookup and later compilation.

Build closure-index arrays temporarily, then copy the finalized indexes into
VM-owned storage referenced by each lambda.  Preserve declaration-origin type
without retaining pointers to released block scopes.

src/njs_function.c
src/njs_generator.c
src/njs_parser.c
src/njs_parser.h
src/njs_variable.c
src/njs_variable.h
src/njs_vm.c
src/test/njs_unit_test.c

index 035e5efe3605cae47ce0e678b1a51f186c9e65a6..3cc76b35019040f412f7fee522682f2529029b8d 100644 (file)
@@ -1056,7 +1056,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     file = njs_str_value("runtime");
 
-    ret = njs_parser_init(vm, &parser, NULL, &file, str.start,
+    ret = njs_parser_init(vm, &parser, NULL, 0, &file, str.start,
                           str.start + str.length);
     if (njs_slow_path(ret != NJS_OK)) {
         goto done;
index 6ef38390e802db678544bebe53731b918ea6c762..29def6a175d921453d8926c42150633b187f2fd9 100644 (file)
@@ -1097,7 +1097,7 @@ njs_generate_name(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_scope_t     *scope;
     njs_vmcode_variable_t  *variable;
 
-    var = njs_variable_reference(vm, node);
+    var = njs_variable_reference(vm, generator->mem_pool, node);
     if (njs_slow_path(var == NULL)) {
         ret = njs_generate_global_reference(vm, generator, node, 1);
         if (njs_slow_path(ret != NJS_OK)) {
@@ -1133,7 +1133,7 @@ njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_scope_t     *scope;
     njs_vmcode_variable_t  *variable;
 
-    var = njs_variable_reference(vm, node);
+    var = njs_variable_reference(vm, generator->mem_pool, node);
 
     if (retvar != NULL) {
         *retvar = var;
@@ -2213,7 +2213,7 @@ njs_generate_for_in_name_assign(njs_vm_t *vm, njs_generator_t *generator,
     lvalue = foreach->left;
     expr = node->right;
 
-    var = njs_variable_reference(vm, lvalue);
+    var = njs_variable_reference(vm, generator->mem_pool, lvalue);
 
     if (var != NULL) {
         ctx->index_next_value = lvalue->index;
@@ -3003,7 +3003,7 @@ njs_generate_statement(njs_vm_t *vm, njs_generator_t *generator,
     right = node->right;
 
     if (right != NULL && right->token_type == NJS_TOKEN_NAME) {
-        var = njs_variable_reference(vm, right);
+        var = njs_variable_reference(vm, generator->mem_pool, right);
         if (njs_slow_path(var == NULL)) {
             goto statement;
         }
@@ -3178,7 +3178,7 @@ njs_generate_global_property_set(njs_vm_t *vm, njs_generator_t *generator,
     njs_variable_t         *var;
     njs_vmcode_prop_set_t  *prop_set;
 
-    var = njs_variable_reference(vm, node_dst);
+    var = njs_variable_reference(vm, generator->mem_pool, node_dst);
     if (var == NULL) {
         njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
                           NJS_VMCODE_PROPERTY_ATOM_SET, node_src);
@@ -4102,7 +4102,7 @@ njs_generate_function_expression(njs_vm_t *vm, njs_generator_t *generator,
     njs_function_lambda_t  *lambda;
     njs_vmcode_function_t  *function;
 
-    var = njs_variable_reference(vm, node->left);
+    var = njs_variable_reference(vm, generator->mem_pool, node->left);
     if (njs_slow_path(var == NULL)) {
         ret = njs_generate_reference_error(vm, generator, node->left);
         if (njs_slow_path(ret != NJS_OK)) {
@@ -4843,7 +4843,7 @@ njs_generate_function_declaration(njs_vm_t *vm, njs_generator_t *generator,
     njs_function_t         *function;
     njs_function_lambda_t  *lambda;
 
-    var = njs_variable_reference(vm, node);
+    var = njs_variable_reference(vm, generator->mem_pool, node);
     if (njs_slow_path(var == NULL)) {
         ret = njs_generate_reference_error(vm, generator, node);
         if (njs_slow_path(ret != NJS_OK)) {
@@ -4884,8 +4884,10 @@ njs_generate_function_scope(njs_vm_t *vm, njs_generator_t *prev,
     njs_function_lambda_t *lambda, njs_parser_node_t *node,
     const njs_str_t *name)
 {
+    size_t           size;
     njs_int_t        ret;
     njs_uint_t       depth;
+    njs_index_t      *closures;
     njs_vm_code_t    *code;
     njs_generator_t  generator;
 
@@ -4915,10 +4917,22 @@ njs_generate_function_scope(njs_vm_t *vm, njs_generator_t *prev,
     }
 
     lambda->start = generator.code_start;
-    lambda->closures = generator.closures->start;
     lambda->nclosures = generator.closures->items;
     lambda->nlocal = node->scope->items;
 
+    if (lambda->nclosures != 0) {
+        size = lambda->nclosures * sizeof(njs_index_t);
+
+        closures = njs_mp_alloc(vm->mem_pool, size);
+        if (njs_slow_path(closures == NULL)) {
+            njs_memory_error(vm);
+            return NJS_ERROR;
+        }
+
+        memcpy(closures, generator.closures->start, size);
+        lambda->closures = closures;
+    }
+
     return NJS_OK;
 }
 
@@ -4980,7 +4994,8 @@ njs_generate_scope(njs_vm_t *vm, njs_generator_t *generator,
         generator->lines = code->lines;
     }
 
-    generator->closures = njs_arr_create(vm->mem_pool, 4, sizeof(njs_index_t));
+    generator->closures = njs_arr_create(generator->mem_pool, 4,
+                                         sizeof(njs_index_t));
     if (njs_slow_path(generator->closures == NULL)) {
         return NULL;
     }
@@ -5860,7 +5875,7 @@ njs_generate_try_left(njs_vm_t *vm, njs_generator_t *generator,
     if (node->token_type == NJS_TOKEN_CATCH) {
         /* A "try/catch" case. */
 
-        var = njs_variable_reference(vm, node->left);
+        var = njs_variable_reference(vm, generator->mem_pool, node->left);
         if (njs_slow_path(var == NULL)) {
             return NJS_ERROR;
         }
@@ -5879,7 +5894,8 @@ njs_generate_try_left(njs_vm_t *vm, njs_generator_t *generator,
     if (node->left != NULL) {
         /* A try/catch/finally case. */
 
-        var = njs_variable_reference(vm, node->left->left);
+        var = njs_variable_reference(vm, generator->mem_pool,
+                                     node->left->left);
         if (njs_slow_path(var == NULL)) {
             return NJS_ERROR;
         }
@@ -6241,7 +6257,7 @@ njs_generate_import_statement(njs_vm_t *vm, njs_generator_t *generator,
 
     lvalue = node->left;
 
-    var = njs_variable_reference(vm, lvalue);
+    var = njs_variable_reference(vm, generator->mem_pool, lvalue);
     if (njs_slow_path(var == NULL)) {
         return NJS_ERROR;
     }
index 5238114aab1f8f9dbfa75bc58cd65f9c79abff35..91b324960faf98871d90c12355c01dd2c7419e24 100644 (file)
@@ -540,7 +540,7 @@ njs_parser_reject(njs_parser_t *parser)
 
 njs_int_t
 njs_parser_init(njs_vm_t *vm, njs_parser_t *parser, njs_parser_scope_t *scope,
-    njs_str_t *file, u_char *start, u_char *end)
+    njs_bool_t persistent_global, njs_str_t *file, u_char *start, u_char *end)
 {
     njs_int_t    ret;
     njs_lexer_t  *lexer;
@@ -548,6 +548,8 @@ njs_parser_init(njs_vm_t *vm, njs_parser_t *parser, njs_parser_scope_t *scope,
     njs_memzero(parser, sizeof(njs_parser_t));
 
     parser->scope = scope;
+    parser->persistent_scope = scope;
+    parser->persistent_global = persistent_global;
 
     parser->mem_pool = njs_mp_fast_create(2 * njs_pagesize(), 128, 512, 16);
     if (njs_slow_path(parser->mem_pool == NULL)) {
@@ -573,12 +575,38 @@ njs_parser_init(njs_vm_t *vm, njs_parser_t *parser, njs_parser_scope_t *scope,
 void
 njs_parser_destroy(njs_parser_t *parser)
 {
-    njs_parser_scope_t  *scope;
+    njs_variable_t       *var;
+    njs_rbtree_node_t    *node;
+    njs_parser_scope_t   *scope;
+    njs_variable_node_t  *var_node;
 
     for (scope = parser->scope; scope != NULL; scope = scope->parent) {
         scope->top = NULL;
     }
 
+    if (parser->persistent_scope != NULL) {
+        node = njs_rbtree_min(&parser->persistent_scope->variables);
+
+        while (njs_rbtree_is_there_successor(
+                   &parser->persistent_scope->variables, node))
+        {
+            var_node = (njs_variable_node_t *) node;
+            var = var_node->variable;
+
+            if (var->original != parser->persistent_scope) {
+                var->original = NULL;
+            }
+
+            node = njs_rbtree_node_successor(
+                       &parser->persistent_scope->variables, node);
+        }
+
+        njs_rbtree_init(&parser->persistent_scope->references,
+                        njs_parser_scope_rbtree_compare);
+        parser->persistent_scope->closures = NULL;
+        parser->persistent_scope->declarations = NULL;
+    }
+
     if (parser->mem_pool != NULL) {
         njs_mp_destroy(parser->mem_pool);
         parser->mem_pool = NULL;
@@ -697,7 +725,13 @@ njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type,
     njs_variable_t      *var;
     njs_parser_scope_t  *scope, *parent;
 
-    scope = njs_mp_zalloc(parser->vm->mem_pool, sizeof(njs_parser_scope_t));
+    if (parser->scope == NULL && parser->persistent_global) {
+        scope = njs_mp_zalloc(parser->vm->mem_pool,
+                              sizeof(njs_parser_scope_t));
+
+    } else {
+        scope = njs_mp_zalloc(parser->mem_pool, sizeof(njs_parser_scope_t));
+    }
     if (njs_slow_path(scope == NULL)) {
         return NJS_ERROR;
     }
@@ -711,6 +745,10 @@ njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type,
     scope->parent = parent;
     parser->scope = scope;
 
+    if (parent == NULL && parser->persistent_global) {
+        parser->persistent_scope = scope;
+    }
+
     if (type == NJS_SCOPE_FUNCTION || type == NJS_SCOPE_GLOBAL) {
         if (init_this) {
             /* Add this as first variable. */
@@ -9025,7 +9063,7 @@ njs_parser_variable_reference(njs_parser_t *parser, njs_parser_scope_t *scope,
         return NJS_OK;
     }
 
-    rb_parse_node = njs_mp_alloc(parser->vm->mem_pool,
+    rb_parse_node = njs_mp_alloc(parser->mem_pool,
                                  sizeof(njs_parser_rbtree_node_t));
     if (njs_slow_path(rb_parse_node == NULL)) {
         return NJS_ERROR;
index 882d903da8dfb282cdc060b584d64c538980fa20..54d3cd8c474fad57b64792534f4baa4d08ca3ca2 100644 (file)
@@ -94,6 +94,7 @@ struct njs_parser_s {
     njs_parser_node_t               *node;
     njs_parser_node_t               *target;
     njs_parser_scope_t              *scope;
+    njs_parser_scope_t              *persistent_scope;
     njs_rbtree_t                    labels;
     njs_variable_type_t             var_type;
     njs_int_t                       ret;
@@ -102,6 +103,7 @@ struct njs_parser_s {
     uint8_t                         allow_in;
 
     uint8_t                         module;
+    uint8_t                         persistent_global;
 
     njs_str_t                       file;
     uint32_t                        line;
@@ -144,7 +146,8 @@ njs_int_t njs_parser_failed_state(njs_parser_t *parser,
 intptr_t njs_parser_scope_rbtree_compare(njs_rbtree_node_t *node1,
     njs_rbtree_node_t *node2);
 njs_int_t njs_parser_init(njs_vm_t *vm, njs_parser_t *parser,
-    njs_parser_scope_t *scope, njs_str_t *file, u_char *start, u_char *end);
+    njs_parser_scope_t *scope, njs_bool_t persistent_global, njs_str_t *file,
+    u_char *start, u_char *end);
 void njs_parser_destroy(njs_parser_t *parser);
 njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser);
 
index 176438fb1f5820b9c2eb730c8bf6ae525622e507..4f5ba0eae8225a165cc6411680c86fae99bda75e 100644 (file)
@@ -13,8 +13,8 @@ static njs_declaration_t *njs_variable_scope_function_add(njs_parser_t *parser,
     njs_parser_scope_t *scope);
 static njs_parser_scope_t *njs_variable_scope_find(njs_parser_t *parser,
      njs_parser_scope_t *scope, uintptr_t atom_id, njs_variable_type_t type);
-static njs_variable_t *njs_variable_alloc(njs_vm_t *vm, uintptr_t atom_id,
-    njs_variable_type_t type);
+static njs_variable_t *njs_variable_alloc(njs_vm_t *vm, njs_mp_t *mem_pool,
+    uintptr_t atom_id, njs_variable_type_t type);
 
 
 njs_variable_t *
@@ -96,7 +96,7 @@ static njs_declaration_t *
 njs_variable_scope_function_add(njs_parser_t *parser, njs_parser_scope_t *scope)
 {
     if (scope->declarations == NULL) {
-        scope->declarations = njs_arr_create(parser->vm->mem_pool, 1,
+        scope->declarations = njs_arr_create(parser->mem_pool, 1,
                                              sizeof(njs_declaration_t));
         if (njs_slow_path(scope->declarations == NULL)) {
             return NULL;
@@ -202,11 +202,11 @@ njs_variable_scope_find(njs_parser_t *parser, njs_parser_scope_t *scope,
         goto failed;
     }
 
-    if (var->original->type == NJS_SCOPE_BLOCK) {
+    if (var->original_type == NJS_SCOPE_BLOCK) {
         if (type == NJS_VARIABLE_FUNCTION
             || var->type == NJS_VARIABLE_FUNCTION)
         {
-            if (var->original == root) {
+            if (var->original != NULL && var->original == root) {
                 goto failed;
             }
         }
@@ -251,6 +251,7 @@ njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope,
     njs_parser_scope_t *original, uintptr_t atom_id,
     njs_variable_type_t type, njs_index_t index)
 {
+    njs_mp_t             *mem_pool;
     njs_variable_t       *var;
     njs_rbtree_node_t    *node;
     njs_parser_scope_t   *root;
@@ -264,7 +265,10 @@ njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope,
         return ((njs_variable_node_t *) node)->variable;
     }
 
-    var = njs_variable_alloc(parser->vm, atom_id, type);
+    mem_pool = (scope == parser->persistent_scope) ? parser->vm->mem_pool
+                                                   : parser->mem_pool;
+
+    var = njs_variable_alloc(parser->vm, mem_pool, atom_id, type);
     if (njs_slow_path(var == NULL)) {
         goto memory_error;
     }
@@ -272,6 +276,7 @@ njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope,
     var->scope = scope;
     var->index = index;
     var->original = original;
+    var->original_type = original->type;
 
     if (index == NJS_INDEX_NONE) {
         root = njs_function_scope(scope);
@@ -284,7 +289,7 @@ njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope,
         root->items++;
     }
 
-    var_node_new = njs_variable_node_alloc(parser->vm, var, atom_id);
+    var_node_new = njs_variable_node_alloc(mem_pool, var, atom_id);
     if (njs_slow_path(var_node_new == NULL)) {
         goto memory_error;
     }
@@ -390,7 +395,7 @@ njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node)
 
 
 static njs_index_t
-njs_variable_closure(njs_vm_t *vm, njs_variable_t *var,
+njs_variable_closure(njs_vm_t *vm, njs_mp_t *mem_pool, njs_variable_t *var,
     njs_parser_scope_t *scope)
 {
     njs_index_t               index, prev_index, *idx;
@@ -460,7 +465,7 @@ njs_variable_closure(njs_vm_t *vm, njs_variable_t *var,
             if (parse_node == NULL) {
                 /* Create new reference for closure. */
 
-                parse_node = njs_mp_alloc(vm->mem_pool,
+                parse_node = njs_mp_alloc(mem_pool,
                                           sizeof(njs_parser_rbtree_node_t));
                 if (njs_slow_path(parse_node == NULL)) {
                     return NJS_INDEX_ERROR;
@@ -482,7 +487,8 @@ njs_variable_closure(njs_vm_t *vm, njs_variable_t *var,
 
 
 njs_variable_t *
-njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node)
+njs_variable_reference(njs_vm_t *vm, njs_mp_t *mem_pool,
+    njs_parser_node_t *node)
 {
     njs_bool_t                closure;
     njs_rbtree_node_t         *rb_node;
@@ -527,7 +533,7 @@ njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node)
 
     ref->variable->closure = closure;
 
-    node->index = njs_variable_closure(vm, ref->variable, scope);
+    node->index = njs_variable_closure(vm, mem_pool, ref->variable, scope);
     if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
         return NULL;
     }
@@ -548,11 +554,12 @@ njs_label_find(njs_parser_t *parser, uintptr_t atom_id)
 
 
 static njs_variable_t *
-njs_variable_alloc(njs_vm_t *vm, uintptr_t atom_id, njs_variable_type_t type)
+njs_variable_alloc(njs_vm_t *vm, njs_mp_t *mem_pool, uintptr_t atom_id,
+    njs_variable_type_t type)
 {
     njs_variable_t  *var;
 
-    var = njs_mp_zalloc(vm->mem_pool, sizeof(njs_variable_t));
+    var = njs_mp_zalloc(mem_pool, sizeof(njs_variable_t));
     if (njs_slow_path(var == NULL)) {
         njs_memory_error(vm);
         return NULL;
index d417cd377f8b5d562800cbd01e78a54b9f6dd21c..8b89a427827f700564eb55bf62089a861598b6dc 100644 (file)
@@ -26,6 +26,7 @@ typedef struct {
     uint8_t               self;
     uint8_t               init;
     uint8_t               closure;
+    njs_scope_t           original_type:8;
 
     njs_parser_scope_t    *scope;
     njs_parser_scope_t    *original;
@@ -62,7 +63,8 @@ njs_variable_t *njs_variable_function_add(njs_parser_t *parser,
 njs_int_t njs_label_add(njs_parser_t *parser, uintptr_t atom_id);
 njs_bool_t njs_label_find(njs_parser_t *parser, uintptr_t atom_id);
 njs_int_t njs_label_remove(njs_parser_t *parser, uintptr_t atom_id);
-njs_variable_t *njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node);
+njs_variable_t *njs_variable_reference(njs_vm_t *vm, njs_mp_t *mem_pool,
+    njs_parser_node_t *node);
 njs_variable_t *njs_variable_scope_add(njs_parser_t *parser,
     njs_parser_scope_t *scope, njs_parser_scope_t *original,
     uintptr_t atom_id, njs_variable_type_t type, njs_index_t index);
@@ -70,11 +72,11 @@ njs_int_t njs_name_copy(njs_vm_t *vm, njs_str_t *dst, const njs_str_t *src);
 
 
 njs_inline njs_variable_node_t *
-njs_variable_node_alloc(njs_vm_t *vm, njs_variable_t *var, uintptr_t key)
+njs_variable_node_alloc(njs_mp_t *mem_pool, njs_variable_t *var, uintptr_t key)
 {
     njs_variable_node_t  *node;
 
-    node = njs_mp_zalloc(vm->mem_pool, sizeof(njs_variable_node_t));
+    node = njs_mp_zalloc(mem_pool, sizeof(njs_variable_node_t));
 
     if (njs_fast_path(node != NULL)) {
         node->key = key;
index 3b517b6a3d8e097828dfbeac36bb472c4d1b574a..bbb7bb2982c72c12cc5642e5e6f970844d145c8c 100644 (file)
@@ -225,8 +225,8 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
 
     global_items = (vm->global_scope != NULL) ? vm->global_scope->items : 0;
 
-    ret = njs_parser_init(vm, &parser, vm->global_scope, &vm->options.file,
-                          *start, end);
+    ret = njs_parser_init(vm, &parser, vm->global_scope, 1,
+                          &vm->options.file, *start, end);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
@@ -341,7 +341,7 @@ njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start,
         return NULL;
     }
 
-    ret = njs_parser_init(vm, &parser, NULL, &module->name, *start, end);
+    ret = njs_parser_init(vm, &parser, NULL, 0, &module->name, *start, end);
     if (njs_slow_path(ret != NJS_OK)) {
         return NULL;
     }
index a0b2bd16cd0ff1c3b6fb4498437ae926ea952ac1..b609044c4e62c57aee6745554b37a520e7efcb28 100644 (file)
@@ -15395,6 +15395,11 @@ static njs_unit_test_t  njs_test[] =
               "sum(2, 4);"),
       njs_str("6") },
 
+    { njs_str("(new Function('a',"
+              " 'return function(b) { return function(c) {"
+              " return a + b + c; }; };'))(1)(2)(3)"),
+      njs_str("6") },
+
     { njs_str("var sum = new Function('a, b', 'c', 'return a + b + c');"
               "sum(2, 4, 4);"),
       njs_str("10") },