export async function bundleBuiltinFunctions()

in src/codegen/bundle-functions.ts [303:771]


export async function bundleBuiltinFunctions({ requireTransformer }: BundleBuiltinFunctionsArgs) {
  const filesToProcess = readdirSync(SRC_DIR)
    .filter(x => x.endsWith(".ts") && !x.endsWith(".d.ts"))
    .sort();

  // Bun seems to crash if this is parallelized, :(
  if (PARALLEL) {
    await Promise.all(filesToProcess.map(processFunctionFile));
  } else {
    for (const x of filesToProcess) {
      await processFunctionFile(x);
    }
  }

  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; }
    #include "root.h"
    #include "config.h"
    #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 += `
#pragma mark ${basename}
`;

    const lowerBasename = low(basename);
    for (const fn of functions) {
      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);
}
`;
    }
  }

  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   ")}`;
    }
    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)
    `;

  for (const { basename, internal } of files) {
    if (internal) {
      bundledCPP += `    , m_${low(basename)}(vm)\n`;
    }
  }

  bundledCPP += `{
      UNUSED_PARAM(vm);
  }
    
    template<typename Visitor>
    void JSBuiltinInternalFunctions::visit(Visitor& visitor)
    {
    `;
  for (const { basename, internal } of files) {
    if (internal) bundledCPP += `    m_${low(basename)}.visit(visitor);\n`;
  }

  bundledCPP += `
        UNUSED_PARAM(visitor);
    }
    
    template void JSBuiltinInternalFunctions::visit(AbstractSlotVisitor&);
    template void JSBuiltinInternalFunctions::visit(SlotVisitor&);
    
    SUPPRESS_ASAN void JSBuiltinInternalFunctions::initialize(Zig::GlobalObject& globalObject)
    {
        UNUSED_PARAM(globalObject);
    `;

  for (const { basename, internal } of files) {
    if (internal) {
      bundledCPP += `    m_${low(basename)}.init(globalObject);\n`;
    }
  }

  bundledCPP += `
        JSVMClientData& clientData = *static_cast<JSVMClientData*>(m_vm.clientData);
        Zig::GlobalObject::GlobalPropertyInfo staticGlobals[] = {
    `;

  for (const { basename, internal } of files) {
    if (internal) {
      bundledCPP += `#define DECLARE_GLOBAL_STATIC(name) \\
        Zig::GlobalObject::GlobalPropertyInfo( \\
            clientData.builtinFunctions().${low(basename)}Builtins().name##PrivateName(), ${low(basename)}().m_##name##Function.get() , JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly),
        WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_FUNCTION_NAME(DECLARE_GLOBAL_STATIC)
      #undef DECLARE_GLOBAL_STATIC
      `;
    }
  }

  bundledCPP += `
        };
        globalObject.addStaticGlobals(staticGlobals, std::size(staticGlobals));
        UNUSED_PARAM(clientData);
    }
    
    } // namespace WebCore
    `;

  // C++ Header codegen
  let bundledHeader = `// Generated by ${import.meta.path}
    // Do not edit by hand.
    #pragma once
    namespace Zig { class GlobalObject; }
    #include "root.h"
    #include <JavaScriptCore/BuiltinUtils.h>
    #include <JavaScriptCore/Identifier.h>
    #include <JavaScriptCore/JSFunction.h>
    #include <JavaScriptCore/UnlinkedFunctionExecutable.h>
    #include <JavaScriptCore/VM.h>
    #include <JavaScriptCore/WeakInlines.h>
    
    namespace JSC {
    class FunctionExecutable;
    }
    
    namespace WebCore {
    `;
  for (const { basename, functions, internal } of files) {
    bundledHeader += `/* ${basename}.ts */
    `;
    const lowerBasename = low(basename);

    for (const fn of functions) {
      const name = `${lowerBasename}${cap(fn.name)}Code`;
      bundledHeader += `// ${fn.name}
    #define WEBCORE_BUILTIN_${basename.toUpperCase()}_${fn.name.toUpperCase()} 1
    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};
    
    `;
    }
    bundledHeader += `#define WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_DATA(macro) \\\n`;
    for (const fn of functions) {
      bundledHeader += `    macro(${fn.name}, ${lowerBasename}${cap(fn.name)}, ${fn.params.length}) \\\n`;
    }
    bundledHeader += "\n";
    bundledHeader += `#define WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(macro) \\\n`;
    for (const fn of functions) {
      const name = `${lowerBasename}${cap(fn.name)}Code`;
      bundledHeader += `    macro(${name}, ${fn.name}, ${fn.overriddenName}, s_${name}Length) \\\n`;
    }
    bundledHeader += "\n";
    bundledHeader += `#define WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_FUNCTION_NAME(macro) \\\n`;
    for (const fn of functions) {
      bundledHeader += `    macro(${fn.name}) \\\n`;
    }
    bundledHeader += `
    #define DECLARE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \\
        JSC::FunctionExecutable* codeName##Generator(JSC::VM&);
    
    WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(DECLARE_BUILTIN_GENERATOR)
    #undef DECLARE_BUILTIN_GENERATOR
    
    class ${basename}BuiltinsWrapper : private JSC::WeakHandleOwner {
    public:
        explicit ${basename}BuiltinsWrapper(JSC::VM& vm, RefPtr<JSC::SourceProvider> sourceProvider, BunBuiltinNames &builtinNames);
    
    #define EXPOSE_BUILTIN_EXECUTABLES(name, functionName, overriddenName, length) \\
        JSC::UnlinkedFunctionExecutable* name##Executable(); \\
        const JSC::SourceCode& name##Source() const { return m_##name##Source; }
        WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(EXPOSE_BUILTIN_EXECUTABLES)
    #undef EXPOSE_BUILTIN_EXECUTABLES
    
        WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_FUNCTION_NAME(DECLARE_BUILTIN_IDENTIFIER_ACCESSOR)
    
        void exportNames();
    
    private:
        JSC::VM& m_vm;
    
        WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_FUNCTION_NAME(DECLARE_BUILTIN_NAMES)
    
    #define DECLARE_BUILTIN_SOURCE_MEMBERS(name, functionName, overriddenName, length) \\
        JSC::SourceCode m_##name##Source;\\
        JSC::Weak<JSC::UnlinkedFunctionExecutable> m_##name##Executable;
        WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(DECLARE_BUILTIN_SOURCE_MEMBERS)
    #undef DECLARE_BUILTIN_SOURCE_MEMBERS
    
    };
    
    #define DEFINE_BUILTIN_EXECUTABLES(name, functionName, overriddenName, length) \\
    inline JSC::UnlinkedFunctionExecutable* ${basename}BuiltinsWrapper::name##Executable() \\
    {\\
        if (!m_##name##Executable) {\\
            JSC::Identifier executableName = functionName##PublicName();\\
            if (overriddenName)\\
                executableName = JSC::Identifier::fromString(m_vm, overriddenName);\\
            m_##name##Executable = JSC::Weak<JSC::UnlinkedFunctionExecutable>(JSC::createBuiltinExecutable(m_vm, m_##name##Source, executableName, s_##name##ImplementationVisibility, s_##name##ConstructorKind, s_##name##ConstructAbility, s_##name##InlineAttribute), this, &m_##name##Executable);\\
        }\\
        return m_##name##Executable.get();\\
    }
    WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(DEFINE_BUILTIN_EXECUTABLES)
    #undef DEFINE_BUILTIN_EXECUTABLES
    
    inline void ${basename}BuiltinsWrapper::exportNames()
    {
    #define EXPORT_FUNCTION_NAME(name) m_vm.propertyNames->appendExternalName(name##PublicName(), name##PrivateName());
        WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_FUNCTION_NAME(EXPORT_FUNCTION_NAME)
    #undef EXPORT_FUNCTION_NAME
    }
    `;

    if (internal) {
      bundledHeader += `class ${basename}BuiltinFunctions {
    public:
        explicit ${basename}BuiltinFunctions(JSC::VM& vm) : m_vm(vm) { }
    
        void init(JSC::JSGlobalObject&);
        template<typename Visitor> void visit(Visitor&);
    
    public:
        JSC::VM& m_vm;
    
    #define DECLARE_BUILTIN_SOURCE_MEMBERS(functionName) \\
        JSC::WriteBarrier<JSC::JSFunction> m_##functionName##Function;
        WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_FUNCTION_NAME(DECLARE_BUILTIN_SOURCE_MEMBERS)
    #undef DECLARE_BUILTIN_SOURCE_MEMBERS
    };
    
    inline void ${basename}BuiltinFunctions::init(JSC::JSGlobalObject& globalObject)
    {
    #define EXPORT_FUNCTION(codeName, functionName, overriddenName, length) \\
        m_##functionName##Function.set(m_vm, &globalObject, JSC::JSFunction::create(m_vm, &globalObject, codeName##Generator(m_vm), &globalObject));
        WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(EXPORT_FUNCTION)
    #undef EXPORT_FUNCTION
    }
    
    template<typename Visitor>
    inline void ${basename}BuiltinFunctions::visit(Visitor& visitor)
    {
    #define VISIT_FUNCTION(name) visitor.append(m_##name##Function);
        WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_FUNCTION_NAME(VISIT_FUNCTION)
    #undef VISIT_FUNCTION
    }
    
    template void ${basename}BuiltinFunctions::visit(JSC::AbstractSlotVisitor&);
    template void ${basename}BuiltinFunctions::visit(JSC::SlotVisitor&);
        `;
    }
  }
  bundledHeader += `class JSBuiltinFunctions {
    public:
        explicit JSBuiltinFunctions(JSC::VM& vm, RefPtr<JSC::SourceProvider> provider, BunBuiltinNames &builtinNames);
        void exportNames();
            
    `;

  for (const { basename } of files) {
    bundledHeader += `    ${basename}BuiltinsWrapper& ${low(basename)}Builtins() { return m_${low(
      basename,
    )}Builtins; }\n`;
  }

  bundledHeader += `
    private:
        JSC::VM& m_vm;
    `;

  for (const { basename } of files) {
    bundledHeader += `    ${basename}BuiltinsWrapper m_${low(basename)}Builtins;\n`;
  }

  bundledHeader += `;
    };
    
    class JSBuiltinInternalFunctions {
    public:
        explicit JSBuiltinInternalFunctions(JSC::VM&);
    
        template<typename Visitor> void visit(Visitor&);
        void initialize(Zig::GlobalObject&);
    `;

  for (const { basename, internal } of files) {
    if (internal) {
      bundledHeader += `    ${basename}BuiltinFunctions& ${low(basename)}() { return m_${low(basename)}; }\n`;
    }
  }

  bundledHeader += `
    private:
        JSC::VM& m_vm;
    `;

  for (const { basename, internal } of files) {
    if (internal) {
      bundledHeader += `    ${basename}BuiltinFunctions m_${low(basename)};\n`;
    }
  }

  bundledHeader += `
    };
    
    } // 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);

  // Generate TS types
  let dts = `// Generated by \`bun src/js/builtins/codegen\`
    // Do not edit by hand.
    type RemoveThis<F> = F extends (this: infer T, ...args: infer A) => infer R ? (...args: A) => R : F;
    `;

  for (const { basename, functions, internal } of files) {
    if (internal) {
      dts += `\n// ${basename}.ts\n`;
      for (const fn of functions) {
        dts += `declare const \$${fn.name}: RemoveThis<typeof import("${path.relative(
          CODEGEN_DIR,
          path.join(SRC_DIR, basename),
        )}")[${JSON.stringify(fn.name)}]>;\n`;
      }
    }
  }

  dts += getJS2NativeDTS();

  writeIfNotChanged(path.join(CODEGEN_DIR, "WebCoreJSBuiltins.d.ts"), dts);

  const totalJSSize = files.reduce(
    (acc, { functions }) => acc + functions.reduce((acc, fn) => acc + fn.source.length, 0),
    0,
  );

  if (!KEEP_TMP) {
    await rmSync(TMP_DIR, { recursive: true });
  }

  globalThis.internalFunctionJSSize = totalJSSize;
  globalThis.internalFunctionCount = files.reduce((acc, { functions }) => acc + functions.length, 0);
  globalThis.internalFunctionFileCount = files.length;
}