From e5696ba5854a895f941abbf370c26c01d0b93b49 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Thu, 3 Sep 2026 08:53:14 -0700 Subject: [PATCH] Parser: release non-global semantic state 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 | 2 +- src/njs_generator.c | 40 +++++++++++++++++++++++----------- src/njs_parser.c | 46 ++++++++++++++++++++++++++++++++++++---- src/njs_parser.h | 5 ++++- src/njs_variable.c | 33 ++++++++++++++++------------ src/njs_variable.h | 8 ++++--- src/njs_vm.c | 6 +++--- src/test/njs_unit_test.c | 5 +++++ 8 files changed, 108 insertions(+), 37 deletions(-) diff --git a/src/njs_function.c b/src/njs_function.c index 035e5efe..3cc76b35 100644 --- a/src/njs_function.c +++ b/src/njs_function.c @@ -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; diff --git a/src/njs_generator.c b/src/njs_generator.c index 6ef38390..29def6a1 100644 --- a/src/njs_generator.c +++ b/src/njs_generator.c @@ -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; } diff --git a/src/njs_parser.c b/src/njs_parser.c index 5238114a..91b32496 100644 --- a/src/njs_parser.c +++ b/src/njs_parser.c @@ -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; diff --git a/src/njs_parser.h b/src/njs_parser.h index 882d903d..54d3cd8c 100644 --- a/src/njs_parser.h +++ b/src/njs_parser.h @@ -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); diff --git a/src/njs_variable.c b/src/njs_variable.c index 176438fb..4f5ba0ea 100644 --- a/src/njs_variable.c +++ b/src/njs_variable.c @@ -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; diff --git a/src/njs_variable.h b/src/njs_variable.h index d417cd37..8b89a427 100644 --- a/src/njs_variable.h +++ b/src/njs_variable.h @@ -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; diff --git a/src/njs_vm.c b/src/njs_vm.c index 3b517b6a..bbb7bb29 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -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; } diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index a0b2bd16..b609044c 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -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") }, -- 2.47.3