Skip to content

Commit 59a6ca5

Browse files
committed
module: fix the leak in SourceTextModule and ContextifySript
This is a POC that shows how we can replace the persistent handles to v8::Module and v8::UnboundScript with an internal reference and fix the leaks.
1 parent 80c787f commit 59a6ca5

6 files changed

+58
-4
lines changed

src/module_wrap.cc

+6-3
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,21 @@ ModuleWrap::ModuleWrap(Environment* env,
5353
Local<Object> object,
5454
Local<Module> module,
5555
Local<String> url)
56-
: BaseObject(env, object), module_(env->isolate(), module) {
56+
: BaseObject(env, object),
57+
module_(env->isolate(), module),
58+
module_hash_(module->GetIdentityHash()) {
5759
Local<Value> undefined = Undefined(env->isolate());
60+
object->SetDataInInternalField(kModuleSlot, module);
5861
object->SetInternalField(kURLSlot, url);
5962
object->SetInternalField(kSyntheticEvaluationStepsSlot, undefined);
6063
object->SetInternalField(kContextObjectSlot, undefined);
6164
MakeWeak();
65+
module_.SetWeak();
6266
}
6367

6468
ModuleWrap::~ModuleWrap() {
6569
HandleScope scope(env()->isolate());
66-
Local<Module> module = module_.Get(env()->isolate());
67-
auto range = env()->hash_to_module_map.equal_range(module->GetIdentityHash());
70+
auto range = env()->hash_to_module_map.equal_range(module_hash_);
6871
for (auto it = range.first; it != range.second; ++it) {
6972
if (it->second == this) {
7073
env()->hash_to_module_map.erase(it);

src/module_wrap.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ enum HostDefinedOptions : int {
3333
class ModuleWrap : public BaseObject {
3434
public:
3535
enum InternalFields {
36-
kModuleWrapBaseField = BaseObject::kInternalFieldCount,
36+
kModuleSlot = BaseObject::kInternalFieldCount,
3737
kURLSlot,
3838
kSyntheticEvaluationStepsSlot,
3939
kContextObjectSlot, // Object whose creation context is the target Context
@@ -104,6 +104,7 @@ class ModuleWrap : public BaseObject {
104104
contextify::ContextifyContext* contextify_context_ = nullptr;
105105
bool synthetic_ = false;
106106
bool linked_ = false;
107+
int module_hash_;
107108
};
108109

109110
} // namespace loader

src/node_contextify.cc

+4
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,11 @@ void ContextifyScript::New(const FunctionCallbackInfo<Value>& args) {
862862
"ContextifyScript::New");
863863
return;
864864
}
865+
865866
contextify_script->script_.Reset(isolate, v8_script);
867+
contextify_script->script_.SetWeak();
868+
contextify_script->object()->SetDataInInternalField(kUnboundScriptSlot,
869+
v8_script);
866870

867871
std::unique_ptr<ScriptCompiler::CachedData> new_cached_data;
868872
if (produce_cached_data) {

src/node_contextify.h

+5
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ class ContextifyContext : public BaseObject {
151151

152152
class ContextifyScript : public BaseObject {
153153
public:
154+
enum InternalFields {
155+
kUnboundScriptSlot = BaseObject::kInternalFieldCount,
156+
kInternalFieldCount
157+
};
158+
154159
SET_NO_MEMORY_INFO()
155160
SET_MEMORY_INFO_NAME(ContextifyScript)
156161
SET_SELF_SIZE(ContextifyScript)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
3+
// Flags: --max-old-space-size=16 --trace-gc
4+
5+
require('../common');
6+
const vm = require('vm');
7+
let count = 0;
8+
9+
function main() {
10+
new vm.Script(`"${Math.random().toString().repeat(512)}";`, {
11+
async importModuleDynamically() {},
12+
});
13+
if (count++ < 2 * 1024) {
14+
setTimeout(main, 1);
15+
}
16+
}
17+
main();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
3+
// This tests that vm.SourceTextModule() does not leak.
4+
// See: https://github.com/nodejs/node/issues/33439
5+
// Flags: --experimental-vm-modules --max-old-space-size=16 --trace-gc
6+
7+
require('../common');
8+
9+
const vm = require('vm');
10+
let count = 0;
11+
async function createModule() {
12+
const m = new vm.SourceTextModule(`
13+
const bar = new Array(512).fill("----");
14+
export { bar };
15+
`);
16+
await m.link(() => {});
17+
await m.evaluate();
18+
count++;
19+
if (count < 4096) {
20+
setTimeout(createModule, 1);
21+
}
22+
return m;
23+
}
24+
createModule();

0 commit comments

Comments
 (0)