Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A few fixes related to CommonJS module loading #9540

Merged
merged 17 commits into from Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/bun.js/bindings/BunString.cpp
Expand Up @@ -27,6 +27,7 @@
#include "JSDOMOperation.h"

#include "GCDefferalContext.h"
#include "wtf/text/StringImpl.h"

extern "C" void mi_free(void* ptr);

Expand Down Expand Up @@ -559,4 +560,9 @@ extern "C" bool WTFStringImpl__isThreadSafe(
// return wtf->characters8() == reinterpret_cast_ptr<const LChar*>(reinterpret_cast<const uint8_t*>(wtf) + tailOffset<const LChar*>());

// return wtf->characters16() == reinterpret_cast_ptr<const UChar*>(reinterpret_cast<const uint16_t*>(wtf) + tailOffset<const UChar*>());
}

extern "C" void Bun__WTFStringImpl__ensureHash(WTF::StringImpl* str)
{
str->hash();
}
103 changes: 55 additions & 48 deletions src/bun.js/bindings/CommonJSModuleRecord.cpp
Expand Up @@ -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>
Expand Down Expand Up @@ -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*);

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}

Expand All @@ -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 (moduleObject->hasEvaluated || !moduleObject->sourceCode) {
RELEASE_AND_RETURN(throwScope, JSValue::encode(jsBoolean(true)));
if (this->hasEvaluated || this->sourceCode.isNull()) {
return 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({});
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -593,15 +601,14 @@ class JSCommonJSModulePrototype final : public JSC::JSNonFinalObject {

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(
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand All @@ -1031,26 +1034,24 @@ 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);
}

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;
Expand All @@ -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;
Expand Down Expand Up @@ -1110,6 +1115,8 @@ std::optional<JSC::SourceCode> createCommonJSModule(

moduleObject->toSyntheticSource(globalObject, moduleKey, exportNames, exportValues);
}
} else {
// require map was cleared of the entry
}
},
sourceOrigin,
Expand All @@ -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,
Expand Down
35 changes: 19 additions & 16 deletions src/bun.js/bindings/CommonJSModuleRecord.h
@@ -1,6 +1,8 @@
#pragma once
#include "JavaScriptCore/JSGlobalObject.h"
#include "root.h"
#include "headers-handwritten.h"
#include "wtf/NakedPtr.h"

namespace Zig {
class GlobalObject;
Expand Down Expand Up @@ -34,30 +36,34 @@ class JSCommonJSModule final : public JSC::JSDestructibleObject {
mutable JSC::WriteBarrier<JSString> m_dirname;
mutable JSC::WriteBarrier<Unknown> m_paths;
mutable JSC::WriteBarrier<Unknown> m_parent;
mutable JSC::WriteBarrier<JSSourceCode> sourceCode;
bool ignoreESModuleAnnotation { false };
JSC::SourceCode sourceCode = JSC::SourceCode();

void setSourceCode(JSC::SourceCode&& sourceCode);

static void destroy(JSC::JSCell*);
~JSCommonJSModule();

void clearSourceCode() { sourceCode = JSC::SourceCode(); }

void finishCreation(JSC::VM& vm,
JSC::JSString* id, JSValue filename,
JSC::JSString* dirname, JSC::JSSourceCode* sourceCode);
JSC::JSString* dirname, const JSC::SourceCode& sourceCode);

static JSC::Structure* createStructure(JSC::JSGlobalObject* globalObject);

bool evaluate(Zig::GlobalObject* globalObject, const WTF::String& sourceURL, ResolvedSource resolvedSource, bool isBuiltIn);
inline bool evaluate(Zig::GlobalObject* globalObject, const WTF::String& sourceURL, ResolvedSource resolvedSource)
bool evaluate(Zig::GlobalObject* globalObject, const WTF::String& sourceURL, ResolvedSource& resolvedSource, bool isBuiltIn);
inline bool evaluate(Zig::GlobalObject* globalObject, const WTF::String& sourceURL, ResolvedSource& resolvedSource)
{
return evaluate(globalObject, sourceURL, resolvedSource, false);
}
bool evaluate(Zig::GlobalObject* globalObject, const WTF::String& key, const SyntheticSourceProvider::SyntheticSourceGenerator& generator);
bool evaluate(Zig::GlobalObject* globalObject, const WTF::String& key, JSSourceCode* sourceCode);
bool evaluate(Zig::GlobalObject* globalObject, const WTF::String& key, const JSC::SourceCode& sourceCode);

static JSCommonJSModule* create(JSC::VM& vm, JSC::Structure* structure,
JSC::JSString* id,
JSValue filename,
JSC::JSString* dirname, JSC::JSSourceCode* sourceCode);
JSC::JSString* dirname, const JSC::SourceCode& sourceCode);

static JSCommonJSModule* create(
Zig::GlobalObject* globalObject,
Expand All @@ -79,10 +85,11 @@ class JSCommonJSModule final : public JSC::JSDestructibleObject {
JSValue exportsObject();
JSValue id();

bool load(JSC::VM& vm, Zig::GlobalObject* globalObject, WTF::NakedPtr<JSC::Exception>&);

DECLARE_INFO;
DECLARE_VISIT_CHILDREN;


static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);

template<typename, SubspaceAccess mode>
Expand All @@ -106,25 +113,21 @@ class JSCommonJSModule final : public JSC::JSDestructibleObject {
}
};

JSCommonJSModule* createCommonJSModuleWithoutRunning(
Zig::GlobalObject* globalObject,
Ref<Zig::SourceProvider> sourceProvider,
const WTF::String& sourceURL,
ResolvedSource source);

JSC::Structure* createCommonJSModuleStructure(
Zig::GlobalObject* globalObject);

std::optional<JSC::SourceCode> createCommonJSModule(
Zig::GlobalObject* globalObject,
ResolvedSource source,
JSC::JSValue specifierValue,
ResolvedSource& source,
bool isBuiltIn);

inline std::optional<JSC::SourceCode> createCommonJSModule(
Zig::GlobalObject* globalObject,
ResolvedSource source)
JSC::JSValue specifierValue,
ResolvedSource& source)
{
return createCommonJSModule(globalObject, source, false);
return createCommonJSModule(globalObject, specifierValue, source, false);
}

class RequireResolveFunctionPrototype final : public JSC::JSNonFinalObject {
Expand Down