mirror of
https://github.com/oven-sh/bun
synced 2026-02-11 19:38:58 +00:00
Use one JSC::SourceProvider instead of 322 (#12761)
This commit is contained in:
@@ -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 <JavaScriptCore/JSObjectInlines.h>
|
||||
|
||||
#include "BunBuiltinNames.h"
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
static const LChar combinedSourceCodeBuffer[${combinedSourceCodeLength + 1}] = { ${combinedSourceCodeChars}, 0 };
|
||||
static const std::span<const LChar> 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<JSVMClientData*>(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<JSVMClientData*>(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<JSC::SourceProvider> 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<JSC::SourceProvider> createBuiltinsSourceProvider() {
|
||||
return JSC::StringSourceProvider::create(StringImpl::createWithoutCopying(internalCombinedSource), SourceOrigin(), String(), SourceTaintedOrigin());
|
||||
}
|
||||
`;
|
||||
|
||||
bundledCPP += `
|
||||
JSBuiltinFunctions::JSBuiltinFunctions(JSC::VM& vm, RefPtr<JSC::SourceProvider> 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<typename Visitor>
|
||||
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<const LChar*>(s_##name), static_cast<size_t>(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<JSC::SourceProvider> 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<JSC::SourceProvider> 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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user