Compare commits

...

2 Commits

Author SHA1 Message Date
Jarred Sumner
f401253e07 Support webkit repo dir 2023-10-29 06:24:27 -07:00
Jarred Sumner
d6ff8970c3 Move builtin executalbe metadata parsing to build-time 2023-10-29 06:20:19 -07:00
4 changed files with 221 additions and 9 deletions

View File

@@ -138,7 +138,6 @@ if(UNIX)
else()
# Windows uses Clang-CL
# TODO: good configuration in this regard. -G Ninja will pick clang-cl if possible, which should be fine for most users.
find_program(
STRIP
NAMES llvm-strip
@@ -180,6 +179,7 @@ message(STATUS "C++ Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERS
# --- End LLVM ---
set(SHELL "bash")
set(SCRIPT_EXTENSION "sh")
if(WIN32)
set(SCRIPT_EXTENSION "ps1")
endif()
@@ -400,7 +400,15 @@ else()
# -DPTHREAD_JIT_PERMISSIONS_API=1
# -DUSE_PTHREAD_JIT_PERMISSIONS_API=ON
# -DENABLE_REMOTE_INSPECTOR=ON
message(FATAL_ERROR "TODO: Setting WEBKIT_DIR to the WebKit repository to enable automatic builds. For now you need to run the release script, and point to the packaged directory.")
message(STATUS "Using specified WebKit repository: ${WEBKIT_DIR}/WebKitBuild/Release")
include_directories(
"${WEBKIT_DIR}/WebKitBuild/Release/JavaScriptCore/Headers"
"${WEBKIT_DIR}/WebKitBuild/Release/JavaScriptCore/PrivateHeaders"
"${WEBKIT_DIR}/WebKitBuild/Release/WTF/Headers"
"${WEBKIT_DIR}/WebKitBuild/Release/bmalloc/Headers"
"${WEBKIT_DIR}/WebKitBuild/Release/"
)
set(WEBKIT_LIB_DIR "${WEBKIT_DIR}/WebKitBuild/Release/lib")
else()
if(NOT EXISTS "${WEBKIT_DIR}/lib/${libWTF}.${STATIC_LIB_EXT}" OR NOT EXISTS "${WEBKIT_DIR}/lib/${libJavaScriptCore}.${STATIC_LIB_EXT}")
if(WEBKIT_DIR MATCHES "src/bun.js/WebKit$")
@@ -414,10 +422,10 @@ else()
set(WEBKIT_LIB_DIR "${WEBKIT_DIR}/lib")
message(STATUS "Using specified WebKit directory: ${WEBKIT_DIR}")
set(ASSERT_ENABLED "0")
message(STATUS "WebKit assertions: OFF")
endif()
set(ASSERT_ENABLED "0")
message(STATUS "WebKit assertions: OFF")
endif()
# --- CMake Macros ---
@@ -521,6 +529,7 @@ set(BUN_OBJECT_LUT_SOURCES
)
set(BUN_OBJECT_LUT_OUTPUTS "")
set(BUN_HASH_LUT_GENERATOR "${BUN_CODEGEN_SRC}/create-hash-table.ts")
if(NOT BUN_LINK_ONLY)
macro(GENERATE_HASH_LUT _input _output _display_name)
if(NOT NO_CODEGEN)
@@ -533,6 +542,7 @@ if(NOT BUN_LINK_ONLY)
COMMENT "Generating ${_display_name}"
)
endif()
list(APPEND BUN_OBJECT_LUT_OUTPUTS "${_output}")
# list(APPEND Bun_HEADERS ${_output})
@@ -849,6 +859,7 @@ if(NOT MSVC)
-ferror-limit=${ERROR_LIMIT}
-fno-omit-frame-pointer
)
if(NOT WIN32)
target_compile_options(${bun} PUBLIC -fPIC)
endif()

View File

@@ -0,0 +1,183 @@
// This file is intended to be compatible with "BuiltinExecutableMetadata.h" in JSC.
// That is why the code is written like it's C++.
export interface BuiltinExecutableMetadata {
startColumn: number;
endColumn: number;
functionKeywordStart: number;
functionNameStart: number;
parametersStart: number;
isInStrictContext: boolean;
isArrowFunctionBodyExpression: boolean;
isAsyncFunction: boolean;
asyncOffset: number;
parameterCount: number;
lineCount: number;
offsetOfLastNewline: number;
positionBeforeLastNewlineLineStartOffset: number;
closeBraceOffsetFromEnd: number;
toCpp(): string;
}
function strlen(s) {
return s.length;
}
export function parseBuiltinExecutable(code: string): BuiltinExecutableMetadata {
const characters = code;
const length = code.length;
let startColumn = 0;
let endColumn = 0;
let functionKeywordStart = 0;
let functionNameStart = 0;
let parametersStart = 0;
let isInStrictContext = false;
let isArrowFunctionBodyExpression = false;
let isAsyncFunction = false;
let asyncOffset = 0;
let parameterCount = 0;
let lineCount = 0;
let offsetOfLastNewline = 0;
let positionBeforeLastNewlineLineStartOffset = 0;
let closeBraceOffsetFromEnd = 1;
const regularFunctionBegin = "(function (";
const asyncFunctionBegin = "(async function (";
console.assert(length >= strlen("(function (){})"), "Code is too short to be a function");
isAsyncFunction = length >= strlen("(async function (){})") && characters.startsWith(asyncFunctionBegin, 0);
console.assert(isAsyncFunction || characters.startsWith(regularFunctionBegin, 0), "Code is not a function");
asyncOffset = isAsyncFunction ? strlen("async ") : 0;
parametersStart = strlen("function (") + asyncOffset;
startColumn = parametersStart;
functionKeywordStart = strlen("(") + asyncOffset;
functionNameStart = parametersStart;
isInStrictContext = false;
isArrowFunctionBodyExpression = false;
parameterCount = 0;
{
let i = parametersStart + 1;
let commas = 0;
let insideCurlyBrackets = false;
let sawOneParam = false;
let hasRestParam = false;
while (true) {
console.assert(i < length, "Unexpected end of code");
if (characters[i] == ")") break;
if (characters[i] == "}") insideCurlyBrackets = false;
else if (characters[i] == "{" || insideCurlyBrackets) {
insideCurlyBrackets = true;
++i;
continue;
} else if (characters[i] == ",") ++commas;
else if (!isWhiteSpace(characters[i])) sawOneParam = true;
if (i + 2 < length && characters[i] == "." && characters[i + 1] == "." && characters[i + 2] == ".") {
hasRestParam = true;
i += 2;
}
++i;
}
if (commas) parameterCount = commas + 1;
else if (sawOneParam) parameterCount = 1;
else parameterCount = 0;
if (hasRestParam) {
console.assert(parameterCount, "Rest parameter must be preceded by another parameter");
--parameterCount;
}
}
lineCount = 0;
endColumn = 0;
offsetOfLastNewline = 0;
let offsetOfSecondToLastNewline: number | null = null;
for (let i = 0; i < length; ++i) {
if (characters[i] == "\n") {
if (lineCount) offsetOfSecondToLastNewline = offsetOfLastNewline;
++lineCount;
endColumn = 0;
offsetOfLastNewline = i;
} else ++endColumn;
if (!isInStrictContext && (characters[i] == '"' || characters[i] == "'")) {
const useStrictLength = strlen("use strict");
if (i + 1 + useStrictLength < length) {
if (characters.substring(i + 1).startsWith("use strict")) {
isInStrictContext = true;
i += 1 + useStrictLength;
}
}
}
}
positionBeforeLastNewlineLineStartOffset = offsetOfSecondToLastNewline !== null ? offsetOfSecondToLastNewline + 1 : 0;
closeBraceOffsetFromEnd = 1;
while (true) {
if (characters[length - closeBraceOffsetFromEnd] == "}") break;
++closeBraceOffsetFromEnd;
}
return {
startColumn,
endColumn,
functionKeywordStart,
functionNameStart,
parametersStart,
isInStrictContext,
isArrowFunctionBodyExpression,
isAsyncFunction,
asyncOffset,
parameterCount,
lineCount,
offsetOfLastNewline,
positionBeforeLastNewlineLineStartOffset,
closeBraceOffsetFromEnd,
toCpp() {
return `JSC::BuiltinExecutableMetadata(
${this.startColumn},
${this.endColumn},
${this.functionKeywordStart},
${this.functionNameStart},
${this.parametersStart},
${this.isInStrictContext},
${this.isArrowFunctionBodyExpression},
${this.isAsyncFunction},
${this.asyncOffset},
${this.parameterCount},
${this.lineCount},
${this.offsetOfLastNewline},
${this.positionBeforeLastNewlineLineStartOffset},
${this.closeBraceOffsetFromEnd}
)`;
},
};
}
// JSC::Lexer<LChar>::isWhiteSpace(LChar)
function isWhiteSpace(ch: string) {
switch (ch.charCodeAt(0)) {
case 0x09: // Tab
case 0x0b: // Vertical tab
case 0x0c: // Form feed
case 0x20: // Space
case 0xa0: // No-break space
case 0xc: // Byte order mark
return true;
default:
return false;
}
}

View File

@@ -8,6 +8,7 @@ import { createAssertClientJS, createLogClientJS } from "./client-js";
import { builtinModules } from "node:module";
import { define } from "./replacements";
import { createInternalModuleRegistry } from "./internal-module-registry-scanner";
import { parseBuiltinExecutable } from "./BuiltinExecutableMetadata";
const BASE = path.join(import.meta.dir, "../js");
const debug = process.argv[2] === "--debug=ON";
@@ -207,6 +208,7 @@ if (out.exitCode !== 0) {
mark("Bundle modules");
const outputs = new Map();
const executables = new Map();
for (const entrypoint of bundledEntryPoints) {
const file_path = entrypoint.slice(TMP_DIR.length + 1).replace(/\.ts$/, ".js");
@@ -248,6 +250,7 @@ for (const entrypoint of bundledEntryPoints) {
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, captured);
outputs.set(file_path.replace(".js", ""), captured);
executables.set(file_path.replace(".js", ""), parseBuiltinExecutable(captured));
}
mark("Postprocesss modules");
@@ -333,9 +336,15 @@ if (!debug) {
`// clang-format off
#pragma once
#include <JavaScriptCore/BuiltinExecutableMetadata.h>
namespace Bun {
namespace InternalModuleRegistryConstants {
${moduleList.map((id, n) => declareASCIILiteral(`${idToEnumName(id)}Code`, outputs.get(id.slice(0, -3)))).join("\n")}
${moduleList
.map((id, n) =>
declareASCIILiteral(`${idToEnumName(id)}Code`, outputs.get(id.slice(0, -3)), executables.get(id.slice(0, -3))),
)
.join("\n")}
}
}`,
);
@@ -346,9 +355,15 @@ namespace InternalModuleRegistryConstants {
`// clang-format off
#pragma once
#include <JavaScriptCore/BuiltinExecutableMetadata.h>
namespace Bun {
namespace InternalModuleRegistryConstants {
${moduleList.map((id, n) => `${declareASCIILiteral(`${idToEnumName(id)}Code`, "")}`).join("\n")}
${moduleList
.map((id, n) =>
declareASCIILiteral(`${idToEnumName(id)}Code`, outputs.get(id.slice(0, -3)), executables.get(id.slice(0, -3))),
)
.join("\n")}
}
}`,
);

View File

@@ -1,6 +1,7 @@
import fs from "fs";
import path from "path";
import { isAscii } from "buffer";
import { BuiltinExecutableMetadata } from "./BuiltinExecutableMetadata";
// MSVC has a max of 16k characters per string literal
// Combining string literals didn't support constexpr apparently
@@ -21,10 +22,12 @@ export function fmtCPPString(str: string, nullTerminated: boolean = true) {
return [chars, normalized.length + (nullTerminated ? 1 : 0)];
}
export function declareASCIILiteral(name: string, value: string) {
export function declareASCIILiteral(name: string, value: string, metadata: BuiltinExecutableMetadata) {
const [chars, count] = fmtCPPString(value);
return `static constexpr const char ${name}Bytes[${count}] = ${chars};
static constexpr ASCIILiteral ${name} = ASCIILiteral::fromLiteralUnsafe(${name}Bytes);`;
static constexpr ASCIILiteral ${name} = ASCIILiteral::fromLiteralUnsafe(${name}Bytes);
static const JSC::BuiltinExecutableMetadata ${name}BuiltinExecutableMetadata = ${metadata.toCpp()};
`;
}
export function cap(str: string) {