]> git.kaiwu.me - njs.git/commitdiff
Parser: release temporary compilation memory
authorDmitry Volyntsev <xeioex@nginx.com>
Thu, 3 Sep 2026 15:53:03 +0000 (08:53 -0700)
committerDmitry Volyntsev <xeioexception@gmail.com>
Thu, 10 Sep 2026 03:11:04 +0000 (20:11 -0700)
Create a pool for each parser invocation and allocate parser nodes, array-item
storage, continuations, preread tokens, and active labels from it.  Destroy the
pool after generation and on parser or generator failures.

Keep atoms, lambdas, regexp patterns, scopes, variables, bytecode, and runtime
constants in the VM pool.  Clear persistent scope AST roots before releasing
the temporary pool.

src/njs_function.c
src/njs_generator.c
src/njs_lexer.c
src/njs_lexer.h
src/njs_parser.c
src/njs_parser.h
src/njs_variable.c
src/njs_vm.c

index b6a8a467ec628cdaf5b869217ec99019afc5f0f4..fafbd1aabb20e58777990fa74b3af9e670a08e2c 100644 (file)
@@ -989,6 +989,11 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_function_lambda_t   *lambda;
     const njs_token_type_t  *type;
 
+    njs_memzero(&chain, sizeof(njs_chb_t));
+    str.length = 0;
+    str.start = NULL;
+    parser.mem_pool = NULL;
+
     static const njs_token_type_t  safe_ast[] = {
         NJS_TOKEN_END,
         NJS_TOKEN_FUNCTION_EXPRESSION,
@@ -1023,7 +1028,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     for (i = 1; i < nargs - 1; i++) {
         ret = njs_value_to_chain(vm, &chain, njs_argument(args, i));
         if (njs_slow_path(ret < NJS_OK)) {
-            return ret;
+            goto done;
         }
 
         if (i != (nargs - 2)) {
@@ -1036,7 +1041,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     if (nargs > 1) {
         ret = njs_value_to_chain(vm, &chain, njs_argument(args, nargs - 1));
         if (njs_slow_path(ret < NJS_OK)) {
-            return ret;
+            goto done;
         }
     }
 
@@ -1045,7 +1050,8 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ret = njs_chb_join(&chain, &str);
     if (njs_slow_path(ret != NJS_OK)) {
         njs_memory_error(vm);
-        return NJS_ERROR;
+        ret = NJS_ERROR;
+        goto done;
     }
 
     file = njs_str_value("runtime");
@@ -1053,12 +1059,12 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ret = njs_parser_init(vm, &parser, NULL, &file, str.start,
                           str.start + str.length);
     if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
+        goto done;
     }
 
     ret = njs_parser(vm, &parser);
     if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
+        goto done;
     }
 
     if (!vm->options.unsafe) {
@@ -1092,7 +1098,8 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     ret = njs_generator_init(&generator, &file, 0, 1);
     if (njs_slow_path(ret != NJS_OK)) {
         njs_internal_error(vm, "njs_generator_init() failed");
-        return NJS_ERROR;
+        ret = NJS_ERROR;
+        goto done;
     }
 
     code = njs_generate_scope(vm, &generator, parser.scope,
@@ -1102,24 +1109,25 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
             njs_internal_error(vm, "njs_generate_scope() failed");
         }
 
-        return NJS_ERROR;
+        ret = NJS_ERROR;
+        goto done;
     }
 
-    njs_chb_destroy(&chain);
-
     if ((code->end - code->start)
         != (sizeof(njs_vmcode_function_t) + sizeof(njs_vmcode_return_t))
         || ((njs_vmcode_generic_t *) code->start)->code != NJS_VMCODE_FUNCTION)
     {
         njs_syntax_error(vm, "single function literal required");
-        return NJS_ERROR;
+        ret = NJS_ERROR;
+        goto done;
     }
 
     lambda = ((njs_vmcode_function_t *) code->start)->lambda;
 
     function = njs_function_alloc(vm, lambda, (njs_bool_t) async);
     if (njs_slow_path(function == NULL)) {
-        return NJS_ERROR;
+        ret = NJS_ERROR;
+        goto done;
     }
 
     function->global = 1;
@@ -1130,17 +1138,32 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     ret = njs_function_name_set(vm, function, &name, NULL);
     if (njs_slow_path(ret == NJS_ERROR)) {
-        return ret;
+        goto done;
     }
 
     njs_set_function(retval, function);
 
-    return NJS_OK;
+    ret = NJS_OK;
+    goto done;
 
 fail:
 
     njs_type_error(vm, "function constructor is disabled in \"safe\" mode");
-    return NJS_ERROR;
+    ret = NJS_ERROR;
+
+done:
+
+    if (parser.mem_pool != NULL) {
+        njs_parser_destroy(&parser);
+    }
+
+    njs_chb_destroy(&chain);
+
+    if (str.start != NULL) {
+        njs_mp_free(vm->mem_pool, str.start);
+    }
+
+    return ret;
 }
 
 
index 09cc657d6977652f790fdbba68a2708675399cbf..6d034d84e42ee2db052c4a83e3120542f24a808e 100644 (file)
@@ -5044,6 +5044,7 @@ njs_generate_scope(njs_vm_t *vm, njs_generator_t *generator,
     code->name = *name;
 
     generator->code_size = generator->code_end - generator->code_start;
+    scope->top = NULL;
 
     return code;
 }
index b0448a516468407622e401b56ffe19821d9b5705..db49197d7155c4af4b3fb52195618a361edd70e3 100644 (file)
@@ -293,14 +293,15 @@ static const njs_lexer_multi_t  njs_assignment_token[] = {
 
 
 njs_int_t
-njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file,
-    u_char *start, u_char *end)
+njs_lexer_init(njs_vm_t *vm, njs_mp_t *mem_pool, njs_lexer_t *lexer,
+    njs_str_t *file, u_char *start, u_char *end)
 {
     lexer->file = *file;
     lexer->start = start;
     lexer->end = end;
     lexer->line = 1;
     lexer->vm = vm;
+    lexer->mem_pool = mem_pool;
 
     njs_queue_init(&lexer->preread);
 
@@ -314,7 +315,7 @@ njs_lexer_next_token(njs_lexer_t *lexer)
     njs_int_t          ret;
     njs_lexer_token_t  *token;
 
-    token = njs_mp_zalloc(lexer->vm->mem_pool, sizeof(njs_lexer_token_t));
+    token = njs_mp_zalloc(lexer->mem_pool, sizeof(njs_lexer_token_t));
     if (njs_slow_path(token == NULL)) {
         return NULL;
     }
@@ -429,7 +430,7 @@ njs_lexer_consume_token(njs_lexer_t *lexer, unsigned length)
 
         njs_queue_remove(lnk);
 
-        njs_mp_free(lexer->vm->mem_pool, token);
+        njs_mp_free(lexer->mem_pool, token);
     }
 }
 
index a0b37b4bae678fa3bb63b998bd280ead683d2a30..c816e57048137acc8bf3c91a710fcd30c293f458 100644 (file)
@@ -263,14 +263,15 @@ typedef struct {
     njs_str_t                       file;
 
     njs_vm_t                        *vm;
+    njs_mp_t                        *mem_pool;
 
     u_char                          *start;
     u_char                          *end;
 } njs_lexer_t;
 
 
-njs_int_t njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file,
-    u_char *start, u_char *end);
+njs_int_t njs_lexer_init(njs_vm_t *vm, njs_mp_t *mem_pool,
+    njs_lexer_t *lexer, njs_str_t *file, u_char *start, u_char *end);
 
 njs_lexer_token_t *njs_lexer_token(njs_lexer_t *lexer,
     njs_bool_t with_end_line);
index efc1d2907e3b6bf324b90fff005b9ad0210ca27c..02c38f0dadc6e6f0749d712daf435d4c9e83a371 100644 (file)
@@ -542,18 +542,47 @@ 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_int_t    ret;
     njs_lexer_t  *lexer;
 
     njs_memzero(parser, sizeof(njs_parser_t));
 
     parser->scope = scope;
 
+    parser->mem_pool = njs_mp_fast_create(2 * njs_pagesize(), 128, 512, 16);
+    if (njs_slow_path(parser->mem_pool == NULL)) {
+        njs_memory_error(vm);
+        return NJS_ERROR;
+    }
+
     lexer = &parser->lexer0;
     parser->lexer = lexer;
 
     njs_rbtree_init(&parser->labels, njs_parser_scope_rbtree_compare);
 
-    return njs_lexer_init(vm, lexer, file, start, end);
+    ret = njs_lexer_init(vm, parser->mem_pool, lexer, file, start, end);
+    if (njs_slow_path(ret != NJS_OK)) {
+        njs_mp_destroy(parser->mem_pool);
+        parser->mem_pool = NULL;
+    }
+
+    return ret;
+}
+
+
+void
+njs_parser_destroy(njs_parser_t *parser)
+{
+    njs_parser_scope_t  *scope;
+
+    for (scope = parser->scope; scope != NULL; scope = scope->parent) {
+        scope->top = NULL;
+    }
+
+    if (parser->mem_pool != NULL) {
+        njs_mp_destroy(parser->mem_pool);
+        parser->mem_pool = NULL;
+    }
 }
 
 
@@ -8387,7 +8416,7 @@ njs_parser_array_item(njs_parser_t *parser, njs_parser_node_t *array,
     items = array->u.array.items;
 
     if (items == NULL) {
-        items = njs_arr_create(parser->vm->mem_pool, 4,
+        items = njs_arr_create(parser->mem_pool, 4,
                                sizeof(njs_parser_array_item_t));
         if (njs_slow_path(items == NULL)) {
             return NJS_ERROR;
index 3498d48efa70d1ad4d47daed4a2d3ff14f1f63ed..e5bb3c820abe660a0f9adb29883a28a318bcd504 100644 (file)
@@ -90,6 +90,7 @@ struct njs_parser_s {
     njs_lexer_t                     lexer0;
     njs_lexer_t                     *lexer;
     njs_vm_t                        *vm;
+    njs_mp_t                        *mem_pool;
     njs_parser_node_t               *node;
     njs_parser_node_t               *target;
     njs_parser_scope_t              *scope;
@@ -144,6 +145,7 @@ 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);
+void njs_parser_destroy(njs_parser_t *parser);
 njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser);
 
 njs_bool_t njs_variable_closure_test(njs_parser_scope_t *root,
@@ -205,7 +207,7 @@ njs_parser_node_new(njs_parser_t *parser, njs_token_type_t type)
 {
     njs_parser_node_t  *node;
 
-    node = njs_mp_zalloc(parser->vm->mem_pool, sizeof(njs_parser_node_t));
+    node = njs_mp_zalloc(parser->mem_pool, sizeof(njs_parser_node_t));
 
     if (njs_fast_path(node != NULL)) {
         node->token_type = type;
@@ -219,7 +221,7 @@ njs_parser_node_new(njs_parser_t *parser, njs_token_type_t type)
 njs_inline void
 njs_parser_node_free(njs_parser_t *parser, njs_parser_node_t *node)
 {
-    njs_mp_free(parser->vm->mem_pool, node);
+    njs_mp_free(parser->mem_pool, node);
 }
 
 
@@ -332,7 +334,7 @@ njs_parser_stack_pop(njs_parser_t *parser)
     parser->allow_in = entry->allow_in;
     parser->var_type = entry->var_type;
 
-    njs_mp_free(parser->vm->mem_pool, entry);
+    njs_mp_free(parser->mem_pool, entry);
 
     return NJS_OK;
 }
@@ -367,8 +369,7 @@ _njs_parser_after(njs_parser_t *parser, njs_queue_link_t *link, void *node,
 {
     njs_parser_stack_entry_t  *entry;
 
-    entry = njs_mp_alloc(parser->vm->mem_pool,
-                         sizeof(njs_parser_stack_entry_t));
+    entry = njs_mp_alloc(parser->mem_pool, sizeof(njs_parser_stack_entry_t));
     if (njs_slow_path(entry == NULL)) {
         return NJS_ERROR;
     }
index 47ee500948a5ea01bfc8eb03e199c8de4e7b2d47..176438fb1f5820b9c2eb730c8bf6ae525622e507 100644 (file)
@@ -306,7 +306,7 @@ njs_label_add(njs_parser_t *parser, uintptr_t atom_id)
 {
     njs_parser_label_t  *label;
 
-    label = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_parser_label_t));
+    label = njs_mp_alloc(parser->mem_pool, sizeof(njs_parser_label_t));
     if (njs_slow_path(label == NULL)) {
         njs_memory_error(parser->vm);
         return NJS_ERROR;
@@ -335,7 +335,7 @@ njs_label_remove(njs_parser_t *parser, uintptr_t atom_id)
     }
 
     njs_rbtree_delete(&parser->labels, (njs_rbtree_part_t *) node);
-    njs_mp_free(parser->vm->mem_pool, node);
+    njs_mp_free(parser->mem_pool, node);
 
     return NJS_OK;
 }
index ba2fca5507c4acf392e2040e8489823ee4f29147..4ddc2fd0de098e54f2e80394610b785fe0ffa5ae 100644 (file)
@@ -233,6 +233,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
 
     ret = njs_parser(vm, &parser);
     if (njs_slow_path(ret != NJS_OK)) {
+        njs_parser_destroy(&parser);
         return NJS_ERROR;
     }
 
@@ -240,10 +241,12 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
         NJS_CHB_MP_INIT(&chain, njs_vm_memory_pool(vm));
         ret = njs_parser_serialize_ast(parser.node, &chain);
         if (njs_slow_path(ret == NJS_ERROR)) {
+            njs_parser_destroy(&parser);
             return ret;
         }
 
         if (njs_slow_path(njs_chb_join(&chain, &ast) != NJS_OK)) {
+            njs_parser_destroy(&parser);
             return NJS_ERROR;
         }
 
@@ -259,6 +262,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
     ret = njs_generator_init(&generator, &vm->options.file, 0, 0);
     if (njs_slow_path(ret != NJS_OK)) {
         njs_internal_error(vm, "njs_generator_init() failed");
+        njs_parser_destroy(&parser);
         return NJS_ERROR;
     }
 
@@ -268,9 +272,12 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
             njs_internal_error(vm, "njs_generate_scope() failed");
         }
 
+        njs_parser_destroy(&parser);
         return NJS_ERROR;
     }
 
+    njs_parser_destroy(&parser);
+
     if (scope->items > global_items) {
         global = vm->levels[NJS_LEVEL_GLOBAL];
 
@@ -342,6 +349,7 @@ njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start,
 
     ret = njs_parser(vm, &parser);
     if (njs_slow_path(ret != NJS_OK)) {
+        njs_parser_destroy(&parser);
         return NULL;
     }
 
@@ -350,6 +358,7 @@ njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start,
     ret = njs_generator_init(&generator, &module->name, 0, 0);
     if (njs_slow_path(ret != NJS_OK)) {
         njs_internal_error(vm, "njs_generator_init() failed");
+        njs_parser_destroy(&parser);
         return NULL;
     }
 
@@ -357,12 +366,14 @@ njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start,
     if (njs_slow_path(code == NULL)) {
         njs_internal_error(vm, "njs_generate_scope() failed");
 
+        njs_parser_destroy(&parser);
         return NULL;
     }
 
     lambda = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_lambda_t));
     if (njs_fast_path(lambda == NULL)) {
         njs_memory_error(vm);
+        njs_parser_destroy(&parser);
         return NULL;
     }
 
@@ -373,6 +384,8 @@ njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start,
 
     module->function.u.lambda = lambda;
 
+    njs_parser_destroy(&parser);
+
     return module;
 }