Fix memory leak when requiring or importing modules that get GC'd later (#12997)

This commit is contained in:
Jarred Sumner
2024-08-01 12:05:37 -07:00
committed by GitHub
parent e585f900c9
commit 59c5c0fe48
14 changed files with 366 additions and 82 deletions

View File

@@ -42,6 +42,24 @@ using namespace JSC;
using namespace Zig;
using namespace WebCore;
class ResolvedSourceCodeHolder {
public:
ResolvedSourceCodeHolder(ErrorableResolvedSource* res_)
: res(res_)
{
}
~ResolvedSourceCodeHolder()
{
if (res->success && res->result.value.source_code.tag == BunStringTag::WTFStringImpl && res->result.value.needsDeref) {
res->result.value.needsDeref = false;
res->result.value.source_code.impl.wtf->deref();
}
}
ErrorableResolvedSource* res;
};
extern "C" BunLoaderType Bun__getDefaultLoader(JSC::JSGlobalObject*, BunString* specifier);
static JSC::JSInternalPromise* rejectedInternalPromise(JSC::JSGlobalObject* globalObject, JSC::JSValue value)
@@ -289,13 +307,7 @@ static JSValue handleVirtualModuleResult(
auto onLoadResult = handleOnLoadResult(globalObject, virtualModuleResult, specifier, wasModuleMock);
JSC::VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
WTF::String sourceCodeStringForDeref;
const auto getSourceCodeStringForDeref = [&]() {
if (res->success && res->result.value.needsDeref && res->result.value.source_code.tag == BunStringTag::WTFStringImpl) {
res->result.value.needsDeref = false;
sourceCodeStringForDeref = String(res->result.value.source_code.impl.wtf);
}
};
ResolvedSourceCodeHolder sourceCodeHolder(res);
const auto reject = [&](JSC::JSValue exception) -> JSValue {
if constexpr (allowPromise) {
@@ -342,7 +354,6 @@ static JSValue handleVirtualModuleResult(
if (!res->success) {
return reject(JSValue::decode(reinterpret_cast<EncodedJSValue>(res->result.err.ptr)));
}
getSourceCodeStringForDeref();
auto provider = Zig::SourceProvider::create(globalObject, res->result.value);
return resolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider)));
@@ -396,13 +407,7 @@ extern "C" void Bun__onFulfillAsyncModule(
BunString* specifier,
BunString* referrer)
{
WTF::String sourceCodeStringForDeref;
const auto getSourceCodeStringForDeref = [&]() {
if (res->result.value.needsDeref && res->result.value.source_code.tag == BunStringTag::WTFStringImpl) {
res->result.value.needsDeref = false;
sourceCodeStringForDeref = String(res->result.value.source_code.impl.wtf);
}
};
ResolvedSourceCodeHolder sourceCodeHolder(res);
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSC::JSInternalPromise* promise = jsCast<JSC::JSInternalPromise*>(JSC::JSValue::decode(encodedPromiseValue));
@@ -414,7 +419,6 @@ extern "C" void Bun__onFulfillAsyncModule(
return promise->reject(globalObject, exception);
}
getSourceCodeStringForDeref();
auto specifierValue = Bun::toJS(globalObject, *specifier);
if (auto entry = globalObject->esmRegistryMap()->get(globalObject, specifierValue)) {
@@ -467,13 +471,7 @@ JSValue fetchCommonJSModule(
memset(&resValue, 0, sizeof(ErrorableResolvedSource));
ErrorableResolvedSource* res = &resValue;
WTF::String sourceCodeStringForDeref;
const auto getSourceCodeStringForDeref = [&]() {
if (res->success && res->result.value.needsDeref && res->result.value.source_code.tag == BunStringTag::WTFStringImpl) {
res->result.value.needsDeref = false;
sourceCodeStringForDeref = String(res->result.value.source_code.impl.wtf);
}
};
ResolvedSourceCodeHolder sourceCodeHolder(res);
auto& builtinNames = WebCore::clientData(vm)->builtinNames();
bool wasModuleMock = false;
@@ -600,8 +598,6 @@ JSValue fetchCommonJSModule(
}
Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, false);
getSourceCodeStringForDeref();
if (res->success && res->result.value.isCommonJSModule) {
target->evaluate(globalObject, specifier->toWTFString(BunString::ZeroCopy), res->result.value);
RETURN_IF_EXCEPTION(scope, {});
@@ -669,6 +665,7 @@ static JSValue fetchESMSourceCode(
void* bunVM = globalObject->bunVM();
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
ResolvedSourceCodeHolder sourceCodeHolder(res);
const auto reject = [&](JSC::JSValue exception) -> JSValue {
if constexpr (allowPromise) {
@@ -755,23 +752,13 @@ static JSValue fetchESMSourceCode(
}
}
WTF::String sourceCodeStringForDeref;
const auto getSourceCodeStringForDeref = [&]() {
if (res->success && res->result.value.needsDeref && res->result.value.source_code.tag == BunStringTag::WTFStringImpl) {
res->result.value.needsDeref = false;
sourceCodeStringForDeref = String(res->result.value.source_code.impl.wtf);
}
};
if constexpr (allowPromise) {
auto* pendingCtx = Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, true);
getSourceCodeStringForDeref();
if (pendingCtx) {
return pendingCtx;
}
} else {
Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, false);
getSourceCodeStringForDeref();
}
if (res->success && res->result.value.isCommonJSModule) {