diff --git a/Source/JavaScriptCore/Sources.txt b/Source/JavaScriptCore/Sources.txt index 0b20a052ff0f5..be21723c18377 100644 --- a/Source/JavaScriptCore/Sources.txt +++ b/Source/JavaScriptCore/Sources.txt @@ -1064,6 +1064,7 @@ runtime/ScopeOffset.cpp runtime/ScopedArguments.cpp runtime/ScopedArgumentsTable.cpp runtime/ScriptExecutable.cpp +runtime/ScriptFetchParameters.cpp runtime/SetConstructor.cpp runtime/SetIteratorPrototype.cpp runtime/SetPrototype.cpp diff --git a/Source/JavaScriptCore/runtime/JSModuleLoader.cpp b/Source/JavaScriptCore/runtime/JSModuleLoader.cpp index 98e72cefe5f26..7a55e9b4bb344 100644 --- a/Source/JavaScriptCore/runtime/JSModuleLoader.cpp +++ b/Source/JavaScriptCore/runtime/JSModuleLoader.cpp @@ -497,8 +497,9 @@ JSPromise* JSModuleLoader::importModule(JSGlobalObject* globalObject, JSString* } #if USE(BUN_JSC_ADDITIONS) if (!attributes.isEmpty()) { - if (!fetchParams) - fetchParams = ScriptFetchParameters::create(ScriptFetchParameters::Type::None); + // The attributes map is per-request state: never set it on a shared per-type instance. + if (!fetchParams || fetchParams->type() != ScriptFetchParameters::Type::HostDefined) + fetchParams = ScriptFetchParameters::createUnique(fetchParams ? fetchParams->type() : ScriptFetchParameters::Type::None); fetchParams->setAttributes(WTF::move(attributes)); } #endif diff --git a/Source/JavaScriptCore/runtime/ScriptFetchParameters.cpp b/Source/JavaScriptCore/runtime/ScriptFetchParameters.cpp new file mode 100644 index 0000000000000..5f0c744442f68 --- /dev/null +++ b/Source/JavaScriptCore/runtime/ScriptFetchParameters.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2026 Anthropic PBC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ScriptFetchParameters.h" + +#include +#include +#include + +namespace JSC { + +Ref ScriptFetchParameters::create(Type type) +{ + constexpr size_t sharedTypeCount = static_cast(Type::Text) + 1; + static_assert(static_cast(Type::None) == 0 && static_cast(Type::JavaScript) < sharedTypeCount && static_cast(Type::WebAssembly) < sharedTypeCount && static_cast(Type::JSON) < sharedTypeCount); + if (static_cast(type) >= sharedTypeCount) + return createUnique(type); + static LazyNeverDestroyed, sharedTypeCount>> shared; + static std::once_flag once; + std::call_once(once, [] { + shared.construct(); + for (size_t i = 0; i < sharedTypeCount; ++i) + shared.get()[i] = adoptRef(*new ScriptFetchParameters(static_cast(i))); + }); + return *shared.get()[static_cast(type)]; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ScriptFetchParameters.h b/Source/JavaScriptCore/runtime/ScriptFetchParameters.h index 2cbd57c0b75a4..cf5c52f8e40ad 100644 --- a/Source/JavaScriptCore/runtime/ScriptFetchParameters.h +++ b/Source/JavaScriptCore/runtime/ScriptFetchParameters.h @@ -26,7 +26,7 @@ #pragma once #include -#include +#include #include #if USE(BUN_JSC_ADDITIONS) #include @@ -35,7 +35,8 @@ namespace JSC { -class ScriptFetchParameters : public RefCounted { +// ThreadSafeRefCounted: the shared per-type instances (create(Type)) are process-wide and every VM's module records ref them. +class ScriptFetchParameters : public ThreadSafeRefCounted { public: enum Type : uint8_t { None, @@ -72,10 +73,11 @@ class ScriptFetchParameters : public RefCounted { virtual bool isTopLevelModule() const { return false; } - static Ref create(Type type) - { - return adoptRef(*new ScriptFetchParameters(type)); - } + // The plain per-type parameters carry nothing but the type, so every module request of a given type shares one + // immortal instance instead of allocating its own (a large module graph makes thousands of requests). A request that + // needs its own state (WebCore's ModuleFetchParameters, a HostDefined type string, an attributes map) still allocates. + JS_EXPORT_PRIVATE static Ref create(Type); + static Ref createUnique(Type type) { return adoptRef(*new ScriptFetchParameters(type)); } #if USE(BUN_JSC_ADDITIONS)