A few fixes related to CommonJS module loading (#9540)

* Ensure we always deref the source code

* Move more work to concurrent transpiler

* Update NodeModuleModule.h

* Update string.zig

* Make `Bun.gc()` more effective

* Update text-loader-fixture-dynamic-import-stress.ts

* Update ZigSourceProvider.cpp

* Fixes #6946

* Update ModuleLoader.cpp

* Update ModuleLoader.cpp

* Fixes #8965

* Fixups

* Update ModuleLoader.cpp

* Update ModuleLoader.cpp

* Load it

* Update module_loader.zig

* Update module_loader.zig

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
This commit is contained in:
Jarred Sumner
2024-03-21 10:42:57 -07:00
committed by GitHub
parent 0948727243
commit e124f08caf
27 changed files with 428 additions and 189 deletions

View File

@@ -30,7 +30,10 @@
*/
#include "headers.h"
#include "JavaScriptCore/JSCast.h"
#include <JavaScriptCore/JSMapInlines.h>
#include "root.h"
#include "JavaScriptCore/SourceCode.h"
#include "headers-handwritten.h"
#include "ZigGlobalObject.h"
#include <JavaScriptCore/JSSourceCode.h>
@@ -67,6 +70,8 @@
#include <JavaScriptCore/LazyPropertyInlines.h>
#include <JavaScriptCore/HeapAnalyzer.h>
#include "PathInlines.h"
#include "wtf/NakedPtr.h"
#include "wtf/URL.h"
extern "C" bool Bun__isBunMain(JSC::JSGlobalObject* global, const BunString*);
@@ -97,10 +102,10 @@ extern "C" void Bun__VM__setEntryPointEvalResultCJS(void*, EncodedJSValue);
static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObject, JSCommonJSModule* moduleObject, JSString* dirname, JSValue filename, WTF::NakedPtr<Exception>& exception)
{
JSSourceCode* code = moduleObject->sourceCode.get();
SourceCode code = std::move(moduleObject->sourceCode);
// If an exception occurred somewhere else, we might have cleared the source code.
if (UNLIKELY(code == nullptr)) {
if (UNLIKELY(code.isNull())) {
auto throwScope = DECLARE_THROW_SCOPE(vm);
throwException(globalObject, throwScope, createError(globalObject, "Failed to evaluate module"_s));
exception = throwScope.exception();
@@ -132,25 +137,22 @@ static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObj
globalObject->putDirect(vm, Identifier::fromLatin1(vm, "__filename"_s), filename, 0);
globalObject->putDirect(vm, Identifier::fromLatin1(vm, "__dirname"_s), dirname, 0);
JSValue result = JSC::evaluate(globalObject, code->sourceCode(), jsUndefined(), exception);
JSValue result = JSC::evaluate(globalObject, code, jsUndefined(), exception);
if (UNLIKELY(exception.get() || result.isEmpty())) {
moduleObject->sourceCode.clear();
return false;
}
Bun__VM__setEntryPointEvalResultCJS(globalObject->bunVM(), JSValue::encode(result));
moduleObject->sourceCode.clear();
return true;
}
// This will return 0 if there was a syntax error or an allocation failure
JSValue fnValue = JSC::evaluate(globalObject, code->sourceCode(), jsUndefined(), exception);
JSValue fnValue = JSC::evaluate(globalObject, code, jsUndefined(), exception);
if (UNLIKELY(exception.get() || fnValue.isEmpty())) {
moduleObject->sourceCode.clear();
return false;
}
@@ -171,39 +173,47 @@ static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObj
JSC::call(globalObject, fn, callData, moduleObject, args, exception);
moduleObject->sourceCode.clear();
return exception.get() == nullptr;
}
JSC_DEFINE_HOST_FUNCTION(jsFunctionLoadModule, (JSGlobalObject * lexicalGlobalObject, CallFrame* callframe))
bool JSCommonJSModule::load(JSC::VM& vm, Zig::GlobalObject* globalObject, WTF::NakedPtr<JSC::Exception>& exception)
{
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(globalObject->vm());
JSCommonJSModule* moduleObject = jsDynamicCast<JSCommonJSModule*>(callframe->argument(0));
if (!moduleObject) {
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsBoolean(true)));
if (this->hasEvaluated || this->sourceCode.isNull()) {
return true;
}
if (moduleObject->hasEvaluated || !moduleObject->sourceCode) {
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsBoolean(true)));
}
WTF::NakedPtr<Exception> exception;
evaluateCommonJSModuleOnce(
globalObject->vm(),
jsCast<Zig::GlobalObject*>(globalObject),
moduleObject,
moduleObject->m_dirname.get(),
moduleObject->m_filename.get(),
this,
this->m_dirname.get(),
this->m_filename.get(),
exception);
if (exception.get()) {
// On error, remove the module from the require map/
// so that it can be re-evaluated on the next require.
globalObject->requireMap()->remove(globalObject, moduleObject->id());
globalObject->requireMap()->remove(globalObject, this->id());
return false;
}
return true;
}
JSC_DEFINE_HOST_FUNCTION(jsFunctionLoadModule, (JSGlobalObject * lexicalGlobalObject, CallFrame* callframe))
{
auto& vm = lexicalGlobalObject->vm();
auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
JSCommonJSModule* moduleObject = jsDynamicCast<JSCommonJSModule*>(callframe->argument(0));
if (!moduleObject) {
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsBoolean(true)));
}
WTF::NakedPtr<Exception> exception;
if (!moduleObject->load(vm, globalObject, exception)) {
throwException(globalObject, throwScope, exception.get());
exception.clear();
return JSValue::encode({});
@@ -487,15 +497,13 @@ JSC_DEFINE_HOST_FUNCTION(functionCommonJSModuleRecord_compile, (JSGlobalObject *
sourceString,
"\n})"_s);
SourceCode sourceCode = makeSource(
moduleObject->sourceCode = makeSource(
WTFMove(wrappedString),
SourceOrigin(URL::fileURLWithFileSystemPath(filenameString)),
JSC::SourceTaintedOrigin::Untainted,
filenameString,
WTF::TextPosition(),
JSC::SourceProviderSourceType::Program);
JSSourceCode* jsSourceCode = JSSourceCode::create(vm, WTFMove(sourceCode));
moduleObject->sourceCode.set(vm, moduleObject, jsSourceCode);
auto index = filenameString.reverseFind(PLATFORM_SEP, filenameString.length());
// filenameString is coming from js, any separator could be used
@@ -593,15 +601,14 @@ public:
const JSC::ClassInfo JSCommonJSModulePrototype::s_info = { "ModulePrototype"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCommonJSModulePrototype) };
void JSCommonJSModule::finishCreation(JSC::VM& vm, JSC::JSString* id, JSValue filename, JSC::JSString* dirname, JSC::JSSourceCode* sourceCode)
void JSCommonJSModule::finishCreation(JSC::VM& vm, JSC::JSString* id, JSValue filename, JSC::JSString* dirname, const JSC::SourceCode& sourceCode)
{
Base::finishCreation(vm);
ASSERT(inherits(info()));
m_id.set(vm, this, id);
m_filename.set(vm, this, filename);
m_dirname.set(vm, this, dirname);
if (sourceCode)
this->sourceCode.set(vm, this, sourceCode);
this->sourceCode = sourceCode;
}
JSC::Structure* JSCommonJSModule::createStructure(
@@ -622,7 +629,7 @@ JSCommonJSModule* JSCommonJSModule::create(
JSC::JSString* id,
JSValue filename,
JSC::JSString* dirname,
JSC::JSSourceCode* sourceCode)
const JSC::SourceCode& sourceCode)
{
JSCommonJSModule* cell = new (NotNull, JSC::allocateCell<JSCommonJSModule>(vm)) JSCommonJSModule(vm, structure);
cell->finishCreation(vm, id, filename, dirname, sourceCode);
@@ -664,7 +671,7 @@ JSCommonJSModule* JSCommonJSModule::create(
auto* out = JSCommonJSModule::create(
vm,
globalObject->CommonJSModuleObjectStructure(),
requireMapKey, requireMapKey, dirname, nullptr);
requireMapKey, requireMapKey, dirname, SourceCode());
out->putDirect(
vm,
@@ -898,7 +905,6 @@ void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor)
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
visitor.append(thisObject->m_id);
visitor.append(thisObject->sourceCode);
visitor.append(thisObject->m_filename);
visitor.append(thisObject->m_dirname);
visitor.append(thisObject->m_paths);
@@ -996,19 +1002,16 @@ void RequireResolveFunctionPrototype::finishCreation(JSC::VM& vm)
bool JSCommonJSModule::evaluate(
Zig::GlobalObject* globalObject,
const WTF::String& key,
ResolvedSource source,
ResolvedSource& source,
bool isBuiltIn)
{
auto& vm = globalObject->vm();
auto sourceProvider = Zig::SourceProvider::create(jsCast<Zig::GlobalObject*>(globalObject), source, JSC::SourceProviderSourceType::Program, isBuiltIn);
this->ignoreESModuleAnnotation = source.tag == ResolvedSourceTagPackageJSONTypeModule;
JSC::SourceCode rawInputSource(
WTFMove(sourceProvider));
if (this->hasEvaluated)
return true;
this->sourceCode.set(vm, this, JSC::JSSourceCode::create(vm, WTFMove(rawInputSource)));
this->sourceCode = WTFMove(JSC::SourceCode(WTFMove(sourceProvider)));
WTF::NakedPtr<JSC::Exception> exception;
@@ -1031,18 +1034,16 @@ bool JSCommonJSModule::evaluate(
std::optional<JSC::SourceCode> createCommonJSModule(
Zig::GlobalObject* globalObject,
ResolvedSource source,
JSValue specifierValue,
ResolvedSource& source,
bool isBuiltIn)
{
JSCommonJSModule* moduleObject = nullptr;
WTF::String sourceURL = source.source_url.toWTFString();
JSValue specifierValue = Bun::toJS(globalObject, source.specifier);
JSValue entry = globalObject->requireMap()->get(globalObject, specifierValue);
auto sourceProvider = Zig::SourceProvider::create(jsCast<Zig::GlobalObject*>(globalObject), source, JSC::SourceProviderSourceType::Program, isBuiltIn);
bool ignoreESModuleAnnotation = source.tag == ResolvedSourceTagPackageJSONTypeModule;
SourceOrigin sourceOrigin = sourceProvider->sourceOrigin();
SourceOrigin sourceOrigin;
if (entry) {
moduleObject = jsDynamicCast<JSCommonJSModule*>(entry);
@@ -1050,7 +1051,7 @@ std::optional<JSC::SourceCode> createCommonJSModule(
if (!moduleObject) {
auto& vm = globalObject->vm();
auto* requireMapKey = jsStringWithCache(vm, sourceURL);
auto* requireMapKey = specifierValue.toString(globalObject);
auto index = sourceURL.reverseFind(PLATFORM_SEP, sourceURL.length());
JSString* dirname;
JSString* filename = requireMapKey;
@@ -1060,16 +1061,20 @@ std::optional<JSC::SourceCode> createCommonJSModule(
dirname = jsEmptyString(vm);
}
auto sourceProvider = Zig::SourceProvider::create(jsCast<Zig::GlobalObject*>(globalObject), source, JSC::SourceProviderSourceType::Program, isBuiltIn);
sourceOrigin = sourceProvider->sourceOrigin();
moduleObject = JSCommonJSModule::create(
vm,
globalObject->CommonJSModuleObjectStructure(),
requireMapKey, filename, dirname, JSC::JSSourceCode::create(vm, SourceCode(WTFMove(sourceProvider))));
requireMapKey, filename, dirname, WTFMove(JSC::SourceCode(WTFMove(sourceProvider))));
moduleObject->putDirect(vm,
WebCore::clientData(vm)->builtinNames().exportsPublicName(),
JSC::constructEmptyObject(globalObject, globalObject->objectPrototype()), 0);
globalObject->requireMap()->set(globalObject, requireMapKey, moduleObject);
} else {
sourceOrigin = Zig::toSourceOrigin(sourceURL, isBuiltIn);
}
moduleObject->ignoreESModuleAnnotation = ignoreESModuleAnnotation;
@@ -1110,6 +1115,8 @@ std::optional<JSC::SourceCode> createCommonJSModule(
moduleObject->toSyntheticSource(globalObject, moduleKey, exportNames, exportValues);
}
} else {
// require map was cleared of the entry
}
},
sourceOrigin,
@@ -1134,7 +1141,7 @@ JSObject* JSCommonJSModule::createBoundRequireFunction(VM& vm, JSGlobalObject* l
auto moduleObject = Bun::JSCommonJSModule::create(
vm,
globalObject->CommonJSModuleObjectStructure(),
filename, filename, dirname, nullptr);
filename, filename, dirname, SourceCode());
JSFunction* requireFunction = JSC::JSBoundFunction::create(vm,
globalObject,