diff --git a/.github/workflows/bun-linux-build.yml b/.github/workflows/bun-linux-build.yml index 93ca4f0240d4e..694adcd9fb2ce 100644 --- a/.github/workflows/bun-linux-build.yml +++ b/.github/workflows/bun-linux-build.yml @@ -260,6 +260,7 @@ jobs: TMPDIR: ${{runner.temp}} TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }} TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }} + BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "true" # if: ${{github.event.inputs.use_bun == 'false'}} run: | node packages/bun-internal-test/src/runner.node.mjs || true diff --git a/.github/workflows/bun-mac-aarch64.yml b/.github/workflows/bun-mac-aarch64.yml index c1c045e56d483..b5bd764a69e09 100644 --- a/.github/workflows/bun-mac-aarch64.yml +++ b/.github/workflows/bun-mac-aarch64.yml @@ -429,6 +429,7 @@ jobs: TMPDIR: ${{runner.temp}} TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }} TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }} + BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "true" # if: ${{github.event.inputs.use_bun == 'false'}} run: | node packages/bun-internal-test/src/runner.node.mjs || true diff --git a/.github/workflows/bun-mac-x64-baseline.yml b/.github/workflows/bun-mac-x64-baseline.yml index 966b97c493900..3c5c13e367713 100644 --- a/.github/workflows/bun-mac-x64-baseline.yml +++ b/.github/workflows/bun-mac-x64-baseline.yml @@ -416,6 +416,7 @@ jobs: TMPDIR: ${{runner.temp}} TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }} TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }} + BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "true" # if: ${{github.event.inputs.use_bun == 'false'}} run: | node packages/bun-internal-test/src/runner.node.mjs || true diff --git a/.github/workflows/bun-mac-x64.yml b/.github/workflows/bun-mac-x64.yml index 09e31f7f10656..686d5f2b6f03a 100644 --- a/.github/workflows/bun-mac-x64.yml +++ b/.github/workflows/bun-mac-x64.yml @@ -413,6 +413,7 @@ jobs: TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }} TMPDIR: ${{runner.temp}} TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }} + BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "true" # if: ${{github.event.inputs.use_bun == 'false'}} run: | node packages/bun-internal-test/src/runner.node.mjs || true diff --git a/.github/workflows/bun-windows.yml b/.github/workflows/bun-windows.yml index 4e92941679d82..e45ecd22c057a 100644 --- a/.github/workflows/bun-windows.yml +++ b/.github/workflows/bun-windows.yml @@ -437,6 +437,7 @@ jobs: TMPDIR: ${{runner.temp}} TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }} TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }} + BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "true" SHELLOPTS: igncr BUN_PATH_BASE: ${{runner.temp}} BUN_PATH: release/${{env.tag}}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile/bun.exe diff --git a/.gitignore b/.gitignore index d00b77a4bc681..32c68dc08c6ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,169 +1,143 @@ -.DS_Store -zig-cache -packages/*/*.wasm -*.o -*.a -profile.json - -.env -node_modules -.envrc -.swcrc -yarn.lock -dist -*.tmp -*.log -*.out.js -*.out.refresh.js -**/package-lock.json -build -*.wat -zig-out -pnpm-lock.yaml -README.md.template -src/deps/zig-clap/example -src/deps/zig-clap/README.md -src/deps/zig-clap/.github -src/deps/zig-clap/.gitattributes -out -outdir - -.trace -cover -coverage -coverv -*.trace -github -out.* -out -.parcel-cache -esbuilddir -*.bun -parceldist -esbuilddir -outdir/ -outcss -.next -txt.js -.idea -.vscode/cpp* -.vscode/clang* - -node_modules_* -*.jsb -*.zip -bun-zigld -bun-singlehtreaded -bun-nomimalloc -bun-mimalloc -examples/lotta-modules/bun-yday -examples/lotta-modules/bun-old -examples/lotta-modules/bun-nofscache - -src/node-fallbacks/out/* -src/node-fallbacks/node_modules -sign.json -release/ -*.dmg -sign.*.json -packages/debug-* -packages/bun-cli/postinstall.js -packages/bun-*/bun -packages/bun-*/bun-profile -packages/bun-*/debug-bun -packages/bun-*/*.o -packages/bun-cli/postinstall.js - -packages/bun-cli/bin/* -bun-test-scratch -misctools/fetch - -src/deps/libiconv -src/deps/openssl -src/tests.zig -*.blob -src/deps/s2n-tls -.npm -.npm.gz - -bun-binary - -src/deps/PLCrashReporter/ - -*.dSYM -*.crash -misctools/sha -packages/bun-wasm/*.mjs -packages/bun-wasm/*.cjs -packages/bun-wasm/*.map -packages/bun-wasm/*.js -packages/bun-wasm/*.d.ts -packages/bun-wasm/*.d.cts -packages/bun-wasm/*.d.mts -*.bc - -src/fallback.version -src/runtime.version -*.sqlite -*.database -*.db -misctools/machbench -*.big -.eslintcache - -/bun-webkit - -src/deps/c-ares/build -src/bun.js/bindings-obj -src/bun.js/debug-bindings-obj - -failing-tests.txt -test.txt -myscript.sh - -cold-jsc-start -cold-jsc-start.d - -/testdir -/test.ts -/test.js - -src/js/out/modules* -src/js/out/functions* -src/js/out/tmp -src/js/out/DebugPath.h - -make-dev-stats.csv - -.uuid -tsconfig.tsbuildinfo - -test/js/bun/glob/fixtures -*.lib -*.pdb -CMakeFiles -build.ninja -.ninja_deps -.ninja_log -CMakeCache.txt -cmake_install.cmake -compile_commands.json - -*.lib -x64 -**/*.vcxproj* -**/*.sln* -**/*.dir -**/*.pdb - -/.webkit-cache -/.cache -/src/deps/libuv -/build-*/ -/kcov-out - -.vs - -**/.verdaccio-db.json -/test-report.md -/test-report.json +.DS_Store +.env +.envrc +.eslintcache +.idea +.next +.ninja_deps +.ninja_log +.npm +.npm.gz +.parcel-cache +.swcrc +.trace +.uuid +.vs +.vscode/clang* +.vscode/cpp* +*.a +*.bc +*.big +*.blob +*.bun +*.crash +*.database +*.db +*.dmg +*.dSYM +*.jsb +*.lib +*.log +*.o +*.out.js +*.out.refresh.js +*.pdb +*.sqlite +*.tmp +*.trace +*.wat +*.zip +**/.verdaccio-db.json +**/*.dir +**/*.pdb +**/*.sln* +**/*.vcxproj* +**/package-lock.json +/.cache +/.webkit-cache +/build-*/ +/bun-webkit +/kcov-out +/src/deps/libuv +/test-report.json +/test-report.md +/test.js +/test.ts +/testdir +build +build.ninja +bun-binary +bun-mimalloc +bun-nomimalloc +bun-singlehtreaded +bun-test-scratch +bun-zigld +cmake_install.cmake +CMakeCache.txt +CMakeFiles +cold-jsc-start +cold-jsc-start.d +compile_commands.json +cover +coverage +coverv +dist +esbuilddir +examples/lotta-modules/bun-nofscache +examples/lotta-modules/bun-old +examples/lotta-modules/bun-yday +failing-tests.txt +github +make-dev-stats.csv +misctools/fetch +misctools/machbench +misctools/sha +myscript.sh +node_modules +node_modules_* +out +out.* +outcss +outdir +outdir/ +packages/*/*.wasm +packages/bun-*/*.o +packages/bun-*/bun +packages/bun-*/bun-profile +packages/bun-*/debug-bun +packages/bun-cli/bin/* +packages/bun-cli/postinstall.js +packages/bun-wasm/*.cjs +packages/bun-wasm/*.d.cts +packages/bun-wasm/*.d.mts +packages/bun-wasm/*.d.ts +packages/bun-wasm/*.js +packages/bun-wasm/*.map +packages/bun-wasm/*.mjs +packages/debug-* +parceldist +pnpm-lock.yaml +profile.json +README.md.template +release/ +sign.*.json +sign.json +src/bun.js/bindings-obj +src/bun.js/bindings/GeneratedJS2Native.zig +src/bun.js/debug-bindings-obj +src/deps/c-ares/build +src/deps/libiconv +src/deps/openssl +src/deps/PLCrashReporter/ +src/deps/s2n-tls +src/deps/zig-clap/.gitattributes +src/deps/zig-clap/.github +src/deps/zig-clap/example +src/deps/zig-clap/README.md +src/fallback.version +src/js/out/DebugPath.h +src/js/out/functions* +src/js/out/modules* +src/js/out/tmp +src/node-fallbacks/node_modules +src/node-fallbacks/out/* +src/runtime.version +src/tests.zig +test.txt +test/js/bun/glob/fixtures +tsconfig.tsbuildinfo +txt.js +x64 +yarn.lock +zig-cache +zig-out diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 6bed0ed4fe626..fda3a058d8a01 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -55,6 +55,7 @@ "name": "BunWithJSCDebug", "forcedInclude": ["${workspaceFolder}/src/bun.js/bindings/root.h"], "includePath": [ + "${workspaceFolder}/build/codegen", "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/", "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/ICU/Headers/", "${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Debug/JavaScriptCore/PrivateHeaders/", diff --git a/CMakeLists.txt b/CMakeLists.txt index d5e17fbaac1ff..52dd5dd168795 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -751,6 +751,7 @@ if(NOT NO_CODEGEN) "${BUN_SRC}/js/thirdparty/*.ts" "${BUN_SRC}/js/internal/*.js" "${BUN_SRC}/js/internal/*.ts" + "${BUN_SRC}/js/internal-for-testing.ts" ) file(GLOB BUN_TS_FUNCTIONS ${CONFIGURE_DEPENDS} "${BUN_SRC}/js/builtins/*.ts") @@ -758,6 +759,8 @@ if(NOT NO_CODEGEN) add_custom_command( OUTPUT + "${BUN_WORKDIR}/codegen/WebCoreJSBuiltins.cpp" + "${BUN_WORKDIR}/codegen/WebCoreJSBuiltins.h" "${BUN_WORKDIR}/codegen/InternalModuleRegistryConstants.h" "${BUN_WORKDIR}/codegen/InternalModuleRegistry+createInternalModuleById.h" "${BUN_WORKDIR}/codegen/InternalModuleRegistry+enum.h" @@ -765,10 +768,12 @@ if(NOT NO_CODEGEN) "${BUN_WORKDIR}/codegen/NativeModuleImpl.h" "${BUN_WORKDIR}/codegen/ResolvedSourceTag.zig" "${BUN_WORKDIR}/codegen/SyntheticModuleType.h" + "${BUN_WORKDIR}/codegen/GeneratedJS2Native.h" + "${BUN_SRC}/bun.js/bindings/GeneratedJS2Native.zig" COMMAND ${BUN_EXECUTABLE} run "${BUN_SRC}/codegen/bundle-modules.ts" "--debug=${DEBUG}" "${BUN_WORKDIR}" DEPENDS ${BUN_TS_MODULES} ${CODEGEN_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Bundling JS modules" + COMMENT "Bundling JS" ) endif() @@ -776,15 +781,6 @@ WEBKIT_ADD_SOURCE_DEPENDENCIES( "${BUN_SRC}/bun.js/bindings/InternalModuleRegistry.cpp" "${BUN_WORKDIR}/codegen/InternalModuleRegistryConstants.h" ) - -add_custom_command( - OUTPUT "${BUN_WORKDIR}/codegen/WebCoreJSBuiltins.cpp" - "${BUN_WORKDIR}/codegen/WebCoreJSBuiltins.h" - COMMAND ${BUN_EXECUTABLE} run "${BUN_SRC}/codegen/bundle-functions.ts" "--debug=${DEBUG}" "${BUN_WORKDIR}" - DEPENDS ${BUN_TS_FUNCTIONS} ${CODEGEN_FILES} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Bundling JS builtin functions" -) list(APPEND BUN_RAW_SOURCES "${BUN_WORKDIR}/codegen/WebCoreJSBuiltins.cpp") # --- Peechy API --- @@ -866,6 +862,7 @@ if(NOT BUN_LINK_ONLY AND NOT BUN_CPP_ONLY) "${BUN_WORKDIR}/codegen/ResolvedSourceTag.zig" "${BUN_IDENTIFIER_CACHE_OUT}" "${BUN_SRC}/api/schema.zig" + "${BUN_SRC}/bun.js/bindings/GeneratedJS2Native.zig" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Building zig code" VERBATIM diff --git a/Dockerfile b/Dockerfile index 9f2efc2a0256b..ca3a651bf36b5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -116,7 +116,7 @@ RUN apt-get update -y \ && case "${arch##*-}" in \ amd64) variant="x64";; \ arm64) variant="aarch64";; \ - *) echo "error: unsupported architecture: $arch"; exit 1 ;; \ + *) echo "unsupported architecture: $arch"; exit 1 ;; \ esac \ && wget "${BUN_DOWNLOAD_URL_BASE}/bun-linux-${variant}.zip" \ && unzip bun-linux-${variant}.zip \ @@ -414,7 +414,7 @@ COPY --from=bun-codegen-for-zig ${BUN_DIR}/packages/bun-error/dist ${BUN_DIR}/pa WORKDIR $BUN_DIR RUN mkdir -p build \ - && bun run $BUN_DIR/src/codegen/bundle-modules-fast.ts $BUN_DIR/build \ + && bun run $BUN_DIR/src/codegen/bundle-modules.ts --debug=OFF $BUN_DIR/build \ && cd build \ && cmake .. \ -G Ninja \ diff --git a/scripts/cross-compile-codegen.sh b/scripts/cross-compile-codegen.sh index cf6810ee1b9c3..15fa1dc2b4c92 100755 --- a/scripts/cross-compile-codegen.sh +++ b/scripts/cross-compile-codegen.sh @@ -37,7 +37,6 @@ task() { fi } -task bun ./src/codegen/bundle-functions.ts --debug=OFF "$OUT" task bun ./src/codegen/bundle-modules.ts --debug=OFF "$OUT" rm -rf "$OUT/tmp_functions" diff --git a/src/bun.js/api/BunObject.zig b/src/bun.js/api/BunObject.zig index 61b1766b9dbf9..f507e53b19113 100644 --- a/src/bun.js/api/BunObject.zig +++ b/src/bun.js/api/BunObject.zig @@ -8,19 +8,13 @@ /// - Run "make dev" pub const BunObject = struct { // --- Callbacks --- - pub const DO_NOT_USE_OR_YOU_WILL_BE_FIRED_mimalloc_dump = dump_mimalloc; - pub const _Os = Bun._Os; - pub const _Path = Bun._Path; pub const allocUnsafe = Bun.allocUnsafe; pub const build = Bun.JSBundler.buildFn; pub const connect = JSC.wrapStaticMethod(JSC.API.Listener, "connect", false); pub const deflateSync = JSC.wrapStaticMethod(JSZlib, "deflateSync", true); pub const file = WebCore.Blob.constructBunFile; - pub const fs = Bun.fs; pub const gc = Bun.runGC; pub const generateHeapSnapshot = Bun.generateHeapSnapshot; - pub const getImportedStyles = Bun.getImportedStyles; - pub const getPublicPath = Bun.getPublicPathJS; pub const gunzipSync = JSC.wrapStaticMethod(JSZlib, "gunzipSync", true); pub const gzipSync = JSC.wrapStaticMethod(JSZlib, "gzipSync", true); pub const indexOfLine = Bun.indexOfLine; @@ -42,8 +36,6 @@ pub const BunObject = struct { pub const which = Bun.which; pub const write = JSC.WebCore.Blob.writeFile; pub const stringWidth = Bun.stringWidth; - pub const shellParse = Bun.shellParse; - pub const shellLex = Bun.shellLex; pub const braces = Bun.braces; pub const shellEscape = Bun.shellEscape; // --- Callbacks --- @@ -128,19 +120,14 @@ pub const BunObject = struct { // --- Getters -- // -- Callbacks -- - @export(BunObject.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_mimalloc_dump, .{ .name = callbackName("DO_NOT_USE_OR_YOU_WILL_BE_FIRED_mimalloc_dump") }); - @export(BunObject._Os, .{ .name = callbackName("_Os") }); - @export(BunObject._Path, .{ .name = callbackName("_Path") }); @export(BunObject.allocUnsafe, .{ .name = callbackName("allocUnsafe") }); @export(BunObject.braces, .{ .name = callbackName("braces") }); @export(BunObject.build, .{ .name = callbackName("build") }); @export(BunObject.connect, .{ .name = callbackName("connect") }); @export(BunObject.deflateSync, .{ .name = callbackName("deflateSync") }); @export(BunObject.file, .{ .name = callbackName("file") }); - @export(BunObject.fs, .{ .name = callbackName("fs") }); @export(BunObject.gc, .{ .name = callbackName("gc") }); @export(BunObject.generateHeapSnapshot, .{ .name = callbackName("generateHeapSnapshot") }); - @export(BunObject.getImportedStyles, .{ .name = callbackName("getImportedStyles") }); @export(BunObject.gunzipSync, .{ .name = callbackName("gunzipSync") }); @export(BunObject.gzipSync, .{ .name = callbackName("gzipSync") }); @export(BunObject.indexOfLine, .{ .name = callbackName("indexOfLine") }); @@ -162,8 +149,6 @@ pub const BunObject = struct { @export(BunObject.which, .{ .name = callbackName("which") }); @export(BunObject.write, .{ .name = callbackName("write") }); @export(BunObject.stringWidth, .{ .name = callbackName("stringWidth") }); - @export(BunObject.shellParse, .{ .name = callbackName("shellParse") }); - @export(BunObject.shellLex, .{ .name = callbackName("shellLex") }); @export(BunObject.shellEscape, .{ .name = callbackName("shellEscape") }); // -- Callbacks -- } @@ -303,163 +288,6 @@ pub fn getCSSImports() []ZigString { return css_imports_list_strings[0..tail]; } -pub fn shellLex( - globalThis: *JSC.JSGlobalObject, - callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { - const arguments_ = callframe.arguments(1); - var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); - const string_args = arguments.nextEat() orelse { - globalThis.throw("shell_parse: expected 2 arguments, got 0", .{}); - return JSC.JSValue.jsUndefined(); - }; - - var arena = std.heap.ArenaAllocator.init(bun.default_allocator); - defer arena.deinit(); - - const template_args = callframe.argumentsPtr()[1..callframe.argumentsCount()]; - var stack_alloc = std.heap.stackFallback(@sizeOf(bun.String) * 4, arena.allocator()); - var jsstrings = std.ArrayList(bun.String).initCapacity(stack_alloc.get(), 4) catch { - globalThis.throwOutOfMemory(); - return .undefined; - }; - defer { - for (jsstrings.items[0..]) |bunstr| { - bunstr.deref(); - } - jsstrings.deinit(); - } - var jsobjs = std.ArrayList(JSValue).init(arena.allocator()); - defer { - for (jsobjs.items) |jsval| { - jsval.unprotect(); - } - } - - var script = std.ArrayList(u8).init(arena.allocator()); - if (!(bun.shell.shellCmdFromJS(globalThis, string_args, template_args, &jsobjs, &jsstrings, &script) catch { - globalThis.throwOutOfMemory(); - return JSValue.undefined; - })) { - return .undefined; - } - - const lex_result = brk: { - if (bun.strings.isAllASCII(script.items[0..])) { - var lexer = Shell.LexerAscii.new(arena.allocator(), script.items[0..], jsstrings.items[0..]); - lexer.lex() catch |err| { - globalThis.throwError(err, "failed to lex shell"); - return JSValue.undefined; - }; - break :brk lexer.get_result(); - } - var lexer = Shell.LexerUnicode.new(arena.allocator(), script.items[0..], jsstrings.items[0..]); - lexer.lex() catch |err| { - globalThis.throwError(err, "failed to lex shell"); - return JSValue.undefined; - }; - break :brk lexer.get_result(); - }; - - if (lex_result.errors.len > 0) { - const str = lex_result.combineErrors(arena.allocator()); - globalThis.throwPretty("{s}", .{str}); - return .undefined; - } - - var test_tokens = std.ArrayList(Shell.Test.TestToken).initCapacity(arena.allocator(), lex_result.tokens.len) catch { - globalThis.throwOutOfMemory(); - return JSValue.undefined; - }; - for (lex_result.tokens) |tok| { - const test_tok = Shell.Test.TestToken.from_real(tok, lex_result.strpool); - test_tokens.append(test_tok) catch { - globalThis.throwOutOfMemory(); - return JSValue.undefined; - }; - } - - const str = std.json.stringifyAlloc(globalThis.bunVM().allocator, test_tokens.items[0..], .{}) catch { - globalThis.throwOutOfMemory(); - return JSValue.undefined; - }; - - defer globalThis.bunVM().allocator.free(str); - var bun_str = bun.String.fromBytes(str); - return bun_str.toJS(globalThis); -} - -pub fn shellParse( - globalThis: *JSC.JSGlobalObject, - callframe: *JSC.CallFrame, -) callconv(.C) JSC.JSValue { - const arguments_ = callframe.arguments(1); - var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); - const string_args = arguments.nextEat() orelse { - globalThis.throw("shell_parse: expected 2 arguments, got 0", .{}); - return JSC.JSValue.jsUndefined(); - }; - - var arena = bun.ArenaAllocator.init(bun.default_allocator); - defer arena.deinit(); - - const template_args = callframe.argumentsPtr()[1..callframe.argumentsCount()]; - var stack_alloc = std.heap.stackFallback(@sizeOf(bun.String) * 4, arena.allocator()); - var jsstrings = std.ArrayList(bun.String).initCapacity(stack_alloc.get(), 4) catch { - globalThis.throwOutOfMemory(); - return .undefined; - }; - defer { - for (jsstrings.items[0..]) |bunstr| { - bunstr.deref(); - } - jsstrings.deinit(); - } - var jsobjs = std.ArrayList(JSValue).init(arena.allocator()); - defer { - for (jsobjs.items) |jsval| { - jsval.unprotect(); - } - } - var script = std.ArrayList(u8).init(arena.allocator()); - if (!(bun.shell.shellCmdFromJS(globalThis, string_args, template_args, &jsobjs, &jsstrings, &script) catch { - globalThis.throwOutOfMemory(); - return JSValue.undefined; - })) { - return .undefined; - } - - var out_parser: ?bun.shell.Parser = null; - var out_lex_result: ?bun.shell.LexResult = null; - - const script_ast = bun.shell.Interpreter.parse(&arena, script.items[0..], jsobjs.items[0..], jsstrings.items[0..], &out_parser, &out_lex_result) catch |err| { - if (err == bun.shell.ParseError.Lex) { - std.debug.assert(out_lex_result != null); - const str = out_lex_result.?.combineErrors(arena.allocator()); - globalThis.throwPretty("{s}", .{str}); - return .undefined; - } - - if (out_parser) |*p| { - const errstr = p.combineErrors(); - globalThis.throwPretty("{s}", .{errstr}); - return .undefined; - } - - globalThis.throwError(err, "failed to lex/parse shell"); - return .undefined; - }; - - const str = std.json.stringifyAlloc(globalThis.bunVM().allocator, script_ast, .{}) catch { - globalThis.throwOutOfMemory(); - return JSValue.undefined; - }; - - defer globalThis.bunVM().allocator.free(str); - var bun_str = bun.String.fromBytes(str); - return bun_str.toJS(globalThis); -} - const ShellTask = struct { arena: std.heap.Arena, script: std.ArrayList(u8), @@ -1415,41 +1243,9 @@ pub fn getPublicPathJS(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFr return ZigString.init(stream.buffer[0..stream.pos]).toValueGC(globalObject); } -fn fs(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { - var module = JSC.Node.NodeJSFS.new(.{ - .node_fs = .{ - .vm = globalObject.bunVM(), - }, - }); - - return module.toJS(globalObject); -} - -fn _Os(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { - return Node.Os.create(globalObject); -} - -fn _Path(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { - const arguments = callframe.arguments(1); - const args = arguments.slice(); - const is_windows = args.len == 1 and args[0].toBoolean(); - return Node.Path.create(globalObject, is_windows); -} - -/// @deprecated -fn getImportedStyles(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { - defer flushCSSImports(); - const styles = getCSSImports(); - if (styles.len == 0) { - return JSC.JSValue.createEmptyArray(globalObject, 0); - } - - return JSValue.createStringArray(globalObject, styles.ptr, styles.len, true); -} - extern fn dump_zone_malloc_stats() void; -pub fn dump_mimalloc(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { +export fn dump_mimalloc(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { globalObject.bunVM().arena.dumpStats(); if (comptime bun.is_heap_breakdown_enabled) { dump_zone_malloc_stats(); @@ -3518,6 +3314,7 @@ const UnsafeObject = struct { .gcAggressionLevel = &gcAggressionLevel, .segfault = &__debug__doSegfault, .arrayBufferToString = &arrayBufferToString, + .mimallocDump = &dump_mimalloc, }; inline for (comptime std.meta.fieldNames(@TypeOf(fields))) |name| { object.put( @@ -5468,9 +5265,6 @@ const InternalTestingAPIs = struct { }; comptime { - if (!JSC.is_bindgen) { - _ = Crypto.JSPasswordObject.JSPasswordObject__create; - BunObject.exportAll(); - @export(InternalTestingAPIs.BunInternalFunction__syntaxHighlighter, .{ .name = "BunInternalFunction__syntaxHighlighter" }); - } + _ = Crypto.JSPasswordObject.JSPasswordObject__create; + BunObject.exportAll(); } diff --git a/src/bun.js/api/bun/h2_frame_parser.zig b/src/bun.js/api/bun/h2_frame_parser.zig index 1aa360183f68e..ab87adb8d7f5d 100644 --- a/src/bun.js/api/bun/h2_frame_parser.zig +++ b/src/bun.js/api/bun/h2_frame_parser.zig @@ -261,7 +261,7 @@ const SingleValueHeaders = bun.ComptimeStringMap(void, .{ .{"x-content-type-options"}, }); -pub export fn BUN__HTTP2__getUnpackedSettings(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { +pub fn jsGetUnpackedSettings(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { JSC.markBinding(@src()); var settings: FullSettingsPayload = .{}; @@ -296,7 +296,7 @@ pub export fn BUN__HTTP2__getUnpackedSettings(globalObject: *JSC.JSGlobalObject, } } -pub export fn BUN__HTTP2_getPackedSettings(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSValue { +pub fn jsGetPackedSettings(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { var settings: FullSettingsPayload = .{}; const args_list = callframe.arguments(1); @@ -408,11 +408,6 @@ pub export fn BUN__HTTP2_getPackedSettings(globalObject: *JSC.JSGlobalObject, ca return binary_type.toJS(bytes, globalObject); } -comptime { - _ = BUN__HTTP2__getUnpackedSettings; - _ = BUN__HTTP2_getPackedSettings; -} - const Handlers = struct { onError: JSC.JSValue = .zero, onWrite: JSC.JSValue = .zero, @@ -2541,3 +2536,11 @@ pub const H2FrameParser = struct { this.deinit(); } }; + +pub fn createNodeHttp2Binding(global: *JSC.JSGlobalObject) JSC.JSValue { + return JSC.JSArray.create(global, &.{ + H2FrameParser.getConstructor(global), + JSC.JSFunction.create(global, "getPackedSettings", jsGetPackedSettings, 0, .{}), + JSC.JSFunction.create(global, "getUnpackedSettings", jsGetUnpackedSettings, 0, .{}), + }); +} diff --git a/src/bun.js/api/glob.zig b/src/bun.js/api/glob.zig index 261a7dc50c480..32724f0204f4e 100644 --- a/src/bun.js/api/glob.zig +++ b/src/bun.js/api/glob.zig @@ -231,9 +231,8 @@ pub const WalkTask = struct { }; fn globWalkResultToJS(globWalk: *GlobWalker, globalThis: *JSGlobalObject) JSValue { - // if (globWalk.matchedPaths.items.len >= 0) { if (globWalk.matchedPaths.items.len == 0) { - return JSC.JSArray.from(globalThis, &[_]JSC.JSValue{}); + return JSC.JSValue.createEmptyArray(globalThis, 0); } return BunString.toJSArray(globalThis, globWalk.matchedPaths.items[0..]); diff --git a/src/bun.js/bindings/BunObject+exports.h b/src/bun.js/bindings/BunObject+exports.h index d5909635216c3..37087710a4a2f 100644 --- a/src/bun.js/bindings/BunObject+exports.h +++ b/src/bun.js/bindings/BunObject+exports.h @@ -33,9 +33,6 @@ // --- Callbacks --- #define FOR_EACH_CALLBACK(macro) \ - macro(DO_NOT_USE_OR_YOU_WILL_BE_FIRED_mimalloc_dump) \ - macro(_Os) \ - macro(_Path) \ macro(allocUnsafe) \ macro(braces) \ macro(build) \ @@ -45,7 +42,6 @@ macro(fs) \ macro(gc) \ macro(generateHeapSnapshot) \ - macro(getImportedStyles) \ macro(gunzipSync) \ macro(gzipSync) \ macro(indexOfLine) \ @@ -67,8 +63,6 @@ macro(which) \ macro(write) \ macro(stringWidth) \ - macro(shellParse) \ - macro(shellLex) \ macro(shellEscape) \ #define DECLARE_ZIG_BUN_OBJECT_CALLBACK(name) extern "C" JSC::EncodedJSValue BunObject_callback_##name(JSC::JSGlobalObject*, JSC::CallFrame*); diff --git a/src/bun.js/bindings/BunObject.cpp b/src/bun.js/bindings/BunObject.cpp index 7f87a1549db3f..6f5d78352bd9d 100644 --- a/src/bun.js/bindings/BunObject.cpp +++ b/src/bun.js/bindings/BunObject.cpp @@ -247,20 +247,6 @@ static JSValue constructBunShell(VM& vm, JSObject* bunObject) bunShell->putDirectNativeFunction(vm, globalObject, Identifier::fromString(vm, "braces"_s), 1, BunObject_callback_braces, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | 0); bunShell->putDirectNativeFunction(vm, globalObject, Identifier::fromString(vm, "escape"_s), 1, BunObject_callback_shellEscape, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | 0); - // auto mainShellFunc = JSFunction::create(vm, globalObject, 2, String("$"_s), BunObject_callback_$, ImplementationVisibility::Public); - // auto mainShellFunc = JSFunction::create(vm, globalObject, 2, String("$"_s), BunObject_callback_$, ImplementationVisibility::Public); - // auto mainShellFunc = shellShellCodeGenerator; - if (has_bun_garbage_collector_flag_enabled) { - auto parseIdent - = Identifier::fromString(vm, String("parse"_s)); - auto parseFunc = JSFunction::create(vm, globalObject, 2, String("shellParse"_s), BunObject_callback_shellParse, ImplementationVisibility::Private); - bunShell->putDirect(vm, parseIdent, parseFunc); - - auto lexIdent = Identifier::fromString(vm, String("lex"_s)); - auto lexFunc = JSFunction::create(vm, globalObject, 2, String("lex"_s), BunObject_callback_shellLex, ImplementationVisibility::Private); - bunShell->putDirect(vm, lexIdent, lexFunc); - } - return bunShell; } @@ -535,25 +521,11 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj return JSC::JSValue::encode(JSC::jsString(vm, fileSystemPath)); } -JSC_DEFINE_HOST_FUNCTION(functionHashCode, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::JSValue stringToHash = callFrame->argument(0); - JSC::JSString* str = stringToHash.toStringOrNull(globalObject); - if (!str) { - return JSC::JSValue::encode(jsNumber(0)); - } - - auto view = str->value(globalObject); - return JSC::JSValue::encode(jsNumber(view.hash())); -} - /* Source for BunObject.lut.h @begin bunObjectTable $ constructBunShell ReadOnly|DontDelete|PropertyCallback ArrayBufferSink BunObject_getter_wrap_ArrayBufferSink DontDelete|PropertyCallback CryptoHasher BunObject_getter_wrap_CryptoHasher DontDelete|PropertyCallback - DO_NOT_USE_OR_YOU_WILL_BE_FIRED_mimalloc_dump BunObject_callback_DO_NOT_USE_OR_YOU_WILL_BE_FIRED_mimalloc_dump DontEnum|DontDelete|Function 1 FFI BunObject_getter_wrap_FFI DontDelete|PropertyCallback FileSystemRouter BunObject_getter_wrap_FileSystemRouter DontDelete|PropertyCallback Glob BunObject_getter_wrap_Glob DontDelete|PropertyCallback @@ -567,11 +539,8 @@ JSC_DEFINE_HOST_FUNCTION(functionHashCode, SHA512_256 BunObject_getter_wrap_SHA512_256 DontDelete|PropertyCallback TOML BunObject_getter_wrap_TOML DontDelete|PropertyCallback Transpiler BunObject_getter_wrap_Transpiler DontDelete|PropertyCallback - _Os BunObject_callback__Os DontEnum|DontDelete|Function 1 - _Path BunObject_callback__Path DontEnum|DontDelete|Function 1 allocUnsafe BunObject_callback_allocUnsafe DontDelete|Function 1 argv BunObject_getter_wrap_argv DontDelete|PropertyCallback - assetPrefix BunObject_getter_wrap_assetPrefix DontEnum|DontDelete|PropertyCallback build BunObject_callback_build DontDelete|Function 1 concatArrayBuffers functionConcatTypedArrays DontDelete|Function 1 connect BunObject_callback_connect DontDelete|Function 1 @@ -586,10 +555,8 @@ JSC_DEFINE_HOST_FUNCTION(functionHashCode, fetch Bun__fetch ReadOnly|DontDelete|Function 1 file BunObject_callback_file DontDelete|Function 1 fileURLToPath functionFileURLToPath DontDelete|Function 1 - fs BunObject_callback_fs DontEnum|DontDelete|Function 1 gc BunObject_callback_gc DontDelete|Function 1 generateHeapSnapshot BunObject_callback_generateHeapSnapshot DontDelete|Function 1 - getImportedStyles BunObject_callback_getImportedStyles DontEnum|DontDelete|Function 1 gunzipSync BunObject_callback_gunzipSync DontDelete|Function 1 gzipSync BunObject_callback_gzipSync DontDelete|Function 1 hash BunObject_getter_wrap_hash DontDelete|PropertyCallback @@ -629,7 +596,6 @@ JSC_DEFINE_HOST_FUNCTION(functionHashCode, stderr BunObject_getter_wrap_stderr DontDelete|PropertyCallback stdin BunObject_getter_wrap_stdin DontDelete|PropertyCallback stdout BunObject_getter_wrap_stdout DontDelete|PropertyCallback - stringHashCode functionHashCode DontDelete|Function 1 stringWidth BunObject_callback_stringWidth DontDelete|Function 2 unsafe BunObject_getter_wrap_unsafe DontDelete|PropertyCallback version constructBunVersion ReadOnly|DontDelete|PropertyCallback diff --git a/src/bun.js/bindings/JS2Native.cpp b/src/bun.js/bindings/JS2Native.cpp new file mode 100644 index 0000000000000..f0a89109b324f --- /dev/null +++ b/src/bun.js/bindings/JS2Native.cpp @@ -0,0 +1,37 @@ +#include "JS2Native.h" + +#include +#include +#include + +#include "ZigGlobalObject.h" +#include "GeneratedJS2Native.h" +#include "wtf/Assertions.h" + +extern "C" JSC::EncodedJSValue ByteBlob__JSReadableStreamSource__load(JSC::JSGlobalObject* global); +extern "C" JSC::EncodedJSValue FileReader__JSReadableStreamSource__load(JSC::JSGlobalObject* global); +extern "C" JSC::EncodedJSValue ByteStream__JSReadableStreamSource__load(JSC::JSGlobalObject* global); + +namespace Bun { +namespace JS2Native { + +// This is the implementation of the generated $lazy +JSC_DEFINE_HOST_FUNCTION(jsDollarLazy, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) +{ + JSC::JSValue target = callFrame->uncheckedArgument(0); + +#if BUN_DEBUG + ASSERT_WITH_MESSAGE(target.isInt32(), "In call to $lazy: expected Int32, got %s", target.toWTFString(lexicalGlobalObject).utf8().data()); +#endif + + int id = target.asInt32(); + RELEASE_ASSERT( + id <= JS2NATIVE_COUNT && id >= 0, + "In call to $lazy, got invalid id '%d'. This is a bug in Bun's JS2Native code generator.", + id); + Zig::GlobalObject* ptr = jsCast(lexicalGlobalObject); + return JSValue::encode(JS2NativeGenerated::js2nativePointers[id](ptr)); +} + +} // namespace JS2Native +} // namespace Bun \ No newline at end of file diff --git a/src/bun.js/bindings/JS2Native.h b/src/bun.js/bindings/JS2Native.h new file mode 100644 index 0000000000000..1dd14bfa2c4d8 --- /dev/null +++ b/src/bun.js/bindings/JS2Native.h @@ -0,0 +1,13 @@ +#include "config.h" +#include + +namespace Bun { +namespace JS2Native { + +JSC_DECLARE_HOST_FUNCTION(jsDollarLazy); + +JSC_DECLARE_HOST_FUNCTION(jsDollarCpp); +JSC_DECLARE_HOST_FUNCTION(jsDollarZig); + +} // namespace JS2Native +} // namespace Bun \ No newline at end of file diff --git a/src/bun.js/bindings/JSReadableHelper.cpp b/src/bun.js/bindings/JSReadableHelper.cpp index 5df3ba8f14ac0..7d1576a5f54fa 100644 --- a/src/bun.js/bindings/JSReadableHelper.cpp +++ b/src/bun.js/bindings/JSReadableHelper.cpp @@ -198,13 +198,36 @@ EncodedJSValue emitReadable_(JSGlobalObject* lexicalGlobalObject, JSObject* stre return JSValue::encode(jsUndefined()); } -JSC_DEFINE_HOST_FUNCTION(jsReadable_emitReadable_, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +JSC_DEFINE_HOST_FUNCTION(jsReadable_emitReadable, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) { - JSReadableHelper_EXTRACT_STREAM_STATE - - RELEASE_AND_RETURN(throwScope, emitReadable_(lexicalGlobalObject, stream, state)); + JSReadableHelper_EXTRACT_STREAM_STATE; + RELEASE_AND_RETURN(throwScope, emitReadable_(lexicalGlobalObject, stream, state)); } #undef JSReadableHelper_EXTRACT_STREAM_STATE +JSValue createNodeStreamBinding(Zig::GlobalObject* globalObject) +{ + VM& vm = globalObject->vm(); + auto* obj = constructEmptyObject(globalObject); + obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "BufferList"_s)), reinterpret_cast(globalObject)->JSBufferList(), 0); + obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "ReadableState"_s)), reinterpret_cast(globalObject)->JSReadableState(), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "maybeReadMore"_s)), + JSC::JSFunction::create(vm, globalObject, 0, "maybeReadMore"_s, jsReadable_maybeReadMore, ImplementationVisibility::Public), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "resume"_s)), + JSC::JSFunction::create(vm, globalObject, 0, "resume"_s, jsReadable_resume, ImplementationVisibility::Public), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "emitReadable"_s)), + JSC::JSFunction::create(vm, globalObject, 0, "emitReadable"_s, jsReadable_emitReadable, ImplementationVisibility::Public), 0); + // obj->putDirect( + // vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "onEofChunk"_s)), + // JSC::JSFunction::create(vm, globalObject, 0, "onEofChunk"_s, jsReadable_onEofChunk, ImplementationVisibility::Public), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "EE"_s)), + WebCore::JSEventEmitter::getConstructor(vm, globalObject), 0); + return obj; +} + } // namespace WebCore diff --git a/src/bun.js/bindings/JSReadableHelper.h b/src/bun.js/bindings/JSReadableHelper.h index 9a60fc301c6e3..413bf15fab356 100644 --- a/src/bun.js/bindings/JSReadableHelper.h +++ b/src/bun.js/bindings/JSReadableHelper.h @@ -1,11 +1,14 @@ #pragma once #include "root.h" +#include "ZigGlobalObject.h" namespace WebCore { JSC_DECLARE_HOST_FUNCTION(jsReadable_maybeReadMore); JSC_DECLARE_HOST_FUNCTION(jsReadable_resume); -JSC_DECLARE_HOST_FUNCTION(jsReadable_emitReadable_); +JSC_DECLARE_HOST_FUNCTION(jsReadable_emitReadable); + +JSC::JSValue createNodeStreamBinding(Zig::GlobalObject* globalObject); } // namespace WebCore diff --git a/src/bun.js/bindings/JSReadableState.cpp b/src/bun.js/bindings/JSReadableState.cpp index f3fba3cf9691c..9c52c0ccef6bd 100644 --- a/src/bun.js/bindings/JSReadableState.cpp +++ b/src/bun.js/bindings/JSReadableState.cpp @@ -422,4 +422,4 @@ void JSReadableStateConstructor::initializeProperties(VM& vm, JSC::JSGlobalObjec const ClassInfo JSReadableStateConstructor::s_info = { "ReadableState"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSReadableStateConstructor) }; -} // namespace Zig +} // namespace WebCore diff --git a/src/bun.js/bindings/KeyObject.cpp b/src/bun.js/bindings/KeyObject.cpp index 3912df208d028..9bbd9969f02bb 100644 --- a/src/bun.js/bindings/KeyObject.cpp +++ b/src/bun.js/bindings/KeyObject.cpp @@ -2926,4 +2926,37 @@ JSC::EncodedJSValue KeyObject__SymmetricKeySize(JSC::JSGlobalObject* globalObjec return JSC::JSValue::encode(JSC::jsUndefined()); } -} \ No newline at end of file +JSValue createNodeCryptoBinding(Zig::GlobalObject* globalObject) { + VM&vm = globalObject->vm(); + auto* obj = constructEmptyObject(globalObject); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "symmetricKeySize"_s)), JSC::JSFunction::create(vm, globalObject, 1, "symmetricKeySize"_s, KeyObject__SymmetricKeySize, ImplementationVisibility::Public, NoIntrinsic), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "asymmetricKeyType"_s)), JSC::JSFunction::create(vm, globalObject, 1, "asymmetricKeyType"_s, KeyObject__AsymmetricKeyType, ImplementationVisibility::Public, NoIntrinsic), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "asymmetricKeyDetails"_s)), JSC::JSFunction::create(vm, globalObject, 1, "asymmetricKeyDetails"_s, KeyObject_AsymmetricKeyDetails, ImplementationVisibility::Public, NoIntrinsic), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "equals"_s)), JSC::JSFunction::create(vm, globalObject, 2, "equals"_s, KeyObject__Equals, ImplementationVisibility::Public, NoIntrinsic), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "exports"_s)), JSC::JSFunction::create(vm, globalObject, 2, "exports"_s, KeyObject__Exports, ImplementationVisibility::Public, NoIntrinsic), 0); + + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "createSecretKey"_s)), JSC::JSFunction::create(vm, globalObject, 1, "createSecretKey"_s, KeyObject__createSecretKey, ImplementationVisibility::Public, NoIntrinsic), 0); + + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "createPublicKey"_s)), JSC::JSFunction::create(vm, globalObject, 1, "createPublicKey"_s, KeyObject__createPublicKey, ImplementationVisibility::Public, NoIntrinsic), 0); + + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "createPrivateKey"_s)), JSC::JSFunction::create(vm, globalObject, 1, "createPrivateKey"_s, KeyObject__createPrivateKey, ImplementationVisibility::Public, NoIntrinsic), 0); + + obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "generateKeySync"_s)), JSC::JSFunction::create(vm, globalObject, 2, "generateKeySync"_s, KeyObject__generateKeySync, ImplementationVisibility::Public, NoIntrinsic), 0); + + obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "generateKeyPairSync"_s)), JSC::JSFunction::create(vm, globalObject, 2, "generateKeyPairSync"_s, KeyObject__generateKeyPairSync, ImplementationVisibility::Public, NoIntrinsic), 0); + + obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "sign"_s)), JSC::JSFunction::create(vm, globalObject, 3, "sign"_s, KeyObject__Sign, ImplementationVisibility::Public, NoIntrinsic), 0); + obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "verify"_s)), JSC::JSFunction::create(vm, globalObject, 4, "verify"_s, KeyObject__Verify, ImplementationVisibility::Public, NoIntrinsic), 0); + + return obj; +} + +} // namespace WebCore diff --git a/src/bun.js/bindings/KeyObject.h b/src/bun.js/bindings/KeyObject.h index bb28847ae9143..d70ba80d5c114 100644 --- a/src/bun.js/bindings/KeyObject.h +++ b/src/bun.js/bindings/KeyObject.h @@ -3,6 +3,7 @@ #include "root.h" #include "helpers.h" + namespace WebCore { JSC::EncodedJSValue KeyObject__AsymmetricKeyType(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); @@ -18,4 +19,6 @@ JSC::EncodedJSValue KeyObject__generateKeyPairSync(JSC::JSGlobalObject* lexicalG JSC::EncodedJSValue KeyObject__Sign(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); JSC::EncodedJSValue KeyObject__Verify(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame); -} \ No newline at end of file +JSC::JSValue createNodeCryptoBinding(Zig::GlobalObject* globalObject); + +} // namespace WebCore diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index abf9b8661de68..4bda9e77fa3c0 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -35,6 +35,7 @@ #include "NativeModuleImpl.h" #include "../modules/ObjectModule.h" +#include "wtf/Assertions.h" namespace Bun { using namespace JSC; @@ -75,7 +76,7 @@ static JSC::SyntheticSourceProvider::SyntheticSourceGenerator generateInternalMo JSValue requireResult = globalObject->internalModuleRegistry()->requireId(globalObject, vm, moduleId); RETURN_IF_EXCEPTION(throwScope, void()); auto* object = requireResult.getObject(); - ASSERT(object); + ASSERT_WITH_MESSAGE(object, "Expected object from requireId %s", moduleKey.string().string().utf8().data()); JSC::EnsureStillAliveScope stillAlive(object); diff --git a/src/bun.js/bindings/NodeAsyncHooks.cpp b/src/bun.js/bindings/NodeAsyncHooks.cpp new file mode 100644 index 0000000000000..8828d2d3540cc --- /dev/null +++ b/src/bun.js/bindings/NodeAsyncHooks.cpp @@ -0,0 +1,51 @@ +#include "config.h" + +#include "JavaScriptCore/JSObject.h" +#include "JavaScriptCore/ObjectConstructor.h" +#include "JavaScriptCore/ArrayConstructor.h" + +#include "ZigGlobalObject.h" + +namespace Bun { + +using namespace JSC; + +// `cleanupLayer` is called by js if we set async context in a way we may not +// clear it, specifically within AsyncLocalStorage.prototype.enterWith. this +// function will not clear the async context until the next tick's microtask, +// where it must inherit the context that scheduled that callback. +JSC_DEFINE_HOST_FUNCTION(jsCleanupLater, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + ASSERT(callFrame->argumentCount() == 0); + auto* global = jsCast(globalObject); + global->asyncHooksNeedsCleanup = true; + global->resetOnEachMicrotaskTick(); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +// This is called when AsyncLocalStorage is constructed. +JSC_DEFINE_HOST_FUNCTION(jsSetAsyncHooksEnabled, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + ASSERT(callFrame->argumentCount() == 1); + globalObject->setAsyncContextTrackingEnabled(callFrame->uncheckedArgument(0).toBoolean(globalObject)); + return JSC::JSValue::encode(JSC::jsUndefined()); +} + +JSC::JSValue createAsyncHooksBinding(Zig::GlobalObject* globalObject) +{ + VM& vm = globalObject->vm(); + auto binding = constructEmptyArray(globalObject, nullptr, 2); + binding->putByIndexInline( + globalObject, + (unsigned)0, + JSC::JSFunction::create(vm, globalObject, 0, "setAsyncHooksEnabled"_s, jsSetAsyncHooksEnabled, ImplementationVisibility::Public), + false); + binding->putByIndexInline( + globalObject, + (unsigned)1, + JSC::JSFunction::create(vm, globalObject, 0, "cleanupLater"_s, jsCleanupLater, ImplementationVisibility::Public), + false); + return binding; +} + +} \ No newline at end of file diff --git a/src/bun.js/bindings/NodeAsyncHooks.h b/src/bun.js/bindings/NodeAsyncHooks.h new file mode 100644 index 0000000000000..84e7c95b1303f --- /dev/null +++ b/src/bun.js/bindings/NodeAsyncHooks.h @@ -0,0 +1,8 @@ +#include "config.h" +#include "ZigGlobalObject.h" + +namespace Bun { + +JSC::JSValue createAsyncHooksBinding(Zig::GlobalObject*); + +} \ No newline at end of file diff --git a/src/bun.js/bindings/NodeHTTP.cpp b/src/bun.js/bindings/NodeHTTP.cpp index 3f6717012a019..4159e7ed6eaf8 100644 --- a/src/bun.js/bindings/NodeHTTP.cpp +++ b/src/bun.js/bindings/NodeHTTP.cpp @@ -8,6 +8,7 @@ #include "JavaScriptCore/InternalFieldTuple.h" #include "JavaScriptCore/ObjectConstructor.h" #include "JavaScriptCore/ObjectConstructor.h" +#include "JavaScriptCore/JSFunction.h" #include "wtf/URL.h" #include "JSFetchHeaders.h" #include "JSDOMExceptionHandling.h" @@ -401,4 +402,20 @@ JSC_DEFINE_HOST_FUNCTION(jsHTTPSetHeader, (JSGlobalObject * globalObject, CallFr return JSValue::encode(jsUndefined()); } -} \ No newline at end of file + +JSValue createNodeHTTPInternalBinding(Zig::GlobalObject* globalObject) { + auto* obj = constructEmptyObject(globalObject); + VM& vm = globalObject->vm(); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "setHeader"_s)), + JSC::JSFunction::create(vm, globalObject, 3, "setHeader"_s, jsHTTPSetHeader, ImplementationVisibility::Public), NoIntrinsic); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getHeader"_s)), + JSC::JSFunction::create(vm, globalObject, 2, "getHeader"_s, jsHTTPGetHeader, ImplementationVisibility::Public), NoIntrinsic); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "assignHeaders"_s)), + JSC::JSFunction::create(vm, globalObject, 2, "assignHeaders"_s, jsHTTPAssignHeaders, ImplementationVisibility::Public), NoIntrinsic); + return obj; +} + +} // namespace Bun diff --git a/src/bun.js/bindings/NodeHTTP.h b/src/bun.js/bindings/NodeHTTP.h index 84f9b48e1335c..e79a2b21d1670 100644 --- a/src/bun.js/bindings/NodeHTTP.h +++ b/src/bun.js/bindings/NodeHTTP.h @@ -1,6 +1,11 @@ +#include "config.h" namespace Bun { + JSC_DECLARE_HOST_FUNCTION(jsHTTPAssignHeaders); JSC_DECLARE_HOST_FUNCTION(jsHTTPGetHeader); JSC_DECLARE_HOST_FUNCTION(jsHTTPSetHeader); + +JSC::JSValue createNodeHTTPInternalBinding(Zig::GlobalObject*); + } \ No newline at end of file diff --git a/src/bun.js/bindings/NodeTLS.cpp b/src/bun.js/bindings/NodeTLS.cpp new file mode 100644 index 0000000000000..35568cee5b494 --- /dev/null +++ b/src/bun.js/bindings/NodeTLS.cpp @@ -0,0 +1,39 @@ +#include "config.h" + +#include "JavaScriptCore/JSObject.h" +#include "JavaScriptCore/ObjectConstructor.h" +#include "JavaScriptCore/ArrayConstructor.h" +#include "libusockets.h" + +#include "ZigGlobalObject.h" + +namespace Bun { + +using namespace JSC; + +extern "C" JSC::EncodedJSValue Bun__canonicalizeIP(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); + +JSC::JSValue createNodeTLSBinding(Zig::GlobalObject* globalObject) { + VM& vm = globalObject->vm(); + auto* obj = constructEmptyObject(globalObject); + + struct us_cert_string_t* out; + auto size = us_raw_root_certs(&out); + if (size < 0) { + return jsUndefined(); + } + auto rootCertificates = JSC::JSArray::create(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), size); + for (auto i = 0; i < size; i++) { + auto raw = out[i]; + auto str = WTF::String::fromUTF8(raw.str, raw.len); + rootCertificates->putDirectIndex(globalObject, i, JSC::jsString(vm, str)); + } + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "rootCertificates"_s)), rootCertificates, 0); + + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "canonicalizeIP"_s)), JSC::JSFunction::create(vm, globalObject, 1, "canonicalizeIP"_s, Bun__canonicalizeIP, ImplementationVisibility::Public, NoIntrinsic), 0); + return obj; +} + +} // namespace Bun \ No newline at end of file diff --git a/src/bun.js/bindings/NodeTLS.h b/src/bun.js/bindings/NodeTLS.h new file mode 100644 index 0000000000000..1f1fdb7b3f8c9 --- /dev/null +++ b/src/bun.js/bindings/NodeTLS.h @@ -0,0 +1,8 @@ +#include "config.h" +#include "ZigGlobalObject.h" + +namespace Bun { + +JSC::JSValue createNodeTLSBinding(Zig::GlobalObject*); + +} \ No newline at end of file diff --git a/src/bun.js/bindings/NodeURL.cpp b/src/bun.js/bindings/NodeURL.cpp new file mode 100644 index 0000000000000..4f162d58fba88 --- /dev/null +++ b/src/bun.js/bindings/NodeURL.cpp @@ -0,0 +1,160 @@ +#include "NodeURL.h" +#include "wtf/URLParser.h" +#include + +namespace Bun { + +JSC_DEFINE_HOST_FUNCTION(jsDomainToASCII, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "domainToASCII needs 1 argument"_s); + return JSC::JSValue::encode(JSC::JSValue {}); + } + + auto arg0 = callFrame->argument(0); + if (arg0.isUndefined()) + return JSC::JSValue::encode(jsUndefined()); + if (arg0.isNull()) + return JSC::JSValue::encode(jsNull()); + if (!arg0.isString()) { + throwTypeError(globalObject, scope, "the \"domain\" argument must be a string"_s); + return JSC::JSValue::encode(jsUndefined()); + } + + auto domain = arg0.toWTFString(globalObject); + if (domain.isNull()) + return JSC::JSValue::encode(jsUndefined()); + + // https://url.spec.whatwg.org/#forbidden-host-code-point + if ( + domain.contains(0x0000) || // U+0000 NULL + domain.contains(0x0009) || // U+0009 TAB + domain.contains(0x000A) || // U+000A LF + domain.contains(0x000D) || // U+000D CR + domain.contains(0x0020) || // U+0020 SPACE + domain.contains(0x0023) || // U+0023 (#) + domain.contains(0x002F) || // U+002F (/) + domain.contains(0x003A) || // U+003A (:) + domain.contains(0x003C) || // U+003C (<) + domain.contains(0x003E) || // U+003E (>) + domain.contains(0x003F) || // U+003F (?) + domain.contains(0x0040) || // U+0040 (@) + domain.contains(0x005B) || // U+005B ([) + domain.contains(0x005C) || // U+005C (\) + domain.contains(0x005D) || // U+005D (]) + domain.contains(0x005E) || // U+005E (^) + domain.contains(0x007C) // // U+007C (|). + ) + return JSC::JSValue::encode(jsEmptyString(vm)); + + if (domain.containsOnlyASCII()) + return JSC::JSValue::encode(arg0); + if (domain.is8Bit()) + domain.convertTo16Bit(); + + constexpr static int allowedNameToASCIIErrors = UIDNA_ERROR_EMPTY_LABEL | UIDNA_ERROR_LABEL_TOO_LONG | UIDNA_ERROR_DOMAIN_NAME_TOO_LONG | UIDNA_ERROR_LEADING_HYPHEN | UIDNA_ERROR_TRAILING_HYPHEN | UIDNA_ERROR_HYPHEN_3_4; + constexpr static size_t hostnameBufferLength = 2048; + + auto encoder = &WTF::URLParser::internationalDomainNameTranscoder(); + UChar hostnameBuffer[hostnameBufferLength]; + UErrorCode error = U_ZERO_ERROR; + UIDNAInfo processingDetails = UIDNA_INFO_INITIALIZER; + int32_t numCharactersConverted = uidna_nameToASCII(encoder, StringView(domain).characters16(), domain.length(), hostnameBuffer, hostnameBufferLength, &processingDetails, &error); + + if (U_SUCCESS(error) && !(processingDetails.errors & ~allowedNameToASCIIErrors) && numCharactersConverted) { + return JSC::JSValue::encode(JSC::jsString(vm, WTF::String(hostnameBuffer, numCharactersConverted))); + } + throwTypeError(globalObject, scope, "domainToASCII failed"_s); + return JSC::JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsDomainToUnicode, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (callFrame->argumentCount() < 1) { + throwTypeError(globalObject, scope, "domainToUnicode needs 1 argument"_s); + return JSC::JSValue::encode(JSC::JSValue {}); + } + + auto arg0 = callFrame->argument(0); + if (arg0.isUndefined()) + return JSC::JSValue::encode(jsUndefined()); + if (arg0.isNull()) + return JSC::JSValue::encode(jsNull()); + if (!arg0.isString()) { + throwTypeError(globalObject, scope, "the \"domain\" argument must be a string"_s); + return JSC::JSValue::encode(jsUndefined()); + } + + auto domain = arg0.toWTFString(globalObject); + if (domain.isNull()) + return JSC::JSValue::encode(jsUndefined()); + + // https://url.spec.whatwg.org/#forbidden-host-code-point + if ( + domain.contains(0x0000) || // U+0000 NULL + domain.contains(0x0009) || // U+0009 TAB + domain.contains(0x000A) || // U+000A LF + domain.contains(0x000D) || // U+000D CR + domain.contains(0x0020) || // U+0020 SPACE + domain.contains(0x0023) || // U+0023 (#) + domain.contains(0x002F) || // U+002F (/) + domain.contains(0x003A) || // U+003A (:) + domain.contains(0x003C) || // U+003C (<) + domain.contains(0x003E) || // U+003E (>) + domain.contains(0x003F) || // U+003F (?) + domain.contains(0x0040) || // U+0040 (@) + domain.contains(0x005B) || // U+005B ([) + domain.contains(0x005C) || // U+005C (\) + domain.contains(0x005D) || // U+005D (]) + domain.contains(0x005E) || // U+005E (^) + domain.contains(0x007C) // // U+007C (|). + ) + return JSC::JSValue::encode(jsEmptyString(vm)); + + if (!domain.is8Bit()) + // this function is only for undoing punycode so its okay if utf-16 text makes it out unchanged. + return JSC::JSValue::encode(arg0); + + domain.convertTo16Bit(); + + constexpr static int allowedNameToUnicodeErrors = UIDNA_ERROR_EMPTY_LABEL | UIDNA_ERROR_LABEL_TOO_LONG | UIDNA_ERROR_DOMAIN_NAME_TOO_LONG | UIDNA_ERROR_LEADING_HYPHEN | UIDNA_ERROR_TRAILING_HYPHEN | UIDNA_ERROR_HYPHEN_3_4; + constexpr static int hostnameBufferLength = 2048; + + auto encoder = &WTF::URLParser::internationalDomainNameTranscoder(); + UChar hostnameBuffer[hostnameBufferLength]; + UErrorCode error = U_ZERO_ERROR; + UIDNAInfo processingDetails = UIDNA_INFO_INITIALIZER; + + int32_t numCharactersConverted = uidna_nameToUnicode(encoder, StringView(domain).characters16(), domain.length(), hostnameBuffer, hostnameBufferLength, &processingDetails, &error); + + if (U_SUCCESS(error) && !(processingDetails.errors & ~allowedNameToUnicodeErrors) && numCharactersConverted) { + return JSC::JSValue::encode(JSC::jsString(vm, WTF::String(hostnameBuffer, numCharactersConverted))); + } + throwTypeError(globalObject, scope, "domainToUnicode failed"_s); + return JSC::JSValue::encode(jsUndefined()); +} + +JSC::JSValue createNodeURLBinding(Zig::GlobalObject* globalObject) +{ + VM& vm = globalObject->vm(); + auto binding = constructEmptyArray(globalObject, nullptr, 2); + binding->putByIndexInline( + globalObject, + (unsigned)0, + JSC::JSFunction::create(vm, globalObject, 1, "domainToAscii"_s, jsDomainToASCII, ImplementationVisibility::Public), + false); + binding->putByIndexInline( + globalObject, + (unsigned)1, + JSC::JSFunction::create(vm, globalObject, 1, "domainToUnicode"_s, jsDomainToUnicode, ImplementationVisibility::Public), + false); + return binding; +} + +} // namespace Bun diff --git a/src/bun.js/bindings/NodeURL.h b/src/bun.js/bindings/NodeURL.h new file mode 100644 index 0000000000000..69a13e2d255f9 --- /dev/null +++ b/src/bun.js/bindings/NodeURL.h @@ -0,0 +1,8 @@ +#include "config.h" +#include "ZigGlobalObject.h" + +namespace Bun { + +JSC::JSValue createNodeURLBinding(Zig::GlobalObject*); + +} // namespace Bun \ No newline at end of file diff --git a/src/bun.js/bindings/NodeVMScript.cpp b/src/bun.js/bindings/NodeVM.cpp similarity index 94% rename from src/bun.js/bindings/NodeVMScript.cpp rename to src/bun.js/bindings/NodeVM.cpp index 84428c2a3be67..8064061b08056 100644 --- a/src/bun.js/bindings/NodeVMScript.cpp +++ b/src/bun.js/bindings/NodeVM.cpp @@ -1,6 +1,6 @@ #include "root.h" -#include "NodeVMScript.h" +#include "NodeVM.h" #include "JavaScriptCore/JSObjectInlines.h" #include "wtf/text/ExternalStringImpl.h" @@ -594,4 +594,31 @@ void NodeVMScript::destroy(JSCell* cell) { static_cast(cell)->NodeVMScript::~NodeVMScript(); } + +} // namespace WebCore + +namespace Bun { + +JSC::JSValue createNodeVMBinding(Zig::GlobalObject* globalObject) +{ + VM& vm = globalObject->vm(); + auto* obj = constructEmptyObject(globalObject); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "Script"_s)), + reinterpret_cast(globalObject)->NodeVMScript(), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "createContext"_s)), + JSC::JSFunction::create(vm, globalObject, 0, "createContext"_s, vmModule_createContext, ImplementationVisibility::Public), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "isContext"_s)), + JSC::JSFunction::create(vm, globalObject, 0, "isContext"_s, vmModule_isContext, ImplementationVisibility::Public), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "runInNewContext"_s)), + JSC::JSFunction::create(vm, globalObject, 0, "runInNewContext"_s, vmModuleRunInNewContext, ImplementationVisibility::Public), 0); + obj->putDirect( + vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "runInThisContext"_s)), + JSC::JSFunction::create(vm, globalObject, 0, "runInThisContext"_s, vmModuleRunInThisContext, ImplementationVisibility::Public), 0); + return obj; } + +} // namespace Bun diff --git a/src/bun.js/bindings/NodeVMScript.h b/src/bun.js/bindings/NodeVM.h similarity index 96% rename from src/bun.js/bindings/NodeVMScript.h rename to src/bun.js/bindings/NodeVM.h index cb1476b2683ca..becaf7b4b754f 100644 --- a/src/bun.js/bindings/NodeVMScript.h +++ b/src/bun.js/bindings/NodeVM.h @@ -80,4 +80,11 @@ JSC_DECLARE_HOST_FUNCTION(vmModule_createContext); JSC_DECLARE_HOST_FUNCTION(vmModule_isContext); JSC_DECLARE_HOST_FUNCTION(vmModuleRunInNewContext); JSC_DECLARE_HOST_FUNCTION(vmModuleRunInThisContext); + +} // namespace WebCore + +namespace Bun { + +JSC::JSValue createNodeVMBinding(Zig::GlobalObject*); + } diff --git a/src/bun.js/bindings/Path.cpp b/src/bun.js/bindings/Path.cpp index 5db97f3584863..516b4da73a20b 100644 --- a/src/bun.js/bindings/Path.cpp +++ b/src/bun.js/bindings/Path.cpp @@ -1,7 +1,8 @@ - +#include "Path.h" #include "root.h" #include "headers.h" #include "BunClientData.h" +#include "ZigGlobalObject.h" #include #include @@ -172,25 +173,27 @@ static JSC::JSObject* createPath(JSGlobalObject* globalThis, bool isWindows) Path_functionToNamespacedPath, ImplementationVisibility::Public), 0); - if (isWindows) { - path->putDirect(vm, clientData->builtinNames().sepPublicName(), - JSC::jsString(vm, WTF::String("\\"_s)), 0); - path->putDirect(vm, clientData->builtinNames().delimiterPublicName(), - JSC::jsString(vm, WTF::String(";"_s)), 0); - } else { - path->putDirect(vm, clientData->builtinNames().sepPublicName(), - JSC::jsString(vm, WTF::String("/"_s)), 0); - path->putDirect(vm, clientData->builtinNames().delimiterPublicName(), - JSC::jsString(vm, WTF::String(":"_s)), 0); - } - return path; } } // namespace Zig -extern JSC__JSValue Bun__Path__create(JSC::JSGlobalObject* globalObject, bool isWindows) +namespace Bun { + +JSC::JSValue createNodePathBinding(Zig::GlobalObject* globalObject) { - return JSC::JSValue::encode(JSC::JSValue(Zig::createPath( - globalObject, isWindows))); -} \ No newline at end of file + auto binding = constructEmptyArray(globalObject, nullptr, 2); + binding->putByIndexInline( + globalObject, + (unsigned)0, + Zig::createPath(globalObject, false), + false); + binding->putByIndexInline( + globalObject, + (unsigned)1, + Zig::createPath(globalObject, true), + false); + return binding; +} + +} // namespace Bun diff --git a/src/bun.js/bindings/Path.h b/src/bun.js/bindings/Path.h index e69de29bb2d1d..4cdf983b18789 100644 --- a/src/bun.js/bindings/Path.h +++ b/src/bun.js/bindings/Path.h @@ -0,0 +1,8 @@ +#include "config.h" +#include "ZigGlobalObject.h" + +namespace Bun { + +JSC::JSValue createNodePathBinding(Zig::GlobalObject* globalObject); + +} // namespace Bun diff --git a/src/bun.js/bindings/ProcessBindingTTYWrap.cpp b/src/bun.js/bindings/ProcessBindingTTYWrap.cpp index a70307614cb4e..ed17787fbc775 100644 --- a/src/bun.js/bindings/ProcessBindingTTYWrap.cpp +++ b/src/bun.js/bindings/ProcessBindingTTYWrap.cpp @@ -491,12 +491,12 @@ class TTYWrapConstructor final : public JSC::InternalFunction { const ClassInfo TTYWrapConstructor::s_info = { "TTY"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(TTYWrapConstructor) }; -JSValue createBunTTYFunctions(JSC::JSGlobalObject* globalObject) +JSValue createBunTTYFunctions(Zig::GlobalObject* globalObject) { auto& vm = globalObject->vm(); auto* obj = constructEmptyObject(globalObject); - obj->putDirect(vm, PropertyName(Identifier::fromString(vm, "isTTY"_s)), JSFunction::create(vm, globalObject, 0, "isatty"_s, Zig::jsFunctionTty_isatty, ImplementationVisibility::Public), 0); + obj->putDirect(vm, PropertyName(Identifier::fromString(vm, "isatty"_s)), JSFunction::create(vm, globalObject, 0, "isatty"_s, Zig::jsFunctionTty_isatty, ImplementationVisibility::Public), 0); obj->putDirect(vm, PropertyName(Identifier::fromString(vm, "setRawMode"_s)), JSFunction::create(vm, globalObject, 0, "ttySetMode"_s, jsTTYSetMode, ImplementationVisibility::Public), 0); diff --git a/src/bun.js/bindings/ProcessBindingTTYWrap.h b/src/bun.js/bindings/ProcessBindingTTYWrap.h index bedecea7652dc..dfcd56966facd 100644 --- a/src/bun.js/bindings/ProcessBindingTTYWrap.h +++ b/src/bun.js/bindings/ProcessBindingTTYWrap.h @@ -1,6 +1,7 @@ #pragma once #include "root.h" +#include "ZigGlobalObject.h" namespace JSC { class JSGlobalObject; @@ -9,7 +10,7 @@ class JSValue; namespace Bun { -JSC::JSValue createBunTTYFunctions(JSC::JSGlobalObject* globalObject); +JSC::JSValue createBunTTYFunctions(Zig::GlobalObject* globalObject); JSC::JSValue createNodeTTYWrapObject(JSC::JSGlobalObject* globalObject); JSC_DECLARE_HOST_FUNCTION(Process_functionInternalGetWindowSize); diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 9d63a267e1eb3..c1afa3ca86610 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -63,7 +63,6 @@ #include "wtf/Assertions.h" #include "wtf/Gigacage.h" #include "wtf/URL.h" -#include "wtf/URLParser.h" #include "wtf/text/ExternalStringImpl.h" #include "wtf/text/StringCommon.h" #include "wtf/text/StringImpl.h" @@ -121,7 +120,7 @@ #include "napi.h" #include "JSSQLStatement.h" #include "ModuleLoader.h" -#include "NodeVMScript.h" +#include "NodeVM.h" #include "ProcessIdentifier.h" #include "SerializedScriptValue.h" #include "NodeTTYModule.h" @@ -129,6 +128,8 @@ #include "ZigGeneratedClasses.h" #include "JavaScriptCore/DateInstance.h" +#include "JS2Native.h" + #include "BunPlugin.h" #include "JSEnvironmentVariableMap.h" #include "DOMIsoSubspaces.h" @@ -142,8 +143,6 @@ #include "ProcessBindingConstants.h" -#include - #if ENABLE(REMOTE_INSPECTOR) #include "JavaScriptCore/RemoteInspectorServer.h" #endif @@ -157,7 +156,6 @@ using namespace Bun; extern "C" JSC__JSValue Bun__NodeUtil__jsParseArgs(JSC::JSGlobalObject*, JSC::CallFrame*); extern "C" JSC::EncodedJSValue Bun__fetch(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); -extern "C" JSC::EncodedJSValue Bun__canonicalizeIP(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); extern "C" JSC::EncodedJSValue H2FrameParser__getConstructor(Zig::GlobalObject* globalObject); extern "C" JSC::EncodedJSValue BUN__HTTP2__getUnpackedSettings(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); extern "C" JSC::EncodedJSValue BUN__HTTP2_getPackedSettings(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); @@ -704,14 +702,12 @@ static String computeErrorInfo(JSC::VM& vm, Vector& stackTrace, unsi return computeErrorInfoWithoutPrepareStackTrace(vm, globalObject, stackTrace, line, column, sourceURL, errorInstance); } -static void resetOnEachMicrotaskTick(JSC::VM& vm, Zig::GlobalObject* globalObject); - static void checkIfNextTickWasCalledDuringMicrotask(JSC::VM& vm) { auto* globalObject = Bun__getDefaultGlobal(); if (auto nextTickQueueValue = globalObject->m_nextTickQueue.get()) { auto* queue = jsCast(nextTickQueueValue); - resetOnEachMicrotaskTick(vm, globalObject); + globalObject->resetOnEachMicrotaskTick(); queue->drain(vm, globalObject); } } @@ -729,12 +725,13 @@ static void cleanupAsyncHooksData(JSC::VM& vm) } } -static void resetOnEachMicrotaskTick(JSC::VM& vm, Zig::GlobalObject* globalObject) +void Zig::GlobalObject::resetOnEachMicrotaskTick() { - if (globalObject->asyncHooksNeedsCleanup) { + JSC::VM& vm = this->vm(); + if (this->asyncHooksNeedsCleanup) { vm.setOnEachMicrotaskTick(&cleanupAsyncHooksData); } else { - if (globalObject->m_nextTickQueue) { + if (this->m_nextTickQueue) { vm.setOnEachMicrotaskTick(nullptr); } else { vm.setOnEachMicrotaskTick(&checkIfNextTickWasCalledDuringMicrotask); @@ -803,7 +800,7 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, vm.setOnEachMicrotaskTick([](JSC::VM& vm) -> void { auto* globalObject = Bun__getDefaultGlobal(); if (auto nextTickQueue = globalObject->m_nextTickQueue.get()) { - resetOnEachMicrotaskTick(vm, globalObject); + globalObject->resetOnEachMicrotaskTick(); Bun::JSNextTickQueue* queue = jsCast(nextTickQueue); queue->drain(vm, globalObject); return; @@ -1720,29 +1717,6 @@ JSC_DEFINE_HOST_FUNCTION(functionCallback, (JSC::JSGlobalObject * globalObject, return JSC::JSValue::encode(JSC::call(globalObject, callback, callData, JSC::jsUndefined(), JSC::MarkedArgumentBuffer())); } -// $lazy("async_hooks").cleanupLater -JSC_DEFINE_HOST_FUNCTION(asyncHooksCleanupLater, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - // assumptions and notes: - // - nobody else uses setOnEachMicrotaskTick - // - this is called by js if we set async context in a way we may not clear it - // - AsyncLocalStorage.prototype.run cleans up after itself and does not call this cb - auto* global = jsCast(globalObject); - global->asyncHooksNeedsCleanup = true; - resetOnEachMicrotaskTick(globalObject->vm(), global); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(asyncHooksSetEnabled, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - // assumptions and notes: - // - nobody else uses setOnEachMicrotaskTick - // - this is called by js if we set async context in a way we may not clear it - // - AsyncLocalStorage.prototype.run cleans up after itself and does not call this cb - globalObject->setAsyncContextTrackingEnabled(callFrame->argument(0).toBoolean(globalObject)); - return JSC::JSValue::encode(JSC::jsUndefined()); -} - JSC_DEFINE_CUSTOM_GETTER(noop_getter, (JSGlobalObject*, EncodedJSValue, PropertyName)) { return JSC::JSValue::encode(JSC::jsUndefined()); @@ -1755,479 +1729,9 @@ JSC_DEFINE_CUSTOM_SETTER(noop_setter, return true; } -static NeverDestroyed pathToFileURLString(MAKE_STATIC_STRING_IMPL("pathToFileURL")); -static NeverDestroyed fileURLToPathString(MAKE_STATIC_STRING_IMPL("fileURLToPath")); -static NeverDestroyed domainToASCIIString(MAKE_STATIC_STRING_IMPL("domainToASCII")); -static NeverDestroyed domainToUnicodeString(MAKE_STATIC_STRING_IMPL("domainToUnicode")); - -enum ReadableStreamTag : int32_t { - Invalid = -1, - - /// ReadableStreamDefaultController or ReadableByteStreamController - JavaScript = 0, - - /// ReadableByteStreamController - /// but with a BlobLoader - /// we can skip the BlobLoader and just use the underlying Blob - Blob = 1, - - /// ReadableByteStreamController - /// but with a FileLoader - /// we can skip the FileLoader and just use the underlying File - File = 2, - - /// This is a direct readable stream - /// That means we can turn it into whatever we want - Direct = 3, - - // This is an ambiguous stream of bytes - Bytes = 4, -}; - -extern "C" JSC_DECLARE_HOST_FUNCTION(BunString__getStringWidth); - -JSC_DEFINE_HOST_FUNCTION(jsReceiveMessageOnPort, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) -{ - auto& vm = lexicalGlobalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - if (callFrame->argumentCount() < 1) { - throwTypeError(lexicalGlobalObject, scope, "receiveMessageOnPort needs 1 argument"_s); - return JSC::JSValue::encode(JSC::JSValue {}); - } - - auto port = callFrame->argument(0); - - if (!port.isObject()) { - throwTypeError(lexicalGlobalObject, scope, "the \"port\" argument must be a MessagePort instance"_s); - return JSC::JSValue::encode(jsUndefined()); - } - - if (auto* messagePort = jsDynamicCast(port)) { - return JSC::JSValue::encode(messagePort->wrapped().tryTakeMessage(lexicalGlobalObject)); - } else if (auto* broadcastChannel = jsDynamicCast(port)) { - // TODO: support broadcast channels - return JSC::JSValue::encode(jsUndefined()); - } - - throwTypeError(lexicalGlobalObject, scope, "the \"port\" argument must be a MessagePort instance"_s); - return JSC::JSValue::encode(jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(functionDomainToASCII, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - auto& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - if (callFrame->argumentCount() < 1) { - throwTypeError(globalObject, scope, "domainToASCII needs 1 argument"_s); - return JSC::JSValue::encode(JSC::JSValue {}); - } - - auto arg0 = callFrame->argument(0); - if (arg0.isUndefined()) - return JSC::JSValue::encode(jsUndefined()); - if (arg0.isNull()) - return JSC::JSValue::encode(jsNull()); - if (!arg0.isString()) { - throwTypeError(globalObject, scope, "the \"domain\" argument must be a string"_s); - return JSC::JSValue::encode(jsUndefined()); - } - - auto domain = arg0.toWTFString(globalObject); - if (domain.isNull()) - return JSC::JSValue::encode(jsUndefined()); - - // https://url.spec.whatwg.org/#forbidden-host-code-point - if ( - domain.contains(0x0000) || // U+0000 NULL - domain.contains(0x0009) || // U+0009 TAB - domain.contains(0x000A) || // U+000A LF - domain.contains(0x000D) || // U+000D CR - domain.contains(0x0020) || // U+0020 SPACE - domain.contains(0x0023) || // U+0023 (#) - domain.contains(0x002F) || // U+002F (/) - domain.contains(0x003A) || // U+003A (:) - domain.contains(0x003C) || // U+003C (<) - domain.contains(0x003E) || // U+003E (>) - domain.contains(0x003F) || // U+003F (?) - domain.contains(0x0040) || // U+0040 (@) - domain.contains(0x005B) || // U+005B ([) - domain.contains(0x005C) || // U+005C (\) - domain.contains(0x005D) || // U+005D (]) - domain.contains(0x005E) || // U+005E (^) - domain.contains(0x007C) // // U+007C (|). - ) - return JSC::JSValue::encode(jsEmptyString(vm)); - - if (domain.containsOnlyASCII()) - return JSC::JSValue::encode(arg0); - if (domain.is8Bit()) - domain.convertTo16Bit(); - - constexpr static int allowedNameToASCIIErrors = UIDNA_ERROR_EMPTY_LABEL | UIDNA_ERROR_LABEL_TOO_LONG | UIDNA_ERROR_DOMAIN_NAME_TOO_LONG | UIDNA_ERROR_LEADING_HYPHEN | UIDNA_ERROR_TRAILING_HYPHEN | UIDNA_ERROR_HYPHEN_3_4; - constexpr static size_t hostnameBufferLength = 2048; - - auto encoder = &WTF::URLParser::internationalDomainNameTranscoder(); - UChar hostnameBuffer[hostnameBufferLength]; - UErrorCode error = U_ZERO_ERROR; - UIDNAInfo processingDetails = UIDNA_INFO_INITIALIZER; - int32_t numCharactersConverted = uidna_nameToASCII(encoder, StringView(domain).characters16(), domain.length(), hostnameBuffer, hostnameBufferLength, &processingDetails, &error); - - if (U_SUCCESS(error) && !(processingDetails.errors & ~allowedNameToASCIIErrors) && numCharactersConverted) { - return JSC::JSValue::encode(JSC::jsString(vm, WTF::String(hostnameBuffer, numCharactersConverted))); - } - throwTypeError(globalObject, scope, "domainToASCII failed"_s); - return JSC::JSValue::encode(jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(functionDomainToUnicode, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - auto& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - if (callFrame->argumentCount() < 1) { - throwTypeError(globalObject, scope, "domainToUnicode needs 1 argument"_s); - return JSC::JSValue::encode(JSC::JSValue {}); - } - - auto arg0 = callFrame->argument(0); - if (arg0.isUndefined()) - return JSC::JSValue::encode(jsUndefined()); - if (arg0.isNull()) - return JSC::JSValue::encode(jsNull()); - if (!arg0.isString()) { - throwTypeError(globalObject, scope, "the \"domain\" argument must be a string"_s); - return JSC::JSValue::encode(jsUndefined()); - } - - auto domain = arg0.toWTFString(globalObject); - if (domain.isNull()) - return JSC::JSValue::encode(jsUndefined()); - - // https://url.spec.whatwg.org/#forbidden-host-code-point - if ( - domain.contains(0x0000) || // U+0000 NULL - domain.contains(0x0009) || // U+0009 TAB - domain.contains(0x000A) || // U+000A LF - domain.contains(0x000D) || // U+000D CR - domain.contains(0x0020) || // U+0020 SPACE - domain.contains(0x0023) || // U+0023 (#) - domain.contains(0x002F) || // U+002F (/) - domain.contains(0x003A) || // U+003A (:) - domain.contains(0x003C) || // U+003C (<) - domain.contains(0x003E) || // U+003E (>) - domain.contains(0x003F) || // U+003F (?) - domain.contains(0x0040) || // U+0040 (@) - domain.contains(0x005B) || // U+005B ([) - domain.contains(0x005C) || // U+005C (\) - domain.contains(0x005D) || // U+005D (]) - domain.contains(0x005E) || // U+005E (^) - domain.contains(0x007C) // // U+007C (|). - ) - return JSC::JSValue::encode(jsEmptyString(vm)); - - if (!domain.is8Bit()) - // this function is only for undoing punycode so its okay if utf-16 text makes it out unchanged. - return JSC::JSValue::encode(arg0); - - domain.convertTo16Bit(); - - constexpr static int allowedNameToUnicodeErrors = UIDNA_ERROR_EMPTY_LABEL | UIDNA_ERROR_LABEL_TOO_LONG | UIDNA_ERROR_DOMAIN_NAME_TOO_LONG | UIDNA_ERROR_LEADING_HYPHEN | UIDNA_ERROR_TRAILING_HYPHEN | UIDNA_ERROR_HYPHEN_3_4; - constexpr static int hostnameBufferLength = 2048; - - auto encoder = &WTF::URLParser::internationalDomainNameTranscoder(); - UChar hostnameBuffer[hostnameBufferLength]; - UErrorCode error = U_ZERO_ERROR; - UIDNAInfo processingDetails = UIDNA_INFO_INITIALIZER; - - int32_t numCharactersConverted = uidna_nameToUnicode(encoder, StringView(domain).characters16(), domain.length(), hostnameBuffer, hostnameBufferLength, &processingDetails, &error); - - if (U_SUCCESS(error) && !(processingDetails.errors & ~allowedNameToUnicodeErrors) && numCharactersConverted) { - return JSC::JSValue::encode(JSC::jsString(vm, WTF::String(hostnameBuffer, numCharactersConverted))); - } - throwTypeError(globalObject, scope, "domainToUnicode failed"_s); - return JSC::JSValue::encode(jsUndefined()); -} - -extern "C" EncodedJSValue BunInternalFunction__syntaxHighlighter(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame); - -// we're trying out a new way to do this lazy loading -// this is $lazy() in js code -JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, - (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame)) -{ - - Zig::GlobalObject* globalObject = reinterpret_cast(lexicalGlobalObject); - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - VM& vm = globalObject->vm(); - - switch (callFrame->argumentCount()) { - case 0: { - JSC::throwTypeError(globalObject, scope, "$lazy needs 1 argument (a string)"_s); - scope.release(); - return JSC::JSValue::encode(JSC::JSValue {}); - } - default: { - JSC::JSValue moduleName = callFrame->argument(0); - if (moduleName.isNumber()) { - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - JSC::throwTypeError(globalObject, scope, "$lazy expects a string"_s); - scope.release(); - return JSC::JSValue::encode(JSC::JSValue {}); - } - - auto string = moduleName.toWTFString(globalObject); - if (string.isNull()) { - JSC::throwTypeError(globalObject, scope, "$lazy expects a string"_s); - scope.release(); - return JSC::JSValue::encode(JSC::JSValue {}); - } - - if (string == "sqlite"_s) { - return JSC::JSValue::encode(JSSQLStatementConstructor::create(vm, globalObject, JSSQLStatementConstructor::createStructure(vm, globalObject, globalObject->m_functionPrototype.get()))); - } - - if (string == "http"_s) { - auto* obj = constructEmptyObject(globalObject); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "setHeader"_s)), - JSC::JSFunction::create(vm, globalObject, 3, "setHeader"_s, jsHTTPSetHeader, ImplementationVisibility::Public), NoIntrinsic); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getHeader"_s)), - JSC::JSFunction::create(vm, globalObject, 2, "getHeader"_s, jsHTTPGetHeader, ImplementationVisibility::Public), NoIntrinsic); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "assignHeaders"_s)), - JSC::JSFunction::create(vm, globalObject, 2, "assignHeaders"_s, jsHTTPAssignHeaders, ImplementationVisibility::Public), NoIntrinsic); - return JSC::JSValue::encode(obj); - } - - if (string == "worker_threads"_s) { - - JSValue workerData = jsUndefined(); - JSValue threadId = jsNumber(0); - - if (auto* worker = WebWorker__getParentWorker(globalObject->bunVM())) { - auto& options = worker->options(); - if (worker && options.bun.data) { - auto ports = MessagePort::entanglePorts(*ScriptExecutionContext::getScriptExecutionContext(worker->clientIdentifier()), WTFMove(options.bun.dataMessagePorts)); - RefPtr serialized = WTFMove(options.bun.data); - JSValue deserialized = serialized->deserialize(*globalObject, globalObject, WTFMove(ports)); - RETURN_IF_EXCEPTION(scope, {}); - workerData = deserialized; - } - - // Main thread starts at 1 - threadId = jsNumber(worker->clientIdentifier() - 1); - } - - JSArray* array = constructEmptyArray(globalObject, nullptr); - array->push(globalObject, workerData); - array->push(globalObject, threadId); - array->push(globalObject, JSFunction::create(vm, globalObject, 1, "receiveMessageOnPort"_s, jsReceiveMessageOnPort, ImplementationVisibility::Public, NoIntrinsic)); - - return JSC::JSValue::encode(array); - } - - if (string == "util"_s) { - auto* obj = constructEmptyObject(globalObject); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "parseArgs"_s)), - JSC::JSFunction::create(vm, globalObject, 1, "parseArgs"_s, Bun__NodeUtil__jsParseArgs, ImplementationVisibility::Public), NoIntrinsic); - - return JSValue::encode(obj); - } - - if (string == "getStringWidth"_s) { - return JSValue::encode(JSC::JSFunction::create(vm, globalObject, 1, "getStringWidth"_s, BunString__getStringWidth, ImplementationVisibility::Public)); - } - - if (string == "domainToASCII"_s) { - return JSValue::encode( - JSFunction::create(vm, globalObject, 1, domainToASCIIString, functionDomainToASCII, ImplementationVisibility::Public, NoIntrinsic)); - } - - if (string == "domainToUnicode"_s) { - return JSValue::encode( - JSFunction::create(vm, globalObject, 1, domainToUnicodeString, functionDomainToUnicode, ImplementationVisibility::Public, NoIntrinsic)); - } - - if (string == "pathToFileURL"_s) { - return JSValue::encode( - JSFunction::create(vm, globalObject, 1, pathToFileURLString, functionPathToFileURL, ImplementationVisibility::Public, NoIntrinsic)); - } - - if (string == "fileURLToPath"_s) { - return JSValue::encode( - JSFunction::create(vm, globalObject, 1, fileURLToPathString, functionFileURLToPath, ImplementationVisibility::Public, NoIntrinsic)); - } - - if (string == "bun:stream"_s) { - auto* obj = constructEmptyObject(globalObject); - obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "BufferList"_s)), reinterpret_cast(globalObject)->JSBufferList(), 0); - obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "ReadableState"_s)), reinterpret_cast(globalObject)->JSReadableState(), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "maybeReadMore"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "maybeReadMore"_s, jsReadable_maybeReadMore, ImplementationVisibility::Public), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "resume"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "resume"_s, jsReadable_resume, ImplementationVisibility::Public), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "emitReadable"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "emitReadable"_s, jsReadable_emitReadable_, ImplementationVisibility::Public), 0); - return JSValue::encode(obj); - } - - if (string == "events"_s) { - return JSValue::encode(WebCore::JSEventEmitter::getConstructor(vm, globalObject)); - } - - if (string == "internal/crypto"_s) { - // auto sourceOrigin = callFrame->callerSourceOrigin(vm).url(); - // bool isBuiltin = sourceOrigin.protocolIs("builtin"_s); - // if (!isBuiltin) { - // return JSC::JSValue::encode(JSC::jsUndefined()); - // } - auto* obj = constructEmptyObject(globalObject); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "symmetricKeySize"_s)), JSC::JSFunction::create(vm, globalObject, 1, "symmetricKeySize"_s, KeyObject__SymmetricKeySize, ImplementationVisibility::Public, NoIntrinsic), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "asymmetricKeyType"_s)), JSC::JSFunction::create(vm, globalObject, 1, "asymmetricKeyType"_s, KeyObject__AsymmetricKeyType, ImplementationVisibility::Public, NoIntrinsic), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "asymmetricKeyDetails"_s)), JSC::JSFunction::create(vm, globalObject, 1, "asymmetricKeyDetails"_s, KeyObject_AsymmetricKeyDetails, ImplementationVisibility::Public, NoIntrinsic), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "equals"_s)), JSC::JSFunction::create(vm, globalObject, 2, "equals"_s, KeyObject__Equals, ImplementationVisibility::Public, NoIntrinsic), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "exports"_s)), JSC::JSFunction::create(vm, globalObject, 2, "exports"_s, KeyObject__Exports, ImplementationVisibility::Public, NoIntrinsic), 0); - - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "createSecretKey"_s)), JSC::JSFunction::create(vm, globalObject, 1, "createSecretKey"_s, KeyObject__createSecretKey, ImplementationVisibility::Public, NoIntrinsic), 0); - - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "createPublicKey"_s)), JSC::JSFunction::create(vm, globalObject, 1, "createPublicKey"_s, KeyObject__createPublicKey, ImplementationVisibility::Public, NoIntrinsic), 0); - - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "createPrivateKey"_s)), JSC::JSFunction::create(vm, globalObject, 1, "createPrivateKey"_s, KeyObject__createPrivateKey, ImplementationVisibility::Public, NoIntrinsic), 0); - - obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "generateKeySync"_s)), JSC::JSFunction::create(vm, globalObject, 2, "generateKeySync"_s, KeyObject__generateKeySync, ImplementationVisibility::Public, NoIntrinsic), 0); - - obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "generateKeyPairSync"_s)), JSC::JSFunction::create(vm, globalObject, 2, "generateKeyPairSync"_s, KeyObject__generateKeyPairSync, ImplementationVisibility::Public, NoIntrinsic), 0); - - obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "sign"_s)), JSC::JSFunction::create(vm, globalObject, 3, "sign"_s, KeyObject__Sign, ImplementationVisibility::Public, NoIntrinsic), 0); - obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "verify"_s)), JSC::JSFunction::create(vm, globalObject, 4, "verify"_s, KeyObject__Verify, ImplementationVisibility::Public, NoIntrinsic), 0); - - return JSValue::encode(obj); - } - - if (string == "internal/http2"_s) { - auto* obj = constructEmptyObject(globalObject); - - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "H2FrameParser"_s)), JSValue::decode(H2FrameParser__getConstructor(globalObject)), 0); - - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getPackedSettings"_s)), JSC::JSFunction::create(vm, globalObject, 1, "getPackedSettings"_s, BUN__HTTP2_getPackedSettings, ImplementationVisibility::Public, NoIntrinsic), 0); - - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getUnpackedSettings"_s)), JSC::JSFunction::create(vm, globalObject, 1, "getUnpackedSettings"_s, BUN__HTTP2__getUnpackedSettings, ImplementationVisibility::Public, NoIntrinsic), 0); - return JSValue::encode(obj); - } - - if (string == "internal/tls"_s) { - auto* obj = constructEmptyObject(globalObject); - - auto sourceOrigin = callFrame->callerSourceOrigin(vm).url(); - // expose for tests in debug mode only - // #ifndef BUN_DEBUG - // bool isBuiltin = sourceOrigin.protocolIs("builtin"_s); - // if (!isBuiltin) { - // return JSC::JSValue::encode(JSC::jsUndefined()); - // } - // #endif - struct us_cert_string_t* out; - auto size = us_raw_root_certs(&out); - if (size < 0) { - return JSValue::encode(JSC::jsUndefined()); - } - auto rootCertificates = JSC::JSArray::create(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), size); - for (auto i = 0; i < size; i++) { - auto raw = out[i]; - auto str = WTF::String::fromUTF8(raw.str, raw.len); - rootCertificates->putDirectIndex(globalObject, i, JSC::jsString(vm, str)); - } - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "rootCertificates"_s)), rootCertificates, 0); - - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "canonicalizeIP"_s)), JSC::JSFunction::create(vm, globalObject, 1, "canonicalizeIP"_s, Bun__canonicalizeIP, ImplementationVisibility::Public, NoIntrinsic), 0); - return JSValue::encode(obj); - } - - if (string == "vm"_s) { - auto* obj = constructEmptyObject(globalObject); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "Script"_s)), - reinterpret_cast(globalObject)->NodeVMScript(), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "createContext"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "createContext"_s, vmModule_createContext, ImplementationVisibility::Public), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "isContext"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "isContext"_s, vmModule_isContext, ImplementationVisibility::Public), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "runInNewContext"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "runInNewContext"_s, vmModuleRunInNewContext, ImplementationVisibility::Public), 0); - - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "runInThisContext"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "runInThisContext"_s, vmModuleRunInThisContext, ImplementationVisibility::Public), 0); - return JSValue::encode(obj); - } - - if (string == "async_hooks"_s) { - auto* obj = constructEmptyObject(globalObject); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "setAsyncHooksEnabled"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "setAsyncHooksEnabled"_s, asyncHooksSetEnabled, ImplementationVisibility::Public), 0); - - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "cleanupLater"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "cleanupLater"_s, asyncHooksCleanupLater, ImplementationVisibility::Public), 0); - return JSValue::encode(obj); - } - - if (string == "tty"_s) { - return JSValue::encode(Bun::createBunTTYFunctions(lexicalGlobalObject)); - } - - if (string == "unstable_syntaxHighlight"_s) { - JSFunction* syntaxHighlight = JSFunction::create(vm, globalObject, 1, "syntaxHighlight"_s, BunInternalFunction__syntaxHighlighter, ImplementationVisibility::Public); - - return JSValue::encode(syntaxHighlight); - } - - if (UNLIKELY(string == "noop"_s)) { - auto* obj = constructEmptyObject(globalObject); - obj->putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "getterSetter"_s)), JSC::CustomGetterSetter::create(vm, noop_getter, noop_setter), 0); - Zig::JSFFIFunction* function = Zig::JSFFIFunction::create(vm, reinterpret_cast(globalObject), 0, String(), functionNoop, JSC::NoIntrinsic); - obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "function"_s)), function, 0); - obj->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "functionRegular"_s), 1, functionNoop, ImplementationVisibility::Public, NoIntrinsic, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); - obj->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "callback"_s), 1, functionCallback, ImplementationVisibility::Public, NoIntrinsic, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); - return JSC::JSValue::encode(obj); - } - -#if BUN_DEBUG - // this should always be unreachable unless in development and adding a new lazy value - // and it has not been added above yet. - ASSERT_NOT_REACHED(); -#else - return JSC::JSValue::encode(JSC::jsUndefined()); -#endif - } - // silence warning - RELEASE_ASSERT_NOT_REACHED(); - } -} +// If you are looking for $lazy / functionLazyLoad +// it is moved to JS2Native.cpp, and is called $cpp and $zig +// see .md for more information static inline JSC::EncodedJSValue jsFunctionAddEventListenerBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, Zig::GlobalObject* castedThis) { @@ -3137,7 +2641,7 @@ void GlobalObject::finishCreation(VM& vm) }); m_emitReadableNextTickFunction.initLater( [](const Initializer& init) { - init.set(JSFunction::create(init.vm, init.owner, 4, "emitReadable"_s, WebCore::jsReadable_emitReadable_, ImplementationVisibility::Public)); + init.set(JSFunction::create(init.vm, init.owner, 4, "emitReadable"_s, WebCore::jsReadable_emitReadable, ImplementationVisibility::Public)); }); m_bunSleepThenCallback.initLater( @@ -3852,22 +3356,14 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm) // ----- Private/Static Properties ----- - auto $lazy = JSC::JSFunction::create(vm, this, 0, "$lazy"_s, functionLazyLoad, ImplementationVisibility::Public); - GlobalPropertyInfo staticGlobals[] = { GlobalPropertyInfo { builtinNames.startDirectStreamPrivateName(), JSC::JSFunction::create(vm, this, 1, String(), functionStartDirectStream, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | 0 }, - // TODO: Remove the "Bun.lazy" symbol - // The reason we cant do this easily is our tests rely on this being public to test the internals. - GlobalPropertyInfo { JSC::Identifier::fromUid(vm.symbolRegistry().symbolForKey(MAKE_STATIC_STRING_IMPL("Bun.lazy"))), - $lazy, - PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | 0 }, - GlobalPropertyInfo { builtinNames.lazyPrivateName(), - $lazy, + JSC::JSFunction::create(vm, this, 0, "@lazy"_s, JS2Native::jsDollarLazy, ImplementationVisibility::Public), PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | 0 }, GlobalPropertyInfo(builtinNames.makeThisTypeErrorPrivateName(), JSFunction::create(vm, this, 2, String(), makeThisTypeErrorForBuiltins, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 165cd80ea5401..3d662c811d190 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -322,6 +322,8 @@ class GlobalObject : public JSC::JSGlobalObject { WebCore::ScriptExecutionContext* m_scriptExecutionContext; Bun::GlobalScope& globalEventScope; + void resetOnEachMicrotaskTick(); + enum class PromiseFunctions : uint8_t { Bun__HTTPRequestContext__onReject, Bun__HTTPRequestContext__onRejectStream, diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index c234d5e657ca1..95dde77c719ca 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -1,7 +1,10 @@ #include "root.h" +#include "JavaScriptCore/JSCJSValue.h" +#include "JavaScriptCore/JSGlobalObject.h" #include "JavaScriptCore/DeleteAllCodeEffort.h" + #include "headers.h" #include "BunClientData.h" @@ -46,6 +49,7 @@ #include "JavaScriptCore/Watchdog.h" #include "ZigGlobalObject.h" #include "helpers.h" +#include "JavaScriptCore/JSObjectInlines.h" #include "wtf/Assertions.h" #include "wtf/text/ExternalStringImpl.h" @@ -5468,6 +5472,38 @@ extern "C" EncodedJSValue ExpectStatic__getPrototype(JSC::JSGlobalObject* global return JSValue::encode(reinterpret_cast(globalObject)->JSExpectStaticPrototype()); } +extern "C" EncodedJSValue JSFunction__createFromZig( + JSC::JSGlobalObject* global, + BunString fn_name, + NativeFunction implementation, + unsigned arg_count, + ImplementationVisibility implementation_visibility, + Intrinsic intrinsic, + NativeFunction constructorOrNull) +{ + VM& vm = global->vm(); + auto name = fn_name.toWTFString(); + return JSValue::encode(JSFunction::create( + vm, + global, + arg_count, + name, + implementation, + implementation_visibility, + intrinsic, + constructorOrNull ? constructorOrNull : JSC::callHostFunctionAsConstructor, + nullptr)); +} + +extern "C" EncodedJSValue JSArray__constructArray( + JSC::JSGlobalObject* global, + const JSValue* values, + size_t values_len) +{ + return JSValue::encode( + JSC::constructArray(global, (ArrayAllocationProfile*)nullptr, values, values_len)); +} + extern "C" bool JSGlobalObject__hasException(JSC::JSGlobalObject* globalObject) { return DECLARE_CATCH_SCOPE(globalObject->vm()).exception() != 0; diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 894b02f7b40cd..e4002942dccac 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -2560,34 +2560,58 @@ pub const JSFunction = extern struct { pub const name = "JSC::JSFunction"; pub const namespace = "JSC"; - // pub fn createFromSourceCode( - // global: *JSGlobalObject, - // function_name: ?[*]const u8, - // function_name_len: u16, - // args: ?[*]JSValue, - // args_len: u16, - // source: *const SourceCode, - // origin: *SourceOrigin, - // exception: *?*JSObject, - // ) *JSFunction { - // return cppFn("createFromSourceCode", .{ - // global, - // function_name, - // function_name_len, - // args, - // args_len, - // source, - // origin, - // exception, - // }); - // } + const ImplementationVisibility = enum(u8) { + public, + private, + private_recursive, + }; + + /// In WebKit: Intrinsic.h + const Intrinsic = enum(u8) { + none, + _, + }; + + const CreateJSFunctionOptions = struct { + implementation_visibility: ImplementationVisibility = .public, + intrinsic: Intrinsic = .none, + constructor: ?*const JSHostFunctionType = null, + }; + + extern fn JSFunction__createFromZig( + global: *JSGlobalObject, + fn_name: bun.String, + implementation: *const JSHostFunctionType, + arg_count: u32, + implementation_visibility: ImplementationVisibility, + intrinsic: Intrinsic, + constructor: ?*const JSHostFunctionType, + ) JSValue; + + pub fn create( + global: *JSGlobalObject, + fn_name: anytype, + implementation: *const JSHostFunctionType, + function_length: u32, + options: CreateJSFunctionOptions, + ) JSValue { + return JSFunction__createFromZig( + global, + switch (@TypeOf(fn_name)) { + bun.String => fn_name, + else => bun.String.init(fn_name), + }, + implementation, + function_length, + options.implementation_visibility, + options.intrinsic, + options.constructor, + ); + } pub fn optimizeSoon(value: JSValue) void { cppFn("optimizeSoon", .{value}); } - // pub fn toString(this: *JSFunction, globalThis: *JSGlobalObject) *const JSString { - // return cppFn("toString", .{ this, globalThis }); - // } extern fn JSC__JSFunction__getSourceCode(value: JSValue, out: *ZigString) bool; @@ -2598,8 +2622,6 @@ pub const JSFunction = extern struct { pub const Extern = [_][]const u8{ "fromString", - // "createFromSourceCode", - "getName", "displayName", "calculatedDisplayName", @@ -3004,7 +3026,7 @@ pub const JSGlobalObject = extern struct { return cppFn("deleteModuleRegistryEntry", .{ this, name_ }); } - fn bunVM_(this: *JSGlobalObject) *anyopaque { + fn bunVMUnsafe(this: *JSGlobalObject) *anyopaque { return cppFn("bunVM", .{this}); } @@ -3014,16 +3036,16 @@ pub const JSGlobalObject = extern struct { // you most likely need to run // make clean-jsc-bindings // make bindings -j10 - const assertion = this.bunVM_() == @as(*anyopaque, @ptrCast(JSC.VirtualMachine.get())); + const assertion = this.bunVMUnsafe() == @as(*anyopaque, @ptrCast(JSC.VirtualMachine.get())); if (!assertion) @breakpoint(); std.debug.assert(assertion); } - return @as(*JSC.VirtualMachine, @ptrCast(@alignCast(this.bunVM_()))); + return @as(*JSC.VirtualMachine, @ptrCast(@alignCast(this.bunVMUnsafe()))); } /// We can't do the threadlocal check when queued from another thread pub fn bunVMConcurrently(this: *JSGlobalObject) *JSC.VirtualMachine { - return @as(*JSC.VirtualMachine, @ptrCast(@alignCast(this.bunVM_()))); + return @as(*JSC.VirtualMachine, @ptrCast(@alignCast(this.bunVMUnsafe()))); } pub fn handleRejectedPromises(this: *JSGlobalObject) void { @@ -5836,8 +5858,11 @@ pub const JSHostFunctionPtr = *const JSHostFunctionType; const DeinitFunction = *const fn (ctx: *anyopaque, buffer: [*]u8, len: usize) callconv(.C) void; pub const JSArray = struct { - pub fn from(globalThis: *JSGlobalObject, arguments: []const JSC.JSValue) JSValue { - return JSC.JSValue.c(JSC.C.JSObjectMakeArray(globalThis, arguments.len, @as(?[*]const JSC.C.JSObjectRef, @ptrCast(arguments.ptr)), null)); + // TODO(@paperdave): this can throw + extern fn JSArray__constructArray(*JSGlobalObject, [*]const JSValue, usize) JSValue; + + pub fn create(global: *JSGlobalObject, items: []const JSValue) JSValue { + return JSArray__constructArray(global, items.ptr, items.len); } }; @@ -6264,22 +6289,23 @@ pub fn JSPropertyIterator(comptime options: JSPropertyIteratorOptions) type { } // DOMCall Fields -pub const __DOMCall_ptr = JSC.API.Bun.FFIObject.dom_call; -pub const __DOMCall__reader_u8 = JSC.API.Bun.FFIObject.Reader.DOMCalls.u8; -pub const __DOMCall__reader_u16 = JSC.API.Bun.FFIObject.Reader.DOMCalls.u16; -pub const __DOMCall__reader_u32 = JSC.API.Bun.FFIObject.Reader.DOMCalls.u32; -pub const __DOMCall__reader_ptr = JSC.API.Bun.FFIObject.Reader.DOMCalls.ptr; -pub const __DOMCall__reader_i8 = JSC.API.Bun.FFIObject.Reader.DOMCalls.i8; -pub const __DOMCall__reader_i16 = JSC.API.Bun.FFIObject.Reader.DOMCalls.i16; -pub const __DOMCall__reader_i32 = JSC.API.Bun.FFIObject.Reader.DOMCalls.i32; -pub const __DOMCall__reader_f32 = JSC.API.Bun.FFIObject.Reader.DOMCalls.f32; -pub const __DOMCall__reader_f64 = JSC.API.Bun.FFIObject.Reader.DOMCalls.f64; -pub const __DOMCall__reader_i64 = JSC.API.Bun.FFIObject.Reader.DOMCalls.i64; -pub const __DOMCall__reader_u64 = JSC.API.Bun.FFIObject.Reader.DOMCalls.u64; -pub const __DOMCall__reader_intptr = JSC.API.Bun.FFIObject.Reader.DOMCalls.intptr; +const Bun = JSC.API.Bun; +pub const __DOMCall_ptr = Bun.FFIObject.dom_call; +pub const __DOMCall__reader_u8 = Bun.FFIObject.Reader.DOMCalls.u8; +pub const __DOMCall__reader_u16 = Bun.FFIObject.Reader.DOMCalls.u16; +pub const __DOMCall__reader_u32 = Bun.FFIObject.Reader.DOMCalls.u32; +pub const __DOMCall__reader_ptr = Bun.FFIObject.Reader.DOMCalls.ptr; +pub const __DOMCall__reader_i8 = Bun.FFIObject.Reader.DOMCalls.i8; +pub const __DOMCall__reader_i16 = Bun.FFIObject.Reader.DOMCalls.i16; +pub const __DOMCall__reader_i32 = Bun.FFIObject.Reader.DOMCalls.i32; +pub const __DOMCall__reader_f32 = Bun.FFIObject.Reader.DOMCalls.f32; +pub const __DOMCall__reader_f64 = Bun.FFIObject.Reader.DOMCalls.f64; +pub const __DOMCall__reader_i64 = Bun.FFIObject.Reader.DOMCalls.i64; +pub const __DOMCall__reader_u64 = Bun.FFIObject.Reader.DOMCalls.u64; +pub const __DOMCall__reader_intptr = Bun.FFIObject.Reader.DOMCalls.intptr; pub const DOMCalls = &.{ - .{ .ptr = JSC.API.Bun.FFIObject.dom_call }, - JSC.API.Bun.FFIObject.Reader.DOMCalls, + .{ .ptr = Bun.FFIObject.dom_call }, + Bun.FFIObject.Reader.DOMCalls, }; extern "c" fn JSCInitialize(env: [*]const [*:0]u8, count: usize, cb: *const fn ([*]const u8, len: usize) callconv(.C) void) void; @@ -6318,5 +6344,7 @@ pub const ScriptExecutionStatus = enum(i32) { }; comptime { - _ = bun.String.BunString__getStringWidth; + // this file is gennerated, but cant be placed in the build/codegen folder + // because zig will complain about outside-of-module stuff + _ = @import("./GeneratedJS2Native.zig"); } diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 21aef8bb73b24..efd6b3e6bce16 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -582,10 +582,6 @@ ZIG_DECL void Zig__GlobalObject__resolve(ErrorableString* arg0, JSC__JSGlobalObj #endif -#pragma mark - Bun__Path - -CPP_DECL JSC__JSValue Bun__Path__create(JSC__JSGlobalObject* arg0, bool arg1); - #ifdef __cplusplus ZIG_DECL JSC__JSValue Bun__Path__basename(JSC__JSGlobalObject* arg0, bool arg1, JSC__JSValue* arg2, uint16_t arg3); @@ -602,23 +598,6 @@ ZIG_DECL JSC__JSValue Bun__Path__toNamespacedPath(JSC__JSGlobalObject* arg0, boo #endif -#ifdef __cplusplus - -ZIG_DECL JSC__JSValue ByteBlob__JSReadableStreamSource__load(JSC__JSGlobalObject* arg0); - -#endif - -#ifdef __cplusplus - -ZIG_DECL JSC__JSValue FileReader__JSReadableStreamSource__load(JSC__JSGlobalObject* arg0); - -#endif - -#ifdef __cplusplus - -ZIG_DECL JSC__JSValue ByteStream__JSReadableStreamSource__load(JSC__JSGlobalObject* arg0); - -#endif CPP_DECL JSC__JSValue ArrayBufferSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3); CPP_DECL JSC__JSValue ArrayBufferSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor); CPP_DECL void ArrayBufferSink__detachPtr(JSC__JSValue JSValue0); diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig index 7724cf485867f..810d0d076c4a2 100644 --- a/src/bun.js/bindings/headers.zig +++ b/src/bun.js/bindings/headers.zig @@ -361,7 +361,6 @@ pub extern fn Reader__intptr__put(arg0: *bindings.JSGlobalObject, JSValue1: JSC_ pub extern fn Zig__GlobalObject__create(arg0: ?*anyopaque, arg1: i32, arg2: bool, arg3: bool, arg4: ?*anyopaque) *bindings.JSGlobalObject; pub extern fn Zig__GlobalObject__getModuleRegistryMap(arg0: *bindings.JSGlobalObject) ?*anyopaque; pub extern fn Zig__GlobalObject__resetModuleRegistryMap(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) bool; -pub extern fn Bun__Path__create(arg0: *bindings.JSGlobalObject, arg1: bool) JSC__JSValue; pub extern fn ArrayBufferSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue; pub extern fn ArrayBufferSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue; pub extern fn ArrayBufferSink__detachPtr(JSValue0: JSC__JSValue) void; diff --git a/src/bun.js/bindings/helpers.h b/src/bun.js/bindings/helpers.h index cbdc6ba6b24eb..ec30f32e198e5 100644 --- a/src/bun.js/bindings/helpers.h +++ b/src/bun.js/bindings/helpers.h @@ -13,7 +13,9 @@ using JSC__JSGlobalObject = JSC::JSGlobalObject; using JSC__JSValue = JSC::EncodedJSValue; using JSC__CallFrame = JSC::CallFrame; + namespace Zig { +class GlobalObject; } #include "headers-handwritten.h" diff --git a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp index f7c380fc8f4f3..b1213f6a96ebd 100644 --- a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp +++ b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp @@ -1962,4 +1962,15 @@ void JSSQLStatement::visitOutputConstraints(JSCell* cell, Visitor& visitor) template void JSSQLStatement::visitOutputConstraints(JSCell*, AbstractSlotVisitor&); template void JSSQLStatement::visitOutputConstraints(JSCell*, SlotVisitor&); + +JSValue createJSSQLStatementConstructor(Zig::GlobalObject* globalObject) +{ + VM& vm = globalObject->vm(); + return JSSQLStatementConstructor::create( + vm, + globalObject, + JSSQLStatementConstructor::createStructure(vm, globalObject, globalObject->m_functionPrototype.get()) + ); } + +} // namespace WebCore diff --git a/src/bun.js/bindings/sqlite/JSSQLStatement.h b/src/bun.js/bindings/sqlite/JSSQLStatement.h index 68c04c96b468b..975ccbacd3471 100644 --- a/src/bun.js/bindings/sqlite/JSSQLStatement.h +++ b/src/bun.js/bindings/sqlite/JSSQLStatement.h @@ -84,4 +84,6 @@ class JSSQLStatementConstructor final : public JSC::JSFunction { static_assert(sizeof(JSSQLStatementConstructor) == sizeof(JSFunction), "Allocate JSSQLStatementConstructor in JSFunction IsoSubspace"); Structure* createJSSQLStatementStructure(JSGlobalObject* globalObject); -} \ No newline at end of file +JSValue createJSSQLStatementConstructor(Zig::GlobalObject* globalObject); + +} // namespace WebCore diff --git a/src/bun.js/bindings/webcore/JSAbortController.dep b/src/bun.js/bindings/webcore/JSAbortController.dep deleted file mode 100644 index c62eba8ee9cf7..0000000000000 --- a/src/bun.js/bindings/webcore/JSAbortController.dep +++ /dev/null @@ -1 +0,0 @@ -JSAbortController.h : diff --git a/src/bun.js/bindings/webcore/JSAbortSignal.dep b/src/bun.js/bindings/webcore/JSAbortSignal.dep deleted file mode 100644 index ae214b5049acf..0000000000000 --- a/src/bun.js/bindings/webcore/JSAbortSignal.dep +++ /dev/null @@ -1,2 +0,0 @@ -JSAbortSignal.h : EventTarget.idl -EventTarget.idl : diff --git a/src/bun.js/bindings/webcore/JSAddEventListenerOptions.dep b/src/bun.js/bindings/webcore/JSAddEventListenerOptions.dep deleted file mode 100644 index da4d55f610a34..0000000000000 --- a/src/bun.js/bindings/webcore/JSAddEventListenerOptions.dep +++ /dev/null @@ -1,2 +0,0 @@ -AddEventListenerOptions.h : EventListenerOptions.idl -EventListenerOptions.idl : diff --git a/src/bun.js/bindings/webcore/JSByteLengthQueuingStrategy.dep b/src/bun.js/bindings/webcore/JSByteLengthQueuingStrategy.dep deleted file mode 100644 index 0b697d758005f..0000000000000 --- a/src/bun.js/bindings/webcore/JSByteLengthQueuingStrategy.dep +++ /dev/null @@ -1 +0,0 @@ -JSByteLengthQueuingStrategy.h : diff --git a/src/bun.js/bindings/webcore/JSCloseEvent.dep b/src/bun.js/bindings/webcore/JSCloseEvent.dep deleted file mode 100644 index 25edceaabf08a..0000000000000 --- a/src/bun.js/bindings/webcore/JSCloseEvent.dep +++ /dev/null @@ -1,3 +0,0 @@ -JSCloseEvent.h : Event.idl EventInit.idl -Event.idl : -EventInit.idl : diff --git a/src/bun.js/bindings/webcore/JSCountQueuingStrategy.dep b/src/bun.js/bindings/webcore/JSCountQueuingStrategy.dep deleted file mode 100644 index 413363a34be42..0000000000000 --- a/src/bun.js/bindings/webcore/JSCountQueuingStrategy.dep +++ /dev/null @@ -1 +0,0 @@ -JSCountQueuingStrategy.h : diff --git a/src/bun.js/bindings/webcore/JSCustomEvent.dep b/src/bun.js/bindings/webcore/JSCustomEvent.dep deleted file mode 100644 index 443684f8c0efd..0000000000000 --- a/src/bun.js/bindings/webcore/JSCustomEvent.dep +++ /dev/null @@ -1,3 +0,0 @@ -JSCustomEvent.h : Event.idl EventInit.idl -Event.idl : -EventInit.idl : diff --git a/src/bun.js/bindings/webcore/JSDOMFormData.dep b/src/bun.js/bindings/webcore/JSDOMFormData.dep deleted file mode 100644 index 5757aa3aec6d8..0000000000000 --- a/src/bun.js/bindings/webcore/JSDOMFormData.dep +++ /dev/null @@ -1 +0,0 @@ -JSDOMFormData.h : diff --git a/src/bun.js/bindings/webcore/JSErrorEvent.dep b/src/bun.js/bindings/webcore/JSErrorEvent.dep deleted file mode 100644 index bc136344b7305..0000000000000 --- a/src/bun.js/bindings/webcore/JSErrorEvent.dep +++ /dev/null @@ -1,3 +0,0 @@ -JSErrorEvent.h : Event.idl EventInit.idl -Event.idl : -EventInit.idl : diff --git a/src/bun.js/bindings/webcore/JSEvent.dep b/src/bun.js/bindings/webcore/JSEvent.dep deleted file mode 100644 index 573e0bba51fa5..0000000000000 --- a/src/bun.js/bindings/webcore/JSEvent.dep +++ /dev/null @@ -1 +0,0 @@ -JSEvent.h : diff --git a/src/bun.js/bindings/webcore/JSEventInit.dep b/src/bun.js/bindings/webcore/JSEventInit.dep deleted file mode 100644 index e1d78f2d8bd61..0000000000000 --- a/src/bun.js/bindings/webcore/JSEventInit.dep +++ /dev/null @@ -1 +0,0 @@ -EventInit.h : diff --git a/src/bun.js/bindings/webcore/JSEventListenerOptions.dep b/src/bun.js/bindings/webcore/JSEventListenerOptions.dep deleted file mode 100644 index 6a7075b900b7d..0000000000000 --- a/src/bun.js/bindings/webcore/JSEventListenerOptions.dep +++ /dev/null @@ -1 +0,0 @@ -EventListenerOptions.h : diff --git a/src/bun.js/bindings/webcore/JSEventModifierInit.dep b/src/bun.js/bindings/webcore/JSEventModifierInit.dep deleted file mode 100644 index f6b60a42fc0ae..0000000000000 --- a/src/bun.js/bindings/webcore/JSEventModifierInit.dep +++ /dev/null @@ -1,3 +0,0 @@ -EventModifierInit.h : UIEventInit.idl EventInit.idl -UIEventInit.idl : -EventInit.idl : diff --git a/src/bun.js/bindings/webcore/JSFetchHeaders.dep b/src/bun.js/bindings/webcore/JSFetchHeaders.dep deleted file mode 100644 index e553164bd3d3d..0000000000000 --- a/src/bun.js/bindings/webcore/JSFetchHeaders.dep +++ /dev/null @@ -1 +0,0 @@ -JSFetchHeaders.h : diff --git a/src/bun.js/bindings/webcore/JSMessageEvent.dep b/src/bun.js/bindings/webcore/JSMessageEvent.dep deleted file mode 100644 index da20f76fa3db7..0000000000000 --- a/src/bun.js/bindings/webcore/JSMessageEvent.dep +++ /dev/null @@ -1,3 +0,0 @@ -JSMessageEvent.h : Event.idl EventInit.idl -Event.idl : -EventInit.idl : diff --git a/src/bun.js/bindings/webcore/JSReadableByteStreamController.dep b/src/bun.js/bindings/webcore/JSReadableByteStreamController.dep deleted file mode 100644 index 605eb3e4a88c3..0000000000000 --- a/src/bun.js/bindings/webcore/JSReadableByteStreamController.dep +++ /dev/null @@ -1 +0,0 @@ -JSReadableByteStreamController.h : diff --git a/src/bun.js/bindings/webcore/JSReadableStream.dep b/src/bun.js/bindings/webcore/JSReadableStream.dep deleted file mode 100644 index f4ea48d439d99..0000000000000 --- a/src/bun.js/bindings/webcore/JSReadableStream.dep +++ /dev/null @@ -1 +0,0 @@ -JSReadableStream.h : diff --git a/src/bun.js/bindings/webcore/JSReadableStreamBYOBReader.dep b/src/bun.js/bindings/webcore/JSReadableStreamBYOBReader.dep deleted file mode 100644 index 1acef694c4977..0000000000000 --- a/src/bun.js/bindings/webcore/JSReadableStreamBYOBReader.dep +++ /dev/null @@ -1 +0,0 @@ -JSReadableStreamBYOBReader.h : diff --git a/src/bun.js/bindings/webcore/JSReadableStreamBYOBRequest.dep b/src/bun.js/bindings/webcore/JSReadableStreamBYOBRequest.dep deleted file mode 100644 index 12e0f602d390b..0000000000000 --- a/src/bun.js/bindings/webcore/JSReadableStreamBYOBRequest.dep +++ /dev/null @@ -1 +0,0 @@ -JSReadableStreamBYOBRequest.h : diff --git a/src/bun.js/bindings/webcore/JSReadableStreamDefaultController.dep b/src/bun.js/bindings/webcore/JSReadableStreamDefaultController.dep deleted file mode 100644 index 3e3e1cd158cf2..0000000000000 --- a/src/bun.js/bindings/webcore/JSReadableStreamDefaultController.dep +++ /dev/null @@ -1 +0,0 @@ -JSReadableStreamDefaultController.h : diff --git a/src/bun.js/bindings/webcore/JSReadableStreamDefaultReader.dep b/src/bun.js/bindings/webcore/JSReadableStreamDefaultReader.dep deleted file mode 100644 index 466ee5c615323..0000000000000 --- a/src/bun.js/bindings/webcore/JSReadableStreamDefaultReader.dep +++ /dev/null @@ -1 +0,0 @@ -JSReadableStreamDefaultReader.h : diff --git a/src/bun.js/bindings/webcore/JSReadableStreamSink.dep b/src/bun.js/bindings/webcore/JSReadableStreamSink.dep deleted file mode 100644 index 9c5594686d279..0000000000000 --- a/src/bun.js/bindings/webcore/JSReadableStreamSink.dep +++ /dev/null @@ -1 +0,0 @@ -JSReadableStreamSink.h : diff --git a/src/bun.js/bindings/webcore/JSReadableStreamSource.dep b/src/bun.js/bindings/webcore/JSReadableStreamSource.dep deleted file mode 100644 index f459688e591c5..0000000000000 --- a/src/bun.js/bindings/webcore/JSReadableStreamSource.dep +++ /dev/null @@ -1 +0,0 @@ -JSReadableStreamSource.h : diff --git a/src/bun.js/bindings/webcore/JSTextEncoder.dep b/src/bun.js/bindings/webcore/JSTextEncoder.dep deleted file mode 100644 index bdb1dfb7c51e5..0000000000000 --- a/src/bun.js/bindings/webcore/JSTextEncoder.dep +++ /dev/null @@ -1 +0,0 @@ -JSTextEncoder.h : diff --git a/src/bun.js/bindings/webcore/JSTransformStream.dep b/src/bun.js/bindings/webcore/JSTransformStream.dep deleted file mode 100644 index 30374925b197b..0000000000000 --- a/src/bun.js/bindings/webcore/JSTransformStream.dep +++ /dev/null @@ -1 +0,0 @@ -JSTransformStream.h : diff --git a/src/bun.js/bindings/webcore/JSTransformStreamDefaultController.dep b/src/bun.js/bindings/webcore/JSTransformStreamDefaultController.dep deleted file mode 100644 index 0d59bed06a5dd..0000000000000 --- a/src/bun.js/bindings/webcore/JSTransformStreamDefaultController.dep +++ /dev/null @@ -1 +0,0 @@ -JSTransformStreamDefaultController.h : diff --git a/src/bun.js/bindings/webcore/JSURLSearchParams.dep b/src/bun.js/bindings/webcore/JSURLSearchParams.dep deleted file mode 100644 index 73a94dc7bba02..0000000000000 --- a/src/bun.js/bindings/webcore/JSURLSearchParams.dep +++ /dev/null @@ -1 +0,0 @@ -JSURLSearchParams.h : diff --git a/src/bun.js/bindings/webcore/JSWebSocket.dep b/src/bun.js/bindings/webcore/JSWebSocket.dep deleted file mode 100644 index 69bc1775bb6e1..0000000000000 --- a/src/bun.js/bindings/webcore/JSWebSocket.dep +++ /dev/null @@ -1,2 +0,0 @@ -JSWebSocket.h : EventTarget.idl -EventTarget.idl : diff --git a/src/bun.js/bindings/webcore/JSWritableStream.dep b/src/bun.js/bindings/webcore/JSWritableStream.dep deleted file mode 100644 index d936b45818bf9..0000000000000 --- a/src/bun.js/bindings/webcore/JSWritableStream.dep +++ /dev/null @@ -1 +0,0 @@ -JSWritableStream.h : diff --git a/src/bun.js/bindings/webcore/JSWritableStreamDefaultController.dep b/src/bun.js/bindings/webcore/JSWritableStreamDefaultController.dep deleted file mode 100644 index a1ac70d4768fc..0000000000000 --- a/src/bun.js/bindings/webcore/JSWritableStreamDefaultController.dep +++ /dev/null @@ -1 +0,0 @@ -JSWritableStreamDefaultController.h : diff --git a/src/bun.js/bindings/webcore/JSWritableStreamDefaultWriter.dep b/src/bun.js/bindings/webcore/JSWritableStreamDefaultWriter.dep deleted file mode 100644 index 3016ea66209d6..0000000000000 --- a/src/bun.js/bindings/webcore/JSWritableStreamDefaultWriter.dep +++ /dev/null @@ -1 +0,0 @@ -JSWritableStreamDefaultWriter.h : diff --git a/src/bun.js/bindings/webcore/JSWritableStreamSink.dep b/src/bun.js/bindings/webcore/JSWritableStreamSink.dep deleted file mode 100644 index c1891fdd81ed1..0000000000000 --- a/src/bun.js/bindings/webcore/JSWritableStreamSink.dep +++ /dev/null @@ -1 +0,0 @@ -JSWritableStreamSink.h : diff --git a/src/bun.js/bindings/webcore/Worker.cpp b/src/bun.js/bindings/webcore/Worker.cpp index 59245e9abe62e..16eb1e109f8ff 100644 --- a/src/bun.js/bindings/webcore/Worker.cpp +++ b/src/bun.js/bindings/webcore/Worker.cpp @@ -62,6 +62,9 @@ #include #include "BunWorkerGlobalScope.h" #include "CloseEvent.h" +#include "JSMessagePort.h" +#include "JSBroadcastChannel.h" + namespace WebCore { WTF_MAKE_ISO_ALLOCATED_IMPL(Worker); @@ -172,8 +175,7 @@ ExceptionOr> Worker::create(ScriptExecutionContext& context, const S argv ? reinterpret_cast(argv->data()) : nullptr, argv ? static_cast(argv->size()) : 0, execArgv ? reinterpret_cast(execArgv->data()) : nullptr, - execArgv ? static_cast(execArgv->size()) : 0 - ); + execArgv ? static_cast(execArgv->size()) : 0); if (!impl) { return Exception { TypeError, errorMessage.toWTFString(BunString::ZeroCopy) }; @@ -425,4 +427,65 @@ extern "C" void WebWorker__dispatchError(Zig::GlobalObject* globalObject, Worker worker->dispatchError(message.toWTFString(BunString::ZeroCopy)); } +extern "C" WebCore::Worker* WebWorker__getParentWorker(void*); + +JSC_DEFINE_HOST_FUNCTION(jsReceiveMessageOnPort, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (callFrame->argumentCount() < 1) { + throwTypeError(lexicalGlobalObject, scope, "receiveMessageOnPort needs 1 argument"_s); + return JSC::JSValue::encode(JSC::JSValue {}); + } + + auto port = callFrame->argument(0); + + if (!port.isObject()) { + throwTypeError(lexicalGlobalObject, scope, "the \"port\" argument must be a MessagePort instance"_s); + return JSC::JSValue::encode(jsUndefined()); + } + + if (auto* messagePort = jsDynamicCast(port)) { + return JSC::JSValue::encode(messagePort->wrapped().tryTakeMessage(lexicalGlobalObject)); + } else if (auto* broadcastChannel = jsDynamicCast(port)) { + // TODO: support broadcast channels + return JSC::JSValue::encode(jsUndefined()); + } + + throwTypeError(lexicalGlobalObject, scope, "the \"port\" argument must be a MessagePort instance"_s); + return JSC::JSValue::encode(jsUndefined()); +} + +JSValue createNodeWorkerThreadsBinding(Zig::GlobalObject* globalObject) +{ + VM& vm = globalObject->vm(); + + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + JSValue workerData = jsUndefined(); + JSValue threadId = jsNumber(0); + + if (auto* worker = WebWorker__getParentWorker(globalObject->bunVM())) { + auto& options = worker->options(); + if (worker && options.bun.data) { + auto ports = MessagePort::entanglePorts(*ScriptExecutionContext::getScriptExecutionContext(worker->clientIdentifier()), WTFMove(options.bun.dataMessagePorts)); + RefPtr serialized = WTFMove(options.bun.data); + JSValue deserialized = serialized->deserialize(*globalObject, globalObject, WTFMove(ports)); + RETURN_IF_EXCEPTION(scope, {}); + workerData = deserialized; + } + + // Main thread starts at 1 + threadId = jsNumber(worker->clientIdentifier() - 1); + } + + JSArray* array = constructEmptyArray(globalObject, nullptr, 3); + + array->putByIndexInline(globalObject, (unsigned)0, workerData, false); + array->putByIndexInline(globalObject, (unsigned)1, threadId, false); + array->putByIndexInline(globalObject, (unsigned)2, JSFunction::create(vm, globalObject, 1, "receiveMessageOnPort"_s, jsReceiveMessageOnPort, ImplementationVisibility::Public, NoIntrinsic), false); + + return array; +} + } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/Worker.h b/src/bun.js/bindings/webcore/Worker.h index 997baac6465fa..15d1b7f082685 100644 --- a/src/bun.js/bindings/webcore/Worker.h +++ b/src/bun.js/bindings/webcore/Worker.h @@ -140,4 +140,6 @@ class Worker final : public RefCounted, public EventTargetWithInlineData void* impl_ { nullptr }; }; +JSValue createNodeWorkerThreadsBinding(Zig::GlobalObject* globalObject); + } // namespace WebCore diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 213a2c3f32e1e..c142d172ba64e 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -785,6 +785,11 @@ pub const VirtualMachine = struct { bun.spawn.WaiterThread.setShouldUseWaiterThread(); } + // Only allowed for testing + if (map.get("BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING") != null) { + ModuleLoader.is_allowed_to_use_internal_testing_apis = true; + } + if (strings.eqlComptime(gc_level, "1")) { this.aggressive_garbage_collection = .mild; has_bun_garbage_collector_flag_enabled = true; diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 0449df9a0eb97..92996b495b962 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -143,7 +143,7 @@ inline fn jsSyntheticModule(comptime name: ResolvedSource.Tag, specifier: String .allocator = null, .source_code = bun.String.empty, .specifier = specifier, - .source_url = bun.String.init(@tagName(name)), + .source_url = bun.String.static(@tagName(name)), .hash = 0, .tag = name, .source_code_needs_deref = false, @@ -707,6 +707,8 @@ pub const ModuleLoader = struct { transpile_source_code_arena: ?*bun.ArenaAllocator = null, eval_source: ?*logger.Source = null, + pub var is_allowed_to_use_internal_testing_apis = false; + /// This must be called after calling transpileSourceCode pub fn resetArena(this: *ModuleLoader, jsc_vm: *VirtualMachine) void { std.debug.assert(&jsc_vm.module_loader == this); @@ -2348,6 +2350,22 @@ pub const ModuleLoader = struct { .@"bun:jsc" => return jsSyntheticModule(.@"bun:jsc", specifier), .@"bun:test" => return jsSyntheticModule(.@"bun:test", specifier), + .@"bun:internal-for-testing" => { + if (!Environment.isDebug) { + if (!is_allowed_to_use_internal_testing_apis) + return null; + const is_outside_our_ci = brk: { + const repo = jsc_vm.bundler.env.get("GITHUB_REPOSITORY") orelse break :brk true; + break :brk !strings.endsWithComptime(repo, "/bun"); + }; + if (is_outside_our_ci) { + return null; + } + } + + return jsSyntheticModule(.InternalForTesting, specifier); + }, + // These are defined in src/js/* .@"bun:ffi" => return jsSyntheticModule(.@"bun:ffi", specifier), .@"bun:sqlite" => return jsSyntheticModule(.@"bun:sqlite", specifier), @@ -2549,6 +2567,7 @@ pub const HardcodedModule = enum { @"bun:main", @"bun:test", // usually replaced by the transpiler but `await import("bun:" + "test")` has to work @"bun:sqlite", + @"bun:internal-for-testing", @"detect-libc", @"node:assert", @"node:assert/strict", @@ -2623,6 +2642,7 @@ pub const HardcodedModule = enum { .{ "bun:main", HardcodedModule.@"bun:main" }, .{ "bun:test", HardcodedModule.@"bun:test" }, .{ "bun:sqlite", HardcodedModule.@"bun:sqlite" }, + .{ "bun:internal-for-testing", HardcodedModule.@"bun:internal-for-testing" }, .{ "detect-libc", HardcodedModule.@"detect-libc" }, .{ "node-fetch", HardcodedModule.@"node-fetch" }, .{ "isomorphic-fetch", HardcodedModule.@"isomorphic-fetch" }, @@ -2836,6 +2856,7 @@ pub const HardcodedModule = enum { .{ "bun:jsc", .{ .path = "bun:jsc" } }, .{ "bun:sqlite", .{ .path = "bun:sqlite" } }, .{ "bun:wrap", .{ .path = "bun:wrap" } }, + .{ "bun:internal-for-testing", .{ .path = "bun:internal-for-testing" } }, .{ "ffi", .{ .path = "bun:ffi" } }, // Thirdparty packages we override diff --git a/src/bun.js/node/node_fs_binding.zig b/src/bun.js/node/node_fs_binding.zig index 2327d8fb76782..5b6b399219a88 100644 --- a/src/bun.js/node/node_fs_binding.zig +++ b/src/bun.js/node/node_fs_binding.zig @@ -1,5 +1,5 @@ const bun = @import("root").bun; -const JSC = @import("root").bun.JSC; +const JSC = bun.JSC; const std = @import("std"); const Flavor = JSC.Node.Flavor; const ArgumentsSlice = JSC.Node.ArgumentsSlice; @@ -258,3 +258,14 @@ pub const NodeJSFS = struct { pub const opendir = notimpl; pub const opendirSync = notimpl; }; + +pub fn createBinding(globalObject: *JSC.JSGlobalObject) JSC.JSValue { + var module = globalObject.allocator().create(NodeJSFS) catch bun.outOfMemory(); + module.* = .{}; + + const vm = globalObject.bunVM(); + if (vm.standalone_module_graph != null) + module.node_fs.vm = vm; + + return module.toJS(globalObject); +} diff --git a/src/bun.js/node/node_os.zig b/src/bun.js/node/node_os.zig index 0da6b9a9694df..95c5992c4d330 100644 --- a/src/bun.js/node/node_os.zig +++ b/src/bun.js/node/node_os.zig @@ -11,17 +11,11 @@ const is_bindgen: bool = std.meta.globalOption("bindgen", bool) orelse false; const heap_allocator = bun.default_allocator; const libuv = bun.windows.libuv; - -pub const Os = struct { - pub const name = "Bun__Os"; - pub const code = @embedFile("../os.exports.js"); - +pub const OS = struct { pub fn create(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { - const module = JSC.JSValue.createEmptyObject(globalObject, 22); + const module = JSC.JSValue.createEmptyObject(globalObject, 16); - module.put(globalObject, JSC.ZigString.static("arch"), JSC.NewFunction(globalObject, JSC.ZigString.static("arch"), 0, arch, true)); module.put(globalObject, JSC.ZigString.static("cpus"), JSC.NewFunction(globalObject, JSC.ZigString.static("cpus"), 0, cpus, true)); - module.put(globalObject, JSC.ZigString.static("endianness"), JSC.NewFunction(globalObject, JSC.ZigString.static("endianness"), 0, endianness, true)); module.put(globalObject, JSC.ZigString.static("freemem"), JSC.NewFunction(globalObject, JSC.ZigString.static("freemem"), 0, freemem, true)); module.put(globalObject, JSC.ZigString.static("getPriority"), JSC.NewFunction(globalObject, JSC.ZigString.static("getPriority"), 1, getPriority, true)); module.put(globalObject, JSC.ZigString.static("homedir"), JSC.NewFunction(globalObject, JSC.ZigString.static("homedir"), 0, homedir, true)); @@ -29,33 +23,18 @@ pub const Os = struct { module.put(globalObject, JSC.ZigString.static("loadavg"), JSC.NewFunction(globalObject, JSC.ZigString.static("loadavg"), 0, loadavg, true)); module.put(globalObject, JSC.ZigString.static("machine"), JSC.NewFunction(globalObject, JSC.ZigString.static("machine"), 0, machine, true)); module.put(globalObject, JSC.ZigString.static("networkInterfaces"), JSC.NewFunction(globalObject, JSC.ZigString.static("networkInterfaces"), 0, networkInterfaces, true)); - module.put(globalObject, JSC.ZigString.static("platform"), JSC.NewFunction(globalObject, JSC.ZigString.static("platform"), 0, platform, true)); module.put(globalObject, JSC.ZigString.static("release"), JSC.NewFunction(globalObject, JSC.ZigString.static("release"), 0, release, true)); module.put(globalObject, JSC.ZigString.static("setPriority"), JSC.NewFunction(globalObject, JSC.ZigString.static("setPriority"), 2, setPriority, true)); module.put(globalObject, JSC.ZigString.static("totalmem"), JSC.NewFunction(globalObject, JSC.ZigString.static("totalmem"), 0, totalmem, true)); - module.put(globalObject, JSC.ZigString.static("type"), JSC.NewFunction(globalObject, JSC.ZigString.static("type"), 0, Os.type, true)); + module.put(globalObject, JSC.ZigString.static("type"), JSC.NewFunction(globalObject, JSC.ZigString.static("type"), 0, OS.type, true)); module.put(globalObject, JSC.ZigString.static("uptime"), JSC.NewFunction(globalObject, JSC.ZigString.static("uptime"), 0, uptime, true)); module.put(globalObject, JSC.ZigString.static("userInfo"), JSC.NewFunction(globalObject, JSC.ZigString.static("userInfo"), 0, userInfo, true)); module.put(globalObject, JSC.ZigString.static("version"), JSC.NewFunction(globalObject, JSC.ZigString.static("version"), 0, version, true)); module.put(globalObject, JSC.ZigString.static("machine"), JSC.NewFunction(globalObject, JSC.ZigString.static("machine"), 0, machine, true)); - module.put(globalObject, JSC.ZigString.static("devNull"), JSC.ZigString.init(devNull).withEncoding().toValue(globalObject)); - module.put(globalObject, JSC.ZigString.static("EOL"), JSC.ZigString.init(EOL).withEncoding().toValue(globalObject)); - - // module.put(globalObject, JSC.ZigString.static("constants"), constants.create(globalObject)); - return module; } - pub const EOL: []const u8 = if (Environment.isWindows) "\r\n" else "\n"; - pub const devNull: []const u8 = if (Environment.isWindows) "\\\\.\\nul" else "/dev/null"; - - pub fn arch(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSC.JSValue { - JSC.markBinding(@src()); - - return JSC.ZigString.init(Global.arch_name).withEncoding().toValue(globalThis); - } - const CPUTimes = struct { user: u64 = 0, nice: u64 = 0, @@ -406,7 +385,7 @@ pub const Os = struct { JSC.markBinding(@src()); const result = C.getSystemLoadavg(); - return JSC.JSArray.from(globalThis, &.{ + return JSC.JSArray.create(globalThis, &.{ JSC.JSValue.jsNumber(result[0]), JSC.JSValue.jsNumber(result[1]), JSC.JSValue.jsNumber(result[2]), diff --git a/src/bun.zig b/src/bun.zig index 5338b5e427b80..71743f4d645f6 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -2515,7 +2515,7 @@ pub const HeapBreakdown = if (is_heap_breakdown_enabled) @import("./heap_breakdo /// When used, you must call `bun.destroy` to free the memory. /// default_allocator.destroy should not be used. /// -/// On macOS, you can use `Bun.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_mimalloc_dump()` +/// On macOS, you can use `Bun.unsafe.mimallocDump()` /// to dump the heap. pub inline fn new(comptime T: type, t: T) *T { if (comptime is_heap_breakdown_enabled) { @@ -2531,7 +2531,7 @@ pub inline fn new(comptime T: type, t: T) *T { /// Free a globally-allocated a value /// -/// On macOS, you can use `Bun.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_mimalloc_dump()` +/// On macOS, you can use `Bun.unsafe.mimallocDump()` /// to dump the heap. pub inline fn destroyWithAlloc(allocator: std.mem.Allocator, t: anytype) void { if (comptime is_heap_breakdown_enabled) { @@ -2667,7 +2667,7 @@ pub fn NewRefCounted(comptime T: type, comptime deinit_fn: ?fn (self: *T) void) /// /// Must have used `new` to allocate the value. /// -/// On macOS, you can use `Bun.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_mimalloc_dump()` +/// On macOS, you can use `Bun.unsafe.mimallocDump()` /// to dump the heap. pub inline fn destroy(t: anytype) void { if (comptime is_heap_breakdown_enabled) { diff --git a/src/codegen/builtin-parser.ts b/src/codegen/builtin-parser.ts index 4e35f13ddcb11..eab108b8fae0f 100644 --- a/src/codegen/builtin-parser.ts +++ b/src/codegen/builtin-parser.ts @@ -1,4 +1,21 @@ -import { applyReplacements } from "./replacements"; +import { applyReplacements, function_replacements } from "./replacements"; + +function escapeRegex(str: string) { + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +function createStopRegex(allow_comma: boolean) { + return new RegExp( + "((?:[(,=;:{]|return|\\=\\>)\\s*)\\/[^\\/\\*]|\\/\\*|\\/\\/|['\"}`\\)" + + (allow_comma ? "," : "") + + "]|(? escapeRegex(x) + "\\(").join("|") + + ")", + ); +} + +const stop_regex_comma = createStopRegex(true); +const stop_regex_no_comma = createStopRegex(false); /** * Slices a string until it hits a }, but keeping in mind JS comments, @@ -18,11 +35,7 @@ export function sliceSourceCode( let i = 0; let result = ""; while (contents.length) { - const match = contents.match( - endOnComma && bracketCount <= 1 - ? /((?:[(,=;:{]|return|\=\>)\s*)\/[^\/\*]|\/\*|\/\/|['"}`\),]|(?)\s*)\/[^\/\*]|\/\*|\/\/|['"}`\)]|(? json value. Otherwise, I'd use a real JS parser/ast +// library, instead of RegExp hacks. +// +// For explanation on this, please nag @paperdave to write documentation on how everything works. +import { readdirSync, rmSync } from "fs"; import path from "path"; import { sliceSourceCode } from "./builtin-parser"; import { applyGlobalReplacements, define } from "./replacements"; -import { cap, fmtCPPString, low, writeIfNotChanged } from "./helpers"; -import { createInternalModuleRegistry } from "./internal-module-registry-scanner"; +import { cap, fmtCPPCharArray, low, writeIfNotChanged } from "./helpers"; import { createAssertClientJS, createLogClientJS } from "./client-js"; - -console.log("Bundling Bun builtin functions..."); +import { getJS2NativeDTS } from "./js2native-generator"; const PARALLEL = false; const KEEP_TMP = true; -const debug = process.argv[2] === "--debug=ON"; -const CMAKE_BUILD_ROOT = process.argv[3]; +if (import.meta.main) { + throw new Error("This script is not meant to be run directly"); +} +const CMAKE_BUILD_ROOT = globalThis.CMAKE_BUILD_ROOT; if (!CMAKE_BUILD_ROOT) { - console.error("Usage: bun bundle-functions.ts "); - process.exit(1); + throw new Error("CMAKE_BUILD_ROOT is not defined"); } const SRC_DIR = path.join(import.meta.dir, "../js/builtins"); const CODEGEN_DIR = path.join(CMAKE_BUILD_ROOT, "./codegen"); const TMP_DIR = path.join(CMAKE_BUILD_ROOT, "./tmp_functions"); -const { - // - requireTransformer, -} = createInternalModuleRegistry(path.join(import.meta.dir, "../js")); - -mkdirSync(TMP_DIR, { recursive: true }); - interface ParsedBuiltin { name: string; params: string[]; @@ -37,6 +39,7 @@ interface ParsedBuiltin { source: string; async: boolean; } + interface BundledBuiltin { name: string; directives: Record; @@ -135,7 +138,7 @@ async function processFileSplit(filename: string): Promise<{ functions: BundledB } const { result, rest } = sliceSourceCode(contents.slice(declaration[0].length - 1), true, x => - requireTransformer(x, SRC_DIR + "/" + basename), + globalThis.requireTransformer(x, SRC_DIR + "/" + basename), ); functions.push({ @@ -209,6 +212,11 @@ $$capture_start$$(${fn.async ? "async " : ""}${ .replace(/__intrinsic__/g, "@") .replace(/__no_intrinsic__/g, "") + "\n"; + const errors = [...finalReplacement.matchAll(/@bundleError\((.*)\)/g)]; + if (errors.length) { + throw new Error(`Errors in ${basename}.ts:\n${errors.map(x => x[1]).join("\n")}`); + } + bundledFunctions.push({ name: fn.name, directives: fn.directives, @@ -234,12 +242,8 @@ $$capture_start$$(${fn.async ? "async " : ""}${ }; } -const filesToProcess = readdirSync(SRC_DIR) - .filter(x => x.endsWith(".ts") && !x.endsWith(".d.ts")) - .sort(); - const files: Array<{ basename: string; functions: BundledBuiltin[]; internal: boolean }> = []; -async function processFile(x: string) { +async function processFunctionFile(x: string) { const basename = path.basename(x, ".ts"); try { files.push({ @@ -253,388 +257,398 @@ async function processFile(x: string) { } } -// Bun seems to crash if this is parallelized, :( -if (PARALLEL) { - await Promise.all(filesToProcess.map(processFile)); -} else { - for (const x of filesToProcess) { - await processFile(x); - } +interface BundleBuiltinFunctionsArgs { + requireTransformer: (x: string, filename: string) => string; } -// 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 - -namespace WebCore { +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); + } + } -`; + // 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 + + namespace WebCore { + + `; -for (const { basename, functions } of files) { - bundledCPP += `/* ${basename}.ts */\n`; - const lowerBasename = low(basename); - for (const fn of functions) { - const [code, count] = fmtCPPString(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::${ + for (const { basename, functions } of files) { + bundledCPP += `/* ${basename}.ts */\n`; + 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; -`; - } - 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); \\ -} -WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR) -#undef DEFINE_BUILTIN_GENERATOR - -`; -} - -bundledCPP += ` - -JSBuiltinInternalFunctions::JSBuiltinInternalFunctions(JSC::VM& vm) - : m_vm(vm) -`; - -for (const { basename, internal } of files) { - if (internal) { - bundledCPP += ` , m_${low(basename)}(vm)\n`; + 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; + `; + } + 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); \\ + } + WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(DEFINE_BUILTIN_GENERATOR) + #undef DEFINE_BUILTIN_GENERATOR + + `; } -} - -bundledCPP += ` -{ - UNUSED_PARAM(vm); -} - -template -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); -`; + bundledCPP += ` + + JSBuiltinInternalFunctions::JSBuiltinInternalFunctions(JSC::VM& vm) + : m_vm(vm) + `; -for (const { basename, internal } of files) { - if (internal) { - bundledCPP += ` m_${low(basename)}.init(globalObject);\n`; + for (const { basename, internal } of files) { + if (internal) { + bundledCPP += ` , m_${low(basename)}(vm)\n`; + } } -} -bundledCPP += ` - JSVMClientData& clientData = *static_cast(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 += ` + { + UNUSED_PARAM(vm); + } + + template + void JSBuiltinInternalFunctions::visit(Visitor& visitor) + { + `; + for (const { basename, internal } of files) { + if (internal) bundledCPP += ` m_${low(basename)}.visit(visitor);\n`; } -} -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 -#include -#include -#include -#include -#include - -namespace JSC { -class FunctionExecutable; -} - -namespace WebCore { -`; -for (const { basename, functions, internal } of files) { - bundledHeader += `/* ${basename}.ts */ -`; - const lowerBasename = low(basename); + 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 fn of functions) { - 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; - -`; - } - 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`; + for (const { basename, internal } of files) { + if (internal) { + bundledCPP += ` m_${low(basename)}.init(globalObject);\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 + bundledCPP += ` + JSVMClientData& clientData = *static_cast(m_vm.clientData); + Zig::GlobalObject::GlobalPropertyInfo staticGlobals[] = { + `; -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(s_##name, length), { }, JSC::SourceTaintedOrigin::Untainted)) - WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(INITIALIZE_BUILTIN_SOURCE_MEMBERS) -#undef INITIALIZE_BUILTIN_SOURCE_MEMBERS - { + 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 + `; } + } -#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 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::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 void visit(Visitor&); - -public: - JSC::VM& m_vm; - -#define DECLARE_BUILTIN_SOURCE_MEMBERS(functionName) \\ - JSC::WriteBarrier 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, codeName##Generator(m_vm), &globalObject)); - WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(EXPORT_FUNCTION) -#undef EXPORT_FUNCTION -} - -template -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 -} + bundledCPP += ` + }; + globalObject.addStaticGlobals(staticGlobals, std::size(staticGlobals)); + UNUSED_PARAM(clientData); + } + + } // namespace WebCore + `; -template void ${basename}BuiltinFunctions::visit(JSC::AbstractSlotVisitor&); -template void ${basename}BuiltinFunctions::visit(JSC::SlotVisitor&); + // 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 + #include + #include + #include + #include + #include + + namespace JSC { + class FunctionExecutable; + } + + namespace WebCore { `; - } -} -bundledHeader += `class JSBuiltinFunctions { -public: - explicit JSBuiltinFunctions(JSC::VM& vm) - : m_vm(vm) -`; + for (const { basename, functions, internal } of files) { + bundledHeader += `/* ${basename}.ts */ + `; + const lowerBasename = low(basename); -for (const { basename } of files) { - bundledHeader += ` , m_${low(basename)}Builtins(m_vm)\n`; -} + for (const fn of functions) { + 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; + + `; + } + 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) + : 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(s_##name, length), { }, JSC::SourceTaintedOrigin::Untainted)) + WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(INITIALIZE_BUILTIN_SOURCE_MEMBERS) + #undef INITIALIZE_BUILTIN_SOURCE_MEMBERS + { + } + + #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 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::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 + } + `; -bundledHeader += ` - { -`; + if (internal) { + bundledHeader += `class ${basename}BuiltinFunctions { + public: + explicit ${basename}BuiltinFunctions(JSC::VM& vm) : m_vm(vm) { } + + void init(JSC::JSGlobalObject&); + template void visit(Visitor&); + + public: + JSC::VM& m_vm; + + #define DECLARE_BUILTIN_SOURCE_MEMBERS(functionName) \\ + JSC::WriteBarrier 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, codeName##Generator(m_vm), &globalObject)); + WEBCORE_FOREACH_${basename.toUpperCase()}_BUILTIN_CODE(EXPORT_FUNCTION) + #undef EXPORT_FUNCTION + } + + template + 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) + : m_vm(vm) + `; -for (const { basename, internal } of files) { - if (internal) { - bundledHeader += ` m_${low(basename)}Builtins.exportNames();\n`; + for (const { basename } of files) { + bundledHeader += ` , m_${low(basename)}Builtins(m_vm)\n`; } -} -bundledHeader += ` } -`; + bundledHeader += ` + { + `; -for (const { basename } of files) { - bundledHeader += ` ${basename}BuiltinsWrapper& ${low(basename)}Builtins() { return m_${low( - basename, - )}Builtins; }\n`; -} + for (const { basename, internal } of files) { + if (internal) { + bundledHeader += ` m_${low(basename)}Builtins.exportNames();\n`; + } + } -bundledHeader += ` -private: - JSC::VM& m_vm; -`; + bundledHeader += ` } + `; -for (const { basename } of files) { - bundledHeader += ` ${basename}BuiltinsWrapper m_${low(basename)}Builtins;\n`; -} + for (const { basename } of files) { + bundledHeader += ` ${basename}BuiltinsWrapper& ${low(basename)}Builtins() { return m_${low( + basename, + )}Builtins; }\n`; + } -bundledHeader += `; -}; + bundledHeader += ` + private: + JSC::VM& m_vm; + `; -class JSBuiltinInternalFunctions { -public: - explicit JSBuiltinInternalFunctions(JSC::VM&); + for (const { basename } of files) { + bundledHeader += ` ${basename}BuiltinsWrapper m_${low(basename)}Builtins;\n`; + } - template void visit(Visitor&); - void initialize(Zig::GlobalObject&); -`; + bundledHeader += `; + }; + + class JSBuiltinInternalFunctions { + public: + explicit JSBuiltinInternalFunctions(JSC::VM&); + + template 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`; + for (const { basename, internal } of files) { + if (internal) { + bundledHeader += ` ${basename}BuiltinFunctions& ${low(basename)}() { return m_${low(basename)}; }\n`; + } } -} -bundledHeader += ` -private: - JSC::VM& m_vm; -`; + bundledHeader += ` + private: + JSC::VM& m_vm; + `; -for (const { basename, internal } of files) { - if (internal) { - bundledHeader += ` ${basename}BuiltinFunctions m_${low(basename)};\n`; + for (const { basename, internal } of files) { + if (internal) { + bundledHeader += ` ${basename}BuiltinFunctions m_${low(basename)};\n`; + } } -} -bundledHeader += ` -}; - -} // namespace WebCore -`; + bundledHeader += ` + }; + + } // namespace WebCore + `; -writeIfNotChanged(path.join(CODEGEN_DIR, "WebCoreJSBuiltins.h"), bundledHeader); -writeIfNotChanged(path.join(CODEGEN_DIR, "WebCoreJSBuiltins.cpp"), bundledCPP); + 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 extends (this: infer T, ...args: infer A) => infer R ? (...args: A) => R : F; -`; + // Generate TS types + let dts = `// Generated by \`bun src/js/builtins/codegen\` + // Do not edit by hand. + type RemoveThis = 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;\n`; + 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;\n`; + } } } -} -writeIfNotChanged(path.join(CODEGEN_DIR, "WebCoreJSBuiltins.d.ts"), dts); + dts += getJS2NativeDTS(); -const totalJSSize = files.reduce( - (acc, { functions }) => acc + functions.reduce((acc, fn) => acc + fn.source.length, 0), - 0, -); + writeIfNotChanged(path.join(CODEGEN_DIR, "WebCoreJSBuiltins.d.ts"), dts); -if (!KEEP_TMP) { - await rmSync(TMP_DIR, { recursive: true }); -} + 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 }); + } -console.log( - `Embedded JS size: %s bytes (across %s functions, %s files)`, - totalJSSize, - files.reduce((acc, { functions }) => acc + functions.length, 0), - files.length, -); -console.log(`[${performance.now().toFixed(1)}ms]`); + globalThis.internalFunctionJSSize = totalJSSize; + globalThis.internalFunctionCount = files.reduce((acc, { functions }) => acc + functions.length, 0); + globalThis.internalFunctionFileCount = files.length; +} diff --git a/src/codegen/bundle-modules-fast.ts b/src/codegen/bundle-modules-fast.ts deleted file mode 100644 index f699370cff262..0000000000000 --- a/src/codegen/bundle-modules-fast.ts +++ /dev/null @@ -1,77 +0,0 @@ -// This script is run when you change anything in src/js/* -import path from "path"; -import { cap, writeIfNotChanged } from "./helpers"; -import { createInternalModuleRegistry } from "./internal-module-registry-scanner"; - -const BASE = path.join(import.meta.dir, "../js"); -const CMAKE_BUILD_ROOT = process.argv[2]; - -if (!CMAKE_BUILD_ROOT) { - console.error("Usage: bun bundle-modules-fast.ts "); - process.exit(1); -} - -const CODEGEN_DIR = path.join(CMAKE_BUILD_ROOT, "codegen"); - -let start = performance.now(); -function mark(log: string) { - const now = performance.now(); - console.log(`${log} (${(now - start).toFixed(0)}ms)`); - start = now; -} - -const { - // - moduleList, - nativeModuleIds, -} = createInternalModuleRegistry(BASE); - -function idToEnumName(id: string) { - return id - .replace(/\.[mc]?[tj]s$/, "") - .replace(/[^a-zA-Z0-9]+/g, " ") - .split(" ") - .map(x => (["jsc", "ffi", "vm", "tls", "os", "ws", "fs", "dns"].includes(x) ? x.toUpperCase() : cap(x))) - .join(""); -} - -function idToPublicSpecifierOrEnumName(id: string) { - id = id.replace(/\.[mc]?[tj]s$/, ""); - if (id.startsWith("node/")) { - return "node:" + id.slice(5).replaceAll(".", "/"); - } else if (id.startsWith("bun/")) { - return "bun:" + id.slice(4).replaceAll(".", "/"); - } else if (id.startsWith("internal/")) { - return "internal:" + id.slice(9).replaceAll(".", "/"); - } else if (id.startsWith("thirdparty/")) { - return id.slice(11).replaceAll(".", "/"); - } - return idToEnumName(id); -} - -// This is a generated enum for zig code (exports.zig) -writeIfNotChanged( - path.join(CODEGEN_DIR, "ResolvedSourceTag.zig"), - `// zig fmt: off -pub const ResolvedSourceTag = enum(u32) { - // Predefined - javascript = 0, - package_json_type_module = 1, - wasm = 2, - object = 3, - file = 4, - esm = 5, - json_for_object_loader = 6, - - // Built in modules are loaded through InternalModuleRegistry by numerical ID. - // In this enum are represented as \`(1 << 9) & id\` -${moduleList.map((id, n) => ` @"${idToPublicSpecifierOrEnumName(id)}" = ${(1 << 9) | n},`).join("\n")} - // Native modules run through a different system using ESM registry. -${Object.entries(nativeModuleIds) - .map(([id, n]) => ` @"${id}" = ${(1 << 10) | n},`) - .join("\n")} -}; -`, -); - -mark("Generate Code"); diff --git a/src/codegen/bundle-modules.ts b/src/codegen/bundle-modules.ts index 0c5cff5ea9178..cb121c8f01fbe 100644 --- a/src/codegen/bundle-modules.ts +++ b/src/codegen/bundle-modules.ts @@ -1,23 +1,39 @@ // This script is run when you change anything in src/js/* +// +// Originally, the builtin bundler only supported function files, but then the module files were +// added to this, which has made this entire setup extremely convoluted and a mess. +// +// One day, this entire setup should be rewritten, but also it would be cool if Bun natively +// supported macros that aren't json value -> json value. Otherwise, I'd use a real JS parser/ast +// library, instead of RegExp hacks. +// +// For explanation on this, please nag @paperdave to write documentation on how everything works. import fs from "fs"; -import { writeFile, rm, mkdir } from "fs/promises"; +import { writeFile, mkdir } from "fs/promises"; import path from "path"; import { sliceSourceCode } from "./builtin-parser"; -import { cap, declareASCIILiteral, readdirRecursive, resolveSyncOrNull, writeIfNotChanged } from "./helpers"; +import { cap, declareASCIILiteral, writeIfNotChanged } from "./helpers"; import { createAssertClientJS, createLogClientJS } from "./client-js"; import { builtinModules } from "node:module"; import { define } from "./replacements"; import { createInternalModuleRegistry } from "./internal-module-registry-scanner"; +import { getJS2NativeCPP, getJS2NativeZig } from "./js2native-generator"; const BASE = path.join(import.meta.dir, "../js"); const debug = process.argv[2] === "--debug=ON"; const CMAKE_BUILD_ROOT = process.argv[3]; +const timeString = 'Bundled "src/js" for ' + (debug ? "development" : "production"); +console.time(timeString); + if (!CMAKE_BUILD_ROOT) { - console.error("Usage: bun bundle-modules.ts "); + console.error("Usage: bun bundle-modules.ts --debug=[OFF|ON] "); process.exit(1); } +globalThis.CMAKE_BUILD_ROOT = CMAKE_BUILD_ROOT; +const bundleBuiltinFunctions = require("./bundle-functions").bundleBuiltinFunctions; + const TMP_DIR = path.join(CMAKE_BUILD_ROOT, "tmp_modules"); const CODEGEN_DIR = path.join(CMAKE_BUILD_ROOT, "codegen"); const JS_DIR = path.join(CMAKE_BUILD_ROOT, "js"); @@ -31,14 +47,9 @@ function mark(log: string) { start = now; } -const { - // - moduleList, - nativeModuleIds, - nativeModuleEnumToId, - nativeModuleEnums, - requireTransformer, -} = createInternalModuleRegistry(BASE); +const { moduleList, nativeModuleIds, nativeModuleEnumToId, nativeModuleEnums, requireTransformer } = + createInternalModuleRegistry(BASE); +globalThis.requireTransformer = requireTransformer; // these logs surround a very weird issue where writing files and then bundling sometimes doesn't // work, so i have lot of debug logs that blow up the console because not sure what is going on. @@ -154,8 +165,6 @@ $$EXPORT$$(__intrinsic__exports).$$EXPORT_END$$; mark("Preprocess modules"); -await Bun.sleep(10); - // directory caching stuff breaks this sometimes. CLI rules const config_cli = [ process.execPath, @@ -189,21 +198,6 @@ if (out.exitCode !== 0) { process.exit(out.exitCode); } -// const config = ({ debug }: { debug?: boolean }) => -// ({ -// entrypoints: bundledEntryPoints, -// // Whitespace and identifiers are not minified to give better error messages when an error happens in our builtins -// minify: { syntax: !debug, whitespace: false }, -// root: TMP_DIR, -// target: "bun", -// external: builtinModules, -// define: { -// ...define, -// IS_BUN_DEVELOPMENT: String(!!debug), -// __intrinsic__debug: debug ? "$debug_log_enabled" : "false", -// }, -// } satisfies BuildConfig); - mark("Bundle modules"); const outputs = new Map(); @@ -217,21 +211,13 @@ for (const entrypoint of bundledEntryPoints) { let usesAssert = output.includes("$assert"); captured = captured - .replace( - `var __require = (id) => { - return import.meta.require(id); -};`, - "", - ) - .replace(/var\s*__require\s*=\s*\(?id\)?\s*=>\s*{\s*return\s*import.meta.require\(id\)\s*};?/, "") - .replace(/var __require=\(?id\)?=>import.meta.require\(id\);?/, "") .replace(/\$\$EXPORT\$\$\((.*)\).\$\$EXPORT_END\$\$;/, "return $1") .replace(/]\s*,\s*__(debug|assert)_end__\)/g, ")") .replace(/]\s*,\s*__debug_end__\)/g, ")") - // .replace(/__intrinsic__lazy\(/g, "globalThis[globalThis.Symbol.for('Bun.lazy')](") .replace(/import.meta.require\((.*?)\)/g, (expr, specifier) => { throw new Error(`Builtin Bundler: do not use import.meta.require() (in ${file_path}))`); }) + .replace(/return \$\nexport /, "return") .replace(/__intrinsic__/g, "@") .replace(/__no_intrinsic__/g, "") + "\n"; captured = captured.replace( @@ -245,6 +231,11 @@ for (const entrypoint of bundledEntryPoints) { : "") + (usesAssert ? createAssertClientJS(idToPublicSpecifierOrEnumName(file_path).replace(/^node:|^bun:/, "")) : ""), ); + const errors = [...captured.matchAll(/@bundleError\((.*)\)/g)]; + if (errors.length) { + throw new Error(`Errors in ${entrypoint}:\n${errors.map(x => x[1]).join("\n")}`); + } + const outputPath = path.join(JS_DIR, file_path); fs.mkdirSync(path.dirname(outputPath), { recursive: true }); fs.writeFileSync(outputPath, captured); @@ -276,6 +267,12 @@ function idToPublicSpecifierOrEnumName(id: string) { return idToEnumName(id); } +await bundleBuiltinFunctions({ + requireTransformer, +}); + +mark("Bundle Functions"); + // This is a file with a single macro that is used in defining InternalModuleRegistry.h writeIfNotChanged( path.join(CODEGEN_DIR, "InternalModuleRegistry+numberOfModules.h"), @@ -424,13 +421,27 @@ writeIfNotChanged( .join("\n") + "\n", ); -// This is used for debug builds for the base path for dynamic loading -// fs.writeFileSync( -// path.join(OUT_DIR, "DebugPath.h"), -// `// Using __FILE__ does not give an absolute file path -// // This is a workaround for that. -// #define BUN_DYNAMIC_JS_LOAD_PATH "${path.join(OUT_DIR, "")}" -// `, -// ); +writeIfNotChanged(path.join(CODEGEN_DIR, "GeneratedJS2Native.h"), getJS2NativeCPP()); + +// zig will complain if this file is outside of the module +const js2nativeZigPath = path.join(import.meta.dir, "../bun.js/bindings/GeneratedJS2Native.zig"); +writeIfNotChanged(js2nativeZigPath, getJS2NativeZig(js2nativeZigPath)); mark("Generate Code"); + +console.log(""); +console.timeEnd(timeString); +console.log( + ` %s kb`, + Math.floor( + (moduleList.reduce((a, b) => a + outputs.get(b.slice(0, -3).replaceAll("/", path.sep)).length, 0) + + globalThis.internalFunctionJSSize) / + 1000, + ), +); +console.log(` %s internal modules`, moduleList.length); +console.log( + ` %s internal functions across %s files`, + globalThis.internalFunctionCount, + globalThis.internalFunctionFileCount, +); diff --git a/src/codegen/helpers.ts b/src/codegen/helpers.ts index f8dd9bba8f3b8..97f1cc43e81f1 100644 --- a/src/codegen/helpers.ts +++ b/src/codegen/helpers.ts @@ -5,7 +5,7 @@ import { isAscii } from "buffer"; // MSVC has a max of 16k characters per string literal // Combining string literals didn't support constexpr apparently // so we have to do this the gigantic array way -export function fmtCPPString(str: string, nullTerminated: boolean = true) { +export function fmtCPPCharArray(str: string, nullTerminated: boolean = true) { const normalized = str + "\n"; var remain = normalized; @@ -22,7 +22,7 @@ export function fmtCPPString(str: string, nullTerminated: boolean = true) { } export function declareASCIILiteral(name: string, value: string) { - const [chars, count] = fmtCPPString(value); + const [chars, count] = fmtCPPCharArray(value); return `static constexpr const char ${name}Bytes[${count}] = ${chars}; static constexpr ASCIILiteral ${name} = ASCIILiteral::fromLiteralUnsafe(${name}Bytes);`; } @@ -81,6 +81,23 @@ export function writeIfNotChanged(file: string, contents: string) { } } +export function readdirRecursiveWithExclusionsAndExtensionsSync( + dir: string, + exclusions: string[], + exts: string[], +): string[] { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + return entries.flatMap(entry => { + if (exclusions.includes(entry.name)) return []; + const fullPath = path.join(dir, entry.name); + return entry.isDirectory() + ? readdirRecursiveWithExclusionsAndExtensionsSync(fullPath, exclusions, exts) + : exts.includes(path.extname(fullPath)) + ? fullPath + : []; + }); +} + export function pathToUpperSnakeCase(filepath: string) { return filepath .replace(/^.*?:/, "") diff --git a/src/codegen/internal-module-registry-scanner.ts b/src/codegen/internal-module-registry-scanner.ts index 6efcd1fc181e2..5f1dee4621613 100644 --- a/src/codegen/internal-module-registry-scanner.ts +++ b/src/codegen/internal-module-registry-scanner.ts @@ -26,6 +26,9 @@ export function createInternalModuleRegistry(basedir: string) { } } + moduleList.push("internal-for-testing.ts"); + internalRegistry.set("bun:internal-for-testing", moduleList.length - 1); + // Native Module registry const nativeModuleH = fs.readFileSync(path.join(basedir, "../bun.js/modules/_NativeModule.h"), "utf8"); const nativeModuleDefine = nativeModuleH.match(/BUN_FOREACH_NATIVE_MODULE\(macro\)\s*\\\n((.*\\\n)*\n)/); diff --git a/src/codegen/js2native-generator.ts b/src/codegen/js2native-generator.ts new file mode 100644 index 0000000000000..09c71f8217b31 --- /dev/null +++ b/src/codegen/js2native-generator.ts @@ -0,0 +1,207 @@ +// This file implements the global state for $zig and $cpp preprocessor macros +// as well as all the code it generates. +// +// For the actual parsing, see replacements.ts + +import path, { basename, sep } from "path"; +import { readdirRecursiveWithExclusionsAndExtensionsSync } from "./helpers"; + +// +interface NativeCall { + id: number; + type: NativeCallType; + filename: string; + symbol: string; + is_wrapped: boolean; +} + +interface WrapperCall { + type: NativeCallType; + wrap_kind: "new-function"; + symbol_taget: string; + symbol_generated: string; + display_name: string; + call_length: number; + filename: string; +} + +type NativeCallType = "zig" | "cpp"; + +const nativeCalls: NativeCall[] = []; +const wrapperCalls: WrapperCall[] = []; + +const sourceFiles = readdirRecursiveWithExclusionsAndExtensionsSync( + path.join(import.meta.dir, "../"), + ["deps", "node_modules", "WebKit"], + [".cpp", ".zig"], +); + +function callBaseName(x: string) { + return x.split(/[^A-Za-z0-9]/g).pop()!; +} + +function resolveNativeFileId(call_type: NativeCallType, filename: string) { + if (!filename.endsWith("." + call_type)) { + throw new Error( + `Expected filename for $${call_type} to have .${call_type} extension, got ${JSON.stringify(filename)}`, + ); + } + + const resolved = sourceFiles.find(file => file.endsWith(sep + filename)); + if (!resolved) { + throw new Error(`Could not find file ${filename} in $${call_type} call`); + } + + if (call_type === "zig") { + return resolved; + } + + return filename; +} + +export function registerNativeCall( + call_type: NativeCallType, + filename: string, + symbol: string, + create_fn_len: null | number, +) { + const resolved_filename = resolveNativeFileId(call_type, filename); + + const maybe_wrapped_symbol = create_fn_len != null ? "js2native_wrap_" + symbol.replace(/[^A-Za-z]/g, "_") : symbol; + + const existing = nativeCalls.find( + call => + call.is_wrapped == (create_fn_len != null) && + call.filename === resolved_filename && + call.symbol === maybe_wrapped_symbol, + ); + if (existing) { + return existing.id; + } + + const id = nativeCalls.length; + nativeCalls.push({ + id, + type: create_fn_len != null ? "cpp" : call_type, + filename: resolved_filename, + symbol: maybe_wrapped_symbol, + is_wrapped: create_fn_len != null, + }); + if (create_fn_len != null) { + wrapperCalls.push({ + type: call_type, + wrap_kind: "new-function", + symbol_taget: symbol, + symbol_generated: "js2native_wrap_" + symbol.replace(/[^A-Za-z]/g, "_"), + display_name: callBaseName(symbol), + call_length: create_fn_len, + filename: resolved_filename, + }); + } + return id; +} + +function symbol(call: Pick) { + return call.type === "zig" ? `JS2Zig__${call.symbol.replace(/[^A-Za-z]/g, "_")}` : call.symbol; +} + +function cppPointer(call: NativeCall) { + return `&${symbol(call)}`; +} + +export function getJS2NativeCPP() { + const files = [ + ...new Set(nativeCalls.filter(x => x.filename.endsWith(".cpp")).map(x => x.filename.replace(/.cpp$/, ".h"))), + ]; + + return [ + `#pragma once`, + ...files.map(filename => `#include ${JSON.stringify(filename)}`), + "namespace JS2NativeGenerated {", + "using namespace Bun;", + "using namespace JSC;", + "using namespace WebCore;", + ...nativeCalls + .filter(x => x.type === "zig") + .flatMap(call => [ + `extern "C" JSC::EncodedJSValue ${symbol(call)}_workaround(Zig::GlobalObject*);`, + `JSC::JSValue ${symbol(call)}(Zig::GlobalObject* global) {`, + ` return JSValue::decode(${symbol(call)}_workaround(global));`, + `}`, + ]), + ...wrapperCalls.map(x => { + if (x.wrap_kind === "new-function") { + return [ + x.type === "zig" + ? `extern "C" JSC::EncodedJSValue ${symbol({ + type: "zig", + symbol: x.symbol_taget, + })}(JSC::JSGlobalObject*, JSC::CallFrame*);` + : "", + `JSC::JSValue ${x.symbol_generated}(Zig::GlobalObject* globalObject) {`, + ` return JSC::JSFunction::create(globalObject->vm(), globalObject, ${x.call_length}, ${JSON.stringify( + x.display_name, + )}_s, ${symbol({ type: x.type, symbol: x.symbol_taget })}, JSC::ImplementationVisibility::Public);`, + `}`, + ].join("\n"); + } + throw new Error(`Unknown wrap kind ${x.wrap_kind}`); + }), + `typedef JSC::JSValue (*JS2NativeFunction)(Zig::GlobalObject*);`, + `static JS2NativeFunction js2nativePointers[] = {`, + ...nativeCalls.map(x => ` ${cppPointer(x)},`), + `};`, + `};`, + `#define JS2NATIVE_COUNT ${nativeCalls.length}`, + ].join("\n"); +} + +export function getJS2NativeZig(gs2NativeZigPath: string) { + return [ + "//! This file is generated by src/codegen/js2native-generator.ts based on seen calls to the $zig() JS macro", + `const JSC = @import("root").bun.JSC;`, + ...nativeCalls + .filter(x => x.type === "zig") + .flatMap(call => [ + `export fn ${symbol(call)}_workaround(global: u64) callconv(.C) JSC.JSValue {`, + ` return @import(${JSON.stringify(path.relative(path.dirname(gs2NativeZigPath), call.filename))}).${ + call.symbol + }(@ptrFromInt(global));`, + "}", + ]), + ...wrapperCalls + .filter(x => x.type === "zig") + .flatMap(x => [ + `export fn ${symbol({ + type: "zig", + symbol: x.symbol_taget, + })}(global: *JSC.JSGlobalObject, call_frame: *JSC.CallFrame) callconv(.C) JSC.JSValue {`, + ` return @import(${JSON.stringify(path.relative(path.dirname(gs2NativeZigPath), x.filename))}).${ + x.symbol_taget + }(global, call_frame);`, + "}", + ]), + "comptime {", + ...nativeCalls.filter(x => x.type === "zig").flatMap(call => ` _ = &${symbol(call)}_workaround;`), + ...wrapperCalls + .filter(x => x.type === "zig") + .flatMap(x => ` _ = &${symbol({ type: "zig", symbol: x.symbol_taget })};`), + "}", + ].join("\n"); +} + +export function getJS2NativeDTS() { + return [ + "declare type NativeFilenameCPP = " + + sourceFiles + .filter(x => x.endsWith("cpp")) + .map(x => JSON.stringify(basename(x))) + .join("|"), + "declare type NativeFilenameZig = " + + sourceFiles + .filter(x => x.endsWith("zig")) + .map(x => JSON.stringify(basename(x))) + .join("|"), + "", + ].join("\n"); +} diff --git a/src/codegen/replacements.ts b/src/codegen/replacements.ts index 8f8581f591460..79500c57db7a3 100644 --- a/src/codegen/replacements.ts +++ b/src/codegen/replacements.ts @@ -1,5 +1,6 @@ import { LoaderKeys } from "../api/schema"; import { sliceSourceCode } from "./builtin-parser"; +import { registerNativeCall } from "./js2native-generator"; // This is a list of extra syntax replacements to do. Kind of like macros // These are only run on code itself, not string contents or comments. @@ -130,6 +131,8 @@ export interface ReplacementRule { global?: boolean; } +export const function_replacements = ["$debug", "$assert", "$zig", "$newZigFunction", "$cpp", "$newCppFunction"]; + /** Applies source code replacements as defined in `replacements` */ export function applyReplacements(src: string, length: number) { let slice = src.slice(0, length); @@ -139,7 +142,10 @@ export function applyReplacements(src: string, length: number) { slice = slice.replace(replacement.from, replacement.to.replaceAll("$", "__intrinsic__").replaceAll("%", "$")); } let match; - if ((match = slice.match(/__intrinsic__(debug|assert)$/)) && rest.startsWith("(")) { + if ( + (match = slice.match(/__intrinsic__(debug|assert|zig|cpp|newZigFunction|newCppFunction)$/)) && + rest.startsWith("(") + ) { const name = match[1]; if (name === "debug") { const innerSlice = sliceSourceCode(rest, true); @@ -173,6 +179,40 @@ export function applyReplacements(src: string, length: number) { rest2, true, ]; + } else if (["zig", "cpp", "newZigFunction", "newCppFunction"].includes(name)) { + const kind = name.includes("ig") ? "zig" : "cpp"; + const is_create_fn = name.startsWith("new"); + + const inner = sliceSourceCode(rest, true); + let args; + try { + const str = + "[" + + inner.result + .slice(1, -1) + .replaceAll("'", '"') + .replace(/,[\s\n]*$/s, "") + + "]"; + args = JSON.parse(str); + } catch { + throw new Error(`Call is not known at bundle-time: '$${name}${inner.result}'`); + } + if ( + args.length != (is_create_fn ? 3 : 2) || + typeof args[0] !== "string" || + typeof args[1] !== "string" || + (is_create_fn && typeof args[2] !== "number") + ) { + if (is_create_fn) { + throw new Error(`$${name} takes three arguments, but got '$${name}${inner.result}'`); + } else { + throw new Error(`$${name} takes two string arguments, but got '$${name}${inner.result}'`); + } + } + + const id = registerNativeCall(kind, args[0], args[1], is_create_fn ? args[2] : undefined); + + return [slice.slice(0, match.index) + "__intrinsic__lazy(" + id + ")", inner.rest, true]; } } return [slice, rest, false]; diff --git a/src/fmt.zig b/src/fmt.zig index 74bf7a4ec30fc..a66d56cd1f0f8 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -945,6 +945,35 @@ pub const QuickAndDirtyJavaScriptSyntaxHighlighter = struct { } } } + + /// Function for testing in highlighter.test.ts + pub fn jsFunctionSyntaxHighlight(globalThis: *bun.JSC.JSGlobalObject, callframe: *bun.JSC.CallFrame) callconv(.C) bun.JSC.JSValue { + const args = callframe.arguments(1); + if (args.len < 1) { + globalThis.throwNotEnoughArguments("code", 1, 0); + } + + const code = args.ptr[0].toSliceOrNull(globalThis) orelse return .zero; + defer code.deinit(); + var buffer = bun.MutableString.initEmpty(bun.default_allocator); + defer buffer.deinit(); + var writer = buffer.bufferedWriter(); + var formatter = bun.fmt.fmtJavaScript(code.slice(), true); + formatter.limited = false; + std.fmt.format(writer.writer(), "{}", .{formatter}) catch |err| { + globalThis.throwError(err, "Error formatting code"); + return .zero; + }; + + writer.flush() catch |err| { + globalThis.throwError(err, "Error formatting code"); + return .zero; + }; + + var str = bun.String.createUTF8(buffer.list.items); + defer str.deref(); + return str.toJS(globalThis); + } }; pub fn quote(self: string) bun.fmt.QuotedFormatter { @@ -1224,7 +1253,6 @@ fn formatDurationOneDecimal(data: FormatDurationData, comptime _: []const u8, op pub fn fmtDurationOneDecimal(ns: u64) std.fmt.Formatter(formatDurationOneDecimal) { return .{ .data = FormatDurationData{ .ns = ns } }; } -// }; pub fn fmtSlice(data: anytype, comptime delim: []const u8) FormatSlice(@TypeOf(data), delim) { return .{ .slice = data }; diff --git a/src/js/builtins/ConsoleObject.ts b/src/js/builtins/ConsoleObject.ts index c3ad5610f3455..1e35d8de78083 100644 --- a/src/js/builtins/ConsoleObject.ts +++ b/src/js/builtins/ConsoleObject.ts @@ -139,7 +139,7 @@ export function write(this: Console, input) { // to do extra work at startup, since most people do not need `console.Console`. // TODO: probably could extract `getStringWidth`; probably make that a native function. note how it is copied from `readline.js` export function createConsoleConstructor(console: typeof globalThis.console) { - const { inspect, formatWithOptions } = require("node:util"); + const { inspect, formatWithOptions, stripVTControlCharacters } = require("node:util"); const { isBuffer } = require("node:buffer"); const StringPrototypeIncludes = String.prototype.includes; @@ -163,22 +163,7 @@ export function createConsoleConstructor(console: typeof globalThis.console) { const kMinute = 60 * kSecond; const kHour = 60 * kMinute; - // Regex used for ansi escape code splitting - // Adopted from https://github.com/chalk/ansi-regex/blob/HEAD/index.js - // License: MIT, authors: @sindresorhus, Qix-, arjunmehta and LitoMore - // Matches all ansi escape code sequences in a string - var ansiPattern = - "[\\u001B\\u009B][[\\]()#;?]*" + - "(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*" + - "|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)" + - "|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"; - var ansi = new RegExp(ansiPattern, "g"); - - function stripVTControlCharacters(str) { - return (RegExpPrototypeSymbolReplace as any).$call(ansi, str, ""); - } - - var internalGetStringWidth = $lazy("getStringWidth"); + const internalGetStringWidth = $newZigFunction("string.zig", "String.jsGetStringWidth", 1); /** * Returns the number of columns required to display the given string. diff --git a/src/js/builtins/tsconfig.json b/src/js/builtins/tsconfig.json deleted file mode 100644 index 71940a981154a..0000000000000 --- a/src/js/builtins/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "noEmit": true, - "module": "esnext", - "moduleResolution": "bundler" - }, - "include": [ - ".", - "../private.d.ts", - "../builtins.d.ts", - "../out/WebCoreJSBuiltins.d.ts", - "../../../packages/bun-types/index.d.ts", - "../_codegen/builtin-parser.ts" - ] -} diff --git a/src/js/bun/sqlite.ts b/src/js/bun/sqlite.ts index af23482fc5ad4..a01b55c6b8164 100644 --- a/src/js/bun/sqlite.ts +++ b/src/js/bun/sqlite.ts @@ -210,7 +210,7 @@ class Database { } if (!SQL) { - SQL = $lazy("sqlite"); + SQL = $cpp("JSSQLStatement.cpp", "createJSSQLStatementConstructor"); } this.#handle = SQL.open(anonymous ? ":memory:" : filename, flags); @@ -245,7 +245,7 @@ class Database { static #deserialize(serialized, isReadOnly = false) { if (!SQL) { - SQL = $lazy("sqlite"); + SQL = $cpp("JSSQLStatement.cpp", "createJSSQLStatementConstructor"); } return SQL.deserialize(serialized, isReadOnly); @@ -257,7 +257,7 @@ class Database { static setCustomSQLite(path) { if (!SQL) { - SQL = $lazy("sqlite"); + SQL = $cpp("JSSQLStatement.cpp", "createJSSQLStatementConstructor"); } return SQL.setCustomSQLite(path); diff --git a/src/js/internal-for-testing.ts b/src/js/internal-for-testing.ts new file mode 100644 index 0000000000000..ffbf7e7b4375c --- /dev/null +++ b/src/js/internal-for-testing.ts @@ -0,0 +1,23 @@ +// If you want to test an internal API, add a binding into this file. +// +// Then at test time: import ... from "bun:internal-for-testing" +// +// In a debug build, the import is always allowed. +// It is disallowed in release builds unless run in Bun's CI. + +/// + +export const quickAndDirtyJavaScriptSyntaxHighlighter = $newZigFunction( + "fmt.zig", + "QuickAndDirtyJavaScriptSyntaxHighlighter.jsFunctionSyntaxHighlight", + 2, +) as (code: string) => string; + +export const TLSBinding = $cpp("NodeTLS.cpp", "createNodeTLSBinding"); + +export const SQL = $cpp("JSSQLStatement.cpp", "createJSSQLStatementConstructor"); + +export const shellInternals = { + lex: $newZigFunction("shell.zig", "TestingAPIs.shellLex", 1), + parse: $newZigFunction("shell.zig", "TestingAPIs.shellParse", 1), +}; diff --git a/src/js/internal/util/inspect.js b/src/js/internal/util/inspect.js index 21d0baadc3278..797288f40de2b 100644 --- a/src/js/internal/util/inspect.js +++ b/src/js/internal/util/inspect.js @@ -2570,7 +2570,7 @@ function formatWithOptionsInternal(inspectOptions, args) { return str; } -var internalGetStringWidth = $lazy("getStringWidth"); +const internalGetStringWidth = $newZigFunction("string.zig", "String.jsGetStringWidth", 1); /** * Returns the number of columns required to display the given string. */ diff --git a/src/js/node/async_hooks.ts b/src/js/node/async_hooks.ts index 5247bf71d4d68..110571eb0c829 100644 --- a/src/js/node/async_hooks.ts +++ b/src/js/node/async_hooks.ts @@ -22,7 +22,7 @@ // each key is an AsyncLocalStorage object and the value is the associated value. There are a ton of // calls to $assert which will verify this invariant (only during bun-debug) // -const { cleanupLater, setAsyncHooksEnabled } = $lazy("async_hooks"); +const [setAsyncHooksEnabled, cleanupLater] = $cpp("NodeAsyncHooks.cpp", "createAsyncHooksBinding"); // Only run during debug function assertValidAsyncContextArray(array: unknown): array is ReadonlyArray | undefined { diff --git a/src/js/node/crypto.js b/src/js/node/crypto.js index 4b9c486af5a29..2009b0a4ae06d 100644 --- a/src/js/node/crypto.js +++ b/src/js/node/crypto.js @@ -5,6 +5,21 @@ const StreamModule = require("node:stream"); const BufferModule = require("node:buffer"); const StringDecoder = require("node:string_decoder").StringDecoder; +const { + symmetricKeySize, + asymmetricKeyDetails, + asymmetricKeyType, + equals, + exports, + createSecretKey, + createPublicKey, + createPrivateKey, + generateKeySync, + generateKeyPairSync, + sign: nativeSign, + verify: nativeVerify, +} = $cpp("KeyObject.cpp", "createNodeCryptoBinding"); + const MAX_STRING_LENGTH = 536870888; var Buffer = globalThis.Buffer; const EMPTY_BUFFER = Buffer.alloc(0); @@ -12024,20 +12039,6 @@ const harcoded_curves = [ function getCurves() { return harcoded_curves; } -const { - symmetricKeySize, - asymmetricKeyDetails, - asymmetricKeyType, - equals, - exports, - createSecretKey, - createPublicKey, - createPrivateKey, - generateKeySync, - generateKeyPairSync, - sign: nativeSign, - verify: nativeVerify, -} = $lazy("internal/crypto"); class KeyObject { // we use $bunNativePtr so that util.types.isKeyObject can detect it diff --git a/src/js/node/fs.js b/src/js/node/fs.js index d6033efcf7cd9..c1fa424fd5205 100644 --- a/src/js/node/fs.js +++ b/src/js/node/fs.js @@ -5,11 +5,16 @@ const EventEmitter = require("node:events"); const promises = require("node:fs/promises"); const Stream = require("node:stream"); -var _writeStreamPathFastPathSymbol = Symbol.for("Bun.NodeWriteStreamFastPath"); -var _fs = Symbol.for("#fs"); +// Private exports +const { FileHandle, kRef, kUnref, kFd, fs } = promises.$data; +// reusing a different private symbol +// this points to `node_fs_binding.zig`'s `createBinding` function. const constants = $processBindingConstants.fs; +var _writeStreamPathFastPathSymbol = Symbol.for("Bun.NodeWriteStreamFastPath"); +var _fs = Symbol.for("#fs"); + function ensureCallback(callback) { if (!$isCallable(callback)) { const err = new TypeError("Callback must be a function"); @@ -20,7 +25,6 @@ function ensureCallback(callback) { return callback; } -var fs = Bun.fs(); class FSWatcher extends EventEmitter { #watcher; #listener; @@ -510,7 +514,6 @@ var defaultReadStreamOptions = { autoDestroy: true, }; -let { FileHandle, kRef, kUnref, kFd } = promises.$data; let kHandle = Symbol("kHandle"); var ReadStreamClass; diff --git a/src/js/node/fs.promises.ts b/src/js/node/fs.promises.ts index 5296797fb64a4..c0a9b00259b29 100644 --- a/src/js/node/fs.promises.ts +++ b/src/js/node/fs.promises.ts @@ -1,10 +1,9 @@ -import type { Dirent } from "fs"; - // Hardcoded module "node:fs/promises" -const constants = $processBindingConstants.fs; +import type { Dirent } from "fs"; const EventEmitter = require("node:events"); +const fs = $zig("node_fs_binding.zig", "createBinding"); +const constants = $processBindingConstants.fs; -var PromisePrototypeThen = Promise.prototype.then; var PromisePrototypeFinally = Promise.prototype.finally; //TODO var SymbolAsyncDispose = Symbol.asyncDispose; var ObjectFreeze = Object.freeze; @@ -21,8 +20,6 @@ const kTransferList = Symbol("kTransferList"); const kDeserialize = Symbol("kDeserialize"); const kEmptyObject = ObjectFreeze({ __proto__: null }); -var fs = Bun.fs(); - function watch( filename: string | Buffer | URL, options: { encoding?: BufferEncoding; persistent?: boolean; recursive?: boolean; signal?: AbortSignal } = {}, @@ -152,6 +149,7 @@ const private_symbols = { kUnref, kFd, FileHandle: null, + fs, }; const exports = { diff --git a/src/js/node/http.ts b/src/js/node/http.ts index 71d59f9337f6b..3924eeee0be0a 100644 --- a/src/js/node/http.ts +++ b/src/js/node/http.ts @@ -2,7 +2,12 @@ const EventEmitter = require("node:events"); const { isTypedArray } = require("node:util/types"); const { Duplex, Readable, Writable } = require("node:stream"); -const { getHeader, setHeader, assignHeaders: assignHeadersFast } = $lazy("http"); + +const { + getHeader, + setHeader, + assignHeaders: assignHeadersFast, +} = $cpp("NodeHTTP.cpp", "createNodeHTTPInternalBinding"); const GlobalPromise = globalThis.Promise; const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/; diff --git a/src/js/node/http2.ts b/src/js/node/http2.ts index 6909caa1f0034..e36fe65127ef5 100644 --- a/src/js/node/http2.ts +++ b/src/js/node/http2.ts @@ -14,7 +14,7 @@ const EventEmitter = require("node:events"); const { Duplex } = require("node:stream"); const primordials = require("internal/primordials"); -const { H2FrameParser, getPackedSettings, getUnpackedSettings } = $lazy("internal/http2"); +const [H2FrameParser, getPackedSettings, getUnpackedSettings] = $zig("h2_frame_parser.zig", "createNodeHttp2Binding"); const sensitiveHeaders = Symbol.for("nodejs.http2.sensitiveHeaders"); const bunHTTP2Native = Symbol.for("::bunhttp2native::"); diff --git a/src/js/node/os.ts b/src/js/node/os.ts index 71bed4409aa16..2d05c301c3b3e 100644 --- a/src/js/node/os.ts +++ b/src/js/node/os.ts @@ -84,36 +84,42 @@ function lazyCpus({ cpus }) { }; } +// all logic based on `process.platform` and `process.arch` is inlined at bundle time function bound(obj) { return { - availableParallelism: () => { - return navigator.hardwareConcurrency; - }, - arch: obj.arch.bind(obj), + availableParallelism: () => navigator.hardwareConcurrency, + arch: () => process.arch, cpus: lazyCpus(obj), - endianness: obj.endianness.bind(obj), + endianness: () => (process.arch === "arm64" || process.arch === "x64" ? "LE" : $bundleError("TODO: endianness")), freemem: obj.freemem.bind(obj), getPriority: obj.getPriority.bind(obj), homedir: obj.homedir.bind(obj), hostname: obj.hostname.bind(obj), loadavg: obj.loadavg.bind(obj), networkInterfaces: obj.networkInterfaces.bind(obj), - platform: obj.platform.bind(obj), + platform: () => process.platform, release: obj.release.bind(obj), setPriority: obj.setPriority.bind(obj), get tmpdir() { return tmpdir; }, totalmem: obj.totalmem.bind(obj), - type: obj.type.bind(obj), + type: () => + process.platform === "win32" + ? "Windows_NT" + : process.platform === "darwin" + ? "Darwin" + : process.platform === "linux" + ? "Linux" + : $bundleError("TODO: type"), uptime: obj.uptime.bind(obj), userInfo: obj.userInfo.bind(obj), version: obj.version.bind(obj), machine: obj.machine.bind(obj), - devNull: obj.devNull, - EOL: obj.EOL, + devNull: process.platform === "win32" ? "\\\\.\\nul" : "/dev/null", + EOL: process.platform === "win32" ? "\r\n" : "\n", constants: $processBindingConstants.os, }; } -export default bound(Bun._Os()); +export default bound($zig("node_os.zig", "OS.create")); diff --git a/src/js/node/path.ts b/src/js/node/path.ts index 11af76f295cef..f7364a82bb1de 100644 --- a/src/js/node/path.ts +++ b/src/js/node/path.ts @@ -1,32 +1,43 @@ // Hardcoded module "node:path" -function bound(obj) { - const toNamespacedPath = obj.toNamespacedPath.bind(obj); - const result = { - resolve: obj.resolve.bind(obj), - normalize: obj.normalize.bind(obj), - isAbsolute: obj.isAbsolute.bind(obj), - join: obj.join.bind(obj), - relative: obj.relative.bind(obj), - toNamespacedPath, - dirname: obj.dirname.bind(obj), - basename: obj.basename.bind(obj), - extname: obj.extname.bind(obj), - format: obj.format.bind(obj), - parse: obj.parse.bind(obj), - sep: obj.sep, - delimiter: obj.delimiter, - win32: undefined, - posix: undefined, - // Legacy internal API, docs-only deprecated: DEP0080 - _makeLong: toNamespacedPath, - }; - return result; -} - -const posix: any = bound(Bun._Path(false)); -const win32: any = bound(Bun._Path(true)); - +const [bindingPosix, bindingWin32] = $cpp("Path.cpp", "createNodePathBinding"); +const toNamespacedPathPosix = bindingPosix.toNamespacedPath.bind(bindingPosix); +const toNamespacedPathWin32 = bindingWin32.toNamespacedPath.bind(bindingWin32); +const posix = { + resolve: bindingPosix.resolve.bind(bindingPosix), + normalize: bindingPosix.normalize.bind(bindingPosix), + isAbsolute: bindingPosix.isAbsolute.bind(bindingPosix), + join: bindingPosix.join.bind(bindingPosix), + relative: bindingPosix.relative.bind(bindingPosix), + toNamespacedPath: toNamespacedPathPosix, + dirname: bindingPosix.dirname.bind(bindingPosix), + basename: bindingPosix.basename.bind(bindingPosix), + extname: bindingPosix.extname.bind(bindingPosix), + format: bindingPosix.format.bind(bindingPosix), + parse: bindingPosix.parse.bind(bindingPosix), + sep: "/", + delimiter: ":", + win32: undefined as typeof win32, + posix: undefined as typeof posix, + _makeLong: toNamespacedPathPosix, +}; +const win32 = { + resolve: bindingWin32.resolve.bind(bindingWin32), + normalize: bindingWin32.normalize.bind(bindingWin32), + isAbsolute: bindingWin32.isAbsolute.bind(bindingWin32), + join: bindingWin32.join.bind(bindingWin32), + relative: bindingWin32.relative.bind(bindingWin32), + toNamespacedPath: toNamespacedPathWin32, + dirname: bindingWin32.dirname.bind(bindingWin32), + basename: bindingWin32.basename.bind(bindingWin32), + extname: bindingWin32.extname.bind(bindingWin32), + format: bindingWin32.format.bind(bindingWin32), + parse: bindingWin32.parse.bind(bindingWin32), + sep: "\\", + delimiter: ";", + win32: undefined as typeof win32, + posix, + _makeLong: toNamespacedPathWin32, +}; posix.win32 = win32.win32 = win32; -posix.posix = win32.posix = posix; - +posix.posix = posix; export default process.platform === "win32" ? win32 : posix; diff --git a/src/js/node/readline.js b/src/js/node/readline.js index 7536b80172740..47f8ce4e4aba5 100644 --- a/src/js/node/readline.js +++ b/src/js/node/readline.js @@ -27,6 +27,8 @@ // ---------------------------------------------------------------------------- const EventEmitter = require("node:events"); const { StringDecoder } = require("node:string_decoder"); +const internalGetStringWidth = $newZigFunction("string.zig", "String.jsGetStringWidth", 1); + var isWritable; var { inspect } = Bun; @@ -113,7 +115,6 @@ var SafeStringIterator = createSafeIterator(StringPrototypeSymbolIterator, Strin // Section: "Internal" modules // ---------------------------------------------------------------------------- -var internalGetStringWidth = $lazy("getStringWidth"); /** * Returns the number of columns required to display the given string. */ diff --git a/src/js/node/stream.js b/src/js/node/stream.js index b9b1f391100c0..efddc4d5d4b0b 100644 --- a/src/js/node/stream.js +++ b/src/js/node/stream.js @@ -2,9 +2,18 @@ // "readable-stream" npm package // just transpiled and debug logs added. -const EE = $lazy("events"); const StringDecoder = require("node:string_decoder").StringDecoder; +const { + BufferList, + ReadableState, + maybeReadMore: _maybeReadMore, + resume, + emitReadable: _emitReadable, + // onEofChunk, + EE, +} = $cpp("JSReadableHelper.cpp", "createNodeStreamBinding"); + var __getOwnPropNames = Object.getOwnPropertyNames; var __commonJS = (cb, mod) => @@ -2118,34 +2127,38 @@ var require_add_abort_signal = __commonJS({ }); // node_modules/readable-stream/lib/internal/streams/state.js -var require_state = __commonJS({ - "node_modules/readable-stream/lib/internal/streams/state.js"(exports, module) { - "use strict"; - var { MathFloor, NumberIsInteger } = require_primordials(); - var { ERR_INVALID_ARG_VALUE } = require_errors().codes; - function highWaterMarkFrom(options, isDuplex, duplexKey) { - return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null; - } - function getDefaultHighWaterMark(objectMode) { - return objectMode ? 16 : 16 * 1024; - } - function getHighWaterMark(state, options, duplexKey, isDuplex) { - const hwm = highWaterMarkFrom(options, isDuplex, duplexKey); - if (hwm != null) { - if (!NumberIsInteger(hwm) || hwm < 0) { - const name = isDuplex ? `options.${duplexKey}` : "options.highWaterMark"; - throw new ERR_INVALID_ARG_VALUE(name, hwm); - } - return MathFloor(hwm); - } - return getDefaultHighWaterMark(state.objectMode); +var { MathFloor, NumberIsInteger } = require_primordials(); +var { ERR_INVALID_ARG_VALUE } = require_errors().codes; +function highWaterMarkFrom(options, isDuplex, duplexKey) { + return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null; +} + +let hwm_object = 16; +let hwm_bytes = 16 * 1024; + +function getDefaultHighWaterMark(objectMode) { + return objectMode ? hwm_object : hwm_bytes; +} + +function setDefaultHighWaterMark(objectMode, value) { + if (objectMode) { + hwm_object = value; + } else { + hwm_bytes = value; + } +} + +function getHighWaterMark(state, options, duplexKey, isDuplex) { + const hwm = highWaterMarkFrom(options, isDuplex, duplexKey); + if (hwm != null) { + if (!NumberIsInteger(hwm) || hwm < 0) { + const name = isDuplex ? `options.${duplexKey}` : "options.highWaterMark"; + throw new ERR_INVALID_ARG_VALUE(name, hwm); } - module.exports = { - getHighWaterMark, - getDefaultHighWaterMark, - }; - }, -}); + return MathFloor(hwm); + } + return getDefaultHighWaterMark(state.objectMode); +} // node_modules/readable-stream/lib/internal/streams/from.js var require_from = __commonJS({ @@ -2256,10 +2269,8 @@ var require_readable = __commonJS({ Promise: Promise2, SafeSet, SymbolAsyncIterator, - Symbol: Symbol2, } = require_primordials(); - var ReadableState = $lazy("bun:stream").ReadableState; var { Stream, prependListener } = require_legacy(); function Readable(options) { @@ -2510,7 +2521,6 @@ var require_readable = __commonJS({ var { addAbortSignal } = require_add_abort_signal(); var eos = require_end_of_stream(); - const { maybeReadMore: _maybeReadMore, resume, emitReadable: _emitReadable } = $lazy("bun:stream"); function maybeReadMore(stream, state) { process.nextTick(_maybeReadMore, stream, state); } @@ -3445,7 +3455,6 @@ var require_writable = __commonJS({ var Stream = require_legacy().Stream; var destroyImpl = require_destroy(); var { addAbortSignal } = require_add_abort_signal(); - var { getHighWaterMark, getDefaultHighWaterMark } = require_state(); var { ERR_INVALID_ARG_TYPE, ERR_METHOD_NOT_IMPLEMENTED, @@ -5225,6 +5234,8 @@ var require_stream = __commonJS({ Stream._uint8ArrayToBuffer = function _uint8ArrayToBuffer(chunk) { return new Buffer(chunk.buffer, chunk.byteOffset, chunk.byteLength); }; + Stream.setDefaultHighWaterMark = setDefaultHighWaterMark; + Stream.getDefaultHighWaterMark = getDefaultHighWaterMark; }, }); @@ -5232,9 +5243,8 @@ var require_stream = __commonJS({ * Bun native stream wrapper * * This glue code lets us avoid using ReadableStreams to wrap Bun internal streams - * */ -function createNativeStreamReadable(nativeType, Readable) { +function createNativeStreamReadable(Readable) { var closer = [false]; var handleNumberResult = function (nativeReadable, result, view, isClosed) { if (result > 0) { @@ -5468,8 +5478,9 @@ var nativeReadableStreamPrototypes = { 4: undefined, 5: undefined, }; + function getNativeReadableStreamPrototype(nativeType, Readable) { - return (nativeReadableStreamPrototypes[nativeType] ||= createNativeStreamReadable(nativeType, Readable)); + return (nativeReadableStreamPrototypes[nativeType] ??= createNativeStreamReadable(Readable)); } function getNativeReadableStream(Readable, stream, options) { @@ -5488,8 +5499,8 @@ function getNativeReadableStream(Readable, stream, options) { stream.$disturbed = true; return new NativeReadable(ptr, options); } -/** --- Bun native stream wrapper --- */ +/** --- Bun native stream wrapper --- */ var Readable = require_readable(); var Writable = require_writable(); var Duplex = require_duplex(); @@ -5539,7 +5550,6 @@ function NativeWritable_lazyConstruct(stream) { } } -const WritablePrototypeWrite = Writable.prototype.write; function NativeWritablePrototypeWrite(chunk, encoding, cb) { var fileSink = this[_fileSink] ?? NativeWritable_lazyConstruct(this); var result = fileSink.write(chunk); @@ -5575,6 +5585,7 @@ function NativeWritablePrototypeWrite(chunk, encoding, cb) { if (cb) cb(null, chunk.byteLength); return true; } + const WritablePrototypeEnd = Writable.prototype.end; NativeWritable.prototype.end = function end(chunk, encoding, cb, native) { return WritablePrototypeEnd.$call(this, chunk, encoding, cb, native ?? this[_native]); diff --git a/src/js/node/tls.js b/src/js/node/tls.js index 1d73234c9ee7a..35572a26fce4f 100644 --- a/src/js/node/tls.js +++ b/src/js/node/tls.js @@ -3,7 +3,7 @@ const { isArrayBufferView, isTypedArray } = require("node:util/types"); const net = require("node:net"); const { Server: NetServer, [Symbol.for("::bunternal::")]: InternalTCPSocket } = net; const bunSocketInternal = Symbol.for("::bunnetsocketinternal::"); -const { rootCertificates, canonicalizeIP } = $lazy("internal/tls"); +const { rootCertificates, canonicalizeIP } = $cpp("NodeTLS.cpp", "createNodeTLSBinding"); const SymbolReplace = Symbol.replace; const RegExpPrototypeSymbolReplace = RegExp.prototype[SymbolReplace]; diff --git a/src/js/node/tty.ts b/src/js/node/tty.ts index fdd00cf230d0d..32ee24775f9ff 100644 --- a/src/js/node/tty.ts +++ b/src/js/node/tty.ts @@ -1,4 +1,8 @@ -const { setRawMode: ttySetMode, isTTY: isatty, getWindowSize: _getWindowSize } = $lazy("tty"); +const { + setRawMode: ttySetMode, + isatty, + getWindowSize: _getWindowSize, +} = $cpp("ProcessBindingTTYWrap.cpp", "createBunTTYFunctions"); // primordials const NumberIsInteger = Number.isInteger; diff --git a/src/js/node/url.js b/src/js/node/url.js index 554179c23cbd3..156f030abd03d 100644 --- a/src/js/node/url.js +++ b/src/js/node/url.js @@ -24,6 +24,7 @@ "use strict"; const { URL, URLSearchParams } = globalThis; +const [domainToASCII, domainToUnicode] = $cpp("NodeURL.cpp", "Bun::createNodeURLBinding"); function Url() { this.protocol = null; @@ -822,11 +823,6 @@ function urlToHttpOptions(url) { return options; } -const pathToFileURL = $lazy("pathToFileURL"); -const fileURLToPath = $lazy("fileURLToPath"); -const domainToASCII = $lazy("domainToASCII"); -const domainToUnicode = $lazy("domainToUnicode"); - export default { parse: urlParse, resolve: urlResolve, @@ -835,8 +831,8 @@ export default { Url, URLSearchParams, URL, - pathToFileURL, - fileURLToPath, + pathToFileURL: Bun.pathToFileURL, + fileURLToPath: Bun.fileURLToPath, urlToHttpOptions, domainToASCII, domainToUnicode, diff --git a/src/js/node/util.js b/src/js/node/util.js index 1c3f3cc4ab4e5..d9899166d4d5b 100644 --- a/src/js/node/util.js +++ b/src/js/node/util.js @@ -16,7 +16,7 @@ const deepEquals = Bun.deepEquals; const isDeepStrictEqual = (a, b) => deepEquals(a, b, true); var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors; -const { parseArgs } = $lazy("util"); +const parseArgs = $newZigFunction("parse_args.zig", "parseArgs", 1); const inspect = utl.inspect; const formatWithOptions = utl.formatWithOptions; diff --git a/src/js/node/vm.ts b/src/js/node/vm.ts index 48bd60f929d20..6cf4a90cf00a4 100644 --- a/src/js/node/vm.ts +++ b/src/js/node/vm.ts @@ -1,7 +1,7 @@ // Hardcoded module "node:vm" const { throwNotImplemented } = require("internal/shared"); -const vm = $lazy("vm"); +const vm = $cpp("NodeVM.cpp", "Bun::createNodeVMBinding"); const { createContext, isContext, Script, runInNewContext, runInThisContext } = vm; diff --git a/src/js/node/wasi.js b/src/js/node/wasi.js index e96485f838051..53a7448333757 100644 --- a/src/js/node/wasi.js +++ b/src/js/node/wasi.js @@ -768,7 +768,7 @@ var require_wasi = __commonJS({ }, randomFillSync: array => crypto.getRandomValues(array), isTTY: fd => require("node:tty").isatty(fd), - fs: Bun.fs(), + fs: require("node:fs"), path: require("node:path"), }; diff --git a/src/js/node/worker_threads.ts b/src/js/node/worker_threads.ts index bbafed0eba9a4..7754f244dabd3 100644 --- a/src/js/node/worker_threads.ts +++ b/src/js/node/worker_threads.ts @@ -10,7 +10,7 @@ const { MessageChannel, BroadcastChannel, Worker: WebWorker } = globalThis; const SHARE_ENV = Symbol("nodejs.worker_threads.SHARE_ENV"); const isMainThread = Bun.isMainThread; -let [_workerData, _threadId, _receiveMessageOnPort] = $lazy("worker_threads"); +const { 0: _workerData, 1: _threadId, 2: _receiveMessageOnPort } = $cpp("Worker.cpp", "createNodeWorkerThreadsBinding"); type NodeWorkerOptions = import("node:worker_threads").WorkerOptions; @@ -317,6 +317,7 @@ class Worker extends EventEmitter { throwNotImplemented("worker_threads.Worker.getHeapSnapshot"); } } + export default { Worker, workerData, diff --git a/src/js/private.d.ts b/src/js/private.d.ts index d5a11ede86a0b..9b4474c031b46 100644 --- a/src/js/private.d.ts +++ b/src/js/private.d.ts @@ -5,6 +5,11 @@ type BunFSWatchOptions = { encoding?: BufferEncoding; persistent?: boolean; recu type BunWatchEventType = "rename" | "change" | "error" | "close"; type BunWatchListener = (event: WatchEventType, filename: T | undefined) => void; +/** + * If this is not tree-shaken away, the bundle will fail. + */ +declare function $bundleError(...message: any[]): never; + interface BunFSWatcher { /** * Stop watching for changes on the given `BunFSWatcher`. Once stopped, the `BunFSWatcher` object is no longer usable. @@ -100,9 +105,6 @@ declare module "bun" { var TOML: { parse(contents: string): any; }; - function fs(): BunFS; - function _Os(): typeof import("node:os"); - function _Path(isWin32?: boolean): typeof import("node:path"); function jest(): typeof import("bun:test"); var main: string; var tty: Array<{ hasColors: boolean }>; @@ -147,63 +149,6 @@ declare interface Error { code?: string; } -/** - * Load an internal native module. To see implementation details, open ZigGlobalObject.cpp and cmd+f `static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad` - * - * This is only valid in src/js/ as it is replaced with `globalThis[Symbol.for("Bun.lazy")]` at bundle time. - */ -function $lazy(id: T): BunLazyModules[T]; - -interface BunLazyModules { - "bun:jsc": Omit & { - describe: typeof import("bun:jsc").jscDescribe; - describeArray: typeof import("bun:jsc").jscDescribe; - }; - "bun:stream": { - maybeReadMore: Function; - resume: Function; - emitReadable: Function; - ReadableState: Function; - }; - sqlite: any; - "vm": { - createContext: Function; - isContext: Function; - Script: typeof import("node:vm").Script; - runInNewContext: Function; - runInThisContext: Function; - }; - /** typeof === 'undefined', but callable -> throws not implemented */ - "masqueradesAsUndefined": (...args: any) => any; - pathToFileURL: typeof import("node:url").pathToFileURL; - fileURLToPath: typeof import("node:url").fileURLToPath; - noop: { - getterSetter: any; - function: any; - functionRegular: any; - callback: any; - }; - "async_hooks": { - cleanupLater: () => void; - setAsyncHooksEnabled: (enabled: boolean) => void; - }; - "worker_threads": [workerData: any, threadId: number, _receiveMessageOnPort: (port: MessagePort) => any]; - "tty": { - ttySetMode: (fd: number, mode: number) => number; - isatty: (fd: number) => boolean; - getWindowSize: (fd: number, out: number[2]) => boolean; - }; - "getStringWidth": (str: string) => number; - - // ReadableStream related - [1]: any; - [2]: any; - [4]: any; -} - -/** Assign to this variable in src/js/{bun,node,thirdparty} to act as module.exports */ -declare var $exports: any; - interface CommonJSModuleRecord { $require(id: string, mod: any, args_count: number, args: Array): any; children: CommonJSModuleRecord[]; @@ -215,3 +160,16 @@ interface CommonJSModuleRecord { paths: string[]; require: typeof require; } + +declare function $cpp(filename: NativeFilenameCPP, symbol: string): T; +declare function $zig(filename: NativeFilenameZig, symbol: string): T; +declare function $newCppFunction any>( + filename: NativeFilenameCPP, + symbol: string, + argCount: number, +): T; +declare function $newZigFunction any>( + filename: NativeFilenameZig, + symbol: string, + argCount: number, +): T; diff --git a/src/js/tsconfig.json b/src/js/tsconfig.json index 4d278a7c62655..6c24b69da061c 100644 --- a/src/js/tsconfig.json +++ b/src/js/tsconfig.json @@ -12,15 +12,14 @@ }, "include": [ // - "node", - "bun", - "builtins", - "functions", - "internal", - "thirdparty", - "_codegen", - "builtins.d.ts", - "private.d.ts", - "../../packages/bun-types/index.d.ts" + "./node", + "./bun", + "./builtins", + "./functions", + "./internal", + "./thirdparty", + "./builtins.d.ts", + "./private.d.ts", + "../../build/codegen/WebCoreJSBuiltins.d.ts" ] } diff --git a/src/main.zig b/src/main.zig index 697459c7f5eed..c2b6093580c64 100644 --- a/src/main.zig +++ b/src/main.zig @@ -15,8 +15,8 @@ comptime { std.debug.assert(builtin.target.cpu.arch.endian() == .little); } -pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, addr: ?usize) noreturn { - MainPanicHandler.handle_panic(msg, error_return_trace, addr); +pub fn panic(msg: []const u8, trace: ?*std.builtin.StackTrace, addr: ?usize) noreturn { + MainPanicHandler.handle_panic(msg, trace, addr); } const CrashReporter = @import("./crash_reporter.zig"); diff --git a/src/node-fallbacks/url.js b/src/node-fallbacks/url.js index 9311abad62afc..78cc1e860b1b4 100644 --- a/src/node-fallbacks/url.js +++ b/src/node-fallbacks/url.js @@ -20,7 +20,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. "use strict"; -const { URL, URLSearchParams, [Symbol.for("Bun.lazy")]: lazy } = globalThis; +const { URL, URLSearchParams } = globalThis; function util_isString(arg) { return typeof arg === "string"; @@ -739,28 +739,16 @@ export { urlResolveObject as resolveObject }; export { urlFormat as format }; export { Url as Url }; -export var pathToFileURL; -export var fileURLToPath; -export var domainToASCII; -export var domainToUnicode; - -if (lazy) { - pathToFileURL = lazy("pathToFileURL"); - fileURLToPath = lazy("fileURLToPath"); - domainToASCII = lazy("domainToASCII"); - domainToUnicode = lazy("domainToUnicode"); -} - export default { parse: urlParse, resolve: urlResolve, resolveObject: urlResolveObject, format: urlFormat, Url: Url, - pathToFileURL: pathToFileURL, - fileURLToPath: fileURLToPath, - domainToASCII, - domainToUnicode, + // pathToFileURL: pathToFileURL, + // fileURLToPath: fileURLToPath, + // domainToASCII, + // domainToUnicode, URL, URLSearchParams, }; diff --git a/src/shell/shell.zig b/src/shell/shell.zig index f448d9793a1da..5fdf545c36e1d 100644 --- a/src/shell/shell.zig +++ b/src/shell/shell.zig @@ -196,13 +196,13 @@ pub const GlobalJS = struct { return this.globalThis.bunVM(); } - pub inline fn throwInvalidArguments(this: @This(), comptime fmt: []const u8, args: anytype) bun.shell.ShellErr { + pub inline fn throwInvalidArguments(this: @This(), comptime fmt: []const u8, args: anytype) ShellErr { return .{ .invalid_arguments = .{ .val = std.fmt.allocPrint(this.globalThis.bunVM().allocator, fmt, args) catch bun.outOfMemory() }, }; } - pub inline fn throwTODO(this: @This(), msg: []const u8) bun.shell.ShellErr { + pub inline fn throwTODO(this: @This(), msg: []const u8) ShellErr { return .{ .todo = std.fmt.allocPrint(this.globalThis.bunVM().allocator, "{s}", .{msg}) catch bun.outOfMemory(), }; @@ -212,14 +212,14 @@ pub const GlobalJS = struct { this.globalThis.throwValue(err.toJSC(this.globalThis)); } - pub inline fn handleError(this: @This(), err: anytype, comptime fmt: []const u8) bun.shell.ShellErr { + pub inline fn handleError(this: @This(), err: anytype, comptime fmt: []const u8) ShellErr { const str = std.fmt.allocPrint(this.globalThis.bunVM().allocator, "{s} " ++ fmt, .{@errorName(err)}) catch bun.outOfMemory(); return .{ .custom = str, }; } - pub inline fn throw(this: @This(), comptime fmt: []const u8, args: anytype) bun.shell.ShellErr { + pub inline fn throw(this: @This(), comptime fmt: []const u8, args: anytype) ShellErr { const str = std.fmt.allocPrint(this.globalThis.bunVM().allocator, fmt, args) catch bun.outOfMemory(); return .{ .custom = str, @@ -251,7 +251,7 @@ pub const GlobalJS = struct { return loop.platformEventLoop(); } - pub inline fn actuallyThrow(this: @This(), shellerr: bun.shell.ShellErr) void { + pub inline fn actuallyThrow(this: @This(), shellerr: ShellErr) void { shellerr.throwJS(this.globalThis); } }; @@ -277,21 +277,21 @@ pub const GlobalMini = struct { return this.mini; } - // pub inline fn throwShellErr(this: @This(), shell_err: bun.shell.ShellErr + // pub inline fn throwShellErr(this: @This(), shell_err: ShellErr - pub inline fn throwTODO(this: @This(), msg: []const u8) bun.shell.ShellErr { + pub inline fn throwTODO(this: @This(), msg: []const u8) ShellErr { return .{ .todo = std.fmt.allocPrint(this.mini.allocator, "{s}", .{msg}) catch bun.outOfMemory(), }; } - pub inline fn throwInvalidArguments(this: @This(), comptime fmt: []const u8, args: anytype) bun.shell.ShellErr { + pub inline fn throwInvalidArguments(this: @This(), comptime fmt: []const u8, args: anytype) ShellErr { return .{ .invalid_arguments = .{ .val = std.fmt.allocPrint(this.allocator(), fmt, args) catch bun.outOfMemory() }, }; } - pub inline fn handleError(this: @This(), err: anytype, comptime fmt: []const u8) bun.shell.ShellErr { + pub inline fn handleError(this: @This(), err: anytype, comptime fmt: []const u8) ShellErr { const str = std.fmt.allocPrint(this.mini.allocator, "{s} " ++ fmt, .{@errorName(err)}) catch bun.outOfMemory(); return .{ .custom = str, @@ -316,14 +316,14 @@ pub const GlobalMini = struct { return this.mini.top_level_dir; } - pub inline fn throw(this: @This(), comptime fmt: []const u8, args: anytype) bun.shell.ShellErr { + pub inline fn throw(this: @This(), comptime fmt: []const u8, args: anytype) ShellErr { const str = std.fmt.allocPrint(this.allocator(), fmt, args) catch bun.outOfMemory(); return .{ .custom = str, }; } - pub inline fn actuallyThrow(this: @This(), shellerr: bun.shell.ShellErr) void { + pub inline fn actuallyThrow(this: @This(), shellerr: ShellErr) void { _ = this; // autofix shellerr.throwMini(); } @@ -2800,7 +2800,7 @@ pub fn handleTemplateValue( const idx = out_jsobjs.items.len; template_value.protect(); try out_jsobjs.append(template_value); - const slice = try std.fmt.bufPrint(jsobjref_buf[0..], "{s}{d}", .{ bun.shell.LEX_JS_OBJREF_PREFIX, idx }); + const slice = try std.fmt.bufPrint(jsobjref_buf[0..], "{s}{d}", .{ LEX_JS_OBJREF_PREFIX, idx }); try out_script.appendSlice(slice); return true; } @@ -3188,3 +3188,163 @@ pub fn needsEscapeUtf8AsciiLatin1Slow(str: []const u8) bool { return false; } pub const ExitCode = eval.ExitCode; + +/// Used in JS tests, see `internal-for-testing.ts` and shell tests. +pub const TestingAPIs = struct { + pub fn shellLex( + globalThis: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSC.JSValue { + const arguments_ = callframe.arguments(1); + var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); + const string_args = arguments.nextEat() orelse { + globalThis.throw("shell_parse: expected 2 arguments, got 0", .{}); + return JSC.JSValue.jsUndefined(); + }; + + var arena = std.heap.ArenaAllocator.init(bun.default_allocator); + defer arena.deinit(); + + const template_args = callframe.argumentsPtr()[1..callframe.argumentsCount()]; + var stack_alloc = std.heap.stackFallback(@sizeOf(bun.String) * 4, arena.allocator()); + var jsstrings = std.ArrayList(bun.String).initCapacity(stack_alloc.get(), 4) catch { + globalThis.throwOutOfMemory(); + return .undefined; + }; + defer { + for (jsstrings.items[0..]) |bunstr| { + bunstr.deref(); + } + jsstrings.deinit(); + } + var jsobjs = std.ArrayList(JSValue).init(arena.allocator()); + defer { + for (jsobjs.items) |jsval| { + jsval.unprotect(); + } + } + + var script = std.ArrayList(u8).init(arena.allocator()); + if (!(shellCmdFromJS(globalThis, string_args, template_args, &jsobjs, &jsstrings, &script) catch { + globalThis.throwOutOfMemory(); + return JSValue.undefined; + })) { + return .undefined; + } + + const lex_result = brk: { + if (bun.strings.isAllASCII(script.items[0..])) { + var lexer = LexerAscii.new(arena.allocator(), script.items[0..], jsstrings.items[0..]); + lexer.lex() catch |err| { + globalThis.throwError(err, "failed to lex shell"); + return JSValue.undefined; + }; + break :brk lexer.get_result(); + } + var lexer = LexerUnicode.new(arena.allocator(), script.items[0..], jsstrings.items[0..]); + lexer.lex() catch |err| { + globalThis.throwError(err, "failed to lex shell"); + return JSValue.undefined; + }; + break :brk lexer.get_result(); + }; + + if (lex_result.errors.len > 0) { + const str = lex_result.combineErrors(arena.allocator()); + globalThis.throwPretty("{s}", .{str}); + return .undefined; + } + + var test_tokens = std.ArrayList(Test.TestToken).initCapacity(arena.allocator(), lex_result.tokens.len) catch { + globalThis.throwOutOfMemory(); + return JSValue.undefined; + }; + for (lex_result.tokens) |tok| { + const test_tok = Test.TestToken.from_real(tok, lex_result.strpool); + test_tokens.append(test_tok) catch { + globalThis.throwOutOfMemory(); + return JSValue.undefined; + }; + } + + const str = std.json.stringifyAlloc(globalThis.bunVM().allocator, test_tokens.items[0..], .{}) catch { + globalThis.throwOutOfMemory(); + return JSValue.undefined; + }; + + defer globalThis.bunVM().allocator.free(str); + var bun_str = bun.String.fromBytes(str); + return bun_str.toJS(globalThis); + } + + pub fn shellParse( + globalThis: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSC.JSValue { + const arguments_ = callframe.arguments(1); + var arguments = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments_.slice()); + const string_args = arguments.nextEat() orelse { + globalThis.throw("shell_parse: expected 2 arguments, got 0", .{}); + return JSC.JSValue.jsUndefined(); + }; + + var arena = bun.ArenaAllocator.init(bun.default_allocator); + defer arena.deinit(); + + const template_args = callframe.argumentsPtr()[1..callframe.argumentsCount()]; + var stack_alloc = std.heap.stackFallback(@sizeOf(bun.String) * 4, arena.allocator()); + var jsstrings = std.ArrayList(bun.String).initCapacity(stack_alloc.get(), 4) catch { + globalThis.throwOutOfMemory(); + return .undefined; + }; + defer { + for (jsstrings.items[0..]) |bunstr| { + bunstr.deref(); + } + jsstrings.deinit(); + } + var jsobjs = std.ArrayList(JSValue).init(arena.allocator()); + defer { + for (jsobjs.items) |jsval| { + jsval.unprotect(); + } + } + var script = std.ArrayList(u8).init(arena.allocator()); + if (!(shellCmdFromJS(globalThis, string_args, template_args, &jsobjs, &jsstrings, &script) catch { + globalThis.throwOutOfMemory(); + return JSValue.undefined; + })) { + return .undefined; + } + + var out_parser: ?Parser = null; + var out_lex_result: ?LexResult = null; + + const script_ast = Interpreter.parse(&arena, script.items[0..], jsobjs.items[0..], jsstrings.items[0..], &out_parser, &out_lex_result) catch |err| { + if (err == ParseError.Lex) { + std.debug.assert(out_lex_result != null); + const str = out_lex_result.?.combineErrors(arena.allocator()); + globalThis.throwPretty("{s}", .{str}); + return .undefined; + } + + if (out_parser) |*p| { + const errstr = p.combineErrors(); + globalThis.throwPretty("{s}", .{errstr}); + return .undefined; + } + + globalThis.throwError(err, "failed to lex/parse shell"); + return .undefined; + }; + + const str = std.json.stringifyAlloc(globalThis.bunVM().allocator, script_ast, .{}) catch { + globalThis.throwOutOfMemory(); + return JSValue.undefined; + }; + + defer globalThis.bunVM().allocator.free(str); + var bun_str = bun.String.fromBytes(str); + return bun_str.toJS(globalThis); + } +}; diff --git a/src/string.zig b/src/string.zig index dd34b813a71c5..f0a19f15e7612 100644 --- a/src/string.zig +++ b/src/string.zig @@ -1183,7 +1183,7 @@ pub const String = extern struct { return try concat(strings.len, allocator, strings); } - pub export fn BunString__getStringWidth(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + pub export fn jsGetStringWidth(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { const args = callFrame.arguments(1).slice(); if (args.len == 0 or !args.ptr[0].isString()) { diff --git a/test/js/bun/shell/lex.test.ts b/test/js/bun/shell/lex.test.ts index dc2274b1ab20a..a92185c1fbc27 100644 --- a/test/js/bun/shell/lex.test.ts +++ b/test/js/bun/shell/lex.test.ts @@ -1,5 +1,7 @@ import { $ } from "bun"; import { TestBuilder, redirect } from "./util"; +import { shellInternals } from "bun:internal-for-testing"; +const { lex } = shellInternals; const BUN = process.argv0; @@ -8,12 +10,12 @@ $.nothrow(); describe("lex shell", () => { test("basic", () => { const expected = [{ "Text": "next" }, { "Delimit": {} }, { "Text": "dev" }, { "Delimit": {} }, { "Eof": {} }]; - const result = JSON.parse($.lex`next dev`); + const result = JSON.parse(lex`next dev`); expect(result).toEqual(expected); }); test("var edgecase", () => { - expect(JSON.parse($.lex`$PWD/test.txt`)).toEqual([ + expect(JSON.parse(lex`$PWD/test.txt`)).toEqual([ { "Var": "PWD" }, { "Text": "/test.txt" }, { "Delimit": {} }, @@ -30,7 +32,7 @@ describe("lex shell", () => { { "Var": "PORT" }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`next dev $PORT`); + const result = JSON.parse(lex`next dev $PORT`); expect(result).toEqual(expected); }); @@ -43,7 +45,7 @@ describe("lex shell", () => { { "Var": "PORT" }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`next dev "$PORT"`); + const result = JSON.parse(lex`next dev "$PORT"`); expect(result).toEqual(expected); }); @@ -57,7 +59,7 @@ describe("lex shell", () => { { "Var": "PORT" }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`next dev foo"$PORT"`); + const result = JSON.parse(lex`next dev foo"$PORT"`); expect(result).toEqual(expected); }); @@ -71,7 +73,7 @@ describe("lex shell", () => { { "Text": "NICE" }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`echo foo"$NICE"good"NICE"`); + const result = JSON.parse(lex`echo foo"$NICE"good"NICE"`); expect(result).toEqual(expected); }); @@ -92,7 +94,7 @@ describe("lex shell", () => { { "Text": "NICE;" }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`echo foo; bar baz; echo "NICE;"`); + const result = JSON.parse(lex`echo foo; bar baz; echo "NICE;"`); expect(result).toEqual(expected); }); @@ -106,7 +108,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`next dev 'hello how is it going'`); + const result = JSON.parse(lex`next dev 'hello how is it going'`); expect(result).toEqual(expected); }); @@ -126,7 +128,7 @@ describe("lex shell", () => { { "Var": "FULLNAME" }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`NAME=zack FULLNAME="$NAME radisic" LOL= ; echo $FULLNAME`); + const result = JSON.parse(lex`NAME=zack FULLNAME="$NAME radisic" LOL= ; echo $FULLNAME`); expect(result).toEqual(expected); }); @@ -158,7 +160,7 @@ describe("lex shell", () => { Eof: {}, }, ]; - const result = JSON.parse($.lex`NAME=zack foo=$bar echo $NAME`); + const result = JSON.parse(lex`NAME=zack foo=$bar echo $NAME`); expect(result).toEqual(expected); }); @@ -198,7 +200,7 @@ describe("lex shell", () => { Eof: {}, }, ]; - const result = JSON.parse($.lex`export NAME=zack FOO=bar export NICE=lmao`); + const result = JSON.parse(lex`export NAME=zack FOO=bar export NICE=lmao`); // console.log(result); expect(result).toEqual(expected); }); @@ -218,7 +220,7 @@ describe("lex shell", () => { { "BraceEnd": {} }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`echo {ts,tsx,js,jsx}`); + const result = JSON.parse(lex`echo {ts,tsx,js,jsx}`); expect(result).toEqual(expected); }); @@ -235,7 +237,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`echo foo && echo bar`); + const result = JSON.parse(lex`echo foo && echo bar`); expect(result).toEqual(expected); }); @@ -252,7 +254,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`echo foo || echo bar`); + const result = JSON.parse(lex`echo foo || echo bar`); expect(result).toEqual(expected); }); @@ -269,7 +271,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`echo foo | echo bar`); + const result = JSON.parse(lex`echo foo | echo bar`); expect(result).toEqual(expected); }); @@ -286,7 +288,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - const result = JSON.parse($.lex`echo foo & echo bar`); + const result = JSON.parse(lex`echo foo & echo bar`); expect(result).toEqual(expected); }); @@ -305,7 +307,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - let result = JSON.parse($.lex`echo foo > cat secrets.txt`); + let result = JSON.parse(lex`echo foo > cat secrets.txt`); expect(result).toEqual(expected); expected = [ @@ -325,7 +327,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - result = $.lex`cmd1 0> file.txt`; + result = lex`cmd1 0> file.txt`; expect(JSON.parse(result)).toEqual(expected); expected = [ @@ -345,7 +347,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - result = $.lex`cmd1 1> file.txt`; + result = lex`cmd1 1> file.txt`; expect(JSON.parse(result)).toEqual(expected); expected = [ @@ -365,7 +367,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - result = $.lex`cmd1 2> file.txt`; + result = lex`cmd1 2> file.txt`; expect(JSON.parse(result)).toEqual(expected); expected = [ @@ -385,7 +387,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - result = $.lex`cmd1 &> file.txt`; + result = lex`cmd1 &> file.txt`; expect(JSON.parse(result)).toEqual(expected); expected = [ @@ -405,7 +407,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - result = $.lex`cmd1 1>> file.txt`; + result = lex`cmd1 1>> file.txt`; expect(JSON.parse(result)).toEqual(expected); expected = [ @@ -425,7 +427,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - result = $.lex`cmd1 2>> file.txt`; + result = lex`cmd1 2>> file.txt`; expect(JSON.parse(result)).toEqual(expected); expected = [ @@ -445,7 +447,7 @@ describe("lex shell", () => { { "Delimit": {} }, { "Eof": {} }, ]; - result = $.lex`cmd1 &>> file.txt`; + result = lex`cmd1 &>> file.txt`; expect(JSON.parse(result)).toEqual(expected); }); @@ -496,7 +498,7 @@ describe("lex shell", () => { ]; const buffer = new Uint8Array(1 << 20); const buffer2 = new Uint8Array(1 << 20); - const result = JSON.parse($.lex`echo foo > ${buffer} && echo lmao > ${buffer2}`); + const result = JSON.parse(lex`echo foo > ${buffer} && echo lmao > ${buffer2}`); expect(result).toEqual(expected); }); @@ -531,7 +533,7 @@ describe("lex shell", () => { }, ]; - const result = $.lex`echo foo $(ls)`; + const result = lex`echo foo $(ls)`; expect(JSON.parse(result)).toEqual(expected); }); @@ -597,7 +599,7 @@ describe("lex shell", () => { }, ]; - const result = $.lex`echo foo $(ls $(ls) $(ls))`; + const result = lex`echo foo $(ls $(ls) $(ls))`; // console.log(JSON.parse(result)); expect(JSON.parse(result)).toEqual(expected); }); @@ -633,7 +635,7 @@ describe("lex shell", () => { }, ]; - const result = $.lex`echo $(FOO=bar $FOO)`; + const result = lex`echo $(FOO=bar $FOO)`; expect(JSON.parse(result)).toEqual(expected); }); @@ -669,7 +671,7 @@ describe("lex shell", () => { }, ]; - const result = $.lex`echo $(FOO=bar $FOO)NICE`; + const result = lex`echo $(FOO=bar $FOO)NICE`; expect(JSON.parse(result)).toEqual(expected); }); @@ -705,7 +707,7 @@ describe("lex shell", () => { }, ]; - const result = $.lex`echo foo \`ls\``; + const result = lex`echo foo \`ls\``; expect(JSON.parse(result)).toEqual(expected); }); diff --git a/test/js/bun/shell/parse.test.ts b/test/js/bun/shell/parse.test.ts index 132348160d438..0496dd778273c 100644 --- a/test/js/bun/shell/parse.test.ts +++ b/test/js/bun/shell/parse.test.ts @@ -1,7 +1,6 @@ -import { $ } from "bun"; import { TestBuilder, redirect } from "./util"; - -const BUN = process.argv0; +import { shellInternals } from "bun:internal-for-testing"; +const { parse } = shellInternals; describe("parse shell", () => { test("basic", () => { @@ -33,7 +32,7 @@ describe("parse shell", () => { ], }; - const result = $.parse`echo foo`; + const result = parse`echo foo`; expect(JSON.parse(result)).toEqual(expected); }); @@ -55,7 +54,7 @@ describe("parse shell", () => { ], }; - const result = JSON.parse($.parse`echo foo > lmao.txt`); + const result = JSON.parse(parse`echo foo > lmao.txt`); expect(result).toEqual(expected); }); @@ -85,7 +84,7 @@ describe("parse shell", () => { ], }; - const result = JSON.parse($.parse`"FOO $NICE!"`); + const result = JSON.parse(parse`"FOO $NICE!"`); console.log("Result", JSON.stringify(result)); expect(result).toEqual(expected); }); @@ -121,7 +120,7 @@ describe("parse shell", () => { }, ], }; - const result = JSON.parse($.parse`echo > foo.txt | echo hi`); + const result = JSON.parse(parse`echo > foo.txt | echo hi`); // console.log(result); expect(result).toEqual(expected); }); @@ -169,7 +168,7 @@ describe("parse shell", () => { }, ], }; - const result = JSON.parse($.parse`echo foo && echo bar || echo lmao`); + const result = JSON.parse(parse`echo foo && echo bar || echo lmao`); // console.log(result); expect(result).toEqual(expected); }); @@ -237,7 +236,7 @@ describe("parse shell", () => { ], }; - const result = $.parse`FOO=bar && echo foo && echo bar | echo lmao | cat > foo.txt`; + const result = parse`FOO=bar && echo foo && echo bar | echo lmao | cat > foo.txt`; // console.log(result); expect(JSON.parse(result)).toEqual(expected); }); @@ -270,7 +269,7 @@ describe("parse shell", () => { ], }; - const result = JSON.parse($.parse`FOO=bar BAR=baz export LMAO=nice`); + const result = JSON.parse(parse`FOO=bar BAR=baz export LMAO=nice`); console.log("Result", JSON.stringify(result)); expect(result).toEqual(expected); }); @@ -308,7 +307,7 @@ describe("parse shell", () => { const buffer = new Uint8Array(1 << 20); const buffer2 = new Uint8Array(1 << 20); - const result = JSON.parse($.parse`echo foo > ${buffer} && echo foo > ${buffer2}`); + const result = JSON.parse(parse`echo foo > ${buffer} && echo foo > ${buffer2}`); // console.log("Result", JSON.stringify(result)); expect(result).toEqual(expected); @@ -390,7 +389,7 @@ describe("parse shell", () => { ], }; - const result = JSON.parse($.parse`echo "$(echo 1; echo 2)"`); + const result = JSON.parse(parse`echo "$(echo 1; echo 2)"`); expect(result).toEqual(expected); }); @@ -470,7 +469,7 @@ describe("parse shell", () => { }, ], }; - const result = JSON.parse($.parse`echo $(ls foo) && echo nice`); + const result = JSON.parse(parse`echo $(ls foo) && echo nice`); expect(result).toEqual(expected); }); @@ -536,7 +535,7 @@ describe("parse shell", () => { }, ], }; - const result = JSON.parse($.parse`echo $(FOO=bar $FOO)`); + const result = JSON.parse(parse`echo $(FOO=bar $FOO)`); expect(result).toEqual(expected); }); @@ -575,7 +574,7 @@ describe("parse shell", () => { }, ], }; - const result = JSON.parse($.parse`FOO=bar BAR=baz; BUN_DEBUG_QUIET_LOGS=1 echo`); + const result = JSON.parse(parse`FOO=bar BAR=baz; BUN_DEBUG_QUIET_LOGS=1 echo`); expect(result).toEqual(expected); }); }); diff --git a/test/js/bun/sqlite/sql-raw.test.js b/test/js/bun/sqlite/sql-raw.test.js index c571cbcc78864..ccdf2ff5eb2f6 100644 --- a/test/js/bun/sqlite/sql-raw.test.js +++ b/test/js/bun/sqlite/sql-raw.test.js @@ -1,6 +1,6 @@ import { expect, it } from "bun:test"; +import { SQL } from "bun:internal-for-testing"; -var SQL = globalThis[Symbol.for("Bun.lazy")]("sqlite"); const dbPath = import.meta.dir + "/northwind.testdb"; it("works", () => { diff --git a/test/js/bun/util/highlighter.test.ts b/test/js/bun/util/highlighter.test.ts index 422d6bfe50ff0..c2ae8201a03da 100644 --- a/test/js/bun/util/highlighter.test.ts +++ b/test/js/bun/util/highlighter.test.ts @@ -1,7 +1,5 @@ import { test, expect } from "bun:test"; - -// @ts-expect-error -const highlighter: (code: string) => string = globalThis[Symbol.for("Bun.lazy")]("unstable_syntaxHighlight"); +import { quickAndDirtyJavaScriptSyntaxHighlighter as highlighter } from "bun:internal-for-testing"; test("highlighter", () => { expect(highlighter("`can do ${123} ${'123'} ${`123`}`").length).toBeLessThan(150); diff --git a/test/js/node/tls/node-tls-internals.test.ts b/test/js/node/tls/node-tls-internals.test.ts index f67c39e23acf7..af635869eb297 100644 --- a/test/js/node/tls/node-tls-internals.test.ts +++ b/test/js/node/tls/node-tls-internals.test.ts @@ -1,15 +1,11 @@ import { createTest } from "node-harness"; -const { describe, expect, it } = createTest(import.meta.path); -//@ts-ignore -const $lazy = globalThis[Symbol.for("Bun.lazy")]; -const tlsInternals = $lazy("internal/tls"); +import { TLSBinding } from "bun:internal-for-testing"; +const { describe, expect } = createTest(import.meta.path); -describe("node/tls", () => { - // this is only exposed on debug builds so we skip on release builds - const test = tlsInternals ? it : it.skip; +const { canonicalizeIP, rootCertificates } = TLSBinding; +describe("NodeTLS.cpp", () => { test("canonicalizeIP", () => { - const { canonicalizeIP } = tlsInternals; expect(canonicalizeIP("127.0.0.1")).toBe("127.0.0.1"); expect(canonicalizeIP("10.1.0.1")).toBe("10.1.0.1"); expect(canonicalizeIP("::1")).toBe("::1"); @@ -29,7 +25,6 @@ describe("node/tls", () => { }); test("rootCertificates", () => { - const { rootCertificates } = tlsInternals; expect(rootCertificates).toBeInstanceOf(Array); expect(rootCertificates.length).toBeGreaterThan(0); expect(typeof rootCertificates[0]).toBe("string"); diff --git a/test/tsconfig.json b/test/tsconfig.json index 248cb6747785c..cc96fd4e479fa 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,5 +1,5 @@ { - "include": [".", "../packages/bun-types/index.d.ts"], + "include": [".", "../packages/bun-types/index.d.ts", "./testing-internals.d.ts"], "compilerOptions": { "lib": ["ESNext"], "module": "ESNext", @@ -25,6 +25,7 @@ "mkfifo": ["mkfifo.ts"], "node-harness": ["js/node/harness.ts"], "deno:harness": ["js/deno/harness.ts"], + "bun:internal-for-testing": ["../src/js/internal-for-testing.ts"], "foo/bar": ["js/bun/resolve/baz.js"], "@faasjs/*": ["js/bun/resolve/*.js", "js/bun/resolve/*/src/index.js"], "@faasjs/larger/*": ["js/bun/resolve/*/larger-index.js"]