Skip to content

Commit 7b4638c

Browse files
devsnektargos
authored andcommitted
vm: fix gc bug with modules and compiled functions
Backport-PR-URL: #28779 PR-URL: #28671 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Guy Bedford <[email protected]>
1 parent 674d33c commit 7b4638c

File tree

7 files changed

+69
-45
lines changed

7 files changed

+69
-45
lines changed

lib/internal/modules/cjs/loader.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,7 @@ Module.prototype._compile = function(content, filename) {
717717
} : undefined,
718718
});
719719
} else {
720-
compiledWrapper = compileFunction(
720+
const compiled = compileFunction(
721721
content,
722722
filename,
723723
0,
@@ -736,13 +736,14 @@ Module.prototype._compile = function(content, filename) {
736736
);
737737
if (experimentalModules) {
738738
const { callbackMap } = internalBinding('module_wrap');
739-
callbackMap.set(compiledWrapper, {
739+
callbackMap.set(compiled.cacheKey, {
740740
importModuleDynamically: async (specifier) => {
741741
const loader = await asyncESM.loaderPromise;
742742
return loader.import(specifier, normalizeReferrerURL(filename));
743743
}
744744
});
745745
}
746+
compiledWrapper = compiled.function;
746747
}
747748

748749
var inspectorWrapper = null;

lib/vm.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ function compileFunction(code, params, options = {}) {
383383
}
384384
});
385385

386-
return _compileFunction(
386+
const result = _compileFunction(
387387
code,
388388
filename,
389389
lineOffset,
@@ -394,6 +394,16 @@ function compileFunction(code, params, options = {}) {
394394
contextExtensions,
395395
params
396396
);
397+
398+
if (produceCachedData) {
399+
result.function.cachedDataProduced = result.cachedDataProduced;
400+
}
401+
402+
if (result.cachedData) {
403+
result.function.cachedData = result.cachedData;
404+
}
405+
406+
return result.function;
397407
}
398408

399409

src/env.cc

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@ using v8::NewStringType;
3939
using v8::Number;
4040
using v8::Object;
4141
using v8::Private;
42+
using v8::ScriptOrModule;
4243
using v8::SnapshotCreator;
4344
using v8::StackTrace;
4445
using v8::String;
4546
using v8::Symbol;
4647
using v8::TracingController;
4748
using v8::Undefined;
4849
using v8::Value;
50+
using v8::WeakCallbackInfo;
4951
using worker::Worker;
5052

5153
int const Environment::kNodeContextTag = 0x6e6f64;
@@ -385,9 +387,22 @@ Environment::Environment(IsolateData* isolate_data,
385387
CreateProperties();
386388
}
387389

388-
CompileFnEntry::CompileFnEntry(Environment* env, uint32_t id)
389-
: env(env), id(id) {
390-
env->compile_fn_entries.insert(this);
390+
static void WeakCallbackCompiledFn(
391+
const WeakCallbackInfo<CompiledFnEntry>& data) {
392+
CompiledFnEntry* entry = data.GetParameter();
393+
entry->env->id_to_function_map.erase(entry->id);
394+
delete entry;
395+
}
396+
397+
CompiledFnEntry::CompiledFnEntry(Environment* env,
398+
uint32_t id,
399+
Local<ScriptOrModule> script)
400+
: env(env),
401+
id(id),
402+
cache_key(env->isolate(), Object::New(env->isolate())),
403+
script(env->isolate(), script) {
404+
this->script.SetWeak(
405+
this, WeakCallbackCompiledFn, v8::WeakCallbackType::kParameter);
391406
}
392407

393408
Environment::~Environment() {
@@ -398,12 +413,6 @@ Environment::~Environment() {
398413
// CleanupHandles() should have removed all of them.
399414
CHECK(file_handle_read_wrap_freelist_.empty());
400415

401-
// dispose the Persistent references to the compileFunction
402-
// wrappers used in the dynamic import callback
403-
for (auto& entry : compile_fn_entries) {
404-
delete entry;
405-
}
406-
407416
HandleScope handle_scope(isolate());
408417

409418
#if HAVE_INSPECTOR

src/env.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
157157
V(cached_data_produced_string, "cachedDataProduced") \
158158
V(cached_data_rejected_string, "cachedDataRejected") \
159159
V(cached_data_string, "cachedData") \
160+
V(cache_key_string, "cacheKey") \
160161
V(change_string, "change") \
161162
V(channel_string, "channel") \
162163
V(chunks_sent_since_last_write_string, "chunksSentSinceLastWrite") \
@@ -500,10 +501,14 @@ struct ContextInfo {
500501
bool is_default = false;
501502
};
502503

503-
struct CompileFnEntry {
504+
struct CompiledFnEntry {
504505
Environment* env;
505506
uint32_t id;
506-
CompileFnEntry(Environment* env, uint32_t id);
507+
v8::Global<v8::Object> cache_key;
508+
v8::Global<v8::ScriptOrModule> script;
509+
CompiledFnEntry(Environment* env,
510+
uint32_t id,
511+
v8::Local<v8::ScriptOrModule> script);
507512
};
508513

509514
// Listing the AsyncWrap provider types first enables us to cast directly
@@ -990,8 +995,7 @@ class Environment : public MemoryRetainer {
990995
std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map;
991996
std::unordered_map<uint32_t, contextify::ContextifyScript*>
992997
id_to_script_map;
993-
std::unordered_set<CompileFnEntry*> compile_fn_entries;
994-
std::unordered_map<uint32_t, v8::Global<v8::Function>> id_to_function_map;
998+
std::unordered_map<uint32_t, CompiledFnEntry*> id_to_function_map;
995999

9961000
inline uint32_t get_next_module_id();
9971001
inline uint32_t get_next_script_id();

src/module_wrap.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,7 @@ static MaybeLocal<Promise> ImportModuleDynamically(
10411041
ModuleWrap* wrap = ModuleWrap::GetFromID(env, id);
10421042
object = wrap->object();
10431043
} else if (type == ScriptType::kFunction) {
1044-
object = env->id_to_function_map.find(id)->second.Get(iso);
1044+
object = env->id_to_function_map.find(id)->second->cache_key.Get(iso);
10451045
} else {
10461046
UNREACHABLE();
10471047
}

src/node_contextify.cc

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ using v8::PropertyHandlerFlags;
6666
using v8::Script;
6767
using v8::ScriptCompiler;
6868
using v8::ScriptOrigin;
69+
using v8::ScriptOrModule;
6970
using v8::String;
7071
using v8::Symbol;
7172
using v8::Uint32;
@@ -305,15 +306,6 @@ void ContextifyContext::WeakCallback(
305306
delete context;
306307
}
307308

308-
void ContextifyContext::WeakCallbackCompileFn(
309-
const WeakCallbackInfo<CompileFnEntry>& data) {
310-
CompileFnEntry* entry = data.GetParameter();
311-
if (entry->env->compile_fn_entries.erase(entry) != 0) {
312-
entry->env->id_to_function_map.erase(entry->id);
313-
delete entry;
314-
}
315-
}
316-
317309
// static
318310
ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox(
319311
Environment* env,
@@ -1117,9 +1109,11 @@ void ContextifyContext::CompileFunction(
11171109
}
11181110
}
11191111

1112+
Local<ScriptOrModule> script;
11201113
MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunctionInContext(
11211114
parsing_context, &source, params.size(), params.data(),
1122-
context_extensions.size(), context_extensions.data(), options);
1115+
context_extensions.size(), context_extensions.data(), options,
1116+
v8::ScriptCompiler::NoCacheReason::kNoCacheNoReason, &script);
11231117

11241118
if (maybe_fn.IsEmpty()) {
11251119
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
@@ -1129,13 +1123,17 @@ void ContextifyContext::CompileFunction(
11291123
return;
11301124
}
11311125
Local<Function> fn = maybe_fn.ToLocalChecked();
1132-
env->id_to_function_map.emplace(std::piecewise_construct,
1133-
std::make_tuple(id),
1134-
std::make_tuple(isolate, fn));
1135-
CompileFnEntry* gc_entry = new CompileFnEntry(env, id);
1136-
env->id_to_function_map[id].SetWeak(gc_entry,
1137-
WeakCallbackCompileFn,
1138-
v8::WeakCallbackType::kParameter);
1126+
1127+
CompiledFnEntry* entry = new CompiledFnEntry(env, id, script);
1128+
env->id_to_function_map.emplace(id, entry);
1129+
Local<Object> cache_key = entry->cache_key.Get(isolate);
1130+
1131+
Local<Object> result = Object::New(isolate);
1132+
if (result->Set(parsing_context, env->function_string(), fn).IsNothing())
1133+
return;
1134+
if (result->Set(parsing_context, env->cache_key_string(), cache_key)
1135+
.IsNothing())
1136+
return;
11391137

11401138
if (produce_cached_data) {
11411139
const std::unique_ptr<ScriptCompiler::CachedData> cached_data(
@@ -1146,18 +1144,22 @@ void ContextifyContext::CompileFunction(
11461144
env,
11471145
reinterpret_cast<const char*>(cached_data->data),
11481146
cached_data->length);
1149-
if (fn->Set(
1150-
parsing_context,
1151-
env->cached_data_string(),
1152-
buf.ToLocalChecked()).IsNothing()) return;
1147+
if (result
1148+
->Set(parsing_context,
1149+
env->cached_data_string(),
1150+
buf.ToLocalChecked())
1151+
.IsNothing())
1152+
return;
11531153
}
1154-
if (fn->Set(
1155-
parsing_context,
1156-
env->cached_data_produced_string(),
1157-
Boolean::New(isolate, cached_data_produced)).IsNothing()) return;
1154+
if (result
1155+
->Set(parsing_context,
1156+
env->cached_data_produced_string(),
1157+
Boolean::New(isolate, cached_data_produced))
1158+
.IsNothing())
1159+
return;
11581160
}
11591161

1160-
args.GetReturnValue().Set(fn);
1162+
args.GetReturnValue().Set(result);
11611163
}
11621164

11631165
static void StartSigintWatchdog(const FunctionCallbackInfo<Value>& args) {

src/node_contextify.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ class ContextifyContext {
6363
const v8::FunctionCallbackInfo<v8::Value>& args);
6464
static void WeakCallback(
6565
const v8::WeakCallbackInfo<ContextifyContext>& data);
66-
static void WeakCallbackCompileFn(
67-
const v8::WeakCallbackInfo<CompileFnEntry>& data);
6866
static void PropertyGetterCallback(
6967
v8::Local<v8::Name> property,
7068
const v8::PropertyCallbackInfo<v8::Value>& args);

0 commit comments

Comments
 (0)