Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 32 additions & 11 deletions Source/JavaScriptCore/parser/SourceProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN
#include <wtf/Lock.h>
#include <wtf/Noncopyable.h>
#include <wtf/RefCountedFixedVector.h>
#include <wtf/Variant.h>
#include <wtf/Vector.h>
#include <wtf/text/TextPosition.h>
#include <wtf/text/WTFString.h>
Expand Down Expand Up @@ -348,15 +349,20 @@ class BuiltinsSourceProvider final : public StringSourceProvider {
// binds to them; see SyntheticModuleRecord::tryCreateWithExportNamesAndValues(..., JSObject* lazyExportsSource).
// The generator returns nullptr when it provided every value.
using LazySyntheticSourceGenerator = WTF::Function<JSObject*(JSGlobalObject*, Identifier, Vector<Identifier, 4>& exportNames, MarkedArgumentBuffer& exportValues)>;
// A generator that captures nothing. The value it exports is the payload of the JSSourceCode that holds the
// provider: JSSourceCode::createWithPayload() makes the two together, and is the only way to make a provider
// with this form. That cell traces the payload, so the generator can run every time a module is made from the
// source, which the loader does never, once, or more than once.
using PayloadSyntheticSourceGenerator = void (*)(JSGlobalObject*, Identifier, JSValue payload, Vector<Identifier, 4>& exportNames, MarkedArgumentBuffer& exportValues);

static Ref<SyntheticSourceProvider> create(SyntheticSourceGenerator&& generator, const SourceOrigin& sourceOrigin, String sourceURL)
{
return adoptRef(*new SyntheticSourceProvider(WTF::move(generator), nullptr, sourceOrigin, WTF::move(sourceURL)));
return adoptRef(*new SyntheticSourceProvider(Generator(WTF::InPlaceType<SyntheticSourceGenerator>, WTF::move(generator)), sourceOrigin, WTF::move(sourceURL)));
}

static Ref<SyntheticSourceProvider> createWithLazyExports(LazySyntheticSourceGenerator&& generator, const SourceOrigin& sourceOrigin, String sourceURL)
{
return adoptRef(*new SyntheticSourceProvider(nullptr, WTF::move(generator), sourceOrigin, WTF::move(sourceURL)));
return adoptRef(*new SyntheticSourceProvider(Generator(WTF::InPlaceType<LazySyntheticSourceGenerator>, WTF::move(generator)), sourceOrigin, WTF::move(sourceURL)));
}

// For a generator that evaluates a module written by the user to find out what it exports (a CommonJS module).
Expand All @@ -371,6 +377,7 @@ class BuiltinsSourceProvider final : public StringSourceProvider {
}

bool isDeferred() const { return m_isDeferred; }
bool takesPayload() const { return std::holds_alternative<PayloadSyntheticSourceGenerator>(m_generator); }

unsigned hash() const final
{
Expand All @@ -383,27 +390,41 @@ class BuiltinsSourceProvider final : public StringSourceProvider {
}

// Returns the object that exports declared without a value are read from, or nullptr if there are none.
JSObject* generate(JSGlobalObject* globalObject, Identifier moduleKey, Vector<Identifier, 4>& exportNames, MarkedArgumentBuffer& exportValues)
// `payload` is JSSourceCode::payload() of the cell that holds this provider. Only a
// PayloadSyntheticSourceGenerator reads it.
JSObject* generate(JSGlobalObject* globalObject, Identifier moduleKey, Vector<Identifier, 4>& exportNames, MarkedArgumentBuffer& exportValues, JSValue payload)
{
if (m_lazyGenerator)
return m_lazyGenerator(globalObject, moduleKey, exportNames, exportValues);
m_generator(globalObject, moduleKey, exportNames, exportValues);
if (auto* generator = std::get_if<PayloadSyntheticSourceGenerator>(&m_generator)) {
ASSERT(payload);
(*generator)(globalObject, moduleKey, payload, exportNames, exportValues);
return nullptr;
}
if (auto* generator = std::get_if<LazySyntheticSourceGenerator>(&m_generator))
return (*generator)(globalObject, moduleKey, exportNames, exportValues);
std::get<SyntheticSourceGenerator>(m_generator)(globalObject, moduleKey, exportNames, exportValues);
return nullptr;
}


private:
JS_EXPORT_PRIVATE SyntheticSourceProvider(SyntheticSourceGenerator&& generator, LazySyntheticSourceGenerator&& lazyGenerator, const SourceOrigin& sourceOrigin, String&& sourceURL, String&& preRedirectURL = String())
friend class JSSourceCode;

// A provider has one of the three generator forms, so they share one slot.
using Generator = Variant<SyntheticSourceGenerator, LazySyntheticSourceGenerator, PayloadSyntheticSourceGenerator>;

static Ref<SyntheticSourceProvider> createWithPayloadGenerator(PayloadSyntheticSourceGenerator generator, const SourceOrigin& sourceOrigin, String&& sourceURL)
{
return adoptRef(*new SyntheticSourceProvider(Generator(WTF::InPlaceType<PayloadSyntheticSourceGenerator>, generator), sourceOrigin, WTF::move(sourceURL)));
}

JS_EXPORT_PRIVATE SyntheticSourceProvider(Generator&& generator, const SourceOrigin& sourceOrigin, String&& sourceURL, String&& preRedirectURL = String())
: SourceProvider(sourceOrigin, WTF::move(sourceURL), WTF::move(preRedirectURL), SourceTaintedOrigin::Untainted, TextPosition(), SourceProviderSourceType::Synthetic)
, m_source("[native code]"_s)
, m_generator(WTF::move(generator))
, m_lazyGenerator(WTF::move(lazyGenerator))
{
}

String m_source;
SyntheticSourceGenerator m_generator;
LazySyntheticSourceGenerator m_lazyGenerator;
Generator m_generator;
bool m_isDeferred { false };
};

Expand Down
2 changes: 1 addition & 1 deletion Source/JavaScriptCore/runtime/JSModuleLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1507,7 +1507,7 @@ JSPromise* JSModuleLoader::makeModule(JSGlobalObject* globalObject, const Identi

MarkedArgumentBuffer args;
Vector<Identifier, 4> exportNames;
JSObject* lazyExportsSource = syntheticSourceProvider->generate(globalObject, moduleKey, exportNames, args);
JSObject* lazyExportsSource = syntheticSourceProvider->generate(globalObject, moduleKey, exportNames, args, jsSourceCode->payload());
RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(vm, scope));

auto* moduleRecord = SyntheticModuleRecord::tryCreateWithExportNamesAndValues(globalObject, this, moduleKey, exportNames, args, lazyExportsSource);
Expand Down
21 changes: 21 additions & 0 deletions Source/JavaScriptCore/runtime/JSSourceCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,25 @@ void JSSourceCode::destroy(JSCell* cell)
static_cast<JSSourceCode*>(cell)->~JSSourceCode();
}

#if USE(BUN_JSC_ADDITIONS)
JSSourceCode* JSSourceCode::createWithPayload(VM& vm, SyntheticSourceProvider::PayloadSyntheticSourceGenerator generator, JSValue payload, const SourceOrigin& sourceOrigin, String&& sourceURL)
{
ASSERT(payload);
auto* result = create(vm, SourceCode(SyntheticSourceProvider::createWithPayloadGenerator(generator, sourceOrigin, WTF::move(sourceURL))));
result->m_payload.set(vm, result, payload);
return result;
}

template<typename Visitor>
void JSSourceCode::visitChildrenImpl(JSCell* cell, Visitor& visitor)
{
auto* thisObject = uncheckedDowncast<JSSourceCode>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
visitor.append(thisObject->m_payload);
}

DEFINE_VISIT_CHILDREN(JSSourceCode);
#endif

} // namespace JSC
20 changes: 20 additions & 0 deletions Source/JavaScriptCore/runtime/JSSourceCode.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ class JSSourceCode final : public JSCell {
static constexpr DestructionMode needsDestruction = NeedsDestruction;

DECLARE_EXPORT_INFO;
#if USE(BUN_JSC_ADDITIONS)
DECLARE_VISIT_CHILDREN;
#endif

template<typename CellType, SubspaceAccess mode>
static GCClient::IsoSubspace* subspaceFor(VM& vm)
Expand All @@ -60,6 +63,14 @@ class JSSourceCode final : public JSCell {
return create(vm, vm.sourceCodeStructure.get(), WTF::move(sourceCode));
}

#if USE(BUN_JSC_ADDITIONS)
// The source of a synthetic module that exports `payload`. JSModuleLoader::makeModule() passes `payload` to
// `generator` each time it makes a module from this source, and this cell keeps `payload` alive until then.
JS_EXPORT_PRIVATE static JSSourceCode* createWithPayload(VM&, SyntheticSourceProvider::PayloadSyntheticSourceGenerator, JSValue payload, const SourceOrigin&, String&& sourceURL);

JSValue payload() const { return m_payload.get(); }
#endif

const SourceCode& sourceCode() const
{
return m_sourceCode;
Expand All @@ -75,6 +86,15 @@ class JSSourceCode final : public JSCell {
}

SourceCode m_sourceCode;
#if USE(BUN_JSC_ADDITIONS)
WriteBarrier<Unknown> m_payload;
#endif
};

#if USE(BUN_JSC_ADDITIONS) && CPU(ADDRESS64)
// The payload takes the bytes that rounding up to the cell size left unused: a JSSourceCode is a 32-byte cell with it
// and without it.
static_assert(sizeof(JSSourceCode) <= 32);
#endif

} // namespace JSC
5 changes: 4 additions & 1 deletion Source/JavaScriptCore/runtime/SyntheticModuleRecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,12 @@ void SyntheticModuleRecord::runDeferredGenerator(JSGlobalObject* globalObject)
};

Ref generator = *m_deferredGenerator;
// A deferred generator runs after makeModule() returned, without the JSSourceCode, so it is never one that takes
// that cell's payload. createDeferred() cannot make one.
ASSERT(!generator->takesPayload());
MarkedArgumentBuffer exportValues;
Vector<Identifier, 4> exportNames;
JSObject* lazyExportsSource = generator->generate(globalObject, moduleKey(), exportNames, exportValues);
JSObject* lazyExportsSource = generator->generate(globalObject, moduleKey(), exportNames, exportValues, { });
if (Exception* exception = scope.exception()) [[unlikely]] {
failed(exception);
return;
Expand Down
Loading