diff --git a/Source/JavaScriptCore/parser/SourceProvider.h b/Source/JavaScriptCore/parser/SourceProvider.h index 423b7514e2f09..d862c17251519 100644 --- a/Source/JavaScriptCore/parser/SourceProvider.h +++ b/Source/JavaScriptCore/parser/SourceProvider.h @@ -43,6 +43,7 @@ WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN #include #include #include +#include #include #include #include @@ -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& 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& exportNames, MarkedArgumentBuffer& exportValues); static Ref 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, WTF::move(generator)), sourceOrigin, WTF::move(sourceURL))); } static Ref 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, 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). @@ -371,6 +377,7 @@ class BuiltinsSourceProvider final : public StringSourceProvider { } bool isDeferred() const { return m_isDeferred; } + bool takesPayload() const { return std::holds_alternative(m_generator); } unsigned hash() const final { @@ -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& 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& 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(&m_generator)) { + ASSERT(payload); + (*generator)(globalObject, moduleKey, payload, exportNames, exportValues); + return nullptr; + } + if (auto* generator = std::get_if(&m_generator)) + return (*generator)(globalObject, moduleKey, exportNames, exportValues); + std::get(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; + + static Ref createWithPayloadGenerator(PayloadSyntheticSourceGenerator generator, const SourceOrigin& sourceOrigin, String&& sourceURL) + { + return adoptRef(*new SyntheticSourceProvider(Generator(WTF::InPlaceType, 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 }; }; diff --git a/Source/JavaScriptCore/runtime/JSModuleLoader.cpp b/Source/JavaScriptCore/runtime/JSModuleLoader.cpp index ef4f008c26d6a..5572a30611015 100644 --- a/Source/JavaScriptCore/runtime/JSModuleLoader.cpp +++ b/Source/JavaScriptCore/runtime/JSModuleLoader.cpp @@ -1507,7 +1507,7 @@ JSPromise* JSModuleLoader::makeModule(JSGlobalObject* globalObject, const Identi MarkedArgumentBuffer args; Vector 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); diff --git a/Source/JavaScriptCore/runtime/JSSourceCode.cpp b/Source/JavaScriptCore/runtime/JSSourceCode.cpp index 4e200e56dcfa4..f92e66ab9a266 100644 --- a/Source/JavaScriptCore/runtime/JSSourceCode.cpp +++ b/Source/JavaScriptCore/runtime/JSSourceCode.cpp @@ -37,4 +37,25 @@ void JSSourceCode::destroy(JSCell* cell) static_cast(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 +void JSSourceCode::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = uncheckedDowncast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(thisObject->m_payload); +} + +DEFINE_VISIT_CHILDREN(JSSourceCode); +#endif + } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/JSSourceCode.h b/Source/JavaScriptCore/runtime/JSSourceCode.h index 18f61d5eb82c2..c97ff1885a447 100644 --- a/Source/JavaScriptCore/runtime/JSSourceCode.h +++ b/Source/JavaScriptCore/runtime/JSSourceCode.h @@ -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 static GCClient::IsoSubspace* subspaceFor(VM& vm) @@ -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; @@ -75,6 +86,15 @@ class JSSourceCode final : public JSCell { } SourceCode m_sourceCode; +#if USE(BUN_JSC_ADDITIONS) + WriteBarrier 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 diff --git a/Source/JavaScriptCore/runtime/SyntheticModuleRecord.cpp b/Source/JavaScriptCore/runtime/SyntheticModuleRecord.cpp index 1205bf2ff6e82..5ba6294cb5656 100644 --- a/Source/JavaScriptCore/runtime/SyntheticModuleRecord.cpp +++ b/Source/JavaScriptCore/runtime/SyntheticModuleRecord.cpp @@ -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 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;