From c3e86ead170a5eee0bba90dc4c753438780441ba Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Tue, 29 Sep 2026 23:06:42 -0700 Subject: [PATCH] VM: preserve dependency code after failed module compilation Commit fd54d52d freed all bytecode added after the parent module's compilation checkpoint, including successfully compiled dependencies. Those dependencies remain cached, so a later import in the same VM executes freed bytecode. This is reachable in the interactive shell and in embedders that continue after a compilation error. The fix is to remove eager bytecode cleanup while retaining removal of the failed module from the cache so subsequent loads can retry. Keep the removed module and its name in the VM pool because cyclic dependencies and code metadata can still reference them. These allocations are released when the VM is destroyed. Add a shell regression test that retries a failed parent import and then executes its cached dependency. --- src/njs_generator.c | 23 ----------------------- src/njs_generator.h | 1 - src/njs_module.c | 3 +-- src/njs_vm.c | 3 --- test/js/module/compile_exception.js | 3 +++ test/shell_test.exp | 9 +++++++++ 6 files changed, 13 insertions(+), 29 deletions(-) create mode 100644 test/js/module/compile_exception.js diff --git a/src/njs_generator.c b/src/njs_generator.c index 3d8a3efa..a3215bcc 100644 --- a/src/njs_generator.c +++ b/src/njs_generator.c @@ -5142,29 +5142,6 @@ njs_generate_scope(njs_vm_t *vm, njs_generator_t *generator, } -void -njs_generator_cleanup(njs_vm_t *vm, njs_uint_t code_index) -{ - njs_vm_code_t *code; - - if (vm->codes == NULL) { - return; - } - - while (vm->codes->items > code_index) { - code = njs_arr_remove_last(vm->codes); - - if (code->start != NULL) { - njs_mp_free(vm->mem_pool, code->start); - } - - if (code->lines != NULL) { - njs_arr_destroy(code->lines); - } - } -} - - static njs_int_t njs_generate_scope_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) diff --git a/src/njs_generator.h b/src/njs_generator.h index ae527c4b..689286aa 100644 --- a/src/njs_generator.h +++ b/src/njs_generator.h @@ -46,7 +46,6 @@ njs_int_t njs_generator_init(njs_generator_t *generator, njs_mp_t *mem_pool, njs_str_t *file, njs_int_t depth, njs_bool_t runtime); njs_vm_code_t *njs_generate_scope(njs_vm_t *vm, njs_generator_t *generator, njs_parser_scope_t *scope, const njs_str_t *name); -void njs_generator_cleanup(njs_vm_t *vm, njs_uint_t code_index); njs_vm_code_t *njs_lookup_code(njs_vm_t *vm, u_char *pc); uint32_t njs_lookup_line(njs_arr_t *lines, uint32_t offset); diff --git a/src/njs_module.c b/src/njs_module.c index 6b968884..e03cb69d 100644 --- a/src/njs_module.c +++ b/src/njs_module.c @@ -151,8 +151,7 @@ njs_module_remove(njs_vm_t *vm, njs_mod_t *module) (void) njs_flathsh_delete(&vm->shared->modules_hash, &fhq); - njs_mp_free(vm->mem_pool, module->name.start); - njs_mp_free(vm->mem_pool, module); + /* Compiled dependencies may still reference this module and its name. */ } diff --git a/src/njs_vm.c b/src/njs_vm.c index 188d8544..ce9b0ef6 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -326,7 +326,6 @@ njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start, u_char *end) { njs_int_t ret; - njs_uint_t code_index; njs_mod_t *module; njs_parser_t parser; njs_vm_code_t *code; @@ -335,7 +334,6 @@ njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start, njs_function_lambda_t *lambda; parser.mem_pool = NULL; - code_index = (vm->codes != NULL) ? vm->codes->items : 0; module = njs_module_find(vm, name, 1); if (module != NULL) { @@ -397,7 +395,6 @@ failed: njs_parser_destroy(&parser); } - njs_generator_cleanup(vm, code_index); njs_module_remove(vm, module); return NULL; diff --git a/test/js/module/compile_exception.js b/test/js/module/compile_exception.js new file mode 100644 index 00000000..0fae8ef1 --- /dev/null +++ b/test/js/module/compile_exception.js @@ -0,0 +1,3 @@ +import name from './name.js'; +export default name; +@ diff --git a/test/shell_test.exp b/test/shell_test.exp index 000ae63b..889b5d56 100644 --- a/test/shell_test.exp +++ b/test/shell_test.exp @@ -448,6 +448,15 @@ njs_test { {"42\r\n" "42\r\n>> " "unhandled promise rejection"} } +njs_test { + {"import broken from './test/js/module/compile_exception.js';\r\n" + "SyntaxError:*\r\n*>> "} + {"import broken from './test/js/module/compile_exception.js';\r\n" + "SyntaxError:*\r\n*>> "} + {"import name from './test/js/module/name.js'; name;\r\n" + "'name'\r\n>> "} +} "-n njs" + # CLI OPTIONS # help -- 2.47.3