diff --git a/src/bun.js/bindings/BunClientData.cpp b/src/bun.js/bindings/BunClientData.cpp index 544827ae7e..3aaf738664 100644 --- a/src/bun.js/bindings/BunClientData.cpp +++ b/src/bun.js/bindings/BunClientData.cpp @@ -26,6 +26,8 @@ namespace WebCore { using namespace JSC; +RefPtr createBuiltinsSourceProvider(); + JSHeapData::JSHeapData(Heap& heap) : m_heapCellTypeForJSWorkerGlobalScope(JSC::IsoHeapCellType::Args()) , m_domBuiltinConstructorSpace ISO_SUBSPACE_INIT(heap, heap.cellHeapCellType, JSDOMBuiltinConstructorBase) @@ -38,15 +40,14 @@ JSHeapData::JSHeapData(Heap& heap) #define CLIENT_ISO_SUBSPACE_INIT(subspace) subspace(m_heapData->subspace) -JSVMClientData::JSVMClientData(VM& vm) - : m_builtinFunctions(vm) - , m_builtinNames(vm) +JSVMClientData::JSVMClientData(VM& vm, RefPtr sourceProvider) + : m_builtinNames(vm) + , m_builtinFunctions(vm, sourceProvider, m_builtinNames) , m_heapData(JSHeapData::ensureHeapData(vm.heap)) , CLIENT_ISO_SUBSPACE_INIT(m_domBuiltinConstructorSpace) , CLIENT_ISO_SUBSPACE_INIT(m_domConstructorSpace) , CLIENT_ISO_SUBSPACE_INIT(m_domNamespaceObjectSpace) , m_clientSubspaces(makeUnique()) - { } @@ -72,7 +73,8 @@ JSVMClientData::~JSVMClientData() } void JSVMClientData::create(VM* vm, void* bunVM) { - JSVMClientData* clientData = new JSVMClientData(*vm); + auto provider = WebCore::createBuiltinsSourceProvider(); + JSVMClientData* clientData = new JSVMClientData(*vm, provider); clientData->bunVM = bunVM; vm->deferredWorkTimer->onAddPendingWork = Bun::JSCTaskScheduler::onAddPendingWork; vm->deferredWorkTimer->onScheduleWorkSoon = Bun::JSCTaskScheduler::onScheduleWorkSoon; @@ -83,6 +85,7 @@ void JSVMClientData::create(VM* vm, void* bunVM) vm->heap.addMarkingConstraint(makeUnique(*vm, clientData->heapData())); vm->m_typedArrayController = adoptRef(new WebCoreTypedArrayController(true)); + clientData->builtinFunctions().exportNames(); } } // namespace WebCore \ No newline at end of file diff --git a/src/bun.js/bindings/BunClientData.h b/src/bun.js/bindings/BunClientData.h index e6862b9553..e0f8fb3034 100644 --- a/src/bun.js/bindings/BunClientData.h +++ b/src/bun.js/bindings/BunClientData.h @@ -76,7 +76,7 @@ class JSVMClientData : public JSC::VM::ClientData { WTF_MAKE_FAST_ALLOCATED; public: - explicit JSVMClientData(JSC::VM&); + explicit JSVMClientData(JSC::VM&, RefPtr); virtual ~JSVMClientData(); diff --git a/src/bun.js/bindings/InternalModuleRegistry.cpp b/src/bun.js/bindings/InternalModuleRegistry.cpp index 501c7a92aa..fdf5c3eb55 100644 --- a/src/bun.js/bindings/InternalModuleRegistry.cpp +++ b/src/bun.js/bindings/InternalModuleRegistry.cpp @@ -1,5 +1,4 @@ #include "InternalModuleRegistry.h" - #include "ZigGlobalObject.h" #include #include diff --git a/src/codegen/bundle-functions.ts b/src/codegen/bundle-functions.ts index dfc66b8368..79f4d2e956 100644 --- a/src/codegen/bundle-functions.ts +++ b/src/codegen/bundle-functions.ts @@ -8,13 +8,19 @@ // library, instead of RegExp hacks. // // For explanation on this, please nag @paperdave to write documentation on how everything works. +// +// The output is intended to be similar to what WebCore does internally with a couple differences: +// +// - We concatenate all the sources into one big string, which then createsa +// single JSC::SourceProvider and pass start/end positions to each function's +// JSC::SourceCode. JSC does this, but WebCore does not seem to. import { readdirSync, rmSync } from "fs"; import path from "path"; import { sliceSourceCode } from "./builtin-parser"; -import { applyGlobalReplacements, define } from "./replacements"; -import { cap, fmtCPPCharArray, low, writeIfNotChanged } from "./helpers"; import { createAssertClientJS, createLogClientJS } from "./client-js"; import { getJS2NativeDTS } from "./generate-js2native"; +import { addCPPCharArray, cap, low, writeIfNotChanged } from "./helpers"; +import { applyGlobalReplacements, define } from "./replacements"; const PARALLEL = false; const KEEP_TMP = true; @@ -52,6 +58,7 @@ interface BundledBuiltin { source: string; params: string[]; visibility: string; + sourceOffset: number; } /** @@ -228,6 +235,10 @@ $$capture_start$$(${fn.async ? "async " : ""}${ constructKind: fn.directives.ConstructKind ?? "None", isLinkTimeConstant: !!fn.directives.linkTimeConstant, intrinsic: fn.directives.intrinsic ?? "NoIntrinsic", + + // Not known yet. + sourceOffset: 0, + overriddenName: fn.directives.getter ? `"get ${fn.name}"_s` : fn.directives.overriddenName @@ -275,6 +286,34 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt } } + let combinedSourceCodeChars = ""; + let combinedSourceCodeLength = 0; + // Compute source offsets + { + for (const { basename, functions } of files) { + for (const fn of functions) { + fn.sourceOffset = combinedSourceCodeLength; + combinedSourceCodeLength += fn.source.length; + if (combinedSourceCodeChars && !combinedSourceCodeChars.endsWith(",")) { + combinedSourceCodeChars += ","; + } + combinedSourceCodeChars += addCPPCharArray(fn.source, false); + + // If you want to see the individual function sources: + // if (true) { + // Bun.write(CODEGEN_DIR + "/functions/" + low(basename) + cap(fn.name) + ".js", fn.source + "\n"); + // } + } + } + } + + let additionalPrivateNames = new Set(); + + function privateName(name) { + additionalPrivateNames.add(name); + return "builtinNames." + name + "PrivateName()"; + } + // C++ codegen let bundledCPP = `// Generated by ${import.meta.path} namespace Zig { class GlobalObject; } @@ -283,48 +322,78 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt #include "JSDOMGlobalObject.h" #include "WebCoreJSClientData.h" #include - + #include "BunBuiltinNames.h" + namespace WebCore { - + static const LChar combinedSourceCodeBuffer[${combinedSourceCodeLength + 1}] = { ${combinedSourceCodeChars}, 0 }; + static const std::span internalCombinedSource = { combinedSourceCodeBuffer, ${combinedSourceCodeLength} }; `; for (const { basename, functions } of files) { - bundledCPP += `/* ${basename}.ts */\n`; + bundledCPP += ` +#pragma mark ${basename} +`; + const lowerBasename = low(basename); for (const fn of functions) { - const [code, count] = fmtCPPCharArray(fn.source, true); - const name = `${lowerBasename}${cap(fn.name)}Code`; - bundledCPP += `// ${fn.name} - const JSC::ConstructAbility s_${name}ConstructAbility = JSC::ConstructAbility::${fn.constructAbility}; - const JSC::InlineAttribute s_${name}InlineAttribute = JSC::InlineAttribute::${ - fn.directives.alwaysInline ? "Always" : "None" - }; - const JSC::ConstructorKind s_${name}ConstructorKind = JSC::ConstructorKind::${fn.constructKind}; - const JSC::ImplementationVisibility s_${name}ImplementationVisibility = JSC::ImplementationVisibility::${ - fn.visibility - }; - const int s_${name}Length = ${fn.source.length}; - const JSC::Intrinsic s_${name}Intrinsic = JSC::NoIntrinsic; - const char s_${name}Bytes[${count}] = ${code}; - const char* s_${name} = s_${name}Bytes; - `; + const name = `${basename}${cap(fn.name)}`; + bundledCPP += ` +JSC::FunctionExecutable* ${lowerBasename}${cap(fn.name)}CodeGenerator(JSC::VM& vm) +{ + auto &builtins = static_cast(vm.clientData)->builtinFunctions().${lowerBasename}Builtins(); + auto *executable = builtins.${lowerBasename}${cap(fn.name)}CodeExecutable(); + return executable->link(vm, nullptr, builtins.${lowerBasename}${cap(fn.name)}CodeSource(), std::nullopt, JSC::NoIntrinsic); +} +`; } - bundledCPP += `#define DEFINE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \\ - JSC::FunctionExecutable* codeName##Generator(JSC::VM& vm) \\ - {\\ - JSVMClientData* clientData = static_cast(vm.clientData); \\ - return clientData->builtinFunctions().${lowerBasename}Builtins().codeName##Executable()->link(vm, nullptr, clientData->builtinFunctions().${lowerBasename}Builtins().codeName##Source(), std::nullopt, s_##codeName##Intrinsic); \\ + } + + const initializeSourceCodeFn = (fn: BundledBuiltin, basename: string) => { + const name = `${low(basename)}${cap(fn.name)}CodeSource`; + return `m_${name}(SourceCode(sourceProvider.copyRef(), ${fn.sourceOffset}, ${fn.source.length + fn.sourceOffset}, 1, 1))`; + }; + for (const { basename, internal, functions } of files) { + bundledCPP += ` +#pragma mark ${basename} + +${basename}BuiltinsWrapper::${basename}BuiltinsWrapper(JSC::VM& vm, RefPtr sourceProvider, BunBuiltinNames &builtinNames) + : m_vm(vm)`; + + if (internal) { + bundledCPP += `, ${functions.map(fn => `m_${fn.name}PrivateName(${privateName(fn.name)})`).join(",\n ")}`; } - WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR) - #undef DEFINE_BUILTIN_GENERATOR - - `; + bundledCPP += `, ${functions.map(fn => initializeSourceCodeFn(fn, basename)).join(",\n ")} {} +`; } + bundledCPP += ` +RefPtr createBuiltinsSourceProvider() { + return JSC::StringSourceProvider::create(StringImpl::createWithoutCopying(internalCombinedSource), SourceOrigin(), String(), SourceTaintedOrigin()); +} +`; + + bundledCPP += ` +JSBuiltinFunctions::JSBuiltinFunctions(JSC::VM& vm, RefPtr provider, BunBuiltinNames& builtinNames) : m_vm(vm), + ${files.map(({ basename }) => `m_${low(basename)}Builtins(vm, provider, builtinNames)`).join(", ")} +{} + +void JSBuiltinFunctions::exportNames() { +`; + + for (const { basename, internal } of files) { + if (internal) { + bundledCPP += ` m_${low(basename)}Builtins.exportNames();\n`; + } + } + + bundledCPP += ` +} + +`; + bundledCPP += ` - JSBuiltinInternalFunctions::JSBuiltinInternalFunctions(JSC::VM& vm) - : m_vm(vm) +JSBuiltinInternalFunctions::JSBuiltinInternalFunctions(JSC::VM& vm) : m_vm(vm) `; for (const { basename, internal } of files) { @@ -333,10 +402,9 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt } } - bundledCPP += ` - { - UNUSED_PARAM(vm); - } + bundledCPP += `{ + UNUSED_PARAM(vm); + } template void JSBuiltinInternalFunctions::visit(Visitor& visitor) @@ -417,12 +485,10 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt const name = `${lowerBasename}${cap(fn.name)}Code`; bundledHeader += `// ${fn.name} #define WEBCORE_BUILTIN_${basename.toUpperCase()}_${fn.name.toUpperCase()} 1 - extern const char* s_${name}; - extern const int s_${name}Length; - extern const JSC::ConstructAbility s_${name}ConstructAbility; - extern const JSC::InlineAttribute s_${name}InlineAttribute; - extern const JSC::ConstructorKind s_${name}ConstructorKind; - extern const JSC::ImplementationVisibility s_${name}ImplementationVisibility; + static constexpr JSC::ConstructAbility s_${name}ConstructAbility = JSC::ConstructAbility::${fn.constructAbility}; + static constexpr JSC::InlineAttribute s_${name}InlineAttribute = JSC::InlineAttribute::${fn.directives.alwaysInline ? "Always" : "None"}; + static constexpr JSC::ConstructorKind s_${name}ConstructorKind = JSC::ConstructorKind::${fn.constructKind}; + static constexpr JSC::ImplementationVisibility s_${name}ImplementationVisibility = JSC::ImplementationVisibility::${fn.visibility}; `; } @@ -450,14 +516,7 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt class ${basename}BuiltinsWrapper : private JSC::WeakHandleOwner { public: - explicit ${basename}BuiltinsWrapper(JSC::VM& vm) - : m_vm(vm) - WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_FUNCTION_NAME(INITIALIZE_BUILTIN_NAMES) - #define INITIALIZE_BUILTIN_SOURCE_MEMBERS(name, functionName, overriddenName, length) , m_##name##Source(JSC::makeSource(StringImpl::createWithoutCopying({reinterpret_cast(s_##name), static_cast(length)}), { }, JSC::SourceTaintedOrigin::Untainted)) - WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(INITIALIZE_BUILTIN_SOURCE_MEMBERS) - #undef INITIALIZE_BUILTIN_SOURCE_MEMBERS - { - } + explicit ${basename}BuiltinsWrapper(JSC::VM& vm, RefPtr sourceProvider, BunBuiltinNames &builtinNames); #define EXPOSE_BUILTIN_EXECUTABLES(name, functionName, overriddenName, length) \\ JSC::UnlinkedFunctionExecutable* name##Executable(); \\ @@ -544,25 +603,9 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt } bundledHeader += `class JSBuiltinFunctions { public: - explicit JSBuiltinFunctions(JSC::VM& vm) - : m_vm(vm) - `; - - for (const { basename } of files) { - bundledHeader += ` , m_${low(basename)}Builtins(m_vm)\n`; - } - - bundledHeader += ` - { - `; - - for (const { basename, internal } of files) { - if (internal) { - bundledHeader += ` m_${low(basename)}Builtins.exportNames();\n`; - } - } - - bundledHeader += ` } + explicit JSBuiltinFunctions(JSC::VM& vm, RefPtr provider, BunBuiltinNames &builtinNames); + void exportNames(); + `; for (const { basename } of files) { @@ -613,7 +656,53 @@ export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuilt } // namespace WebCore `; + // Handle builtin names + { + const BunBuiltinNamesHeader = require("fs").readFileSync( + path.join(import.meta.dir, "../js/builtins/BunBuiltinNames.h"), + "utf8", + ); + let definedBuiltinNamesStartI = BunBuiltinNamesHeader.indexOf( + "#define BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME", + ); + let definedBuiltinNamesMacroEndI = BunBuiltinNamesHeader.indexOf( + "--- END of BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME ---", + ); + const definedBuiltinNames = BunBuiltinNamesHeader.slice(definedBuiltinNamesStartI, definedBuiltinNamesMacroEndI) + .split("\n") + .map(x => x.trim()) + .filter(x => x.startsWith("macro(")) + .map(x => x.slice(x.indexOf("(") + 1, x.indexOf(")"))) + .map(x => x.trim()) + .sort(); + const uniqueDefinedBuiltinNames = new Set(); + for (let name of definedBuiltinNames) { + const prevSize = uniqueDefinedBuiltinNames.size; + uniqueDefinedBuiltinNames.add(name); + if (uniqueDefinedBuiltinNames.size === prevSize) { + throw new Error(`Duplicate private name "${name}" in BunBuiltinNames.h`); + } + } + for (let additionalPrivateName of additionalPrivateNames) { + if (uniqueDefinedBuiltinNames.has(additionalPrivateName)) { + additionalPrivateNames.delete(additionalPrivateName); + } + } + + let additionalPrivateNamesHeader = `// Generated by ${import.meta.path} +#pragma once + +#ifndef BUN_ADDITIONAL_BUILTIN_NAMES +#define BUN_ADDITIONAL_BUILTIN_NAMES(macro) \\ + ${Array.from(additionalPrivateNames) + .map(x => `macro(${x})`) + .join(" \\\n ")} +#endif +`; + + writeIfNotChanged(path.join(CODEGEN_DIR, "BunBuiltinNames+extras.h"), additionalPrivateNamesHeader); + } writeIfNotChanged(path.join(CODEGEN_DIR, "WebCoreJSBuiltins.h"), bundledHeader); writeIfNotChanged(path.join(CODEGEN_DIR, "WebCoreJSBuiltins.cpp"), bundledCPP); diff --git a/src/codegen/helpers.ts b/src/codegen/helpers.ts index 04f436c25f..a97007b767 100644 --- a/src/codegen/helpers.ts +++ b/src/codegen/helpers.ts @@ -18,7 +18,17 @@ export function fmtCPPCharArray(str: string, nullTerminated: boolean = true) { .join(",") + (nullTerminated ? ",0" : "") + "}"; - return [chars, normalized.length + (nullTerminated ? 1 : 0)]; + return [chars, normalized.length + (nullTerminated ? 1 : 0)] as const; +} + +export function addCPPCharArray(str: string, nullTerminated: boolean = true) { + const normalized = str.trim() + "\n"; + return ( + normalized + .split("") + .map(a => a.charCodeAt(0)) + .join(",") + (nullTerminated ? ",0" : "") + ); } export function declareASCIILiteral(name: string, value: string) { diff --git a/src/js/builtins/BunBuiltinNames.h b/src/js/builtins/BunBuiltinNames.h index 226a340ae3..1ea5e53986 100644 --- a/src/js/builtins/BunBuiltinNames.h +++ b/src/js/builtins/BunBuiltinNames.h @@ -14,6 +14,7 @@ #include #include #include +#include "BunBuiltinNames+extras.h" namespace WebCore { @@ -194,7 +195,6 @@ using namespace JSC; macro(requireESM) \ macro(requireMap) \ macro(requireNativeModule) \ - macro(resolve) \ macro(resolveSync) \ macro(resume) \ macro(self) \ @@ -250,6 +250,8 @@ using namespace JSC; macro(writeRequests) \ macro(writing) \ macro(written) \ + BUN_ADDITIONAL_BUILTIN_NAMES(macro) +// --- END of BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME --- class BunBuiltinNames { public: @@ -268,6 +270,8 @@ public: BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(DECLARE_BUILTIN_IDENTIFIER_ACCESSOR) + const JSC::Identifier& resolvePublicName() const { return m_vm.propertyNames->resolve;} + private: JSC::VM& m_vm; BUN_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(DECLARE_BUILTIN_NAMES)