vm: fix gc bug with modules and compiled functions

PR-URL: https://github.com/nodejs/node/pull/28671
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Guy Bedford <guybedford@gmail.com>
This commit is contained in:
Gus Caplan 2019-07-19 09:30:09 -05:00
parent b7bcfc9d7e
commit 68c83f962e
No known key found for this signature in database
GPG Key ID: F00BD11880E82F0E
7 changed files with 69 additions and 46 deletions

View File

@ -705,7 +705,7 @@ function wrapSafe(filename, content) {
}); });
} }
const compiledWrapper = compileFunction( const compiled = compileFunction(
content, content,
filename, filename,
0, 0,
@ -725,7 +725,7 @@ function wrapSafe(filename, content) {
if (experimentalModules) { if (experimentalModules) {
const { callbackMap } = internalBinding('module_wrap'); const { callbackMap } = internalBinding('module_wrap');
callbackMap.set(compiledWrapper, { callbackMap.set(compiled.cacheKey, {
importModuleDynamically: async (specifier) => { importModuleDynamically: async (specifier) => {
const loader = await asyncESM.loaderPromise; const loader = await asyncESM.loaderPromise;
return loader.import(specifier, normalizeReferrerURL(filename)); return loader.import(specifier, normalizeReferrerURL(filename));
@ -733,7 +733,7 @@ function wrapSafe(filename, content) {
}); });
} }
return compiledWrapper; return compiled.function;
} }
// Run the file contents in the correct scope or sandbox. Expose // Run the file contents in the correct scope or sandbox. Expose

View File

@ -383,7 +383,7 @@ function compileFunction(code, params, options = {}) {
} }
}); });
return _compileFunction( const result = _compileFunction(
code, code,
filename, filename,
lineOffset, lineOffset,
@ -394,6 +394,16 @@ function compileFunction(code, params, options = {}) {
contextExtensions, contextExtensions,
params params
); );
if (produceCachedData) {
result.function.cachedDataProduced = result.cachedDataProduced;
}
if (result.cachedData) {
result.function.cachedData = result.cachedData;
}
return result.function;
} }

View File

@ -39,6 +39,7 @@ using v8::NewStringType;
using v8::Number; using v8::Number;
using v8::Object; using v8::Object;
using v8::Private; using v8::Private;
using v8::ScriptOrModule;
using v8::SnapshotCreator; using v8::SnapshotCreator;
using v8::StackTrace; using v8::StackTrace;
using v8::String; using v8::String;
@ -46,6 +47,7 @@ using v8::Symbol;
using v8::TracingController; using v8::TracingController;
using v8::Undefined; using v8::Undefined;
using v8::Value; using v8::Value;
using v8::WeakCallbackInfo;
using worker::Worker; using worker::Worker;
int const Environment::kNodeContextTag = 0x6e6f64; int const Environment::kNodeContextTag = 0x6e6f64;
@ -385,9 +387,22 @@ Environment::Environment(IsolateData* isolate_data,
CreateProperties(); CreateProperties();
} }
CompileFnEntry::CompileFnEntry(Environment* env, uint32_t id) static void WeakCallbackCompiledFn(
: env(env), id(id) { const WeakCallbackInfo<CompiledFnEntry>& data) {
env->compile_fn_entries.insert(this); CompiledFnEntry* entry = data.GetParameter();
entry->env->id_to_function_map.erase(entry->id);
delete entry;
}
CompiledFnEntry::CompiledFnEntry(Environment* env,
uint32_t id,
Local<ScriptOrModule> script)
: env(env),
id(id),
cache_key(env->isolate(), Object::New(env->isolate())),
script(env->isolate(), script) {
this->script.SetWeak(
this, WeakCallbackCompiledFn, v8::WeakCallbackType::kParameter);
} }
Environment::~Environment() { Environment::~Environment() {
@ -398,12 +413,6 @@ Environment::~Environment() {
// CleanupHandles() should have removed all of them. // CleanupHandles() should have removed all of them.
CHECK(file_handle_read_wrap_freelist_.empty()); CHECK(file_handle_read_wrap_freelist_.empty());
// dispose the Persistent references to the compileFunction
// wrappers used in the dynamic import callback
for (auto& entry : compile_fn_entries) {
delete entry;
}
HandleScope handle_scope(isolate()); HandleScope handle_scope(isolate());
#if HAVE_INSPECTOR #if HAVE_INSPECTOR

View File

@ -179,6 +179,7 @@ constexpr size_t kFsStatsBufferLength =
V(cached_data_produced_string, "cachedDataProduced") \ V(cached_data_produced_string, "cachedDataProduced") \
V(cached_data_rejected_string, "cachedDataRejected") \ V(cached_data_rejected_string, "cachedDataRejected") \
V(cached_data_string, "cachedData") \ V(cached_data_string, "cachedData") \
V(cache_key_string, "cacheKey") \
V(change_string, "change") \ V(change_string, "change") \
V(channel_string, "channel") \ V(channel_string, "channel") \
V(chunks_sent_since_last_write_string, "chunksSentSinceLastWrite") \ V(chunks_sent_since_last_write_string, "chunksSentSinceLastWrite") \
@ -522,10 +523,14 @@ struct ContextInfo {
bool is_default = false; bool is_default = false;
}; };
struct CompileFnEntry { struct CompiledFnEntry {
Environment* env; Environment* env;
uint32_t id; uint32_t id;
CompileFnEntry(Environment* env, uint32_t id); v8::Global<v8::Object> cache_key;
v8::Global<v8::ScriptOrModule> script;
CompiledFnEntry(Environment* env,
uint32_t id,
v8::Local<v8::ScriptOrModule> script);
}; };
// Listing the AsyncWrap provider types first enables us to cast directly // Listing the AsyncWrap provider types first enables us to cast directly
@ -1005,8 +1010,7 @@ class Environment : public MemoryRetainer {
std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map; std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map;
std::unordered_map<uint32_t, contextify::ContextifyScript*> std::unordered_map<uint32_t, contextify::ContextifyScript*>
id_to_script_map; id_to_script_map;
std::unordered_set<CompileFnEntry*> compile_fn_entries; std::unordered_map<uint32_t, CompiledFnEntry*> id_to_function_map;
std::unordered_map<uint32_t, v8::Global<v8::Function>> id_to_function_map;
inline uint32_t get_next_module_id(); inline uint32_t get_next_module_id();
inline uint32_t get_next_script_id(); inline uint32_t get_next_script_id();

View File

@ -1041,7 +1041,7 @@ static MaybeLocal<Promise> ImportModuleDynamically(
ModuleWrap* wrap = ModuleWrap::GetFromID(env, id); ModuleWrap* wrap = ModuleWrap::GetFromID(env, id);
object = wrap->object(); object = wrap->object();
} else if (type == ScriptType::kFunction) { } else if (type == ScriptType::kFunction) {
object = env->id_to_function_map.find(id)->second.Get(iso); object = env->id_to_function_map.find(id)->second->cache_key.Get(iso);
} else { } else {
UNREACHABLE(); UNREACHABLE();
} }

View File

@ -66,6 +66,7 @@ using v8::PropertyHandlerFlags;
using v8::Script; using v8::Script;
using v8::ScriptCompiler; using v8::ScriptCompiler;
using v8::ScriptOrigin; using v8::ScriptOrigin;
using v8::ScriptOrModule;
using v8::String; using v8::String;
using v8::Symbol; using v8::Symbol;
using v8::Uint32; using v8::Uint32;
@ -305,15 +306,6 @@ void ContextifyContext::WeakCallback(
delete context; delete context;
} }
void ContextifyContext::WeakCallbackCompileFn(
const WeakCallbackInfo<CompileFnEntry>& data) {
CompileFnEntry* entry = data.GetParameter();
if (entry->env->compile_fn_entries.erase(entry) != 0) {
entry->env->id_to_function_map.erase(entry->id);
delete entry;
}
}
// static // static
ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox( ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox(
Environment* env, Environment* env,
@ -1117,9 +1109,11 @@ void ContextifyContext::CompileFunction(
} }
} }
Local<ScriptOrModule> script;
MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunctionInContext( MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunctionInContext(
parsing_context, &source, params.size(), params.data(), parsing_context, &source, params.size(), params.data(),
context_extensions.size(), context_extensions.data(), options); context_extensions.size(), context_extensions.data(), options,
v8::ScriptCompiler::NoCacheReason::kNoCacheNoReason, &script);
if (maybe_fn.IsEmpty()) { if (maybe_fn.IsEmpty()) {
if (try_catch.HasCaught() && !try_catch.HasTerminated()) { if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
@ -1129,13 +1123,17 @@ void ContextifyContext::CompileFunction(
return; return;
} }
Local<Function> fn = maybe_fn.ToLocalChecked(); Local<Function> fn = maybe_fn.ToLocalChecked();
env->id_to_function_map.emplace(std::piecewise_construct,
std::make_tuple(id), CompiledFnEntry* entry = new CompiledFnEntry(env, id, script);
std::make_tuple(isolate, fn)); env->id_to_function_map.emplace(id, entry);
CompileFnEntry* gc_entry = new CompileFnEntry(env, id); Local<Object> cache_key = entry->cache_key.Get(isolate);
env->id_to_function_map[id].SetWeak(gc_entry,
WeakCallbackCompileFn, Local<Object> result = Object::New(isolate);
v8::WeakCallbackType::kParameter); if (result->Set(parsing_context, env->function_string(), fn).IsNothing())
return;
if (result->Set(parsing_context, env->cache_key_string(), cache_key)
.IsNothing())
return;
if (produce_cached_data) { if (produce_cached_data) {
const std::unique_ptr<ScriptCompiler::CachedData> cached_data( const std::unique_ptr<ScriptCompiler::CachedData> cached_data(
@ -1146,18 +1144,22 @@ void ContextifyContext::CompileFunction(
env, env,
reinterpret_cast<const char*>(cached_data->data), reinterpret_cast<const char*>(cached_data->data),
cached_data->length); cached_data->length);
if (fn->Set( if (result
parsing_context, ->Set(parsing_context,
env->cached_data_string(), env->cached_data_string(),
buf.ToLocalChecked()).IsNothing()) return; buf.ToLocalChecked())
.IsNothing())
return;
} }
if (fn->Set( if (result
parsing_context, ->Set(parsing_context,
env->cached_data_produced_string(), env->cached_data_produced_string(),
Boolean::New(isolate, cached_data_produced)).IsNothing()) return; Boolean::New(isolate, cached_data_produced))
.IsNothing())
return;
} }
args.GetReturnValue().Set(fn); args.GetReturnValue().Set(result);
} }
static void StartSigintWatchdog(const FunctionCallbackInfo<Value>& args) { static void StartSigintWatchdog(const FunctionCallbackInfo<Value>& args) {

View File

@ -63,8 +63,6 @@ class ContextifyContext {
const v8::FunctionCallbackInfo<v8::Value>& args); const v8::FunctionCallbackInfo<v8::Value>& args);
static void WeakCallback( static void WeakCallback(
const v8::WeakCallbackInfo<ContextifyContext>& data); const v8::WeakCallbackInfo<ContextifyContext>& data);
static void WeakCallbackCompileFn(
const v8::WeakCallbackInfo<CompileFnEntry>& data);
static void PropertyGetterCallback( static void PropertyGetterCallback(
v8::Local<v8::Name> property, v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& args); const v8::PropertyCallbackInfo<v8::Value>& args);