From 7a4e0158d62564edebafd5c8b0b51ca07b54fe4c Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 14 Nov 2023 07:10:09 +0100 Subject: [PATCH] Lots of stuff (#7027) * Use debug mode by default * Enable build with assertions enabled * Update cli.zig * Update bun-linux-build.yml * Fixes * Fix `ASSERT_ENABLED` * try this * Update Dockerfile * mimalloc debug * Update CMakeLists.txt * `Bun.deepMatch` - fix assertion failures cc @dylan-conway, looks like we need to use `putDirectMayBeIndex` and check for `isCell` more carefully. * Object.create support in code generator and callbacks wrapper * Remove unused file * zig upgrade * zls * Fix various errors * Support `BuiltinAccessor` in create_hash_table script * Fix assertion failure in `process.mainModule` * Fix assertion failure in `onerror` * Fix assertion failure when creating a Worker * Fix asssertion failure when loading lots of files in bun test * Fix assertion failure when termating a `Worker` * Add helper for converting BunString to a WTFString * Fix assertion failure in notifyNeedTermination * Add more debug logs in `bun test` * Fix compiler warning in usockets * Fix assertion failure with `Worker` termination (another) * Fix assertion failure in `coerceToInt64` * Fix assertion failure in `BroadcastChannel` * Fix assertion failure in `Headers.prototype.getAll` * Fixes #7067 * Add heap analyzer label for CommonJS modules * Fix assertion failure in module.require && module.require.resolve * Remove unused code * Fix assertion failure in debugger * Fix crash in debugger * Fix assertion failures in bun:sqlite * Bump zig * Bump WebKit * Fix assertion failure in JSPromise::reject && JSInternalPromise::reject * Fix assertion failure in ReadableStream::cancel * Fix assertion failure in AsyncContextFrame::create * Fix assertion failure in bun:sqlite * Fix assertion failure in mocks * Fix assertion failure in ServerWebSocket.close * Fix assertion failure in N-API with subclasses * [napi] Make promises cheaper * undo * Don't check for exceptions in ObjectInitializationScope * Add separate entry point for test runner that doesn't generate code * Don't deref builtin code * Fix preload test * Fix assertion failure in memoryUsage() * Fix preload test, part 2 * Ensure that the env map for a Worker is empty after it is used * The pointer for the Arena allocator used in parsing should not change * Terminate thread on exit * Start to implement scriptExecutionStatus * Update worker.test.ts * Fix Dirent.name setter * Update settings.json * Fix assertion failure in node:http * Use correct value for `JSFinalObject::maxInlineCapacity` * JSFinalObject::maxInlineCapacity x2 * Don't strip when assertions are enabled * Make `m_wasTerminated` atomic * Preserve directives in the transpiler cc @ctjlewis * Workaround assertion failure in ServerWebSocket.sendBinary and ServerWebSocket.sendText * windows * Buffer lockfile serialization in-memory * PR feedback * PR feedback * PR feedback * Windows * quotes * Update CMakeLists.txt * Update bun-linux-build.yml * Update bun-linux-build.yml * Move this code to BunString.cpp * Update BunString.cpp --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- .github/workflows/bun-linux-build.yml | 38 +- .github/workflows/bun-mac-aarch64.yml | 6 +- .github/workflows/zig-fmt.yml | 2 +- .vscode/settings.json | 1 + CMakeLists.txt | 42 +- Dockerfile | 94 +++- Makefile | 2 +- build.zig | 19 +- bun.lockb | Bin 72172 -> 72173 bytes docs/project/building-windows.md | 2 +- docs/project/contributing.md | 2 +- package.json | 1 + packages/bun-usockets/src/crypto/openssl.c | 5 +- scripts/setup-zig.ps1 | 2 +- scripts/setup.sh | 2 +- src/analytics/analytics_thread.zig | 16 +- src/bun.js/api/bun/socket.zig | 15 +- src/bun.js/api/ffi.zig | 2 +- src/bun.js/api/server.classes.ts | 26 +- src/bun.js/api/server.zig | 28 +- src/bun.js/bindings/AsyncContextFrame.cpp | 3 +- src/bun.js/bindings/BunDebugger.cpp | 71 +-- src/bun.js/bindings/BunPlugin.cpp | 10 +- src/bun.js/bindings/BunProcess.cpp | 136 +++--- src/bun.js/bindings/BunString.cpp | 110 +++-- src/bun.js/bindings/CommonJSModuleRecord.cpp | 39 +- src/bun.js/bindings/CommonJSModuleRecord.h | 3 + .../bindings/InternalModuleRegistry.cpp | 2 +- src/bun.js/bindings/JSBundlerPlugin.cpp | 18 +- src/bun.js/bindings/JSMockFunction.cpp | 2 +- src/bun.js/bindings/ModuleLoader.cpp | 30 +- src/bun.js/bindings/NodeHTTP.cpp | 4 +- src/bun.js/bindings/RegularExpression.cpp | 8 +- src/bun.js/bindings/ZigGlobalObject.cpp | 53 ++- src/bun.js/bindings/ZigGlobalObject.h | 1 + src/bun.js/bindings/ZigSourceProvider.cpp | 6 +- src/bun.js/bindings/bindings.cpp | 107 +++-- src/bun.js/bindings/bindings.zig | 15 +- src/bun.js/bindings/codegen.zig | 33 ++ src/bun.js/bindings/exports.zig | 2 +- src/bun.js/bindings/headers-handwritten.h | 22 +- src/bun.js/bindings/napi.h | 8 +- src/bun.js/bindings/sqlite/JSSQLStatement.cpp | 35 +- .../bindings/webcore/BroadcastChannel.cpp | 1 - src/bun.js/bindings/webcore/HTTPParsers.cpp | 1 - src/bun.js/bindings/webcore/JSDOMFormData.cpp | 4 +- .../bindings/webcore/JSFetchHeaders.cpp | 17 +- .../bindings/webcore/JSURLSearchParams.cpp | 2 +- .../bindings/webcore/ParsedContentType.cpp | 421 ------------------ .../bindings/webcore/ParsedContentType.h | 76 ---- .../bindings/webcore/ReadableStream.cpp | 21 + src/bun.js/bindings/webcore/ReadableStream.h | 1 + src/bun.js/bindings/webcore/WebSocket.cpp | 3 +- src/bun.js/bindings/webcore/Worker.cpp | 35 +- src/bun.js/bindings/webcore/Worker.h | 2 +- .../webcrypto/Bun_base64URLEncodeToString.h | 5 +- src/bun.js/event_loop.zig | 22 + src/bun.js/javascript.zig | 274 ++++++++---- src/bun.js/module_loader.zig | 47 +- src/bun.js/modules/BunJSCModule.h | 6 +- src/bun.js/node/node.classes.ts | 2 + src/bun.js/node/node_os.zig | 4 +- src/bun.js/test/jest.zig | 11 +- src/bun.js/web_worker.zig | 17 +- src/bun.js/webcore/blob.zig | 36 +- src/bun.zig | 21 +- src/bun_dev_http_server.zig | 4 +- src/cli.zig | 7 +- src/cli/test_command.zig | 2 +- src/codegen/class-definitions.ts | 3 + src/codegen/create_hash_table | 10 +- src/codegen/generate-classes.ts | 293 ++++++++++-- src/deps/zig-clap/build.zig | 55 --- src/env.zig | 2 +- src/exact_size_matcher.zig | 12 +- src/fs.zig | 4 +- src/http/websocket.zig | 16 +- src/http/websocket_http_client.zig | 10 +- src/install/install.zig | 2 +- src/install/lockfile.zig | 154 ++++--- src/install/npm.zig | 6 +- src/js/builtins/ProcessObjectInternals.ts | 12 + src/js_ast.zig | 54 ++- src/js_parser.zig | 11 +- src/js_printer.zig | 7 +- src/jsc.zig | 6 +- src/napi/napi.zig | 20 +- src/resolver/resolve_path.zig | 4 +- src/string_immutable.zig | 10 +- src/url.zig | 4 +- src/wyhash.zig | 2 +- test/js/node/fs/fs.test.ts | 7 + test/js/web/worker.test.ts | 3 +- test/transpiler/transpiler.test.js | 22 + 94 files changed, 1580 insertions(+), 1214 deletions(-) create mode 100644 src/bun.js/bindings/codegen.zig delete mode 100644 src/bun.js/bindings/webcore/ParsedContentType.cpp delete mode 100644 src/bun.js/bindings/webcore/ParsedContentType.h delete mode 100644 src/deps/zig-clap/build.zig diff --git a/.github/workflows/bun-linux-build.yml b/.github/workflows/bun-linux-build.yml index c9556afb23..8c8fa002ac 100644 --- a/.github/workflows/bun-linux-build.yml +++ b/.github/workflows/bun-linux-build.yml @@ -45,17 +45,43 @@ jobs: build_arch: amd64 runner: big-ubuntu build_machine_arch: x86_64 + assertions: "OFF" + zig_optimize: "ReleaseFast" + target: "artifact" - cpu: nehalem tag: linux-x64-baseline arch: x86_64 build_arch: amd64 runner: big-ubuntu build_machine_arch: x86_64 + assertions: "OFF" + zig_optimize: "ReleaseFast" + target: "artifact" + - cpu: haswell + tag: linux-x64-assertions + arch: x86_64 + build_arch: amd64 + runner: big-ubuntu + build_machine_arch: x86_64 + assertions: "ON" + zig_optimize: "ReleaseSafe" + target: "artifact-assertions" + - cpu: nehalem + tag: linux-x64-baseline-assertions + arch: x86_64 + build_arch: amd64 + runner: big-ubuntu + build_machine_arch: x86_64 + assertions: "ON" + zig_optimize: "ReleaseSafe" + target: "artifact-assertions" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive + ref: ${{github.sha}} + clean: true - uses: docker/setup-buildx-action@v2 id: buildx with: @@ -84,7 +110,8 @@ jobs: BUILD_MACHINE_ARCH=${{matrix.build_machine_arch}} CPU_TARGET=${{matrix.cpu}} GIT_SHA=${{github.sha}} - + ASSERTIONS=${{matrix.assertions}} + ZIG_OPTIMIZE=${{matrix.zig_optimize}} SCCACHE_BUCKET=bun SCCACHE_REGION=auto SCCACHE_S3_USE_SSL=true @@ -92,7 +119,7 @@ jobs: AWS_ACCESS_KEY_ID=${{ secrets.CACHE_S3_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY=${{ secrets.CACHE_S3_SECRET_ACCESS_KEY }} platforms: linux/${{matrix.build_arch}} - target: artifact + target: ${{matrix.target}} outputs: type=local,dest=${{runner.temp}}/release - name: Zip run: | @@ -187,12 +214,15 @@ jobs: include: - tag: linux-x64 - tag: linux-x64-baseline + - tag: linux-x64-assertions + - tag: linux-x64-baseline-assertions steps: - id: checkout name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: false + clean: true - id: download name: Download uses: actions/download-artifact@v3 diff --git a/.github/workflows/bun-mac-aarch64.yml b/.github/workflows/bun-mac-aarch64.yml index d7123131d7..d247923971 100644 --- a/.github/workflows/bun-mac-aarch64.yml +++ b/.github/workflows/bun-mac-aarch64.yml @@ -239,7 +239,11 @@ jobs: artifact: bun-obj-darwin-aarch64 runner: macos-arm64 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + submodules: recursive + ref: ${{github.sha}} + clean: true - name: Checkout submodules run: git submodule update --init --recursive --depth=1 --progress --force diff --git a/.github/workflows/zig-fmt.yml b/.github/workflows/zig-fmt.yml index dff35ddf03..3f3ac5a186 100644 --- a/.github/workflows/zig-fmt.yml +++ b/.github/workflows/zig-fmt.yml @@ -1,7 +1,7 @@ name: zig-fmt env: - ZIG_VERSION: 0.12.0-dev.1297+a9e66ed73 + ZIG_VERSION: 0.12.0-dev.1571+03adafd80 on: pull_request: diff --git a/.vscode/settings.json b/.vscode/settings.json index 7f69a256a5..60b9fbc4ed 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -68,6 +68,7 @@ "src/deps/c-ares": true, "src/deps/tinycc": true, "src/deps/zstd": true, + "**/*.i": true, "packages/bun-uws/fuzzing/seed-corpus/**/*": true }, "C_Cpp.files.exclude": { diff --git a/CMakeLists.txt b/CMakeLists.txt index d7ee3a41b6..29ab2a77b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_policy(SET CMP0091 NEW) cmake_policy(SET CMP0067 NEW) set(Bun_VERSION "1.0.11") -set(WEBKIT_TAG 7cd84abfa787e4b96b27c3ef1e28a4eb1aa49aa1) +set(WEBKIT_TAG 16badcb5df6b1190051b4b3caa1a5aeb4e2fc441) set(BUN_WORKDIR "${CMAKE_CURRENT_BINARY_DIR}") message(STATUS "Configuring Bun ${Bun_VERSION} in ${BUN_WORKDIR}") @@ -24,11 +24,11 @@ endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(DEBUG ON) - set(ZIG_OPTIMIZE "Debug") + set(DEFAULT_ZIG_OPTIMIZE "Debug") set(bun "bun-debug") elseif(CMAKE_BUILD_TYPE STREQUAL "Release") set(DEBUG OFF) - set(ZIG_OPTIMIZE "ReleaseFast") + set(DEFAULT_ZIG_OPTIMIZE "ReleaseFast") if(WIN32) # lld-link will strip it for you, so we can build directly to bun.exe @@ -216,6 +216,11 @@ if(DEFINED ENV{CI} OR DEFINED ENV{GITHUB_ACTIONS}) endif() set(DEFAULT_USE_STATIC_LIBATOMIC ON) +set(DEFAULT_USE_DEBUG_JSC, OFF) + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(DEFAULT_USE_DEBUG_JSC ON) +endif() if(UNIX AND NOT APPLE) execute_process(COMMAND cat /etc/os-release COMMAND head -n1 OUTPUT_VARIABLE LINUX_DISTRO) @@ -239,7 +244,8 @@ option(USE_CUSTOM_TINYCC "Use Bun's recommended version of tinycc" ON) option(USE_CUSTOM_LIBUV "Use Bun's recommended version of libuv (Windows only)" ON) option(USE_BASELINE_BUILD "Build Bun for baseline (older) CPUs" OFF) -option(USE_DEBUG_JSC "Enable assertions and use a debug build of JavaScriptCore" OFF) +option(ZIG_OPTIMIZE "Optimization level for Zig" ${DEFAULT_ZIG_OPTIMIZE}) +option(USE_DEBUG_JSC "Enable assertions and use a debug build of JavaScriptCore" ${DEFAULT_USE_DEBUG_JSC}) option(USE_UNIFIED_SOURCES "Use unified sources to speed up the build" OFF) option(USE_STATIC_LIBATOMIC "Statically link libatomic, requires the presence of libatomic.a" ${DEFAULT_USE_STATIC_LIBATOMIC}) @@ -247,6 +253,10 @@ if(NOT CANARY) set(CANARY 0) endif() +if(NOT ZIG_OPTIMIZE) + set(ZIG_OPTIMIZE ${DEFAULT_ZIG_OPTIMIZE}) +endif() + set(ERROR_LIMIT 100 CACHE STRING "Maximum number of errors to show when compiling C++ code") set(ARCH x86_64) @@ -362,6 +372,7 @@ if(NOT WEBKIT_DIR) set(ASSERT_ENABLED "0") if(USE_DEBUG_JSC) + add_compile_definitions("BUN_DEBUG=1") set(BUN_WEBKIT_PACKAGE_NAME_SUFFIX "-debug") set(ASSERT_ENABLED "1") elseif(NOT DEBUG AND NOT WIN32) @@ -805,12 +816,13 @@ add_compile_definitions( "NOMINMAX" "IS_BUILD" "BUILDING_JSCONLY__" - "ASSERT_ENABLED=$,1,0>" "BUN_DYNAMIC_JS_LOAD_PATH=\"${BUN_WORKDIR}/js\"" ) if(NOT ASSERT_ENABLED) add_compile_definitions("NDEBUG=1") +else() + add_compile_definitions("ASSERT_ENABLED=1") endif() include_directories( @@ -1019,7 +1031,7 @@ if(APPLE) endif() # --- Stripped Binary "bun" -if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT WIN32) +if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT WIN32 AND NOT ASSERT_ENABLED) # add_custom_command( # TARGET ${bun} # POST_BUILD @@ -1100,11 +1112,21 @@ if(USE_CUSTOM_MIMALLOC) if(WIN32) target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/mimalloc.lib") elseif(APPLE) - # https://github.com/microsoft/mimalloc/issues/512 - # Linking mimalloc via object file on macOS x64 can cause heap corruption - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libmimalloc.a") + if(USE_DEBUG_JSC OR CMAKE_BUILD_TYPE STREQUAL "Debug") + message(STATUS "Using debug mimalloc") + target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libmimalloc-debug.a") + else() + # https://github.com/microsoft/mimalloc/issues/512 + # Linking mimalloc via object file on macOS x64 can cause heap corruption + target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libmimalloc.a") + endif() else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libmimalloc.o") + if(USE_DEBUG_JSC OR CMAKE_BUILD_TYPE STREQUAL "Debug") + message(STATUS "Using debug mimalloc") + target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libmimalloc-debug.a") + else() + target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libmimalloc.o") + endif() endif() else() find_package(mimalloc REQUIRED) diff --git a/Dockerfile b/Dockerfile index 37e301a093..0eebf60c15 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,10 +19,13 @@ ARG GIT_SHA="" ARG BUN_VERSION="bun-v1.0.7" ARG BUN_DOWNLOAD_URL_BASE="https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/${BUN_VERSION}" ARG CANARY=0 +ARG ASSERTIONS=OFF +ARG ZIG_OPTIMIZE=ReleaseFast +ARG CMAKE_BUILD_TYPE=Release ARG NODE_VERSION="20" ARG LLVM_VERSION="16" -ARG ZIG_VERSION="0.12.0-dev.1297+a9e66ed73" +ARG ZIG_VERSION="0.12.0-dev.1571+03adafd80" ARG SCCACHE_BUCKET ARG SCCACHE_REGION @@ -178,6 +181,7 @@ FROM bun-base as mimalloc ARG BUN_DIR ARG CPU_TARGET +ARG ASSERTIONS ENV CPU_TARGET=${CPU_TARGET} COPY Makefile ${BUN_DIR}/Makefile @@ -185,8 +189,23 @@ COPY src/deps/mimalloc ${BUN_DIR}/src/deps/mimalloc ENV CCACHE_DIR=/ccache -RUN --mount=type=cache,target=/ccache cd ${BUN_DIR} && \ - make mimalloc && rm -rf src/deps/mimalloc Makefile +RUN --mount=type=cache,target=/ccache cd ${BUN_DIR} && \ + make mimalloc && rm -rf src/deps/mimalloc Makefile; + +FROM bun-base as mimalloc-debug + +ARG BUN_DIR +ARG CPU_TARGET +ARG ASSERTIONS +ENV CPU_TARGET=${CPU_TARGET} + +COPY Makefile ${BUN_DIR}/Makefile +COPY src/deps/mimalloc ${BUN_DIR}/src/deps/mimalloc + +ENV CCACHE_DIR=/ccache + +RUN --mount=type=cache,target=/ccache cd ${BUN_DIR} && \ + make mimalloc-debug && rm -rf src/deps/mimalloc Makefile; FROM bun-base as zlib @@ -310,18 +329,21 @@ RUN cd $BUN_DIR/src/node-fallbacks \ FROM bun-base as bun-webkit ARG BUILDARCH +ARG ASSERTIONS COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt RUN mkdir ${BUN_DIR}/bun-webkit \ && WEBKIT_TAG=$(grep 'set(WEBKIT_TAG' "${BUN_DIR}/CMakeLists.txt" | awk '{print $2}' | cut -f 1 -d ')') \ - && WEBKIT_URL="https://github.com/oven-sh/WebKit/releases/download/autobuild-${WEBKIT_TAG}/bun-webkit-linux-${BUILDARCH}-lto.tar.gz" \ + && WEBKIT_SUFFIX=$(if [ "${ASSERTIONS}" = "ON" ]; then echo "debug"; else echo "lto"; fi) \ + && WEBKIT_URL="https://github.com/oven-sh/WebKit/releases/download/autobuild-${WEBKIT_TAG}/bun-webkit-linux-${BUILDARCH}-${WEBKIT_SUFFIX}.tar.gz" \ && echo "Downloading ${WEBKIT_URL}" \ && curl -fsSL "${WEBKIT_URL}" | tar -xz -C ${BUN_DIR}/bun-webkit --strip-components=1 FROM bun-base as bun-cpp-objects ARG CANARY +ARG ASSERTIONS COPY --from=bun-webkit ${BUN_DIR}/bun-webkit ${BUN_DIR}/bun-webkit @@ -335,7 +357,7 @@ ENV CCACHE_DIR=/ccache RUN --mount=type=cache,target=/ccache mkdir ${BUN_DIR}/build \ && cd ${BUN_DIR}/build \ && mkdir -p tmp_modules tmp_functions js codegen \ - && cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBUN_CPP_ONLY=1 -DWEBKIT_DIR=/build/bun/bun-webkit -DCANARY=${CANARY} \ + && cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DUSE_DEBUG_JSC=${ASSERTIONS} -DBUN_CPP_ONLY=1 -DWEBKIT_DIR=/build/bun/bun-webkit -DCANARY=${CANARY} \ && bash compile-cpp-only.sh -v FROM bun-base-with-zig as bun-codegen-for-zig @@ -361,6 +383,8 @@ ARG TRIPLET ARG GIT_SHA ARG CPU_TARGET ARG CANARY=0 +ARG ASSERTIONS=OFF +ARG ZIG_OPTIMIZE=ReleaseFast COPY *.zig package.json CMakeLists.txt ${BUN_DIR}/ COPY completions ${BUN_DIR}/completions @@ -380,6 +404,7 @@ RUN mkdir -p build \ && cmake .. \ -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ + -DZIG_OPTIMIZE="${ZIG_OPTIMIZE}" \ -DCPU_TARGET="${CPU_TARGET}" \ -DZIG_TARGET="${TRIPLET}" \ -DWEBKIT_DIR="omit" \ @@ -400,6 +425,7 @@ FROM bun-base as bun-link ARG CPU_TARGET ARG CANARY +ARG ASSERTIONS ENV CPU_TARGET=${CPU_TARGET} @@ -433,6 +459,7 @@ RUN cmake .. \ -DCMAKE_BUILD_TYPE=Release \ -DBUN_LINK_ONLY=1 \ -DBUN_ZIG_OBJ="${BUN_DIR}/build/bun-zig.o" \ + -DUSE_DEBUG_JSC=${ASSERTIONS} \ -DBUN_CPP_ARCHIVE="${BUN_DIR}/build/bun-cpp-objects.a" \ -DWEBKIT_DIR="${BUN_DIR}/bun-webkit" \ -DBUN_DEPS_OUT_DIR="${BUN_DEPS_OUT_DIR}" \ @@ -447,4 +474,59 @@ RUN cmake .. \ FROM scratch as artifact -COPY --from=bun-link /build/out / \ No newline at end of file +COPY --from=bun-link /build/out / + +FROM bun-base as bun-link-assertions + +ARG CPU_TARGET +ARG CANARY +ARG ASSERTIONS + +ENV CPU_TARGET=${CPU_TARGET} + +WORKDIR $BUN_DIR + +RUN mkdir -p build bun-webkit + +# lol +COPY src/bun.js/bindings/sqlite/sqlite3.c ${BUN_DIR}/src/bun.js/bindings/sqlite/sqlite3.c + +COPY src/symbols.dyn src/linker.lds ${BUN_DIR}/src/ + +COPY CMakeLists.txt ${BUN_DIR}/CMakeLists.txt +COPY --from=zlib ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ +COPY --from=base64 ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ +COPY --from=libarchive ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ +COPY --from=boringssl ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ +COPY --from=lolhtml ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ +COPY --from=mimalloc-debug ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ +COPY --from=zstd ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ +COPY --from=tinycc ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ +COPY --from=c-ares ${BUN_DEPS_OUT_DIR}/* ${BUN_DEPS_OUT_DIR}/ +COPY --from=bun-compile-zig-obj /tmp/bun-zig.o ${BUN_DIR}/build/bun-zig.o +COPY --from=bun-cpp-objects ${BUN_DIR}/build/bun-cpp-objects.a ${BUN_DIR}/build/bun-cpp-objects.a +COPY --from=bun-cpp-objects ${BUN_DIR}/bun-webkit/lib ${BUN_DIR}/bun-webkit/lib + +WORKDIR $BUN_DIR/build + +RUN cmake .. \ + -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUN_LINK_ONLY=1 \ + -DBUN_ZIG_OBJ="${BUN_DIR}/build/bun-zig.o" \ + -DUSE_DEBUG_JSC=ON \ + -DBUN_CPP_ARCHIVE="${BUN_DIR}/build/bun-cpp-objects.a" \ + -DWEBKIT_DIR="${BUN_DIR}/bun-webkit" \ + -DBUN_DEPS_OUT_DIR="${BUN_DEPS_OUT_DIR}" \ + -DCPU_TARGET="${CPU_TARGET}" \ + -DNO_CONFIGURE_DEPENDS=1 \ + -DCANARY="${CANARY}" \ + && ninja -v \ + && ./bun --revision \ + && mkdir -p /build/out \ + && mv bun bun-profile /build/out \ + && rm -rf ${BUN_DIR} ${BUN_DEPS_OUT_DIR} + +FROM scratch as artifact-assertions + +COPY --from=bun-link-assertions /build/out / \ No newline at end of file diff --git a/Makefile b/Makefile index 61a9d335ed..1cc7824053 100644 --- a/Makefile +++ b/Makefile @@ -1355,7 +1355,7 @@ mimalloc-debug: -GNinja \ . \ && ninja - cp $(BUN_DEPS_DIR)/mimalloc/$(_MIMALLOC_DEBUG_FILE) $(BUN_DEPS_OUT_DIR)/$(MIMALLOC_FILE) + cp $(BUN_DEPS_DIR)/mimalloc/$(_MIMALLOC_DEBUG_FILE) $(BUN_DEPS_OUT_DIR)/$(_MIMALLOC_DEBUG_FILE) # mimalloc is built as object files so that it can overload the system malloc on linux diff --git a/build.zig b/build.zig index 747b2404e1..c4ee8dd99c 100644 --- a/build.zig +++ b/build.zig @@ -1,4 +1,4 @@ -const recommended_zig_version = "0.12.0-dev.1297+a9e66ed73"; +const recommended_zig_version = "0.12.0-dev.1571+03adafd80"; const zig_version = @import("builtin").zig_version; const std = @import("std"); @@ -169,7 +169,7 @@ const fmt = struct { }; var x64 = "x64"; -var optimize: std.builtin.OptimizeMode = undefined; +var optimize: std.builtin.OptimizeMode = .Debug; const Build = std.Build; const CrossTarget = std.zig.CrossTarget; @@ -219,7 +219,11 @@ pub fn build_(b: *Build) !void { // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. optimize = b.standardOptimizeOption(.{}); - const generated_code_directory = b.option([]const u8, "generated-code", "Set the generated code directory") orelse "./build"; + var generated_code_directory = b.option([]const u8, "generated-code", "Set the generated code directory") orelse ""; + + if (generated_code_directory.len == 0) { + generated_code_directory = b.pathFromRoot("build/codegen"); + } var output_dir_buf = std.mem.zeroes([4096]u8); var bin_label = if (optimize == std.builtin.OptimizeMode.Debug) "packages/debug-bun-" else "packages/bun-"; @@ -403,11 +407,11 @@ pub fn build_(b: *Build) !void { // Generated Code // TODO: exit with a better error early if these files do not exist. it is an indication someone ran `zig build` directly without the code generators. - obj.addModule("generated/ZigGeneratedClasses.zig", b.createModule(.{ - .source_file = .{ .path = b.fmt("{s}/ZigGeneratedClasses.zig", .{generated_code_directory}) }, + obj.addModule("ZigGeneratedClasses", b.createModule(.{ + .source_file = .{ .path = b.pathJoin(&.{ generated_code_directory, "ZigGeneratedClasses.zig" }) }, })); - obj.addModule("generated/ResolvedSourceTag.zig", b.createModule(.{ - .source_file = .{ .path = b.fmt("{s}/ResolvedSourceTag.zig", .{generated_code_directory}) }, + obj.addModule("ResolvedSourceTag", b.createModule(.{ + .source_file = .{ .path = b.pathJoin(&.{ generated_code_directory, "ResolvedSourceTag.zig" }) }, })); obj.linkLibC(); @@ -415,6 +419,7 @@ pub fn build_(b: *Build) !void { obj.strip = false; obj.omit_frame_pointer = optimize != .Debug; obj.subsystem = .Console; + // Disable stack probing on x86 so we don't need to include compiler_rt if (target.getCpuArch().isX86() or target.isWindows()) obj.disable_stack_probing = true; diff --git a/bun.lockb b/bun.lockb index c6d9f15ae5f5e9f6c2593ee45f154546db6e4168..855e72d2b8c6928d8f768bcb16d5b55c1575a858 100755 GIT binary patch delta 1498 zcmaJ>do+}39R5DWH27v*W@Om0CW{fxn45Bm#$8F~*xYKzomH+Od#oC_FzU2RA3K#x z4O%DU_NC^?wGfh24zgh=p(M7;qBJ{O$LZ{8&%Xb>zu)sdzsot#dENyC^%6nl(zZN# z$M#TMpKz;oK0qy#T8m#xu~o;EM;O<%AH42hI1lsc-5)_1r<*=|OmVAkVLI0QOA-qa zVFC8yzW~Gq2PuGNuq|W)r$QJ+fbYk^`FJ)WAnnC|!C{QCD(s_6M_i0^mEch_9l@C8 zqTnH2qkpR49t%}CpJH^z(g!qg z=iR4xoO|ujaJTD8pt-ZoW7*w(c>bikCc4qqB3DEs%d zEtT74zbsTLy*(Zd^IV9WkrW%ltlEkR{tJX};qhKzb#xcvdrLrSJd@Uoo$?nF+D}+O zHh_ino%uqN&zeBAdK@M~(zwlQA!pHlsg^}`>ic2$Q z3mkTjrn%}JM&5GC5Jxc^WmcQn*rh}Z53Hh4FZD)NT~(Ou+jBZ^fjD}G;};=07;a>C zK9V~#K^jw&H+A>8jW?k`drYRS`2@C_#vq1+TJ5L6-6U9 zYvxaf^n2+aNUFq%YR~iz%xZnpU+cd0mwAfB*fiyx#dFPZ<*p^!#y%r5r9j}L%100Y zev-u-2i04&NRBBhKG<|(2qD-fynF?A@)qT{Q*B#oa874Knk+?G_DVGtT2lidoBEe8 z?6?{H-3t9w1NwM^Dd+jkxrb*F>sSGfBRXCs2Du&5ftp>0ulS=g6-Ti0SGNJl>$JTW zREzfxj!&1QA$l4O(%p9L_$YSelIVVUGe)N|lskN-YqHS_QAlw?b5(CAd{fuY_z`n0j95kW!iM)tpq z8^WhEiJaNldrMM?FxV<@Xs4pxF?LRiu#8+*$REfrX_|3ujnQEAnV4nOqAL~R)tjTB z6}#@)=}eT`ytth?8E=9n!84h$9ESiRlUkTbMDeUACAx{emRm~m)*>^P9_B@B`{t-8 zOG^VOBjYx^-Ypq!#oEw$%PVOHK0 zv73~#c>hLA=lJ>4U2KDX?>U)~HIb{?ukEy0G6O3DfRfD2n-zR@_UUBw%7NxIGZxa` zhuI8zBE1r4P@TPW%Y5j<=pJ)j`{u~(-6CX+qjZQoi zmPDs9*)%#eI50XeE{XazKR!4-G~{SxXdJ_w#`-_vr+l}naWLS3v|d3EQW)9Kt(=l;(5p5N#D{+{zWpIHb#1Hrije5QQ8 zBxkO^WdOfbi0(E%x(?qn^RygUl@$73xmNll%~114)XOFLDGEv z&bj;BS*oDJd+rhp~6e--fLCZZ~1OrY$3C8 zOj5~M2sUgC5`)onEKPgq^Z2yBLJyGenABiQQQDOUx!&$whSQn{AfERnvX*rWtaX zMJbC}pcp}HlA6QqPEj4YSjJn9g^4Owps;8L`gny~SnByIQh91twBTBNN53Fe>!x6k zN;x&woITtxDbuO1waxkj=mu_Ime%DuuK&}G$NIIE%WgJYDVQ#>s8liqz*cov0T}Ej zCI0f@OWNZ#9u68i>|tHL3|rfP$lam(OFQZoRTn~@dl`mh(38d1Xs8>P==mDKUWb^D zY&7z3nTjBYVnT9rBhK*NK%VIw6ph;-_<#~Oc~9K9b-(mcGsdjE?v_^rx~S%S*B+g93#4~l;rwJT-iQ-p7P#>wTj&<$dnS3XLT zzHrv|g)rq07erm()GLgYT{cDf7*Z;eizRH!J~)%e`u z+Ty+Y^MiL@W%+p 0); + this.protection_count -= 1; + } this.onOpen.unprotect(); this.onClose.unprotect(); this.onData.unprotect(); @@ -310,6 +316,9 @@ const Handlers = struct { } pub fn protect(this: *Handlers) void { + if (comptime Environment.allow_assert) { + this.protection_count += 1; + } this.onOpen.protect(); this.onClose.protect(); this.onData.protect(); @@ -1120,10 +1129,14 @@ fn NewSocket(comptime ssl: bool) type { }, }; - pub usingnamespace JSSocketType(ssl); + pub usingnamespace if (!ssl) + JSC.Codegen.JSTCPSocket + else + JSC.Codegen.JSTLSSocket; pub fn hasPendingActivity(this: *This) callconv(.C) bool { @fence(.Acquire); + return this.has_pending_activity.load(.Acquire); } diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index bb035c4e2b..db3ba16668 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -432,7 +432,7 @@ pub const FFI = struct { return JSC.toInvalidArguments("Expected at least one symbol", .{}, global); } - var obj = JSValue.createEmptyObject(global, if (symbols.count() < 64) symbols.count() else 0); + var obj = JSValue.createEmptyObject(global, symbols.count()); obj.ensureStillAlive(); defer obj.ensureStillAlive(); for (symbols.values()) |*function| { diff --git a/src/bun.js/api/server.classes.ts b/src/bun.js/api/server.classes.ts index cd59d10909..78598768a2 100644 --- a/src/bun.js/api/server.classes.ts +++ b/src/bun.js/api/server.classes.ts @@ -83,18 +83,28 @@ export default [ sendText: { fn: "sendText", length: 2, - DOMJIT: { - returns: "int", - args: ["JSString", "bool"], - }, + // ASSERTION FAILED: m_data[index].lockCount + // /Users/jarred/actions-runner/_work/WebKit/WebKit/Source/JavaScriptCore/dfg/DFGRegisterBank.h(204) : void JSC::DFG::RegisterBank::unlock(RegID) [BankInfo = JSC::GPRInfo] + // 1 0x102740124 WTFCrash + // 3 0x103076bac JSC::MacroAssemblerARM64::add64(JSC::AbstractMacroAssembler::TrustedImm64, JSC::ARM64Registers::RegisterID, JSC::ARM64Registers::RegisterID) + // 4 0x10309a2d0 JSC::DFG::SpeculativeJIT::compileCallDOM(JSC::DFG::Node*)::$_0::operator()(JSC::DFG::Edge) const + // DOMJIT: { + // returns: "int", + // args: ["JSString", "bool"], + // }, }, sendBinary: { fn: "sendBinary", length: 2, - DOMJIT: { - returns: "int", - args: ["JSUint8Array", "bool"], - }, + // ASSERTION FAILED: m_data[index].lockCount + // /Users/jarred/actions-runner/_work/WebKit/WebKit/Source/JavaScriptCore/dfg/DFGRegisterBank.h(204) : void JSC::DFG::RegisterBank::unlock(RegID) [BankInfo = JSC::GPRInfo] + // 1 0x102740124 WTFCrash + // 3 0x103076bac JSC::MacroAssemblerARM64::add64(JSC::AbstractMacroAssembler::TrustedImm64, JSC::ARM64Registers::RegisterID, JSC::ARM64Registers::RegisterID) + // 4 0x10309a2d0 JSC::DFG::SpeculativeJIT::compileCallDOM(JSC::DFG::Node*)::$_0::operator()(JSC::DFG::Edge) const + // DOMJIT: { + // returns: "int", + // args: ["JSUint8Array", "bool"], + // }, }, publishText: { fn: "publishText", diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 7f52d43e79..5e9abc282a 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -4628,12 +4628,34 @@ pub const ServerWebSocket = struct { return .undefined; } - this.closed = true; + const code = brk: { + if (args.ptr[0].isEmpty() or args.ptr[0].isUndefined()) { + // default exception code + break :brk 1000; + } + + if (!args.ptr[0].isNumber()) { + globalThis.throwInvalidArguments("close requires a numeric code or undefined", .{}); + return .zero; + } + + break :brk args.ptr[0].coerce(i32, globalThis); + }; + + var message_value: ZigString.Slice = brk: { + if (args.ptr[1].isEmpty() or args.ptr[1].isUndefined()) break :brk ZigString.Slice.empty; + + if (args.ptr[1].toSliceOrNull(globalThis)) |slice| { + break :brk slice; + } + + // toString() failed, that means an exception occurred. + return .zero; + }; - const code = if (args.len > 0) args.ptr[0].toInt32() else @as(i32, 1000); - var message_value = if (args.len > 1) args.ptr[1].toSlice(globalThis, bun.default_allocator) else ZigString.Slice.empty; defer message_value.deinit(); + this.closed = true; this.websocket.end(code, message_value.slice()); return .undefined; } diff --git a/src/bun.js/bindings/AsyncContextFrame.cpp b/src/bun.js/bindings/AsyncContextFrame.cpp index 08afe6e35d..9c846ea693 100644 --- a/src/bun.js/bindings/AsyncContextFrame.cpp +++ b/src/bun.js/bindings/AsyncContextFrame.cpp @@ -20,7 +20,8 @@ AsyncContextFrame* AsyncContextFrame::create(VM& vm, JSC::Structure* structure, AsyncContextFrame* AsyncContextFrame::create(JSGlobalObject* global, JSValue callback, JSValue context) { auto& vm = global->vm(); - AsyncContextFrame* asyncContextData = new (NotNull, allocateCell(vm)) AsyncContextFrame(vm, jsCast(global)->AsyncContextFrameStructure()); + auto* structure = jsCast(global)->AsyncContextFrameStructure(); + AsyncContextFrame* asyncContextData = new (NotNull, allocateCell(vm)) AsyncContextFrame(vm, structure); asyncContextData->finishCreation(vm); asyncContextData->callback.set(vm, asyncContextData, callback); asyncContextData->context.set(vm, asyncContextData, context); diff --git a/src/bun.js/bindings/BunDebugger.cpp b/src/bun.js/bindings/BunDebugger.cpp index 55942d2f6e..9cad0c692e 100644 --- a/src/bun.js/bindings/BunDebugger.cpp +++ b/src/bun.js/bindings/BunDebugger.cpp @@ -83,6 +83,28 @@ public: return ConnectionType::Remote; } + void doConnect(WebCore::ScriptExecutionContext& context) + { + this->status = ConnectionStatus::Connected; + auto* globalObject = context.jsGlobalObject(); + if (this->unrefOnDisconnect) { + Bun__eventLoop__incrementRefConcurrently(reinterpret_cast(globalObject)->bunVM(), 1); + } + globalObject->setInspectable(true); + auto& inspector = globalObject->inspectorDebuggable(); + inspector.setInspectable(true); + globalObject->inspectorController().connectFrontend(*this, true, false); // waitingForConnection + + Inspector::JSGlobalObjectDebugger* debugger = reinterpret_cast(globalObject->debugger()); + if (debugger) { + debugger->runWhilePausedCallback = [](JSC::JSGlobalObject& globalObject, bool& isDoneProcessingEvents) -> void { + BunInspectorConnection::runWhilePaused(globalObject, isDoneProcessingEvents); + }; + } + + this->receiveMessagesOnInspectorThread(context, reinterpret_cast(globalObject), false); + } + void connect() { switch (this->status) { @@ -101,24 +123,7 @@ public: ScriptExecutionContext::ensureOnContextThread(scriptExecutionContextIdentifier, [connection = this](ScriptExecutionContext& context) { switch (connection->status) { case ConnectionStatus::Pending: { - connection->status = ConnectionStatus::Connected; - auto* globalObject = context.jsGlobalObject(); - if (connection->unrefOnDisconnect) { - Bun__eventLoop__incrementRefConcurrently(reinterpret_cast(globalObject)->bunVM(), 1); - } - globalObject->setInspectable(true); - auto& inspector = globalObject->inspectorDebuggable(); - inspector.setInspectable(true); - globalObject->inspectorController().connectFrontend(*connection, true, false); // waitingForConnection - - Inspector::JSGlobalObjectDebugger* debugger = reinterpret_cast(globalObject->debugger()); - if (debugger) { - debugger->runWhilePausedCallback = [](JSC::JSGlobalObject& globalObject, bool& isDoneProcessingEvents) -> void { - BunInspectorConnection::runWhilePaused(globalObject, isDoneProcessingEvents); - }; - } - - connection->receiveMessagesOnInspectorThread(context, reinterpret_cast(globalObject)); + connection->doConnect(context); break; } default: { @@ -180,10 +185,11 @@ public: for (auto* connection : connections) { if (connection->status == ConnectionStatus::Pending) { connection->connect(); + continue; } if (connection->status != ConnectionStatus::Disconnected) { - connection->receiveMessagesOnInspectorThread(*global->scriptExecutionContext(), global); + connection->receiveMessagesOnInspectorThread(*global->scriptExecutionContext(), global, true); } } @@ -202,14 +208,14 @@ public: } break; } - connection->receiveMessagesOnInspectorThread(*global->scriptExecutionContext(), global); + connection->receiveMessagesOnInspectorThread(*global->scriptExecutionContext(), global, true); } } else { while (!isDoneProcessingEvents) { size_t closedCount = 0; for (auto* connection : connections) { closedCount += connection->status == ConnectionStatus::Disconnected || connection->status == ConnectionStatus::Disconnecting; - connection->receiveMessagesOnInspectorThread(*global->scriptExecutionContext(), global); + connection->receiveMessagesOnInspectorThread(*global->scriptExecutionContext(), global, true); if (isDoneProcessingEvents) break; } @@ -222,7 +228,7 @@ public: } } - void receiveMessagesOnInspectorThread(ScriptExecutionContext& context, Zig::GlobalObject* globalObject) + void receiveMessagesOnInspectorThread(ScriptExecutionContext& context, Zig::GlobalObject* globalObject, bool connectIfNeeded) { this->jsThreadMessageScheduledCount.store(0); WTF::Vector messages; @@ -236,14 +242,21 @@ public: Inspector::JSGlobalObjectDebugger* debugger = reinterpret_cast(globalObject->debugger()); if (!debugger) { + if (connectIfNeeded && this->status == ConnectionStatus::Pending) { + this->doConnect(context); + return; + } + for (auto message : messages) { dispatcher.dispatchMessageFromRemote(WTFMove(message)); - debugger = reinterpret_cast(globalObject->debugger()); - if (debugger) { - debugger->runWhilePausedCallback = [](JSC::JSGlobalObject& globalObject, bool& isDoneProcessingEvents) -> void { - runWhilePaused(globalObject, isDoneProcessingEvents); - }; + if (!debugger) { + debugger = reinterpret_cast(globalObject->debugger()); + if (debugger) { + debugger->runWhilePausedCallback = [](JSC::JSGlobalObject& globalObject, bool& isDoneProcessingEvents) -> void { + runWhilePaused(globalObject, isDoneProcessingEvents); + }; + } } } } else { @@ -304,7 +317,7 @@ public: this->jsWaitForMessageFromInspectorLock.unlock(); } else if (this->jsThreadMessageScheduledCount++ == 0) { ScriptExecutionContext::postTaskTo(scriptExecutionContextIdentifier, [connection = this](ScriptExecutionContext& context) { - connection->receiveMessagesOnInspectorThread(context, reinterpret_cast(context.jsGlobalObject())); + connection->receiveMessagesOnInspectorThread(context, reinterpret_cast(context.jsGlobalObject()), true); }); } } @@ -358,7 +371,7 @@ public: } static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) { - return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray, 2); + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info(), JSC::NonArray); } BunInspectorConnection* connection() diff --git a/src/bun.js/bindings/BunPlugin.cpp b/src/bun.js/bindings/BunPlugin.cpp index 920a281d42..48267619ce 100644 --- a/src/bun.js/bindings/BunPlugin.cpp +++ b/src/bun.js/bindings/BunPlugin.cpp @@ -682,13 +682,13 @@ DEFINE_VISIT_CHILDREN(JSModuleMock); EncodedJSValue BunPlugin::OnLoad::run(JSC::JSGlobalObject* globalObject, BunString* namespaceString, BunString* path) { - Group* groupPtr = this->group(namespaceString ? Bun::toWTFString(*namespaceString) : String()); + Group* groupPtr = this->group(namespaceString ? namespaceString->toWTFString(BunString::ZeroCopy) : String()); if (groupPtr == nullptr) { return JSValue::encode(jsUndefined()); } Group& group = *groupPtr; - auto pathString = Bun::toWTFString(*path); + auto pathString = path->toWTFString(BunString::ZeroCopy); JSC::JSFunction* function = group.find(globalObject, pathString); if (!function) { @@ -740,7 +740,7 @@ EncodedJSValue BunPlugin::OnLoad::run(JSC::JSGlobalObject* globalObject, BunStri EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, BunString* namespaceString, BunString* path, BunString* importer) { - Group* groupPtr = this->group(namespaceString ? Bun::toWTFString(*namespaceString) : String()); + Group* groupPtr = this->group(namespaceString ? namespaceString->toWTFString(BunString::ZeroCopy) : String()); if (groupPtr == nullptr) { return JSValue::encode(jsUndefined()); } @@ -753,7 +753,7 @@ EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, BunS auto& callbacks = group.callbacks; - WTF::String pathString = Bun::toWTFString(*path); + WTF::String pathString = path->toWTFString(BunString::ZeroCopy); for (size_t i = 0; i < filters.size(); i++) { if (!filters[i].get()->match(globalObject, pathString, 0)) { continue; @@ -852,7 +852,7 @@ JSC::JSValue runVirtualModule(Zig::GlobalObject* globalObject, BunString* specif return fallback(); } auto& virtualModules = *globalObject->onLoadPlugins.virtualModules; - WTF::String specifierString = Bun::toWTFString(*specifier); + WTF::String specifierString = specifier->toWTFString(BunString::ZeroCopy); if (auto virtualModuleFn = virtualModules.get(specifierString)) { auto& vm = globalObject->vm(); diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index 11a1cf5c4d..7fcb35a827 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -54,7 +54,8 @@ using namespace JSC; #define REPORTED_NODE_VERSION "20.8.0" #define processObjectBindingCodeGenerator processObjectInternalsBindingCodeGenerator -#define processObjectMainModuleCodeGenerator moduleMainCodeGenerator +#define setProcessObjectInternalsMainModuleCodeGenerator processObjectInternalsSetMainModuleCodeGenerator +#define setProcessObjectMainModuleCodeGenerator setMainModuleCodeGenerator #if !defined(BUN_WEBKIT_VERSION) #define BUN_WEBKIT_VERSION "unknown" @@ -1990,6 +1991,7 @@ extern "C" void Process__emitMessageEvent(Zig::GlobalObject* global, EncodedJSVa { auto* process = static_cast(global->processObject()); auto& vm = global->vm(); + auto ident = Identifier::fromString(vm, "message"_s); if (process->wrapped().hasEventListeners(ident)) { JSC::MarkedArgumentBuffer args; @@ -2011,72 +2013,72 @@ extern "C" void Process__emitDisconnectEvent(Zig::GlobalObject* global) /* Source for Process.lut.h @begin processObjectTable - abort Process_functionAbort Function 1 - allowedNodeEnvironmentFlags Process_stubEmptySet PropertyCallback - arch constructArch PropertyCallback - argv constructArgv PropertyCallback - argv0 constructArgv0 PropertyCallback - assert Process_functionAssert Function 1 - binding Process_functionBinding Function 1 - browser constructBrowser PropertyCallback - chdir Process_functionChdir Function 1 - config constructProcessConfigObject PropertyCallback - connected processConnected CustomAccessor - constrainedMemory Process_functionConstrainedMemory Function 0 - cpuUsage Process_functionCpuUsage Function 1 - cwd Process_functionCwd Function 1 - debugPort processDebugPort CustomAccessor - disconnect constructProcessDisconnect PropertyCallback - dlopen Process_functionDlopen Function 1 - emitWarning Process_emitWarning Function 1 - env constructEnv PropertyCallback - execArgv constructExecArgv PropertyCallback - execPath constructExecPath PropertyCallback - exit Process_functionExit Function 1 - exitCode processExitCode CustomAccessor - features constructFeatures PropertyCallback - getActiveResourcesInfo Process_stubFunctionReturningArray Function 0 - getegid Process_functiongetegid Function 0 - geteuid Process_functiongeteuid Function 0 - getgid Process_functiongetgid Function 0 - getgroups Process_functiongetgroups Function 0 - getuid Process_functiongetuid Function 0 - hrtime constructProcessHrtimeObject PropertyCallback - isBun constructIsBun PropertyCallback - kill Process_functionKill Function 2 - mainModule JSBuiltin ReadOnly|Builtin|Accessor|Function 0 - memoryUsage constructMemoryUsage PropertyCallback - moduleLoadList Process_stubEmptyArray PropertyCallback - nextTick constructProcessNextTickFn PropertyCallback - openStdin Process_functionOpenStdin Function 0 - pid constructPid PropertyCallback - platform constructPlatform PropertyCallback - ppid constructPpid PropertyCallback - reallyExit Process_functionReallyExit Function 1 - release constructProcessReleaseObject PropertyCallback - revision constructRevision PropertyCallback - setSourceMapsEnabled Process_stubEmptyFunction Function 1 - send constructProcessSend PropertyCallback - stderr constructStderr PropertyCallback - stdin constructStdin PropertyCallback - stdout constructStdout PropertyCallback - title processTitle CustomAccessor - umask Process_functionUmask Function 1 - uptime Process_functionUptime Function 1 - version constructVersion PropertyCallback - versions constructVersions PropertyCallback - _debugEnd Process_stubEmptyFunction Function 0 - _debugProcess Process_stubEmptyFunction Function 0 - _fatalException Process_stubEmptyFunction Function 1 - _getActiveRequests Process_stubFunctionReturningArray Function 0 - _getActiveHandles Process_stubFunctionReturningArray Function 0 - _linkedBinding Process_stubEmptyFunction Function 0 - _preload_modules Process_stubEmptyArray PropertyCallback - _rawDebug Process_stubEmptyFunction Function 0 - _startProfilerIdleNotifier Process_stubEmptyFunction Function 0 - _stopProfilerIdleNotifier Process_stubEmptyFunction Function 0 - _tickCallback Process_stubEmptyFunction Function 0 - _kill Process_functionReallyKill Function 2 + abort Process_functionAbort Function 1 + allowedNodeEnvironmentFlags Process_stubEmptySet PropertyCallback + arch constructArch PropertyCallback + argv constructArgv PropertyCallback + argv0 constructArgv0 PropertyCallback + assert Process_functionAssert Function 1 + binding Process_functionBinding Function 1 + browser constructBrowser PropertyCallback + chdir Process_functionChdir Function 1 + config constructProcessConfigObject PropertyCallback + connected processConnected CustomAccessor + constrainedMemory Process_functionConstrainedMemory Function 0 + cpuUsage Process_functionCpuUsage Function 1 + cwd Process_functionCwd Function 1 + debugPort processDebugPort CustomAccessor + disconnect constructProcessDisconnect PropertyCallback + dlopen Process_functionDlopen Function 1 + emitWarning Process_emitWarning Function 1 + env constructEnv PropertyCallback + execArgv constructExecArgv PropertyCallback + execPath constructExecPath PropertyCallback + exit Process_functionExit Function 1 + exitCode processExitCode CustomAccessor + features constructFeatures PropertyCallback + getActiveResourcesInfo Process_stubFunctionReturningArray Function 0 + getegid Process_functiongetegid Function 0 + geteuid Process_functiongeteuid Function 0 + getgid Process_functiongetgid Function 0 + getgroups Process_functiongetgroups Function 0 + getuid Process_functiongetuid Function 0 + hrtime constructProcessHrtimeObject PropertyCallback + isBun constructIsBun PropertyCallback + kill Process_functionKill Function 2 + mainModule processObjectInternalsMainModuleCodeGenerator Builtin|Accessor + memoryUsage constructMemoryUsage PropertyCallback + moduleLoadList Process_stubEmptyArray PropertyCallback + nextTick constructProcessNextTickFn PropertyCallback + openStdin Process_functionOpenStdin Function 0 + pid constructPid PropertyCallback + platform constructPlatform PropertyCallback + ppid constructPpid PropertyCallback + reallyExit Process_functionReallyExit Function 1 + release constructProcessReleaseObject PropertyCallback + revision constructRevision PropertyCallback + setSourceMapsEnabled Process_stubEmptyFunction Function 1 + send constructProcessSend PropertyCallback + stderr constructStderr PropertyCallback + stdin constructStdin PropertyCallback + stdout constructStdout PropertyCallback + title processTitle CustomAccessor + umask Process_functionUmask Function 1 + uptime Process_functionUptime Function 1 + version constructVersion PropertyCallback + versions constructVersions PropertyCallback + _debugEnd Process_stubEmptyFunction Function 0 + _debugProcess Process_stubEmptyFunction Function 0 + _fatalException Process_stubEmptyFunction Function 1 + _getActiveRequests Process_stubFunctionReturningArray Function 0 + _getActiveHandles Process_stubFunctionReturningArray Function 0 + _linkedBinding Process_stubEmptyFunction Function 0 + _preload_modules Process_stubEmptyArray PropertyCallback + _rawDebug Process_stubEmptyFunction Function 0 + _startProfilerIdleNotifier Process_stubEmptyFunction Function 0 + _stopProfilerIdleNotifier Process_stubEmptyFunction Function 0 + _tickCallback Process_stubEmptyFunction Function 0 + _kill Process_functionReallyKill Function 2 @end */ #include "BunProcess.lut.h" diff --git a/src/bun.js/bindings/BunString.cpp b/src/bun.js/bindings/BunString.cpp index 9052b3b5ae..ed05c09583 100644 --- a/src/bun.js/bindings/BunString.cpp +++ b/src/bun.js/bindings/BunString.cpp @@ -3,11 +3,30 @@ #include #include "helpers.h" #include "simdutf.h" +#include "JSDOMURL.h" +#include "DOMURL.h" +#include "ZigGlobalObject.h" +#include "IDLTypes.h" +#include "JSDOMWrapperCache.h" + +#include "JSDOMAttribute.h" +#include "JSDOMBinding.h" +#include "JSDOMConstructor.h" +#include "JSDOMConvertAny.h" +#include "JSDOMConvertBase.h" +#include "JSDOMConvertBoolean.h" +#include "JSDOMConvertInterface.h" +#include "JSDOMConvertStrings.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObjectInlines.h" +#include "JSDOMOperation.h" + #include #include #include "GCDefferalContext.h" #include #include +#include using namespace JSC; extern "C" BunString BunString__fromBytes(const char* bytes, size_t length); @@ -74,34 +93,12 @@ JSC::JSValue toJS(JSC::JSGlobalObject* globalObject, BunString bunString, size_t RELEASE_ASSERT(bunString.impl.wtf->refCount() > 0); } #endif - return jsSubstring(globalObject, jsUndefined(), Bun::toWTFString(bunString), 0, length); + return jsSubstring(globalObject, jsUndefined(), bunString.toWTFString(BunString::ZeroCopy), 0, length); } BunString toString(const char* bytes, size_t length) { return BunString__fromBytes(bytes, length); } -WTF::String toWTFString(const BunString& bunString) -{ - if (bunString.tag == BunStringTag::ZigString) { - if (Zig::isTaggedUTF8Ptr(bunString.impl.zig.ptr)) { - return Zig::toStringCopy(bunString.impl.zig); - } else { - return Zig::toString(bunString.impl.zig); - } - - } else if (bunString.tag == BunStringTag::StaticZigString) { - return Zig::toStringStatic(bunString.impl.zig); - } - - if (bunString.tag == BunStringTag::WTFStringImpl) { -#if BUN_DEBUG - RELEASE_ASSERT(bunString.impl.wtf->refCount() > 0); -#endif - return WTF::String(bunString.impl.wtf); - } - - return WTF::String(); -} BunString fromJS(JSC::JSGlobalObject* globalObject, JSValue value) { @@ -271,7 +268,7 @@ extern "C" JSC::EncodedJSValue BunString__toJSON( JSC::JSGlobalObject* globalObject, BunString* bunString) { - JSC::JSValue result = JSC::JSONParse(globalObject, Bun::toWTFString(*bunString)); + JSC::JSValue result = JSC::JSONParse(globalObject, bunString->toWTFString()); if (!result) { result = JSC::JSValue(JSC::createSyntaxError(globalObject, "Failed to parse JSON"_s)); @@ -323,7 +320,20 @@ extern "C" void BunString__toWTFString(BunString* bunString) extern "C" BunString URL__getFileURLString(BunString* filePath) { - return Bun::toStringRef(WTF::URL::fileURLWithFileSystemPath(Bun::toWTFString(*filePath)).stringWithoutFragmentIdentifier()); + return Bun::toStringRef(WTF::URL::fileURLWithFileSystemPath(filePath->toWTFString()).stringWithoutFragmentIdentifier()); +} + +extern "C" JSC__JSValue BunString__toJSDOMURL(JSC::JSGlobalObject* lexicalGlobalObject, BunString* bunString) +{ + auto& globalObject = *jsCast(lexicalGlobalObject); + auto& vm = globalObject.vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + auto str = bunString->toWTFString(BunString::ZeroCopy); + + auto object = WebCore::DOMURL::create(str, String()); + auto jsValue = WebCore::toJSNewlyCreated>(*lexicalGlobalObject, globalObject, throwScope, WTFMove(object)); + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(jsValue)); } extern "C" WTF::URL* URL__fromJS(EncodedJSValue encodedValue, JSC::JSGlobalObject* globalObject) @@ -362,7 +372,7 @@ extern "C" BunString URL__getHrefFromJS(EncodedJSValue encodedValue, JSC::JSGlob extern "C" BunString URL__getHref(BunString* input) { - auto&& str = Bun::toWTFString(*input); + auto&& str = input->toWTFString(); auto url = WTF::URL(str); if (!url.isValid() || url.isEmpty()) return { BunStringTag::Dead }; @@ -372,7 +382,7 @@ extern "C" BunString URL__getHref(BunString* input) extern "C" BunString URL__pathFromFileURL(BunString* input) { - auto&& str = Bun::toWTFString(*input); + auto&& str = input->toWTFString(); auto url = WTF::URL(str); if (!url.isValid() || url.isEmpty()) return { BunStringTag::Dead }; @@ -382,8 +392,8 @@ extern "C" BunString URL__pathFromFileURL(BunString* input) extern "C" BunString URL__getHrefJoin(BunString* baseStr, BunString* relativeStr) { - auto base = Bun::toWTFString(*baseStr); - auto relative = Bun::toWTFString(*relativeStr); + auto base = baseStr->toWTFString(); + auto relative = relativeStr->toWTFString(); auto url = WTF::URL(WTF::URL(base), relative); if (!url.isValid() || url.isEmpty()) return { BunStringTag::Dead }; @@ -393,7 +403,7 @@ extern "C" BunString URL__getHrefJoin(BunString* baseStr, BunString* relativeStr extern "C" WTF::URL* URL__fromString(BunString* input) { - auto&& str = Bun::toWTFString(*input); + auto&& str = input->toWTFString(); auto url = WTF::URL(str); if (!url.isValid()) return nullptr; @@ -467,3 +477,43 @@ size_t BunString::utf8ByteLength(const WTF::String& str) return simdutf::utf8_length_from_utf16(reinterpret_cast(str.characters16()), static_cast(str.length())); } } + +WTF::String BunString::toWTFString() const +{ + if (this->tag == BunStringTag::ZigString) { + if (Zig::isTaggedExternalPtr(this->impl.zig.ptr)) { + return Zig::toString(this->impl.zig); + } else { + return Zig::toStringCopy(this->impl.zig); + } + } else if (this->tag == BunStringTag::StaticZigString) { + return Zig::toStringCopy(this->impl.zig); + } else if (this->tag == BunStringTag::WTFStringImpl) { + return WTF::String(this->impl.wtf); + } + + return WTF::String(); +} + +WTF::String BunString::toWTFString(ZeroCopyTag) const +{ + if (this->tag == BunStringTag::ZigString) { + if (Zig::isTaggedUTF8Ptr(this->impl.zig.ptr)) { + return Zig::toStringCopy(this->impl.zig); + } else { + return Zig::toString(this->impl.zig); + } + + } else if (this->tag == BunStringTag::StaticZigString) { + return Zig::toStringStatic(this->impl.zig); + } + + if (this->tag == BunStringTag::WTFStringImpl) { +#if BUN_DEBUG + RELEASE_ASSERT(this->impl.wtf->refCount() > 0); +#endif + return WTF::String(this->impl.wtf); + } + + return WTF::String(); +} \ No newline at end of file diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp index da9f89fc81..bd6d0c704f 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.cpp +++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp @@ -64,6 +64,7 @@ #include #include #include +#include extern "C" bool Bun__isBunMain(JSC::JSGlobalObject* global, const char* input_ptr, uint64_t input_len); @@ -91,6 +92,16 @@ static bool canPerformFastEnumeration(Structure* s) static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObject, JSCommonJSModule* moduleObject, JSString* dirname, JSValue filename, WTF::NakedPtr& exception) { + JSSourceCode* code = moduleObject->sourceCode.get(); + + // If an exception occurred somewhere else, we might have cleared the source code. + if (UNLIKELY(code == nullptr)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + throwException(globalObject, throwScope, createError(globalObject, "Failed to evaluate module"_s)); + exception = throwScope.exception(); + return false; + } + JSC::Structure* thisObjectStructure = globalObject->commonJSFunctionArgumentsStructure(); JSFunction* resolveFunction = JSC::JSBoundFunction::create(vm, @@ -118,7 +129,7 @@ static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObj // there is some possible GC issue where `thisObject` is gc'd before it should be globalObject->m_BunCommonJSModuleValue.set(vm, globalObject, thisObject); - JSValue empty = JSC::evaluate(globalObject, moduleObject->sourceCode.get()->sourceCode(), thisObject, exception); + JSValue empty = JSC::evaluate(globalObject, code->sourceCode(), thisObject, exception); ensureStillAliveHere(thisObject); globalObject->m_BunCommonJSModuleValue.clear(); @@ -198,14 +209,14 @@ Structure* RequireFunctionPrototype::createStructure( JSC::VM& vm, JSC::JSGlobalObject* globalObject) { - return Structure::create(vm, globalObject, globalObject->functionPrototype(), TypeInfo(JSFunctionType, StructureFlags), info()); + return Structure::create(vm, globalObject, globalObject->functionPrototype(), TypeInfo(ObjectType, StructureFlags), info()); } Structure* RequireResolveFunctionPrototype::createStructure( JSC::VM& vm, JSC::JSGlobalObject* globalObject) { - return Structure::create(vm, globalObject, globalObject->functionPrototype(), TypeInfo(JSFunctionType, StructureFlags), info()); + return Structure::create(vm, globalObject, globalObject->functionPrototype(), TypeInfo(ObjectType, StructureFlags), info()); } RequireResolveFunctionPrototype* RequireResolveFunctionPrototype::create(JSC::JSGlobalObject* globalObject) @@ -811,7 +822,6 @@ void JSCommonJSModule::toSyntheticSource(JSC::JSGlobalObject* globalObject, auto result = this->exportsObject(); auto& vm = globalObject->vm(); - Identifier esModuleMarker = builtinNames(vm).__esModulePublicName(); populateESMExports(globalObject, result, exportNames, exportValues, this->ignoreESModuleAnnotation); } @@ -845,6 +855,20 @@ void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor) } DEFINE_VISIT_CHILDREN(JSCommonJSModule); + +void JSCommonJSModule::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast(cell); + + if (auto* id = thisObject->m_id.get()) { + if (!id->isRope()) { + auto label = id->tryGetValue(false); + analyzer.setLabelForCell(cell, label); + } + } + Base::analyzeHeap(cell, analyzer); +} + const JSC::ClassInfo JSCommonJSModule::s_info = { "Module"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCommonJSModule) }; const JSC::ClassInfo RequireResolveFunctionPrototype::s_info = { "resolve"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(RequireResolveFunctionPrototype) }; const JSC::ClassInfo RequireFunctionPrototype::s_info = { "require"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(RequireFunctionPrototype) }; @@ -936,7 +960,7 @@ std::optional createCommonJSModule( bool isBuiltIn) { JSCommonJSModule* moduleObject; - WTF::String sourceURL = Bun::toWTFString(source.source_url); + WTF::String sourceURL = source.source_url.toWTFString(); JSValue specifierValue = Bun::toJS(globalObject, source.specifier); JSValue entry = globalObject->requireMap()->get(globalObject, specifierValue); @@ -959,13 +983,10 @@ std::optional createCommonJSModule( dirname = JSC::jsSubstring(globalObject, requireMapKey, 0, index); } - JSC::SourceCode rawInputSource( - WTFMove(sourceProvider)); - moduleObject = JSCommonJSModule::create( vm, globalObject->CommonJSModuleObjectStructure(), - requireMapKey, filename, dirname, JSC::JSSourceCode::create(vm, WTFMove(rawInputSource))); + requireMapKey, filename, dirname, JSC::JSSourceCode::create(vm, SourceCode(WTFMove(sourceProvider)))); moduleObject->putDirect(vm, WebCore::clientData(vm)->builtinNames().exportsPublicName(), diff --git a/src/bun.js/bindings/CommonJSModuleRecord.h b/src/bun.js/bindings/CommonJSModuleRecord.h index 219174a14f..530369bf67 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.h +++ b/src/bun.js/bindings/CommonJSModuleRecord.h @@ -82,6 +82,9 @@ public: DECLARE_INFO; DECLARE_VISIT_CHILDREN; + + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) { diff --git a/src/bun.js/bindings/InternalModuleRegistry.cpp b/src/bun.js/bindings/InternalModuleRegistry.cpp index 37b02c911d..1fbfbf7981 100644 --- a/src/bun.js/bindings/InternalModuleRegistry.cpp +++ b/src/bun.js/bindings/InternalModuleRegistry.cpp @@ -17,7 +17,7 @@ extern "C" void ByteRangeMapping__generate(BunString sourceURL, BunString code, static void maybeAddCodeCoverage(JSC::VM& vm, const JSC::SourceCode& code) { -#ifdef BUN_DEBUG +#if ASSERT_ENABLED bool isCodeCoverageEnabled = !!vm.controlFlowProfiler(); bool shouldGenerateCodeCoverage = isCodeCoverageEnabled && BunTest__shouldGenerateCodeCoverage(Bun::toString(code.provider()->sourceURL())); if (shouldGenerateCodeCoverage) { diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp index d75f23d6a9..6867a78485 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.cpp +++ b/src/bun.js/bindings/JSBundlerPlugin.cpp @@ -63,7 +63,7 @@ bool BundlerPlugin::anyMatchesCrossThread(JSC::VM& vm, const BunString* namespac return false; // Avoid unnecessary string copies - auto namespaceString = namespaceStr ? Bun::toWTFString(*namespaceStr) : String(); + auto namespaceString = namespaceStr ? namespaceStr->toWTFString(BunString::ZeroCopy) : String(); auto* group = this->onLoad.group(namespaceString); if (group == nullptr) { @@ -71,7 +71,7 @@ bool BundlerPlugin::anyMatchesCrossThread(JSC::VM& vm, const BunString* namespac } auto& filters = *group; - auto pathString = Bun::toWTFString(*path); + auto pathString = path->toWTFString(BunString::ZeroCopy); for (auto& filter : filters) { Yarr::MatchingContextHolder regExpContext(vm, usesPatternContextBuffer, nullptr, Yarr::MatchFrom::CompilerThread); @@ -85,14 +85,14 @@ bool BundlerPlugin::anyMatchesCrossThread(JSC::VM& vm, const BunString* namespac return false; // Avoid unnecessary string copies - auto namespaceString = namespaceStr ? Bun::toWTFString(*namespaceStr) : String(); + auto namespaceString = namespaceStr ? namespaceStr->toWTFString(BunString::ZeroCopy) : String(); auto* group = this->onResolve.group(namespaceString); if (group == nullptr) { return false; } - auto pathString = Bun::toWTFString(*path); + auto pathString = path->toWTFString(BunString::ZeroCopy); auto& filters = *group; for (auto& filter : filters) { @@ -294,8 +294,8 @@ extern "C" bool JSBundlerPlugin__anyMatches(Bun::JSBundlerPlugin* pluginObject, extern "C" void JSBundlerPlugin__matchOnLoad(JSC::JSGlobalObject* globalObject, Bun::JSBundlerPlugin* plugin, const BunString* namespaceString, const BunString* path, void* context, uint8_t defaultLoaderId) { - WTF::String namespaceStringStr = namespaceString ? Bun::toWTFString(*namespaceString) : WTF::String(); - WTF::String pathStr = path ? Bun::toWTFString(*path) : WTF::String(); + WTF::String namespaceStringStr = namespaceString ? namespaceString->toWTFString(BunString::ZeroCopy) : WTF::String(); + WTF::String pathStr = path ? path->toWTFString(BunString::ZeroCopy) : WTF::String(); JSFunction* function = plugin->onLoadFunction.get(plugin); if (UNLIKELY(!function)) @@ -330,12 +330,12 @@ extern "C" void JSBundlerPlugin__matchOnLoad(JSC::JSGlobalObject* globalObject, extern "C" void JSBundlerPlugin__matchOnResolve(JSC::JSGlobalObject* globalObject, Bun::JSBundlerPlugin* plugin, const BunString* namespaceString, const BunString* path, const BunString* importer, void* context, uint8_t kindId) { - WTF::String namespaceStringStr = namespaceString ? Bun::toWTFString(*namespaceString) : WTF::String("file"_s); + WTF::String namespaceStringStr = namespaceString ? namespaceString->toWTFString(BunString::ZeroCopy) : WTF::String("file"_s); if (namespaceStringStr.length() == 0) { namespaceStringStr = WTF::String("file"_s); } - WTF::String pathStr = path ? Bun::toWTFString(*path) : WTF::String(); - WTF::String importerStr = importer ? Bun::toWTFString(*importer) : WTF::String(); + WTF::String pathStr = path ? path->toWTFString(BunString::ZeroCopy) : WTF::String(); + WTF::String importerStr = importer ? importer->toWTFString(BunString::ZeroCopy) : WTF::String(); auto& vm = globalObject->vm(); JSFunction* function = plugin->onResolveFunction.get(plugin); diff --git a/src/bun.js/bindings/JSMockFunction.cpp b/src/bun.js/bindings/JSMockFunction.cpp index 15675117f2..68881e11b9 100644 --- a/src/bun.js/bindings/JSMockFunction.cpp +++ b/src/bun.js/bindings/JSMockFunction.cpp @@ -751,7 +751,7 @@ JSMockModule JSMockModule::create(JSC::JSGlobalObject* globalObject) globalObject, JSC::JSFunction::create(init.vm, init.owner, 0, "lastCall"_s, jsMockFunctionGetter_mockGetLastCall, ImplementationVisibility::Public), jsUndefined()), - JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); + JSC::PropertyAttribute::Accessor | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype( diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index 94c852e358..d3efccec9c 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -352,7 +352,7 @@ static JSValue handleVirtualModuleResult( object); auto source = JSC::SourceCode( JSC::SyntheticSourceProvider::create(WTFMove(function), - JSC::SourceOrigin(), Bun::toWTFString(*specifier))); + JSC::SourceOrigin(), specifier->toWTFString(BunString::ZeroCopy))); JSC::ensureStillAliveHere(object); return rejectOrResolve(JSSourceCode::create(globalObject->vm(), WTFMove(source))); } @@ -362,8 +362,8 @@ static JSValue handleVirtualModuleResult( JSFunction* performPromiseThenFunction = globalObject->performPromiseThenFunction(); auto callData = JSC::getCallData(performPromiseThenFunction); ASSERT(callData.type != CallData::Type::None); - auto specifierString = Bun::toWTFString(*specifier); - auto referrerString = Bun::toWTFString(*referrer); + auto specifierString = specifier->toWTFString(BunString::ZeroCopy); + auto referrerString = referrer->toWTFString(BunString::ZeroCopy); PendingVirtualModuleResult* pendingModule = PendingVirtualModuleResult::create(globalObject, specifierString, referrerString); JSC::JSInternalPromise* internalPromise = pendingModule->internalPromise(); MarkedArgumentBuffer arguments; @@ -449,7 +449,7 @@ JSValue fetchCommonJSModule( RELEASE_AND_RETURN(scope, JSValue {}); } case JSPromise::Status::Pending: { - JSC::throwTypeError(globalObject, scope, makeString("require() async module \""_s, Bun::toWTFString(*specifier), "\" is unsupported. use \"await import()\" instead."_s)); + JSC::throwTypeError(globalObject, scope, makeString("require() async module \""_s, specifier->toWTFString(BunString::ZeroCopy), "\" is unsupported. use \"await import()\" instead."_s)); RELEASE_AND_RETURN(scope, JSValue {}); } case JSPromise::Status::Fulfilled: { @@ -477,11 +477,11 @@ JSValue fetchCommonJSModule( auto tag = res->result.value.tag; switch (tag) { // Generated native module cases -#define CASE(str, name) \ - case SyntheticModuleType::name: { \ - target->evaluate(globalObject, Bun::toWTFString(*specifier), generateNativeModule_##name); \ - RETURN_IF_EXCEPTION(scope, {}); \ - RELEASE_AND_RETURN(scope, target); \ +#define CASE(str, name) \ + case SyntheticModuleType::name: { \ + target->evaluate(globalObject, specifier->toWTFString(BunString::ZeroCopy), generateNativeModule_##name); \ + RETURN_IF_EXCEPTION(scope, {}); \ + RELEASE_AND_RETURN(scope, target); \ } BUN_FOREACH_NATIVE_MODULE(CASE) #undef CASE @@ -521,7 +521,7 @@ JSValue fetchCommonJSModule( RELEASE_AND_RETURN(scope, JSValue {}); } case JSPromise::Status::Pending: { - JSC::throwTypeError(globalObject, scope, makeString("require() async module \""_s, Bun::toWTFString(*specifier), "\" is unsupported. use \"await import()\" instead."_s)); + JSC::throwTypeError(globalObject, scope, makeString("require() async module \""_s, specifier->toWTFString(BunString::ZeroCopy), "\" is unsupported. use \"await import()\" instead."_s)); RELEASE_AND_RETURN(scope, JSValue {}); } case JSPromise::Status::Fulfilled: { @@ -561,7 +561,7 @@ JSValue fetchCommonJSModule( Bun__transpileFile(bunVM, globalObject, specifier, referrer, res, false); if (res->success && res->result.value.commonJSExportsLen) { - target->evaluate(globalObject, Bun::toWTFString(*specifier), res->result.value); + target->evaluate(globalObject, specifier->toWTFString(BunString::ZeroCopy), res->result.value); RETURN_IF_EXCEPTION(scope, {}); RELEASE_AND_RETURN(scope, target); } @@ -583,7 +583,7 @@ JSValue fetchCommonJSModule( // When parsing tsconfig.*.json or jsconfig.*.json, we go through Bun's JSON // parser instead to support comments and trailing commas. if (res->result.value.tag == SyntheticModuleType::JSONForObjectLoader) { - JSC::JSValue value = JSC::JSONParse(globalObject, Bun::toWTFString(res->result.value.source_code)); + JSC::JSValue value = JSC::JSONParse(globalObject, res->result.value.source_code.toWTFString(BunString::ZeroCopy)); if (!value) { JSC::throwException(globalObject, scope, JSC::createSyntaxError(globalObject, "Failed to parse JSON"_s)); RELEASE_AND_RETURN(scope, {}); @@ -670,7 +670,7 @@ static JSValue fetchESMSourceCode( return reject(exception); } - auto moduleKey = Bun::toWTFString(*specifier); + auto moduleKey = specifier->toWTFString(BunString::ZeroCopy); auto tag = res->result.value.tag; switch (tag) { @@ -752,7 +752,7 @@ static JSValue fetchESMSourceCode( // When parsing tsconfig.*.json or jsconfig.*.json, we go through Bun's JSON // parser instead to support comments and trailing commas. if (res->result.value.tag == SyntheticModuleType::JSONForObjectLoader) { - JSC::JSValue value = JSC::JSONParse(globalObject, Bun::toWTFString(res->result.value.source_code)); + JSC::JSValue value = JSC::JSONParse(globalObject, res->result.value.source_code.toWTFString(BunString::ZeroCopy)); if (!value) { return reject(JSC::JSValue(JSC::createSyntaxError(globalObject, "Failed to parse JSON"_s))); } @@ -763,7 +763,7 @@ static JSValue fetchESMSourceCode( value); auto source = JSC::SourceCode( JSC::SyntheticSourceProvider::create(WTFMove(function), - JSC::SourceOrigin(), Bun::toWTFString(*specifier))); + JSC::SourceOrigin(), specifier->toWTFString(BunString::ZeroCopy))); JSC::ensureStillAliveHere(value); return rejectOrResolve(JSSourceCode::create(globalObject->vm(), WTFMove(source))); } diff --git a/src/bun.js/bindings/NodeHTTP.cpp b/src/bun.js/bindings/NodeHTTP.cpp index caccd85f34..d0e6835e2b 100644 --- a/src/bun.js/bindings/NodeHTTP.cpp +++ b/src/bun.js/bindings/NodeHTTP.cpp @@ -24,7 +24,7 @@ extern "C" uWS::HttpRequest* Request__getUWSRequest(void*); static EncodedJSValue assignHeadersFromFetchHeaders(FetchHeaders& impl, JSObject* prototype, JSObject* objectValue, JSC::InternalFieldTuple* tuple, JSC::JSGlobalObject* globalObject, JSC::VM& vm) { auto scope = DECLARE_THROW_SCOPE(vm); - uint32_t size = std::min(impl.sizeAfterJoiningSetCookieHeader(), static_cast(63)); + uint32_t size = std::min(impl.sizeAfterJoiningSetCookieHeader(), static_cast(JSFinalObject::maxInlineCapacity)); JSC::JSArray* array = constructEmptyArray(globalObject, nullptr, impl.size() * 2); RETURN_IF_EXCEPTION(scope, {}); JSC::JSObject* obj = JSC::constructEmptyObject(globalObject, prototype, size); @@ -202,7 +202,7 @@ static EncodedJSValue assignHeadersFromUWebSockets(uWS::HttpRequest* request, JS size++; } - JSC::JSObject* headersObject = JSC::constructEmptyObject(globalObject, prototype, std::min(size, static_cast(63))); + JSC::JSObject* headersObject = JSC::constructEmptyObject(globalObject, prototype, std::min(size, static_cast(JSFinalObject::maxInlineCapacity))); RETURN_IF_EXCEPTION(scope, {}); JSC::JSArray* array = constructEmptyArray(globalObject, nullptr, size * 2); JSC::JSArray* setCookiesHeaderArray = nullptr; diff --git a/src/bun.js/bindings/RegularExpression.cpp b/src/bun.js/bindings/RegularExpression.cpp index e812722b5f..48b3b8bb82 100644 --- a/src/bun.js/bindings/RegularExpression.cpp +++ b/src/bun.js/bindings/RegularExpression.cpp @@ -11,7 +11,7 @@ extern "C" RegularExpression* Yarr__RegularExpression__init(BunString pattern, u // TODO: Remove this, we technically are accessing options before we finalize them. // This means you cannot use BUN_JSC_dumpCompiledRegExpPatterns on the flag passed to `bun test -t` Options::AllowUnfinalizedAccessScope scope; - return new RegularExpression(Bun::toWTFString(pattern), OptionSet(static_cast(flags))); + return new RegularExpression(pattern.toWTFString(BunString::ZeroCopy), OptionSet(static_cast(flags))); } extern "C" void Yarr__RegularExpression__deinit(RegularExpression* re) { @@ -27,13 +27,13 @@ extern "C" int Yarr__RegularExpression__matchedLength(RegularExpression* re) } extern "C" int Yarr__RegularExpression__searchRev(RegularExpression* re, BunString string) { - return re->searchRev(Bun::toWTFString(string)); + return re->searchRev(string.toWTFString(BunString::ZeroCopy)); } // extern "C" int Yarr__RegularExpression__match(RegularExpression* re, BunString string, int32_t start, int32_t* matchLength) // { -// return re->match(Bun::toWTFString(string), start, matchLength); +// return re->match(string.toWTFString(BunString::ZeroCopy), start, matchLength); // } extern "C" int Yarr__RegularExpression__matches(RegularExpression* re, BunString string) { - return re->match(Bun::toWTFString(string), 0, 0); + return re->match(string.toWTFString(BunString::ZeroCopy), 0, 0); } \ No newline at end of file diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index c15d7f61cb..19eaaf093c 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -564,16 +564,15 @@ static void resetOnEachMicrotaskTick(JSC::VM& vm, Zig::GlobalObject* globalObjec extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, int32_t executionContextId, bool miniMode, void* worker_ptr) { + auto heapSize = miniMode ? JSC::HeapType::Small : JSC::HeapType::Large; - JSC::VM& vm = JSC::VM::create(heapSize).leakRef(); - // This must happen before JSVMClientData::create vm.heap.acquireAccess(); + JSC::JSLockHolder locker(vm); WebCore::JSVMClientData::create(&vm, Bun__getVM()); - JSC::JSLockHolder locker(vm); Zig::GlobalObject* globalObject; if (UNLIKELY(executionContextId > -1)) { @@ -584,14 +583,19 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, if (auto* worker = static_cast(worker_ptr)) { auto& options = worker->options(); + + // ensure remote termination works. + vm.ensureTerminationException(); + vm.forbidExecutionOnTermination(); + if (options.bun.env) { - auto map = *options.bun.env; - auto size = map.size(); - auto env = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), size > 63 ? 63 : size); - for (auto k : map) { + auto map = WTFMove(options.bun.env); + auto size = map->size(); + auto env = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), size >= JSFinalObject::maxInlineCapacity ? JSFinalObject::maxInlineCapacity : size); + for (auto k : *map) { env->putDirect(vm, JSC::Identifier::fromString(vm, WTFMove(k.key)), JSC::jsString(vm, WTFMove(k.value))); } - map.clear(); + map->clear(); globalObject->m_processEnvObject.set(vm, globalObject, env); } } @@ -619,7 +623,6 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, } }); - vm.ref(); return globalObject; } @@ -777,6 +780,22 @@ extern "C" JSC__JSValue JSC__JSValue__makeWithNameAndPrototype(JSC__JSGlobalObje return JSC::JSValue::encode(JSC::JSValue(object)); } +extern "C" int Bun__VM__scriptExecutionStatus(void*); +JSC::ScriptExecutionStatus Zig::GlobalObject::scriptExecutionStatus(JSC::JSGlobalObject* globalObject, JSC::JSObject*) +{ + switch (Bun__VM__scriptExecutionStatus(jsCast(globalObject)->bunVM())) { + case 0: + return JSC::ScriptExecutionStatus::Running; + case 1: + return JSC::ScriptExecutionStatus::Suspended; + case 2: + return JSC::ScriptExecutionStatus::Stopped; + default: { + RELEASE_ASSERT_NOT_REACHED(); + } + } +} + const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &supportsRichSourceInfo, &shouldInterruptScript, @@ -813,6 +832,8 @@ GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure) // m_scriptExecutionContext = globalEventScope.m_context; mockModule = Bun::JSMockModule::create(this); globalEventScope.m_context = m_scriptExecutionContext; + // FIXME: is there a better way to do this? this event handler should always be tied to the global object + globalEventScope.relaxAdoptionRequirement(); } GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure, WebCore::ScriptExecutionContextIdentifier contextId) @@ -828,6 +849,8 @@ GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure, WebCore::Scri // m_scriptExecutionContext = globalEventScope.m_context; mockModule = Bun::JSMockModule::create(this); globalEventScope.m_context = m_scriptExecutionContext; + // FIXME: is there a better way to do this? this event handler should always be tied to the global object + globalEventScope.relaxAdoptionRequirement(); } GlobalObject::~GlobalObject() @@ -2017,12 +2040,12 @@ extern "C" void ReadableStream__cancel(JSC__JSValue possibleReadableStream, Zig: if (UNLIKELY(!readableStream)) return; - if (!ReadableStream(*globalObject, *readableStream).isLocked()) { + if (!ReadableStream::isLocked(globalObject, readableStream)) { return; } WebCore::Exception exception { AbortError }; - ReadableStream(*globalObject, *readableStream).cancel(exception); + ReadableStream::cancel(*globalObject, readableStream, exception); } extern "C" void ReadableStream__detach(JSC__JSValue possibleReadableStream, Zig::GlobalObject* globalObject); @@ -4085,10 +4108,10 @@ JSC::Identifier GlobalObject::moduleLoaderResolve(JSGlobalObject* jsGlobalObject if (res.success) { if (queryString.len > 0) { - return JSC::Identifier::fromString(globalObject->vm(), makeString(Bun::toWTFString(res.result.value), Zig::toString(queryString))); + return JSC::Identifier::fromString(globalObject->vm(), makeString(res.result.value.toWTFString(BunString::ZeroCopy), Zig::toString(queryString))); } - return Identifier::fromString(globalObject->vm(), toWTFString(res.result.value)); + return Identifier::fromString(globalObject->vm(), res.result.value.toWTFString(BunString::ZeroCopy)); } else { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); throwException(scope, res.result.err, globalObject); @@ -4157,9 +4180,9 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* j JSC::Identifier resolvedIdentifier; if (queryString.len == 0) { - resolvedIdentifier = JSC::Identifier::fromString(vm, Bun::toWTFString(resolved.result.value)); + resolvedIdentifier = JSC::Identifier::fromString(vm, resolved.result.value.toWTFString(BunString::ZeroCopy)); } else { - resolvedIdentifier = JSC::Identifier::fromString(vm, makeString(Bun::toWTFString(resolved.result.value), Zig::toString(queryString))); + resolvedIdentifier = JSC::Identifier::fromString(vm, makeString(resolved.result.value.toWTFString(BunString::ZeroCopy), Zig::toString(queryString))); } auto result = JSC::importModule(globalObject, resolvedIdentifier, diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index b1eab732df..162c402499 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -167,6 +167,7 @@ public: static JSC::JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue, JSC::JSValue, JSC::JSValue); static JSC::JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue, JSC::JSModuleRecord*, JSC::JSValue); static JSC::JSValue moduleLoaderEvaluate(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::JSValue); + static ScriptExecutionStatus scriptExecutionStatus(JSGlobalObject*, JSObject*); static void promiseRejectionTracker(JSGlobalObject*, JSC::JSPromise*, JSC::JSPromiseRejectionOperation); void setConsole(void* console); WebCore::JSBuiltinInternalFunctions& builtinInternalFunctions() { return m_builtinInternalFunctions; } diff --git a/src/bun.js/bindings/ZigSourceProvider.cpp b/src/bun.js/bindings/ZigSourceProvider.cpp index df1f8cc293..7ea83f9a92 100644 --- a/src/bun.js/bindings/ZigSourceProvider.cpp +++ b/src/bun.js/bindings/ZigSourceProvider.cpp @@ -95,14 +95,14 @@ extern "C" bool BunTest__shouldGenerateCodeCoverage(BunString sourceURL); Ref SourceProvider::create(Zig::GlobalObject* globalObject, ResolvedSource resolvedSource, JSC::SourceProviderSourceType sourceType, bool isBuiltin) { - auto stringImpl = Bun::toWTFString(resolvedSource.source_code); - auto sourceURLString = Bun::toWTFString(resolvedSource.source_url); + auto stringImpl = resolvedSource.source_code.toWTFString(BunString::ZeroCopy); + auto sourceURLString = resolvedSource.source_url.toWTFString(BunString::ZeroCopy); bool isCodeCoverageEnabled = !!globalObject->vm().controlFlowProfiler(); bool shouldGenerateCodeCoverage = isCodeCoverageEnabled && !isBuiltin && BunTest__shouldGenerateCodeCoverage(resolvedSource.source_url); - if (resolvedSource.needsDeref) { + if (resolvedSource.needsDeref && !isBuiltin) { resolvedSource.source_code.deref(); resolvedSource.specifier.deref(); // source_url gets deref'd by the WTF::String above. diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 82a8d64620..2573757d41 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -1007,41 +1007,41 @@ bool Bun__deepMatch(JSValue objValue, JSValue subsetValue, JSGlobalObject* globa } } - for (size_t i = 0; i < subsetProps.size(); i++) { - JSValue prop = obj->getIfPropertyExists(globalObject, subsetProps[i]); + for (const auto& property : subsetProps) { + JSValue prop = obj->getIfPropertyExists(globalObject, property); RETURN_IF_EXCEPTION(*throwScope, false); if (prop.isEmpty()) { return false; } - JSValue subsetProp = subsetObj->get(globalObject, subsetProps[i]); + JSValue subsetProp = subsetObj->get(globalObject, property); RETURN_IF_EXCEPTION(*throwScope, false); - JSCell* subsetPropCell = subsetProp.asCell(); - JSCell* propCell = prop.asCell(); + JSCell* subsetPropCell = !subsetProp.isEmpty() && subsetProp.isCell() ? subsetProp.asCell() : nullptr; + JSCell* propCell = prop.isCell() ? prop.asCell() : nullptr; if constexpr (enableAsymmetricMatchers) { - if (subsetProp.isCell() && !subsetProp.isEmpty() && subsetPropCell->type() == JSC::JSType(JSDOMWrapperType)) { + if (subsetPropCell && subsetPropCell->type() == JSC::JSType(JSDOMWrapperType)) { switch (matchAsymmetricMatcher(globalObject, subsetPropCell, prop, throwScope)) { case AsymmetricMatcherResult::FAIL: return false; case AsymmetricMatcherResult::PASS: if (replacePropsWithAsymmetricMatchers) { - obj->putDirect(vm, subsetProps[i], subsetProp); + obj->putDirectMayBeIndex(globalObject, property, subsetProp); } // continue to next subset prop continue; case AsymmetricMatcherResult::NOT_MATCHER: break; } - } else if (prop.isCell() && !prop.isEmpty() && propCell->type() == JSC::JSType(JSDOMWrapperType)) { + } else if (propCell && propCell->type() == JSC::JSType(JSDOMWrapperType)) { switch (matchAsymmetricMatcher(globalObject, propCell, subsetProp, throwScope)) { case AsymmetricMatcherResult::FAIL: return false; case AsymmetricMatcherResult::PASS: if (replacePropsWithAsymmetricMatchers) { - subsetObj->putDirect(vm, subsetProps[i], prop); + subsetObj->putDirectMayBeIndex(globalObject, property, prop); } // continue to next subset prop continue; @@ -1419,20 +1419,6 @@ extern "C" JSC__JSValue ZigString__toJSONObject(const ZigString* strPtr, JSC::JS return JSValue::encode(result); } -// TODO: Move this to BunString.cpp -extern "C" JSC__JSValue BunString__toJSDOMURL(JSC::JSGlobalObject* lexicalGlobalObject, BunString* bunString) -{ - auto& globalObject = *reinterpret_cast(lexicalGlobalObject); - auto& vm = globalObject.vm(); - auto throwScope = DECLARE_THROW_SCOPE(vm); - - auto str = Bun::toWTFString(*bunString); - - auto object = WebCore::DOMURL::create(str, String()); - auto jsValue = WebCore::toJSNewlyCreated>(*lexicalGlobalObject, globalObject, throwScope, WTFMove(object)); - RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(jsValue)); -} - JSC__JSValue SystemError__toErrorInstance(const SystemError* arg0, JSC__JSGlobalObject* globalObject) { @@ -1510,7 +1496,7 @@ JSC__JSValue JSC__JSValue__createEmptyObject(JSC__JSGlobalObject* globalObject, size_t initialCapacity) { return JSC::JSValue::encode( - JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), initialCapacity)); + JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), std::min(static_cast(initialCapacity), JSFinalObject::maxInlineCapacity))); } extern "C" uint64_t Bun__Blob__getSizeForBindings(void* blob); @@ -2050,7 +2036,15 @@ JSC__JSObject* JSC__JSString__toObject(JSC__JSString* arg0, JSC__JSGlobalObject* // JSC__JSGlobalObject* arg1, JSC__JSModuleRecord* arg2) { // arg2->depen // } +extern "C" JSC::JSInternalPromise* JSModuleLoader__import(JSC::JSGlobalObject* globalObject, const BunString* moduleNameStr) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + auto* promise = JSC::importModule(globalObject, JSC::Identifier::fromString(globalObject->vm(), moduleNameStr->toWTFString()), jsUndefined(), jsUndefined(), jsUndefined()); + RETURN_IF_EXCEPTION(scope, nullptr); + return promise; +} JSC__JSValue JSC__JSModuleLoader__evaluate(JSC__JSGlobalObject* globalObject, const unsigned char* arg1, size_t arg2, const unsigned char* originUrlPtr, size_t originURLLen, const unsigned char* referrerUrlPtr, size_t referrerUrlLen, JSC__JSValue JSValue5, JSC__JSValue* arg6) @@ -2539,25 +2533,19 @@ JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* globalObject, const BunString* arg1) { - auto name = Bun::toWTFString(*arg1); - name.impl()->ref(); + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + auto name = makeAtomString(arg1->toWTFString()); auto* promise = JSC::loadAndEvaluateModule(globalObject, name, JSC::jsUndefined(), JSC::jsUndefined()); if (!promise) { - // usually this is a GC issue - return jsCast(JSC::JSInternalPromise::rejectedPromise(globalObject, JSC::jsUndefined())); + return nullptr; } JSC::JSNativeStdFunction* resolverFunction = JSC::JSNativeStdFunction::create( - globalObject->vm(), globalObject, 1, String(), resolverFunctionCallback); - JSC::JSNativeStdFunction* rejecterFunction = JSC::JSNativeStdFunction::create( - globalObject->vm(), globalObject, 1, String(), - [&arg1](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue { - return JSC::JSValue::encode( - JSC::JSInternalPromise::rejectedPromise(globalObject, callFrame->argument(0))); - }); + vm, globalObject, 1, String(), resolverFunctionCallback); - auto result = promise->then(globalObject, resolverFunction, rejecterFunction); + auto result = promise->then(globalObject, resolverFunction, nullptr); // if (promise->status(globalObject->vm()) == // JSC::JSPromise::Status::Fulfilled) { @@ -2573,10 +2561,19 @@ JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* globalObject, } #pragma mark - JSC::JSPromise -void JSC__JSPromise__reject(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, +void JSC__JSPromise__reject(JSC__JSPromise* arg0, JSC__JSGlobalObject* globalObject, JSC__JSValue JSValue2) { - arg0->reject(arg1, JSC::JSValue::decode(JSValue2)); + JSValue value = JSC::JSValue::decode(JSValue2); + auto& vm = globalObject->vm(); + JSC::Exception* exception = nullptr; + if (!value.inherits()) { + exception = JSC::Exception::create(vm, value, JSC::Exception::StackCaptureAction::CaptureStack); + } else { + exception = jsCast(value); + } + + arg0->reject(globalObject, exception); } void JSC__JSPromise__rejectAsHandled(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2) @@ -2717,10 +2714,19 @@ JSC__JSInternalPromise* JSC__JSInternalPromise__create(JSC__JSGlobalObject* glob return JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure()); } -void JSC__JSInternalPromise__reject(JSC__JSInternalPromise* arg0, JSC__JSGlobalObject* arg1, +void JSC__JSInternalPromise__reject(JSC__JSInternalPromise* arg0, JSC__JSGlobalObject* globalObject, JSC__JSValue JSValue2) { - arg0->reject(arg1, JSC::JSValue::decode(JSValue2)); + JSValue value = JSC::JSValue::decode(JSValue2); + auto& vm = globalObject->vm(); + JSC::Exception* exception = nullptr; + if (!value.inherits()) { + exception = JSC::Exception::create(vm, value, JSC::Exception::StackCaptureAction::CaptureStack); + } else { + exception = jsCast(value); + } + + arg0->reject(globalObject, exception); } void JSC__JSInternalPromise__rejectAsHandled(JSC__JSInternalPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2) @@ -3364,9 +3370,13 @@ int64_t JSC__JSValue__coerceToInt64(JSC__JSValue JSValue0, JSC__JSGlobalObject* return value.toBigInt64(arg1); } - int64_t result = tryConvertToInt52(value.asDouble()); - if (result != JSValue::notInt52) { - return result; + if (value.isDouble()) { + int64_t result = tryConvertToInt52(value.asDouble()); + if (result != JSValue::notInt52) { + return result; + } + + return static_cast(value.asDouble()); } return value.toInt32(arg1); @@ -4038,7 +4048,16 @@ bool JSC__VM__isEntered(JSC__VM* arg0) { return (*arg0).isEntered(); } void JSC__VM__setExecutionForbidden(JSC__VM* arg0, bool arg1) { (*arg0).setExecutionForbidden(); } // These may be called concurrently from another thread. -void JSC__VM__notifyNeedTermination(JSC__VM* arg0) { (*arg0).notifyNeedTermination(); } +void JSC__VM__notifyNeedTermination(JSC__VM* arg0) +{ + JSC::VM& vm = *arg0; + bool didEnter = vm.currentThreadIsHoldingAPILock(); + if (didEnter) + vm.apiLock().unlock(); + vm.notifyNeedTermination(); + if (didEnter) + vm.apiLock().lock(); +} void JSC__VM__notifyNeedDebuggerBreak(JSC__VM* arg0) { (*arg0).notifyNeedDebuggerBreak(); } void JSC__VM__notifyNeedShellTimeoutCheck(JSC__VM* arg0) { (*arg0).notifyNeedShellTimeoutCheck(); } void JSC__VM__notifyNeedWatchdogCheck(JSC__VM* arg0) { (*arg0).notifyNeedWatchdogCheck(); } diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index f02ed7701a..ec486cb97e 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -1858,13 +1858,18 @@ pub const JSModuleLoader = extern struct { }); } - pub fn loadAndEvaluateModule(globalObject: *JSGlobalObject, module_name: *const bun.String) *JSInternalPromise { + pub fn loadAndEvaluateModule(globalObject: *JSGlobalObject, module_name: *const bun.String) ?*JSInternalPromise { return shim.cppFn("loadAndEvaluateModule", .{ globalObject, module_name, }); } + extern fn JSModuleLoader__import(*JSGlobalObject, *const bun.String) *JSInternalPromise; + pub fn import(globalObject: *JSGlobalObject, module_name: *const bun.String) *JSInternalPromise { + return JSModuleLoader__import(globalObject, module_name); + } + // pub fn dependencyKeysIfEvaluated(this: *JSModuleLoader, globalObject: *JSGlobalObject, moduleRecord: *JSModuleRecord) *JSValue { // return shim.cppFn("dependencyKeysIfEvaluated", .{ this, globalObject, moduleRecord }); // } @@ -3545,7 +3550,6 @@ pub const JSValue = enum(JSValueReprInt) { } pub fn createEmptyObject(global: *JSGlobalObject, len: usize) JSValue { - std.debug.assert(len <= 63); // max inline capacity JSC allows is 63. If you run into this, just set it to 0. return cppFn("createEmptyObject", .{ global, len }); } @@ -4680,6 +4684,7 @@ pub const JSValue = enum(JSValueReprInt) { std.debug.assert(!this.isCell()); // use coerce() instead } + // TODO: this shouldn't be reachable. return cppFn("toInt32", .{ this, }); @@ -5863,3 +5868,9 @@ pub fn initialize() void { }.callback, ); } + +pub const ScriptExecutionStatus = enum(i32) { + running = 0, + suspended = 1, + stopped = 2, +}; diff --git a/src/bun.js/bindings/codegen.zig b/src/bun.js/bindings/codegen.zig new file mode 100644 index 0000000000..eaa3f4e644 --- /dev/null +++ b/src/bun.js/bindings/codegen.zig @@ -0,0 +1,33 @@ +const JSC = @import("root").bun.JSC; +const std = @import("std"); + +pub const CallbackGetterFn = fn (JSC.JSValue) callconv(.C) JSC.JSValue; +pub const CallbackSetterFn = fn (JSC.JSValue, JSC.JSValue) callconv(.C) void; + +pub fn CallbackWrapper(comptime Getter: *const CallbackGetterFn, comptime Setter: *const CallbackSetterFn) type { + return struct { + const GetFn = Getter; + const SetFn = Setter; + container: JSC.JSValue, + + pub inline fn get(self: @This()) ?JSC.JSValue { + const res = GetFn(self.container); + if (res.isEmptyOrUndefinedOrNull()) + return null; + + return res; + } + + pub inline fn set(self: @This(), value: JSC.JSValue) void { + SetFn(self.container, value); + } + + pub inline fn call(self: @This(), globalObject: *JSC.JSGlobalObject, args: []const JSC.JSValue) ?JSC.JSValue { + if (self.get()) |callback| { + return callback.call(globalObject, args); + } + + return null; + } + }; +} diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index ff831b06b6..f6fae9160d 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -207,7 +207,7 @@ pub const ResolvedSource = extern struct { tag: Tag = Tag.javascript, needs_deref: bool = true, - pub const Tag = @import("generated/ResolvedSourceTag.zig").ResolvedSourceTag; + pub const Tag = @import("ResolvedSourceTag").ResolvedSourceTag; }; const Mimalloc = @import("../../allocators/mimalloc.zig"); diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index c1998567f1..a080d934e9 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -6,6 +6,10 @@ typedef struct VirtualMachine VirtualMachine; // exists to make headers.h happy typedef struct CppWebSocket CppWebSocket; +namespace WTF { +class String; +} + typedef struct ZigString { const unsigned char* ptr; size_t len; @@ -42,9 +46,26 @@ typedef struct BunString { BunStringTag tag; BunStringImpl impl; + enum ZeroCopyTag { ZeroCopy }; + + // If it's not a WTFStringImpl, this does nothing inline void ref(); + + // If it's not a WTFStringImpl, this does nothing inline void deref(); + static size_t utf8ByteLength(const WTF::String&); + + // Zero copy is kind of a lie. + // We clone it if it's non-ASCII UTF-8. + // We don't clone it if it was marked as static + // if it was a ZigString, it still allocates a WTF::StringImpl. + // It's only truly zero-copy if it was already a WTFStringImpl (which it is if it came from JS and we didn't use ZigString) + WTF::String toWTFString(ZeroCopyTag) const; + + // This one usually will clone the raw bytes. + WTF::String toWTFString() const; + } BunString; typedef struct ZigErrorType { @@ -248,7 +269,6 @@ namespace Bun { JSC::JSValue toJS(JSC::JSGlobalObject*, BunString); BunString toString(JSC::JSGlobalObject* globalObject, JSC::JSValue value); BunString toString(const char* bytes, size_t length); -WTF::String toWTFString(const BunString& bunString); BunString toString(WTF::String& wtfString); BunString toString(const WTF::String& wtfString); BunString toString(WTF::StringImpl* wtfString); diff --git a/src/bun.js/bindings/napi.h b/src/bun.js/bindings/napi.h index da4b01c204..4f3e699e62 100644 --- a/src/bun.js/bindings/napi.h +++ b/src/bun.js/bindings/napi.h @@ -221,7 +221,13 @@ public: FunctionRareData* rareData = targetFunction->ensureRareData(vm); auto* prototype = newTarget->get(globalObject, vm.propertyNames->prototype).getObject(); RETURN_IF_EXCEPTION(scope, nullptr); - auto* structure = rareData->createInternalFunctionAllocationStructureFromBase(vm, globalObject, prototype, this->structure()); + + // This must be kept in-sync with InternalFunction::createSubclassStructure + Structure* structure = rareData->internalFunctionAllocationStructure(); + if (UNLIKELY(!(structure && structure->classInfoForCells() == this->structure()->classInfoForCells() && structure->globalObject() == globalObject))) { + structure = rareData->createInternalFunctionAllocationStructureFromBase(vm, globalObject, prototype, this->structure()); + } + RETURN_IF_EXCEPTION(scope, nullptr); NapiPrototype* footprint = new (NotNull, allocateCell(vm)) NapiPrototype(vm, structure); footprint->finishCreation(vm); diff --git a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp index e5c3c1d7f8..1652672940 100644 --- a/src/bun.js/bindings/sqlite/JSSQLStatement.cpp +++ b/src/bun.js/bindings/sqlite/JSSQLStatement.cpp @@ -187,7 +187,7 @@ public: JSC::JSValue rebind(JSGlobalObject* globalObject, JSC::JSValue values, bool clone); static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) { - return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::JSFunctionType, StructureFlags), info()); + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); } bool need_update() { return version_db->version.load() != version; } @@ -209,8 +209,6 @@ protected: , stmt(stmt) , version_db(version_db) , columnNames(new PropertyNameArray(globalObject.vm(), PropertyNameMode::Strings, PrivateSymbolMode::Exclude)) - , _structure(globalObject.vm(), this, nullptr) - , _prototype(globalObject.vm(), this, nullptr) { } @@ -238,11 +236,11 @@ static void initializeColumnNames(JSC::JSGlobalObject* lexicalGlobalObject, JSSQ castedThis->_prototype.clear(); int count = sqlite3_column_count(stmt); - if (count == 0) + if (count < 1) return; // Fast path: - if (count < 64) { + if (count <= JSFinalObject::maxInlineCapacity) { // 64 is the maximum we can preallocate here // see https://github.com/oven-sh/bun/issues/987 // also see https://github.com/oven-sh/bun/issues/1646 @@ -294,7 +292,7 @@ static void initializeColumnNames(JSC::JSGlobalObject* lexicalGlobalObject, JSSQ // 64 is the maximum we can preallocate here // see https://github.com/oven-sh/bun/issues/987 - JSC::JSObject* object = JSC::constructEmptyObject(lexicalGlobalObject, lexicalGlobalObject->objectPrototype(), std::min(count, 64)); + JSC::JSObject* object = JSC::constructEmptyObject(lexicalGlobalObject, lexicalGlobalObject->objectPrototype(), std::min(static_cast(count), JSFinalObject::maxInlineCapacity)); for (int i = 0; i < count; i++) { const char* name = sqlite3_column_name(stmt, i); @@ -594,9 +592,13 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementDeserialize, (JSC::JSGlobalObject * lexic } int status = sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL); - assert(status == SQLITE_OK); + if (status != SQLITE_OK) { + // TODO: log a warning here that we can't load extensions + } status = sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, NULL); - assert(status == SQLITE_OK); + if (status != SQLITE_OK) { + // TODO: log a warning here that defensive mode is not enabled + } status = sqlite3_deserialize(db, "main", reinterpret_cast(data), byteLength, byteLength, flags); if (status == SQLITE_BUSY) { @@ -989,10 +991,14 @@ JSC_DEFINE_HOST_FUNCTION(jsSQLStatementOpenStatementFunction, (JSC::JSGlobalObje } int status = sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL); - assert(status == SQLITE_OK); - status = sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, NULL); - assert(status == SQLITE_OK); + if (status != SQLITE_OK) { + // TODO: log a warning here that extensions are unsupported. + } + status = sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, NULL); + if (status != SQLITE_OK) { + // TODO: log a warning here that defensive mode is unsupported. + } auto count = databases().size(); databases().append(new VersionSqlite3(db)); RELEASE_AND_RETURN(scope, JSValue::encode(jsNumber(count))); @@ -1070,6 +1076,8 @@ void JSSQLStatementConstructor::finishCreation(VM& vm) reifyStaticProperties(vm, JSSQLStatementConstructor::info(), JSSQLStatementConstructorTableValues, *this); JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); + + ASSERT(inherits(info())); } static inline JSC::JSValue constructResultObject(JSC::JSGlobalObject* lexicalGlobalObject, JSSQLStatement* castedThis); @@ -1139,10 +1147,10 @@ static inline JSC::JSValue constructResultObject(JSC::JSGlobalObject* lexicalGlo } } else { - if (count <= 64) { + if (count <= JSFinalObject::maxInlineCapacity) { result = JSC::JSFinalObject::create(vm, castedThis->_prototype.get()->structure()); } else { - result = JSC::JSFinalObject::create(vm, JSC::JSFinalObject::createStructure(vm, lexicalGlobalObject, lexicalGlobalObject->objectPrototype(), std::min(count, 64))); + result = JSC::JSFinalObject::create(vm, JSC::JSFinalObject::createStructure(vm, lexicalGlobalObject, lexicalGlobalObject->objectPrototype(), JSFinalObject::maxInlineCapacity)); } for (int i = 0; i < count; i++) { @@ -1650,6 +1658,7 @@ void JSSQLStatement::finishCreation(VM& vm) { Base::finishCreation(vm); reifyStaticProperties(vm, JSSQLStatement::info(), JSSQLStatementTableValues, *this); + ASSERT(inherits(info())); } JSSQLStatement::~JSSQLStatement() diff --git a/src/bun.js/bindings/webcore/BroadcastChannel.cpp b/src/bun.js/bindings/webcore/BroadcastChannel.cpp index 74ff150e62..c78bb74cea 100644 --- a/src/bun.js/bindings/webcore/BroadcastChannel.cpp +++ b/src/bun.js/bindings/webcore/BroadcastChannel.cpp @@ -61,7 +61,6 @@ static HashMap& allBroadcastChann static Lock channelToContextIdentifierLock; static HashMap& channelToContextIdentifier() { - ASSERT(isMainThread()); static NeverDestroyed> map; return map; } diff --git a/src/bun.js/bindings/webcore/HTTPParsers.cpp b/src/bun.js/bindings/webcore/HTTPParsers.cpp index 66aec49254..3974fbc6b7 100644 --- a/src/bun.js/bindings/webcore/HTTPParsers.cpp +++ b/src/bun.js/bindings/webcore/HTTPParsers.cpp @@ -36,7 +36,6 @@ #include "CommonAtomStrings.h" #include "HTTPHeaderField.h" #include "HTTPHeaderNames.h" -#include "ParsedContentType.h" #include #include #include diff --git a/src/bun.js/bindings/webcore/JSDOMFormData.cpp b/src/bun.js/bindings/webcore/JSDOMFormData.cpp index e47564e755..bbad4c6fde 100644 --- a/src/bun.js/bindings/webcore/JSDOMFormData.cpp +++ b/src/bun.js/bindings/webcore/JSDOMFormData.cpp @@ -311,7 +311,7 @@ static inline JSC::EncodedJSValue jsDOMFormDataPrototypeFunction_append2Body(JSC RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); EnsureStillAliveScope argument2 = callFrame->argument(2); - auto filename = argument2.value().isUndefined() ? Bun::toWTFString(Blob__getFileNameString(blobValue->impl())) : convert(*lexicalGlobalObject, argument2.value()); + auto filename = argument2.value().isUndefined() ? Blob__getFileNameString(blobValue->impl()).toWTFString(BunString::ZeroCopy) : convert(*lexicalGlobalObject, argument2.value()); RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.append(WTFMove(name), WTFMove(blobValue), WTFMove(filename)); }))); @@ -618,7 +618,7 @@ JSC::JSValue getInternalProperties(JSC::VM& vm, JSGlobalObject* lexicalGlobalObj obj = constructEmptyObject(lexicalGlobalObject); RETURN_IF_EXCEPTION(throwScope, {}); RELEASE_AND_RETURN(throwScope, obj); - } else if (size < 64) { + } else if (size < JSFinalObject::maxInlineCapacity) { obj = constructEmptyObject(lexicalGlobalObject, lexicalGlobalObject->objectPrototype(), size + 1); } else { obj = constructEmptyObject(lexicalGlobalObject); diff --git a/src/bun.js/bindings/webcore/JSFetchHeaders.cpp b/src/bun.js/bindings/webcore/JSFetchHeaders.cpp index bc6776ac36..d4608127a8 100644 --- a/src/bun.js/bindings/webcore/JSFetchHeaders.cpp +++ b/src/bun.js/bindings/webcore/JSFetchHeaders.cpp @@ -217,6 +217,12 @@ JSC_DEFINE_HOST_FUNCTION(jsFetchHeadersPrototypeFunction_getAll, (JSGlobalObject return JSValue::encode(JSC::constructEmptyArray(lexicalGlobalObject, nullptr, 0)); } + MarkedArgumentBuffer strings; + strings.ensureCapacity(count); + for (unsigned i = 0; i < count; ++i) { + strings.append(jsString(vm, values[i])); + } + JSC::JSArray* array = nullptr; GCDeferralContext deferralContext(lexicalGlobalObject->vm()); JSC::ObjectInitializationScope initializationScope(lexicalGlobalObject->vm()); @@ -224,10 +230,11 @@ JSC_DEFINE_HOST_FUNCTION(jsFetchHeadersPrototypeFunction_getAll, (JSGlobalObject initializationScope, &deferralContext, lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), count))) { + for (unsigned i = 0; i < count; ++i) { - array->initializeIndex(initializationScope, i, jsString(vm, values[i])); - RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); + array->initializeIndex(initializationScope, i, strings.at(i)); } + } else { array = constructEmptyArray(lexicalGlobalObject, nullptr, count); RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); @@ -236,12 +243,12 @@ JSC_DEFINE_HOST_FUNCTION(jsFetchHeadersPrototypeFunction_getAll, (JSGlobalObject return JSValue::encode(jsUndefined()); } for (unsigned i = 0; i < count; ++i) { - array->putDirectIndex(lexicalGlobalObject, i, jsString(vm, values[i])); + array->putDirectIndex(lexicalGlobalObject, i, strings.at(i)); RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); } - RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); } + RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); return JSValue::encode(array); } @@ -605,7 +612,7 @@ JSC::JSValue getInternalProperties(JSC::VM& vm, JSGlobalObject* lexicalGlobalObj obj = constructEmptyObject(lexicalGlobalObject); RETURN_IF_EXCEPTION(throwScope, {}); RELEASE_AND_RETURN(throwScope, obj); - } else if (size < 64) { + } else if (size <= JSFinalObject::maxInlineCapacity) { obj = constructEmptyObject(lexicalGlobalObject, lexicalGlobalObject->objectPrototype(), size); } else { obj = constructEmptyObject(lexicalGlobalObject); diff --git a/src/bun.js/bindings/webcore/JSURLSearchParams.cpp b/src/bun.js/bindings/webcore/JSURLSearchParams.cpp index a1884e8251..1e09b2adb1 100644 --- a/src/bun.js/bindings/webcore/JSURLSearchParams.cpp +++ b/src/bun.js/bindings/webcore/JSURLSearchParams.cpp @@ -404,7 +404,7 @@ JSC::JSValue getInternalProperties(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlob auto throwScope = DECLARE_THROW_SCOPE(vm); JSObject* obj; - if (impl.size() + 1 < 64) { + if (impl.size() + 1 <= JSFinalObject::maxInlineCapacity) { obj = JSC::constructEmptyObject(lexicalGlobalObject, lexicalGlobalObject->objectPrototype(), impl.size() + 1); } else { obj = JSC::constructEmptyObject(lexicalGlobalObject, lexicalGlobalObject->objectPrototype()); diff --git a/src/bun.js/bindings/webcore/ParsedContentType.cpp b/src/bun.js/bindings/webcore/ParsedContentType.cpp deleted file mode 100644 index 38e0f532c1..0000000000 --- a/src/bun.js/bindings/webcore/ParsedContentType.cpp +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2012 Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "ParsedContentType.h" - -#include "HTTPParsers.h" -#include -#include - -namespace WebCore { - -static void skipSpaces(StringView input, unsigned& startIndex) -{ - while (startIndex < input.length() && isASCIIWhitespaceWithoutFF(input[startIndex])) - ++startIndex; -} - -static bool isQuotedStringTokenCharacter(UChar c) -{ - return (c >= ' ' && c <= '~') || (c >= 0x80 && c <= 0xFF) || c == '\t'; -} - -static bool isTokenCharacter(UChar c) -{ - return isASCII(c) && c > ' ' && c != '"' && c != '(' && c != ')' && c != ',' && c != '/' && (c < ':' || c > '@') && (c < '[' || c > ']'); -} - -using CharacterMeetsCondition = bool (*)(UChar); - -static StringView parseToken(StringView input, unsigned& startIndex, CharacterMeetsCondition characterMeetsCondition, Mode mode, bool skipTrailingWhitespace = false) -{ - unsigned inputLength = input.length(); - unsigned tokenStart = startIndex; - unsigned& tokenEnd = startIndex; - - if (tokenEnd >= inputLength) - return StringView(); - - while (tokenEnd < inputLength && characterMeetsCondition(input[tokenEnd])) { - if (mode == Mode::Rfc2045 && !isTokenCharacter(input[tokenEnd])) - break; - ++tokenEnd; - } - - if (tokenEnd == tokenStart) - return StringView(); - if (skipTrailingWhitespace) { - if (mode == Mode::Rfc2045) { - while (input[tokenEnd - 1] == ' ') - --tokenEnd; - } else { - while (isASCIIWhitespaceWithoutFF(input[tokenEnd - 1])) - --tokenEnd; - } - } - return input.substring(tokenStart, tokenEnd - tokenStart); -} - -static bool isNotQuoteOrBackslash(UChar ch) -{ - return ch != '"' && ch != '\\'; -} - -static String collectHTTPQuotedString(StringView input, unsigned& startIndex) -{ - ASSERT(input[startIndex] == '"'); - unsigned inputLength = input.length(); - unsigned& position = startIndex; - position++; - StringBuilder builder; - while (true) { - unsigned positionStart = position; - parseToken(input, position, isNotQuoteOrBackslash, Mode::MimeSniff); - builder.append(input.substring(positionStart, position - positionStart)); - if (position >= inputLength) - break; - UChar quoteOrBackslash = input[position++]; - if (quoteOrBackslash == '\\') { - if (position >= inputLength) { - builder.append(quoteOrBackslash); - break; - } - builder.append(input[position++]); - } else { - ASSERT(quoteOrBackslash == '"'); - break; - } - } - return builder.toString(); -} - -static bool containsNonTokenCharacters(StringView input, Mode mode) -{ - if (mode == Mode::MimeSniff) - return !isValidHTTPToken(input); - for (unsigned index = 0; index < input.length(); ++index) { - if (!isTokenCharacter(input[index])) - return true; - } - return false; -} - -static StringView parseQuotedString(StringView input, unsigned& startIndex) -{ - unsigned inputLength = input.length(); - unsigned quotedStringStart = startIndex + 1; - unsigned& quotedStringEnd = startIndex; - - if (quotedStringEnd >= inputLength) - return StringView(); - - if (input[quotedStringEnd++] != '"' || quotedStringEnd >= inputLength) - return StringView(); - - bool lastCharacterWasBackslash = false; - char currentCharacter; - while ((currentCharacter = input[quotedStringEnd++]) != '"' || lastCharacterWasBackslash) { - if (quotedStringEnd >= inputLength) - return StringView(); - if (currentCharacter == '\\' && !lastCharacterWasBackslash) { - lastCharacterWasBackslash = true; - continue; - } - if (lastCharacterWasBackslash) - lastCharacterWasBackslash = false; - } - if (input[quotedStringEnd - 1] == '"') - quotedStringEnd++; - return input.substring(quotedStringStart, quotedStringEnd - quotedStringStart); -} - -// From http://tools.ietf.org/html/rfc2045#section-5.1: -// -// content := "Content-Type" ":" type "/" subtype -// *(";" parameter) -// ; Matching of media type and subtype -// ; is ALWAYS case-insensitive. -// -// type := discrete-type / composite-type -// -// discrete-type := "text" / "image" / "audio" / "video" / -// "application" / extension-token -// -// composite-type := "message" / "multipart" / extension-token -// -// extension-token := ietf-token / x-token -// -// ietf-token := -// -// x-token := -// -// subtype := extension-token / iana-token -// -// iana-token := -// -// parameter := attribute "=" value -// -// attribute := token -// ; Matching of attributes -// ; is ALWAYS case-insensitive. -// -// value := token / quoted-string -// -// token := 1* -// -// tspecials := "(" / ")" / "<" / ">" / "@" / -// "," / ";" / ":" / "\" / <"> -// "/" / "[" / "]" / "?" / "=" -// ; Must be in quoted-string, -// ; to use within parameter values - -static bool isNotForwardSlash(UChar ch) -{ - return ch != '/'; -} - -static bool isNotSemicolon(UChar ch) -{ - return ch != ';'; -} - -static bool isNotSemicolonOrEqualSign(UChar ch) -{ - return ch != ';' && ch != '='; -} - -static bool containsNewline(UChar ch) -{ - return ch == '\r' || ch == '\n'; -} - -bool ParsedContentType::parseContentType(Mode mode) -{ - if (mode == Mode::Rfc2045 && m_contentType.find(containsNewline) != notFound) - return false; - unsigned index = 0; - unsigned contentTypeLength = m_contentType.length(); - skipSpaces(m_contentType, index); - if (index >= contentTypeLength) { - LOG_ERROR("Invalid Content-Type string '%s'", m_contentType.ascii().data()); - return false; - } - - unsigned contentTypeStart = index; - auto typeRange = parseToken(m_contentType, index, isNotForwardSlash, mode); - if (typeRange.isNull() || containsNonTokenCharacters(typeRange, mode)) { - LOG_ERROR("Invalid Content-Type, invalid type value."); - return false; - } - - if (index >= contentTypeLength || m_contentType[index++] != '/') { - LOG_ERROR("Invalid Content-Type, missing '/'."); - return false; - } - - auto subTypeRange = parseToken(m_contentType, index, isNotSemicolon, mode, mode == Mode::MimeSniff); - if (subTypeRange.isNull() || containsNonTokenCharacters(subTypeRange, mode)) { - LOG_ERROR("Invalid Content-Type, invalid subtype value."); - return false; - } - - // There should not be any quoted strings until we reach the parameters. - size_t semiColonIndex = m_contentType.find(';', contentTypeStart); - if (semiColonIndex == notFound) { - setContentType(m_contentType.substring(contentTypeStart, contentTypeLength - contentTypeStart), mode); - return true; - } - - setContentType(m_contentType.substring(contentTypeStart, semiColonIndex - contentTypeStart), mode); - index = semiColonIndex + 1; - while (true) { - skipSpaces(m_contentType, index); - auto keyRange = parseToken(m_contentType, index, isNotSemicolonOrEqualSign, mode); - if (mode == Mode::Rfc2045 && (keyRange.isNull() || index >= contentTypeLength)) { - LOG_ERROR("Invalid Content-Type parameter name."); - return false; - } - - // Should we tolerate spaces here? - if (mode == Mode::Rfc2045) { - if (index >= contentTypeLength || m_contentType[index++] != '=') { - LOG_ERROR("Invalid Content-Type malformed parameter."); - return false; - } - } else { - if (index >= contentTypeLength) - break; - if (m_contentType[index] != '=' && m_contentType[index] != ';') { - LOG_ERROR("Invalid Content-Type malformed parameter."); - return false; - } - if (m_contentType[index++] == ';') - continue; - } - - // Should we tolerate spaces here? - String parameterValue; - StringView valueRange; - if (index < contentTypeLength && m_contentType[index] == '"') { - if (mode == Mode::MimeSniff) { - parameterValue = collectHTTPQuotedString(m_contentType, index); - parseToken(m_contentType, index, isNotSemicolon, mode); - } else - valueRange = parseQuotedString(m_contentType, index); - } else - valueRange = parseToken(m_contentType, index, isNotSemicolon, mode, mode == Mode::MimeSniff); - - if (parameterValue.isNull()) { - if (valueRange.isNull()) { - if (mode == Mode::MimeSniff) - continue; - LOG_ERROR("Invalid Content-Type, invalid parameter value."); - return false; - } - parameterValue = valueRange.toString(); - } - - // Should we tolerate spaces here? - if (mode == Mode::Rfc2045 && index < contentTypeLength && m_contentType[index++] != ';') { - LOG_ERROR("Invalid Content-Type, invalid character at the end of key/value parameter."); - return false; - } - - if (!keyRange.isNull()) - setContentTypeParameter(keyRange.toString(), parameterValue, mode); - - if (index >= contentTypeLength) - return true; - } - - return true; -} - -std::optional ParsedContentType::create(const String& contentType, Mode mode) -{ - ParsedContentType parsedContentType(mode == Mode::Rfc2045 ? contentType : contentType.trim(isASCIIWhitespaceWithoutFF)); - if (!parsedContentType.parseContentType(mode)) - return std::nullopt; - return { WTFMove(parsedContentType) }; -} - -bool isValidContentType(const String& contentType, Mode mode) -{ - return ParsedContentType::create(contentType, mode) != std::nullopt; -} - -ParsedContentType::ParsedContentType(const String& contentType) - : m_contentType(contentType) -{ -} - -String ParsedContentType::charset() const -{ - return parameterValueForName("charset"_s); -} - -void ParsedContentType::setCharset(String&& charset) -{ - m_parameterValues.set("charset"_s, WTFMove(charset)); -} - -String ParsedContentType::parameterValueForName(const String& name) const -{ - return m_parameterValues.get(name); -} - -size_t ParsedContentType::parameterCount() const -{ - return m_parameterValues.size(); -} - -void ParsedContentType::setContentType(String&& contentRange, Mode mode) -{ - m_mimeType = WTFMove(contentRange); - if (mode == Mode::MimeSniff) - m_mimeType = StringView(m_mimeType).trim(isASCIIWhitespaceWithoutFF).convertToASCIILowercase(); - else - m_mimeType = m_mimeType.trim(deprecatedIsSpaceOrNewline); -} - -static bool containsNonQuoteStringTokenCharacters(const String& input) -{ - for (unsigned index = 0; index < input.length(); ++index) { - if (!isQuotedStringTokenCharacter(input[index])) - return true; - } - return false; -} - -void ParsedContentType::setContentTypeParameter(const String& keyName, const String& keyValue, Mode mode) -{ - String name = keyName; - if (mode == Mode::MimeSniff) { - if (m_parameterValues.contains(name) || !isValidHTTPToken(name) || containsNonQuoteStringTokenCharacters(keyValue)) - return; - name = name.convertToASCIILowercase(); - } - m_parameterValues.set(name, keyValue); - m_parameterNames.append(name); -} - -String ParsedContentType::serialize() const -{ - StringBuilder builder; - builder.append(m_mimeType); - for (auto& name : m_parameterNames) { - builder.append(';'); - builder.append(name); - builder.append('='); - String value = m_parameterValues.get(name); - if (value.isEmpty() || !isValidHTTPToken(value)) { - builder.append('"'); - for (unsigned index = 0; index < value.length(); ++index) { - auto ch = value[index]; - if (ch == '\\' || ch == '"') - builder.append('\\'); - builder.append(ch); - } - builder.append('"'); - } else - builder.append(value); - } - return builder.toString(); -} - -} \ No newline at end of file diff --git a/src/bun.js/bindings/webcore/ParsedContentType.h b/src/bun.js/bindings/webcore/ParsedContentType.h deleted file mode 100644 index 03837e6d97..0000000000 --- a/src/bun.js/bindings/webcore/ParsedContentType.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2012 Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include -#include - -namespace WebCore { - -enum class Mode { - Rfc2045, - MimeSniff -}; -WEBCORE_EXPORT bool isValidContentType(const String&, Mode = Mode::MimeSniff); - -// FIXME: add support for comments. -class ParsedContentType { -public: - WEBCORE_EXPORT static std::optional create(const String&, Mode = Mode::MimeSniff); - ParsedContentType(ParsedContentType&&) = default; - - String mimeType() const { return m_mimeType; } - String charset() const; - void setCharset(String&&); - - // Note that in the case of multiple values for the same name, the last value is returned. - String parameterValueForName(const String&) const; - size_t parameterCount() const; - - WEBCORE_EXPORT String serialize() const; - -private: - ParsedContentType(const String&); - ParsedContentType(const ParsedContentType&) = delete; - ParsedContentType& operator=(ParsedContentType const&) = delete; - bool parseContentType(Mode); - void setContentType(String&&, Mode); - void setContentTypeParameter(const String&, const String&, Mode); - - typedef HashMap KeyValuePairs; - String m_contentType; - KeyValuePairs m_parameterValues; - Vector m_parameterNames; - String m_mimeType; -}; - -} \ No newline at end of file diff --git a/src/bun.js/bindings/webcore/ReadableStream.cpp b/src/bun.js/bindings/webcore/ReadableStream.cpp index 16658638b5..d297363543 100644 --- a/src/bun.js/bindings/webcore/ReadableStream.cpp +++ b/src/bun.js/bindings/webcore/ReadableStream.cpp @@ -175,6 +175,27 @@ void ReadableStream::cancel(const Exception& exception) invokeReadableStreamFunction(lexicalGlobalObject, privateName, JSC::jsUndefined(), arguments); } +void ReadableStream::cancel(WebCore::JSDOMGlobalObject& globalObject, JSReadableStream* readableStream, const Exception& exception) +{ + auto* clientData = static_cast(globalObject.vm().clientData); + auto& privateName = clientData->builtinFunctions().readableStreamInternalsBuiltins().readableStreamCancelPrivateName(); + + auto& vm = globalObject.vm(); + JSC::JSLockHolder lock(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + auto value = createDOMException(&globalObject, exception.code(), exception.message()); + if (UNLIKELY(scope.exception())) { + ASSERT(vm.hasPendingTerminationException()); + return; + } + + MarkedArgumentBuffer arguments; + arguments.append(readableStream); + arguments.append(value); + ASSERT(!arguments.hasOverflowed()); + invokeReadableStreamFunction(globalObject, privateName, JSC::jsUndefined(), arguments); +} + static inline bool checkReadableStream(JSDOMGlobalObject& globalObject, JSReadableStream* readableStream, JSC::JSValue function) { auto& lexicalGlobalObject = globalObject; diff --git a/src/bun.js/bindings/webcore/ReadableStream.h b/src/bun.js/bindings/webcore/ReadableStream.h index 9d3558d0e0..ce460e52c7 100644 --- a/src/bun.js/bindings/webcore/ReadableStream.h +++ b/src/bun.js/bindings/webcore/ReadableStream.h @@ -45,6 +45,7 @@ public: WEBCORE_EXPORT static bool isDisturbed(JSC::JSGlobalObject*, JSReadableStream*); WEBCORE_EXPORT static bool isLocked(JSC::JSGlobalObject*, JSReadableStream*); + WEBCORE_EXPORT static void cancel(WebCore::JSDOMGlobalObject& globalObject, JSReadableStream*, const WebCore::Exception& exception); std::optional, Ref>> tee(); diff --git a/src/bun.js/bindings/webcore/WebSocket.cpp b/src/bun.js/bindings/webcore/WebSocket.cpp index afec51aef8..5a0f7a6a5c 100644 --- a/src/bun.js/bindings/webcore/WebSocket.cpp +++ b/src/bun.js/bindings/webcore/WebSocket.cpp @@ -75,7 +75,6 @@ // #include "WebCoreThreadRun.h" // #endif - namespace WebCore { WTF_MAKE_ISO_ALLOCATED_IMPL(WebSocket); @@ -1457,7 +1456,7 @@ extern "C" void WebSocket__didAbruptClose(WebCore::WebSocket* webSocket, int32_t } extern "C" void WebSocket__didClose(WebCore::WebSocket* webSocket, uint16_t errorCode, const BunString* reason) { - WTF::String wtf_reason = Bun::toWTFString(*reason); + WTF::String wtf_reason = reason->toWTFString(BunString::ZeroCopy); webSocket->didClose(0, errorCode, WTFMove(wtf_reason)); } diff --git a/src/bun.js/bindings/webcore/Worker.cpp b/src/bun.js/bindings/webcore/Worker.cpp index 9ebd3182cd..96ca26acae 100644 --- a/src/bun.js/bindings/webcore/Worker.cpp +++ b/src/bun.js/bindings/webcore/Worker.cpp @@ -162,7 +162,7 @@ ExceptionOr> Worker::create(ScriptExecutionContext& context, const S static_cast(worker->m_clientIdentifier), miniMode, unrefByDefault); if (!impl) { - return Exception { TypeError, Bun::toWTFString(errorMessage) }; + return Exception { TypeError, errorMessage.toWTFString(BunString::ZeroCopy) }; } worker->impl_ = impl; @@ -380,30 +380,19 @@ void Worker::forEachWorker(const FunctiondispatchExit(exitCode); if (globalObject) { - auto* ctx = globalObject->scriptExecutionContext(); - if (ctx) { - ctx->removeFromContextsMap(); - } - - auto& vm = globalObject->vm(); + JSC::VM& vm = globalObject->vm(); + vm.apiLock().unlock(globalObject); vm.notifyNeedTermination(); - if (JSC::JSObject* obj = JSC::jsDynamicCast(globalObject->moduleLoader())) { - auto id = JSC::Identifier::fromString(globalObject->vm(), "registry"_s); - auto registryValue = obj->getIfPropertyExists(globalObject, id); - if (registryValue) { - if (auto* registry = JSC::jsDynamicCast(registryValue)) { - registry->clear(vm); - } - } - } - gcUnprotect(globalObject); - vm.deleteAllCode(JSC::DeleteAllCodeEffort::PreventCollectionAndDeleteAllCode); - vm.heap.reportAbandonedObjectGraph(); - WTF::releaseFastMallocFreeMemoryForThisThread(); - vm.deferredWorkTimer->doWork(vm); + vm.apiLock().lock(globalObject); + + while (!vm.hasOneRef()) + vm.deref(); + + vm.deref(); } } extern "C" void WebWorker__dispatchOnline(Worker* worker, Zig::GlobalObject* globalObject) @@ -415,13 +404,13 @@ extern "C" void WebWorker__dispatchError(Zig::GlobalObject* globalObject, Worker { JSValue error = JSC::JSValue::decode(errorValue); ErrorEvent::Init init; - init.message = Bun::toWTFString(message).isolatedCopy(); + init.message = message.toWTFString(BunString::ZeroCopy).isolatedCopy(); init.error = error; init.cancelable = false; init.bubbles = false; globalObject->globalEventScope.dispatchEvent(ErrorEvent::create(eventNames().errorEvent, init, EventIsTrusted::Yes)); - worker->dispatchError(Bun::toWTFString(message)); + worker->dispatchError(message.toWTFString(BunString::ZeroCopy)); } } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/Worker.h b/src/bun.js/bindings/webcore/Worker.h index f7641d28d1..997baac646 100644 --- a/src/bun.js/bindings/webcore/Worker.h +++ b/src/bun.js/bindings/webcore/Worker.h @@ -132,7 +132,7 @@ private: Deque> m_pendingEvents; Lock m_pendingTasksMutex; Deque> m_pendingTasks; - bool m_wasTerminated { false }; + std::atomic m_wasTerminated { false }; bool m_didStartWorkerGlobalScope { false }; bool m_isOnline { false }; bool m_isClosing { false }; diff --git a/src/bun.js/bindings/webcrypto/Bun_base64URLEncodeToString.h b/src/bun.js/bindings/webcrypto/Bun_base64URLEncodeToString.h index f2ade5d77a..87084d8f99 100644 --- a/src/bun.js/bindings/webcrypto/Bun_base64URLEncodeToString.h +++ b/src/bun.js/bindings/webcrypto/Bun_base64URLEncodeToString.h @@ -8,8 +8,9 @@ WTF::String toWTFString(const BunString& bunString); extern "C" void Zig__Bun_base64URLEncodeToString(const uint8_t* input_ptr, uint64_t len, BunString* ret); -inline String Bun_base64URLEncodeToString(std::span input) { +inline String Bun_base64URLEncodeToString(std::span input) +{ BunString result; Zig__Bun_base64URLEncodeToString(input.data(), input.size(), &result); - return Bun::toWTFString(result); + return result.toWTFString(BunString::ZeroCopy); } diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index a22cf6d6ef..c9f65ed540 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -641,6 +641,7 @@ pub const EventLoop = struct { extern fn JSC__JSGlobalObject__drainMicrotasks(*JSC.JSGlobalObject) void; fn drainMicrotasksWithGlobal(this: *EventLoop, globalObject: *JSC.JSGlobalObject) void { JSC.markBinding(@src()); + JSC__JSGlobalObject__drainMicrotasks(globalObject); this.drainDeferredTasks(); } @@ -1179,6 +1180,22 @@ pub const EventLoop = struct { } } + pub fn waitForPromiseWithTermination(this: *EventLoop, promise: JSC.AnyPromise) void { + var worker = this.virtual_machine.worker orelse @panic("EventLoop.waitForPromiseWithTermination: worker is not initialized"); + switch (promise.status(this.virtual_machine.jsc)) { + JSC.JSPromise.Status.Pending => { + while (!worker.requested_terminate and promise.status(this.virtual_machine.jsc) == .Pending) { + this.tick(); + + if (!worker.requested_terminate and promise.status(this.virtual_machine.jsc) == .Pending) { + this.autoTick(); + } + } + }, + else => {}, + } + } + // TODO: this implementation is terrible // we should not be checking the millitimestamp every time pub fn waitForPromiseWithTimeout(this: *EventLoop, promise: JSC.AnyPromise, timeout: u32) bool { @@ -1269,6 +1286,11 @@ pub const EventLoop = struct { } pub fn enqueueTaskConcurrent(this: *EventLoop, task: *ConcurrentTask) void { JSC.markBinding(@src()); + if (comptime Environment.allow_assert) { + if (this.virtual_machine.has_terminated) { + @panic("EventLoop.enqueueTaskConcurrent: VM has terminated"); + } + } this.concurrent_tasks.push(task); this.wakeup(); diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 0d9e03aca3..a72f099f21 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -582,7 +582,7 @@ pub const VirtualMachine = struct { modules: ModuleLoader.AsyncModule.Queue = .{}, aggressive_garbage_collection: GCLevel = GCLevel.none, - parser_arena: ?@import("root").bun.ArenaAllocator = null, + parser_arena: ?*@import("root").bun.ArenaAllocator = null, gc_controller: JSC.GarbageCollectionController = .{}, worker: ?*JSC.WebWorker = null, @@ -590,6 +590,7 @@ pub const VirtualMachine = struct { debugger: ?Debugger = null, has_started_debugger: bool = false, + has_terminated: bool = false, pub const OnUnhandledRejection = fn (*VirtualMachine, globalObject: *JSC.JSGlobalObject, JSC.JSValue) void; @@ -875,6 +876,20 @@ pub const VirtualMachine = struct { } } + pub fn scriptExecutionStatus(this: *VirtualMachine) callconv(.C) JSC.ScriptExecutionStatus { + if (this.worker) |worker| { + if (worker.requested_terminate) { + return .stopped; + } + } + + return .running; + } + + comptime { + @export(scriptExecutionStatus, .{ .name = "Bun__VM__scriptExecutionStatus" }); + } + pub fn onExit(this: *VirtualMachine) void { this.exit_handler.dispatchOnExit(); @@ -1160,7 +1175,7 @@ pub const VirtualMachine = struct { .ref_strings_mutex = Lock.init(), .file_blobs = JSC.WebCore.Blob.Store.Map.init(allocator), .standalone_module_graph = opts.graph.?, - .parser_arena = @import("root").bun.ArenaAllocator.init(allocator), + .parser_arena = null, }; vm.source_mappings = .{ .map = &vm.saved_source_map_table }; vm.regular_event_loop.tasks = EventLoop.Queue.init( @@ -1269,7 +1284,7 @@ pub const VirtualMachine = struct { .ref_strings = JSC.RefString.Map.init(allocator), .ref_strings_mutex = Lock.init(), .file_blobs = JSC.WebCore.Blob.Store.Map.init(allocator), - .parser_arena = @import("root").bun.ArenaAllocator.init(allocator), + .parser_arena = null, }; vm.source_mappings = .{ .map = &vm.saved_source_map_table }; vm.regular_event_loop.tasks = EventLoop.Queue.init( @@ -1406,7 +1421,7 @@ pub const VirtualMachine = struct { .ref_strings = JSC.RefString.Map.init(allocator), .ref_strings_mutex = Lock.init(), .file_blobs = JSC.WebCore.Blob.Store.Map.init(allocator), - .parser_arena = @import("root").bun.ArenaAllocator.init(allocator), + .parser_arena = null, .standalone_module_graph = worker.parent.standalone_module_graph, .worker = worker, }; @@ -1451,7 +1466,7 @@ pub const VirtualMachine = struct { vm.regular_event_loop.global = vm.global; vm.regular_event_loop.virtual_machine = vm; vm.jsc = vm.global.vm(); - + vm.bundler.setAllocator(allocator); if (source_code_printer == null) { var writer = try js_printer.BufferWriter.init(allocator); source_code_printer = allocator.create(js_printer.BufferPrinter) catch unreachable; @@ -2024,6 +2039,7 @@ pub const VirtualMachine = struct { // TODO: pub fn deinit(this: *VirtualMachine) void { this.source_mappings.deinit(); + this.has_terminated = true; } pub const ExceptionList = std.ArrayList(Api.JsException); @@ -2082,6 +2098,82 @@ pub const VirtualMachine = struct { this.global.deleteModuleRegistryEntry(&str); } + fn loadPreloads(this: *VirtualMachine) !?*JSInternalPromise { + this.is_in_preload = true; + defer this.is_in_preload = false; + + for (this.preload) |preload| { + var result = switch (this.bundler.resolver.resolveAndAutoInstall( + this.bundler.fs.top_level_dir, + normalizeSource(preload), + .stmt, + .read_only, + )) { + .success => |r| r, + .failure => |e| { + this.log.addErrorFmt( + null, + logger.Loc.Empty, + this.allocator, + "{s} resolving preload {any}", + .{ + @errorName(e), + js_printer.formatJSONString(preload), + }, + ) catch unreachable; + return e; + }, + .pending, .not_found => { + this.log.addErrorFmt( + null, + logger.Loc.Empty, + this.allocator, + "preload not found {any}", + .{ + js_printer.formatJSONString(preload), + }, + ) catch unreachable; + return error.ModuleNotFound; + }, + }; + var promise = JSModuleLoader.import(this.global, &String.fromBytes(result.path().?.text)); + + this.pending_internal_promise = promise; + JSValue.fromCell(promise).protect(); + defer JSValue.fromCell(promise).unprotect(); + + // pending_internal_promise can change if hot module reloading is enabled + if (this.isWatcherEnabled()) { + this.eventLoop().performGC(); + switch (this.pending_internal_promise.status(this.global.vm())) { + JSC.JSPromise.Status.Pending => { + while (this.pending_internal_promise.status(this.global.vm()) == .Pending) { + this.eventLoop().tick(); + + if (this.pending_internal_promise.status(this.global.vm()) == .Pending) { + this.eventLoop().autoTick(); + } + } + }, + else => {}, + } + } else { + this.eventLoop().performGC(); + this.waitForPromise(JSC.AnyPromise{ + .Internal = promise, + }); + } + + if (promise.status(this.global.vm()) == .Rejected) + return promise; + } + + // only load preloads once + this.preload.len = 0; + + return null; + } + pub fn reloadEntryPoint(this: *VirtualMachine, entry_path: []const u8) !*JSInternalPromise { this.has_loaded = false; this.main = entry_path; @@ -2095,95 +2187,56 @@ pub const VirtualMachine = struct { ); this.eventLoop().ensureWaker(); - var promise: *JSInternalPromise = undefined; + if (this.debugger != null) { + try Debugger.create(this, this.global); + } + + if (!this.bundler.options.disable_transpilation) { + if (try this.loadPreloads()) |promise| { + JSC.JSValue.fromCell(promise).ensureStillAlive(); + JSC.JSValue.fromCell(promise).protect(); + this.pending_internal_promise = promise; + return promise; + } + + var promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.init(main_file_name)) orelse return error.JSError; + this.pending_internal_promise = promise; + JSC.JSValue.fromCell(promise).ensureStillAlive(); + return promise; + } else { + var promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.init(this.main)) orelse return error.JSError; + this.pending_internal_promise = promise; + JSC.JSValue.fromCell(promise).ensureStillAlive(); + + return promise; + } + } + + pub fn reloadEntryPointForTestRunner(this: *VirtualMachine, entry_path: []const u8) !*JSInternalPromise { + this.has_loaded = false; + this.main = entry_path; + this.main_hash = bun.JSC.Watcher.getHash(entry_path); + + this.eventLoop().ensureWaker(); if (this.debugger != null) { try Debugger.create(this, this.global); } if (!this.bundler.options.disable_transpilation) { - { - this.is_in_preload = true; - defer this.is_in_preload = false; - for (this.preload) |preload| { - var result = switch (this.bundler.resolver.resolveAndAutoInstall( - this.bundler.fs.top_level_dir, - normalizeSource(preload), - .stmt, - .read_only, - )) { - .success => |r| r, - .failure => |e| { - this.log.addErrorFmt( - null, - logger.Loc.Empty, - this.allocator, - "{s} resolving preload {any}", - .{ - @errorName(e), - js_printer.formatJSONString(preload), - }, - ) catch unreachable; - return e; - }, - .pending, .not_found => { - this.log.addErrorFmt( - null, - logger.Loc.Empty, - this.allocator, - "preload not found {any}", - .{ - js_printer.formatJSONString(preload), - }, - ) catch unreachable; - return error.ModuleNotFound; - }, - }; - promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.fromBytes(result.path().?.text)); + if (try this.loadPreloads()) |promise| { + JSC.JSValue.fromCell(promise).ensureStillAlive(); + this.pending_internal_promise = promise; + JSC.JSValue.fromCell(promise).protect(); - this.pending_internal_promise = promise; - JSValue.fromCell(promise).protect(); - defer JSValue.fromCell(promise).unprotect(); - - // pending_internal_promise can change if hot module reloading is enabled - if (this.isWatcherEnabled()) { - this.eventLoop().performGC(); - switch (this.pending_internal_promise.status(this.global.vm())) { - JSC.JSPromise.Status.Pending => { - while (this.pending_internal_promise.status(this.global.vm()) == .Pending) { - this.eventLoop().tick(); - - if (this.pending_internal_promise.status(this.global.vm()) == .Pending) { - this.eventLoop().autoTick(); - } - } - }, - else => {}, - } - } else { - this.eventLoop().performGC(); - this.waitForPromise(JSC.AnyPromise{ - .Internal = promise, - }); - } - - if (promise.status(this.global.vm()) == .Rejected) - return promise; - } + return promise; } - - // only load preloads once - this.preload.len = 0; - - promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.init(main_file_name)); - this.pending_internal_promise = promise; - JSC.JSValue.fromCell(promise).ensureStillAlive(); - } else { - promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.init(this.main)); - this.pending_internal_promise = promise; - JSC.JSValue.fromCell(promise).ensureStillAlive(); } + var promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.fromBytes(this.main)) orelse return error.JSError; + this.pending_internal_promise = promise; + JSC.JSValue.fromCell(promise).ensureStillAlive(); + return promise; } @@ -2191,9 +2244,48 @@ pub const VirtualMachine = struct { pub fn loadEntryPointForWebWorker(this: *VirtualMachine, entry_path: string) anyerror!*JSInternalPromise { var promise = try this.reloadEntryPoint(entry_path); this.eventLoop().performGC(); - this.waitForPromise(JSC.AnyPromise{ + this.eventLoop().waitForPromiseWithTermination(JSC.AnyPromise{ .Internal = promise, }); + if (this.worker) |worker| { + if (worker.requested_terminate) { + return error.WorkerTerminated; + } + } + return this.pending_internal_promise; + } + + pub fn loadEntryPointForTestRunner(this: *VirtualMachine, entry_path: string) anyerror!*JSInternalPromise { + var promise = try this.reloadEntryPointForTestRunner(entry_path); + + // pending_internal_promise can change if hot module reloading is enabled + if (this.isWatcherEnabled()) { + this.eventLoop().performGC(); + switch (this.pending_internal_promise.status(this.global.vm())) { + JSC.JSPromise.Status.Pending => { + while (this.pending_internal_promise.status(this.global.vm()) == .Pending) { + this.eventLoop().tick(); + + if (this.pending_internal_promise.status(this.global.vm()) == .Pending) { + this.eventLoop().autoTick(); + } + } + }, + else => {}, + } + } else { + if (promise.status(this.global.vm()) == .Rejected) { + return promise; + } + + this.eventLoop().performGC(); + this.waitForPromise(JSC.AnyPromise{ + .Internal = promise, + }); + } + + this.eventLoop().autoTick(); + return this.pending_internal_promise; } @@ -2216,6 +2308,10 @@ pub const VirtualMachine = struct { else => {}, } } else { + if (promise.status(this.global.vm()) == .Rejected) { + return promise; + } + this.eventLoop().performGC(); this.waitForPromise(JSC.AnyPromise{ .Internal = promise, @@ -2257,7 +2353,7 @@ pub const VirtualMachine = struct { }; this.runWithAPILock(MacroEntryPointLoader, &loader, MacroEntryPointLoader.load); - return loader.promise; + return loader.promise orelse return error.JSError; } /// A subtlelty of JavaScriptCore: @@ -2271,16 +2367,16 @@ pub const VirtualMachine = struct { const MacroEntryPointLoader = struct { path: string, - promise: *JSInternalPromise = undefined, + promise: ?*JSInternalPromise = null, pub fn load(this: *MacroEntryPointLoader) void { this.promise = VirtualMachine.get()._loadMacroEntryPoint(this.path); } }; - pub inline fn _loadMacroEntryPoint(this: *VirtualMachine, entry_path: string) *JSInternalPromise { + pub inline fn _loadMacroEntryPoint(this: *VirtualMachine, entry_path: string) ?*JSInternalPromise { var promise: *JSInternalPromise = undefined; - promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.init(entry_path)); + promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.init(entry_path)) orelse return null; this.waitForPromise(JSC.AnyPromise{ .Internal = promise, }); diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 7e11eac9b6..07c868acb0 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -601,7 +601,7 @@ pub const ModuleLoader = struct { loader: Api.Loader, hash: u32 = std.math.maxInt(u32), globalThis: *JSC.JSGlobalObject = undefined, - arena: bun.ArenaAllocator, + arena: *bun.ArenaAllocator, // This is the specific state for making it async poll_ref: Async.KeepAlive = .{}, @@ -1314,6 +1314,7 @@ pub const ModuleLoader = struct { this.promise.deinit(); this.parse_result.deinit(); this.arena.deinit(); + this.globalThis.bunVM().allocator.destroy(this.arena); // bun.default_allocator.free(this.stmt_blocks); // bun.default_allocator.free(this.expr_blocks); @@ -1367,37 +1368,41 @@ pub const ModuleLoader = struct { jsc_vm.main_hash == hash and strings.eqlLong(jsc_vm.main, path.text, false); - var arena: bun.ArenaAllocator = undefined; + var arena: ?*bun.ArenaAllocator = brk: { + // Attempt to reuse the Arena from the parser when we can + // This code is potentially re-entrant, so only one Arena can be reused at a time + // That's why we have to check if the Arena is null + // + // Using an Arena here is a significant memory optimization when loading many files + if (jsc_vm.parser_arena) |shared| { + jsc_vm.parser_arena = null; + break :brk shared; + } + // we must allocate the arena so that the pointer it points to is always valid. + var arena = try jsc_vm.allocator.create(bun.ArenaAllocator); + arena.* = bun.ArenaAllocator.init(bun.default_allocator); + break :brk arena; + }; - // Attempt to reuse the Arena from the parser when we can - // This code is potentially re-entrant, so only one Arena can be reused at a time - // That's why we have to check if the Arena is null - // - // Using an Arena here is a significant memory optimization when loading many files - if (jsc_vm.parser_arena) |shared| { - arena = shared; - jsc_vm.parser_arena = null; - } else { - arena = bun.ArenaAllocator.init(bun.default_allocator); - } var give_back_arena = true; defer { if (give_back_arena) { if (jsc_vm.parser_arena == null) { if (jsc_vm.smol) { - _ = arena.reset(.free_all); + _ = arena.?.reset(.free_all); } else { - _ = arena.reset(.{ .retain_with_limit = 8 * 1024 * 1024 }); + _ = arena.?.reset(.{ .retain_with_limit = 8 * 1024 * 1024 }); } jsc_vm.parser_arena = arena; } else { - arena.deinit(); + arena.?.deinit(); + jsc_vm.allocator.destroy(arena.?); } } } - var allocator = arena.allocator(); + var allocator = arena.?.allocator(); var fd: ?StoredFileDescriptorType = null; var package_json: ?*PackageJSON = null; @@ -1623,10 +1628,10 @@ pub const ModuleLoader = struct { .promise_ptr = promise_ptr, .specifier = specifier, .referrer = referrer, - .arena = arena, + .arena = arena.?, }, ); - arena = bun.ArenaAllocator.init(bun.default_allocator); + arena = null; give_back_arena = false; return error.AsyncModule; } @@ -2049,7 +2054,7 @@ pub const ModuleLoader = struct { .allocator = null, .source_code = String.init(Runtime.Runtime.sourceContentBun()), .specifier = specifier, - .source_url = String.init(Runtime.Runtime.Imports.Name), + .source_url = specifier, .hash = Runtime.Runtime.versionHash(), }; } else if (HardcodedModule.Map.getWithEql(specifier, bun.String.eqlComptime)) |hardcoded| { @@ -2059,7 +2064,7 @@ pub const ModuleLoader = struct { .allocator = null, .source_code = bun.String.create(jsc_vm.entry_point.source.contents), .specifier = specifier, - .source_url = String.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), + .source_url = specifier, .hash = 0, .tag = .esm, .needs_deref = true, diff --git a/src/bun.js/modules/BunJSCModule.h b/src/bun.js/modules/BunJSCModule.h index 01c47c63f8..c96a6ffbe9 100644 --- a/src/bun.js/modules/BunJSCModule.h +++ b/src/bun.js/modules/BunJSCModule.h @@ -1,6 +1,8 @@ #include "_NativeModule.h" #include "ExceptionOr.h" +#include "MessagePort.h" +#include "SerializedScriptValue.h" #include #include #include @@ -21,8 +23,6 @@ #include #include #include -#include "MessagePort.h" -#include "SerializedScriptValue.h" #include #include #include @@ -194,7 +194,6 @@ JSC_DEFINE_HOST_FUNCTION(functionMemoryUsageStatistics, (JSGlobalObject * globalObject, CallFrame *)) { auto &vm = globalObject->vm(); - JSC::DisallowGC disallowGC; // this is a C API function auto *stats = toJS(JSGetMemoryUsageStatistics(toRef(globalObject))); @@ -204,6 +203,7 @@ JSC_DEFINE_HOST_FUNCTION(functionMemoryUsageStatistics, ASSERT(heapSizeValue.isNumber()); if (heapSizeValue.toInt32(globalObject) == 0) { vm.heap.collectNow(Sync, CollectionScope::Full); + JSC::DisallowGC disallowGC; stats = toJS(JSGetMemoryUsageStatistics(toRef(globalObject))); } } diff --git a/src/bun.js/node/node.classes.ts b/src/bun.js/node/node.classes.ts index 4ed5b9b2e9..780a826046 100644 --- a/src/bun.js/node/node.classes.ts +++ b/src/bun.js/node/node.classes.ts @@ -229,6 +229,7 @@ export default [ finalize: true, klass: {}, JSType: "0b11101110", + supportsObjectCreate: true, proto: { isBlockDevice: { fn: "isBlockDevice_", @@ -378,6 +379,7 @@ export default [ finalize: true, klass: {}, + supportsObjectCreate: true, proto: { isBlockDevice: { diff --git a/src/bun.js/node/node_os.zig b/src/bun.js/node/node_os.zig index ae8f527cef..e2e96f9e84 100644 --- a/src/bun.js/node/node_os.zig +++ b/src/bun.js/node/node_os.zig @@ -281,10 +281,10 @@ pub const Os = struct { JSC.markBinding(@src()); switch (comptime builtin.target.cpu.arch.endian()) { - .Big => { + .big => { return JSC.ZigString.init("BE").withEncoding().toValue(globalThis); }, - .Little => { + .little => { return JSC.ZigString.init("LE").withEncoding().toValue(globalThis); }, } diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 3250d37a0c..fd4e5d1dab 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -59,7 +59,7 @@ pub const Tag = enum(u3) { skip, todo, }; - +const debug = Output.scoped(.jest, false); pub const TestRunner = struct { tests: TestRunner.Test.List = .{}, log: *logger.Log, @@ -605,6 +605,7 @@ pub const TestScope = struct { } pub fn onReject(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + debug("onReject", .{}); const arguments = callframe.arguments(2); const err = arguments.ptr[0]; globalThis.bunVM().runErrorHandler(err, null); @@ -615,6 +616,7 @@ pub const TestScope = struct { } pub fn onResolve(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + debug("onResolve", .{}); const arguments = callframe.arguments(2); var task: *TestRunnerTask = arguments.ptr[1].asPromisePtr(TestRunnerTask); task.handleResult(.{ .pass = expect.active_test_expectation_counter.actual }, .promise); @@ -632,16 +634,20 @@ pub const TestScope = struct { if (JSC.getFunctionData(function)) |data| { var task = bun.cast(*TestRunnerTask, data); + JSC.setFunctionData(function, null); if (args.len > 0) { const err = args.ptr[0]; if (err.isEmptyOrUndefinedOrNull()) { + debug("done()", .{}); task.handleResult(.{ .pass = expect.active_test_expectation_counter.actual }, .callback); } else { + debug("done(err)", .{}); globalThis.bunVM().runErrorHandlerWithDedupe(err, null); task.handleResult(.{ .fail = expect.active_test_expectation_counter.actual }, .callback); } } else { + debug("done()", .{}); task.handleResult(.{ .pass = expect.active_test_expectation_counter.actual }, .callback); } } @@ -654,6 +660,7 @@ pub const TestScope = struct { task: *TestRunnerTask, ) Result { if (comptime is_bindgen) return undefined; + var vm = VirtualMachine.get(); const func = this.func; Jest.runner.?.did_pending_test_fail = false; @@ -667,6 +674,7 @@ pub const TestScope = struct { vm.autoGarbageCollect(); } JSC.markBinding(@src()); + debug("test({})", .{strings.QuotedFormatter{ .text = this.label }}); var initial_value = JSValue.zero; if (test_elapsed_timer) |timer| { @@ -1092,6 +1100,7 @@ pub const DescribeScope = struct { defer callback.unprotect(); this.push(); defer this.pop(); + debug("describe({})", .{strings.QuotedFormatter{ .text = this.label }}); if (callback == .zero) { this.runTests(globalObject); diff --git a/src/bun.js/web_worker.zig b/src/bun.js/web_worker.zig index 230f30da23..b97f4332e5 100644 --- a/src/bun.js/web_worker.zig +++ b/src/bun.js/web_worker.zig @@ -314,7 +314,7 @@ pub const WebWorker = struct { this.setRef(false); this.requested_terminate = true; if (this.vm) |vm| { - vm.global.vm().notifyNeedTermination(); + vm.jsc.notifyNeedTermination(); vm.eventLoop().wakeup(); } } @@ -322,22 +322,31 @@ pub const WebWorker = struct { /// This handles cleanup, emitting the "close" event, and deinit. /// Only call after the VM is initialized AND on the same thread as the worker. /// Otherwise, call `requestTerminate` to cause the event loop to safely terminate after the next tick. - fn exitAndDeinit(this: *WebWorker) void { + pub fn exitAndDeinit(this: *WebWorker) noreturn { JSC.markBinding(@src()); log("[{d}] exitAndDeinit", .{this.execution_context_id}); var cpp_worker = this.cpp_worker; var exit_code: i32 = 0; var globalObject: ?*JSC.JSGlobalObject = null; + var vm_to_deinit: ?*JSC.VirtualMachine = null; if (this.vm) |vm| { this.vm = null; vm.onExit(); exit_code = vm.exit_handler.exit_code; globalObject = vm.global; - this.arena.deinit(); - vm.deinit(); // NOTE: deinit here isn't implemented, so freeing workers will leak the vm. + vm_to_deinit = vm; } + var arena = this.arena; + WebWorker__dispatchExit(globalObject, cpp_worker, exit_code); this.deinit(); + + if (vm_to_deinit) |vm| { + vm.deinit(); // NOTE: deinit here isn't implemented, so freeing workers will leak the vm. + } + + arena.deinit(); + bun.exitThread(); } comptime { diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index 71e0cd5399..03b6bb4881 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -228,20 +228,20 @@ pub const Blob = struct { comptime Writer: type, writer: Writer, ) !void { - try writer.writeIntNative(u8, serialization_version); + try writer.writeInt(u8, serialization_version, .little); - try writer.writeIntNative(u64, @as(u64, @intCast(this.offset))); + try writer.writeInt(u64, @as(u64, @intCast(this.offset)), .little); - try writer.writeIntNative(u32, @as(u32, @truncate(this.content_type.len))); + try writer.writeInt(u32, @as(u32, @truncate(this.content_type.len)), .little); _ = try writer.write(this.content_type); - try writer.writeIntNative(u8, @intFromBool(this.content_type_was_set)); + try writer.writeInt(u8, @intFromBool(this.content_type_was_set), .little); const store_tag: Store.SerializeTag = if (this.store) |store| if (store.data == .file) .file else .bytes else .empty; - try writer.writeIntNative(u8, @intFromEnum(store_tag)); + try writer.writeInt(u8, @intFromEnum(store_tag), .little); this.resolveSize(); if (this.store) |store| { @@ -301,22 +301,22 @@ pub const Blob = struct { ) !JSValue { const allocator = globalThis.allocator(); - const version = try reader.readIntNative(u8); + const version = try reader.readInt(u8, .little); _ = version; - const offset = try reader.readIntNative(u64); + const offset = try reader.readInt(u64, .little); - const content_type_len = try reader.readIntNative(u32); + const content_type_len = try reader.readInt(u32, .little); const content_type = try readSlice(reader, content_type_len, allocator); - const content_type_was_set: bool = try reader.readIntNative(u8) != 0; + const content_type_was_set: bool = try reader.readInt(u8, .little) != 0; - const store_tag = try reader.readEnum(Store.SerializeTag, .Little); + const store_tag = try reader.readEnum(Store.SerializeTag, .little); const blob: *Blob = switch (store_tag) { .bytes => brk: { - const bytes_len = try reader.readIntNative(u32); + const bytes_len = try reader.readInt(u32, .little); const bytes = try readSlice(reader, bytes_len, allocator); var blob = Blob.init(bytes, allocator, globalThis); @@ -326,11 +326,11 @@ pub const Blob = struct { break :brk blob_; }, .file => brk: { - const pathlike_tag = try reader.readEnum(JSC.Node.PathOrFileDescriptor.SerializeTag, .Little); + const pathlike_tag = try reader.readEnum(JSC.Node.PathOrFileDescriptor.SerializeTag, .little); switch (pathlike_tag) { .fd => { - const fd = @as(bun.FileDescriptor, @intCast(try reader.readIntNative(bun.FileDescriptor))); + const fd = @as(bun.FileDescriptor, @intCast(try reader.readInt(bun.FileDescriptor, .little))); var blob = try allocator.create(Blob); blob.* = Blob.findOrCreateFileFromPath( @@ -343,7 +343,7 @@ pub const Blob = struct { break :brk blob; }, .path => { - const path_len = try reader.readIntNative(u32); + const path_len = try reader.readInt(u32, .little); const path = try readSlice(reader, path_len, default_allocator); @@ -1513,22 +1513,22 @@ pub const Blob = struct { switch (this.data) { .file => |file| { const pathlike_tag: JSC.Node.PathOrFileDescriptor.SerializeTag = if (file.pathlike == .fd) .fd else .path; - try writer.writeIntNative(u8, @intFromEnum(pathlike_tag)); + try writer.writeInt(u8, @intFromEnum(pathlike_tag), .little); switch (file.pathlike) { .fd => |fd| { - try writer.writeIntNative(u32, @as(u32, @intCast(fd))); + try writer.writeInt(u32, @as(u32, @intCast(fd)), .little); }, .path => |path| { const path_slice = path.slice(); - try writer.writeIntNative(u32, @as(u32, @truncate(path_slice.len))); + try writer.writeInt(u32, @as(u32, @truncate(path_slice.len)), .little); _ = try writer.write(path_slice); }, } }, .bytes => |bytes| { const slice = bytes.slice(); - try writer.writeIntNative(u32, @as(u32, @truncate(slice.len))); + try writer.writeInt(u32, @as(u32, @truncate(slice.len)), .little); _ = try writer.write(slice); }, } diff --git a/src/bun.zig b/src/bun.zig index c2cb0f1f01..22157b5036 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -448,15 +448,15 @@ pub const BabyList = @import("./baby_list.zig").BabyList; pub const ByteList = BabyList(u8); pub fn DebugOnly(comptime Type: type) type { - if (comptime Environment.isDebug) { + if (comptime Environment.allow_assert) { return Type; } return void; } -pub fn DebugOnlyDefault(comptime val: anytype) if (Environment.isDebug) @TypeOf(val) else void { - if (comptime Environment.isDebug) { +pub fn DebugOnlyDefault(comptime val: anytype) if (Environment.allow_assert) @TypeOf(val) else void { + if (comptime Environment.allow_assert) { return val; } @@ -2005,3 +2005,18 @@ pub inline fn pathLiteral(comptime literal: anytype) *const [literal.len:0]u8 { return &buf; }; } + +pub fn exitThread() noreturn { + const exiter = struct { + pub extern "C" fn pthread_exit(?*anyopaque) noreturn; + pub extern "kernel32" fn ExitThread(windows.DWORD) noreturn; + }; + + if (comptime Environment.isWindows) { + exiter.ExitThread(0); + } else if (comptime Environment.isPosix) { + exiter.pthread_exit(null); + } else { + @panic("Unsupported platform"); + } +} diff --git a/src/bun_dev_http_server.zig b/src/bun_dev_http_server.zig index 6dfc24889e..1684adbc0e 100644 --- a/src/bun_dev_http_server.zig +++ b/src/bun_dev_http_server.zig @@ -2057,11 +2057,11 @@ const PosixRequestContext = struct { weak_etag_buffer[0] = 'W'; weak_etag_buffer[1] = '/'; weak_etag.update(result.file.src_path.text); - std.mem.writeIntNative(u64, weak_etag_tmp_buffer[0..8], result.file.size); + std.mem.writeInt(u64, weak_etag_tmp_buffer[0..8], result.file.size, .little); weak_etag.update(weak_etag_tmp_buffer[0..8]); if (result.file.mtime) |mtime| { - std.mem.writeIntNative(i128, weak_etag_tmp_buffer[0..16], mtime); + std.mem.writeInt(i128, weak_etag_tmp_buffer[0..16], mtime, .little); weak_etag.update(weak_etag_tmp_buffer[0..16]); } diff --git a/src/cli.zig b/src/cli.zig index afff8bc2e6..4b9dc74a1a 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -53,7 +53,6 @@ pub const Cli = struct { var panicker = MainPanicHandler.init(log); MainPanicHandler.Singleton = &panicker; - Command.start(allocator, log) catch |err| { switch (err) { error.MissingEntryPoint => { @@ -1222,6 +1221,12 @@ pub const Command = struct { }; pub fn start(allocator: std.mem.Allocator, log: *logger.Log) !void { + if (comptime Environment.allow_assert) { + if (bun.getenvZ("MI_VERBOSE") == null) { + bun.Mimalloc.mi_option_set_enabled(.verbose, false); + } + } + const BuildCommand = @import("./cli/build_command.zig").BuildCommand; const AddCommand = @import("./cli/add_command.zig").AddCommand; diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index 998fd36e2a..82fb00bf3f 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -935,7 +935,7 @@ pub const TestCommand = struct { } Output.flush(); - var promise = try vm.loadEntryPoint(file_path); + var promise = try vm.loadEntryPointForTestRunner(file_path); reporter.summary.files += 1; switch (promise.status(vm.global.vm())) { diff --git a/src/codegen/class-definitions.ts b/src/codegen/class-definitions.ts index e7dd8bd4c8..cd462cda48 100644 --- a/src/codegen/class-definitions.ts +++ b/src/codegen/class-definitions.ts @@ -36,6 +36,7 @@ export interface ClassDefinition { estimatedSize?: boolean; hasPendingActivity?: boolean; isEventEmitter?: boolean; + supportsObjectCreate?: boolean; getInternalProperties?: boolean; @@ -44,6 +45,8 @@ export interface ClassDefinition { configurable?: boolean; enumerable?: boolean; structuredClone?: boolean | { transferable: boolean; tag: number }; + + callbacks?: Record; } export interface CustomField { diff --git a/src/codegen/create_hash_table b/src/codegen/create_hash_table index bd604ceaa8..b75924136c 100755 --- a/src/codegen/create_hash_table +++ b/src/codegen/create_hash_table @@ -103,11 +103,15 @@ while () { } elsif (length($att)) { my $get = $val; my $put = "0"; + my $type = "PropertyAttribute::Property"; + if ($att =~ m/Builtin/) { + $type = "PropertyAttribute::BuiltinAccessor"; + } if (!($att =~ m/ReadOnly/)) { $put = "set" . jsc_ucfirst($val); } $hasSetter = "true"; - push(@values, { "type" => "PropertyAttribute::Property", "get" => $get, "put" => $put }); + push(@values, { "type" => $type, "get" => $get, "put" => $put }); } else { push(@values, { "type" => "Lexer", "value" => $val }); } @@ -476,6 +480,10 @@ sub output() { $firstValue = $values[$i]{"function"}; $secondValue = $values[$i]{"params"}; $intrinsic = $values[$i]{"intrinsic"}; + } elsif ($values[$i]{"type"} eq "PropertyAttribute::BuiltinAccessor") { + $typeTag = "BuiltinAccessor"; + $firstValue = $values[$i]{"get"}; + $secondValue = $values[$i]{"put"}; } elsif ($values[$i]{"type"} eq "PropertyAttribute::Property") { $typeTag = "GetterSetter"; $firstValue = $values[$i]{"get"}; diff --git a/src/codegen/generate-classes.ts b/src/codegen/generate-classes.ts index 42f5658070..e29adeafe1 100644 --- a/src/codegen/generate-classes.ts +++ b/src/codegen/generate-classes.ts @@ -2,6 +2,7 @@ import path from "path"; import type { Field, ClassDefinition } from "./class-definitions"; import { writeIfNotChanged } from "./helpers"; +import { camelCase, pascalCase } from "change-case"; const files = process.argv.slice(2); const outBase = files.pop(); @@ -174,6 +175,7 @@ function propRow( prop: Field, isWrapped = true, defaultPropertyAttributes, + supportsObjectCreate = false, ) { var { defaultValue, @@ -246,8 +248,12 @@ function propRow( { "${name}"_s, static_cast(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute${extraPropertyAttributes}), NoIntrinsic, { HashTableValue::GetterSetterType, ${getter}, ${setter} } } `.trim(); } else if (defaultValue) { - } else if (getter) { + } else if (getter && !supportsObjectCreate) { return `{ "${name}"_s, static_cast(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute${extraPropertyAttributes}), NoIntrinsic, { HashTableValue::GetterSetterType, ${getter}, 0 } } +`.trim(); + } else if (getter && supportsObjectCreate) { + setter = getter.replace("Get", "Set"); + return `{ "${name}"_s, static_cast(JSC::PropertyAttribute::CustomAccessor ${extraPropertyAttributes}), NoIntrinsic, { HashTableValue::GetterSetterType, &${getter}, &${setter} } } `.trim(); } else if (setter) { return `{ "${name}"_s, static_cast(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute${extraPropertyAttributes}), NoIntrinsic, { HashTableValue::GetterSetterType, 0, ${setter} } } @@ -275,7 +281,17 @@ export function generateHashTable(nameToUse, symbolName, typeName, obj, props = if ("internal" in props[name] || "value" in props[name]) continue; if (name.startsWith("@@")) continue; - rows.push(propRow(symbolName, typeName, name, props[name], wrapped, defaultPropertyAttributes)); + rows.push( + propRow( + symbolName, + typeName, + name, + props[name], + wrapped, + defaultPropertyAttributes, + obj.supportsObjectCreate || false, + ), + ); } if (rows.length === 0) { @@ -344,7 +360,7 @@ ${ ${"finalize" in obj ? `extern "C" void ${classSymbolName(typeName, "finalize")}(void*);` : ""} ${obj.call ? `extern "C" JSC_DECLARE_HOST_FUNCTION(${classSymbolName(typeName, "call")});` : ""} -${renderDecls(protoSymbolName, typeName, protoFields)} +${renderDecls(protoSymbolName, typeName, protoFields, obj.supportsObjectCreate || false)} STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(${proto}, ${proto}::Base); ${generateHashTable( @@ -476,7 +492,7 @@ function generateConstructorImpl(typeName, obj: ClassDefinition) { const hashTableIdentifier = hashTable.length ? `${name}TableValues` : ""; return ` ${obj.estimatedSize ? `extern "C" size_t ${symbolName(typeName, "estimatedSize")}(void* ptr);` : ""} -${renderStaticDecls(classSymbolName, typeName, fields)} +${renderStaticDecls(classSymbolName, typeName, fields, obj.supportsObjectCreate || false)} ${hashTable} void ${name}::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, ${prototypeName(typeName)}* prototype) @@ -581,7 +597,128 @@ function renderCachedFieldsHeader(typeName, klass, proto, values) { return rows.join("\n"); } -function renderDecls(symbolName, typeName, proto) { +function renderCallbacksHeader(typeName, callbacks: Record) { + const rows: string[] = []; + for (const name in callbacks) { + rows.push(`mutable WriteBarrier m_callback_${name};`); + } + + return rows.join("\n"); +} + +function renderCallbacksCppImpl(typeName, callbacks: Record) { + const rows: string[] = []; + if (Object.keys(callbacks).length === 0) return ""; + for (const name in callbacks) { + rows.push( + ` + extern "C" EncodedJSValue ${symbolName(typeName, "_callback_get_" + name)}(JSC::EncodedJSValue encodedThisValue) { + auto* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); + return JSValue::encode(thisObject->m_callback_${name}.get()); + } + + extern "C" void ${symbolName( + typeName, + "_callback_set_" + name, + )}(JSC::EncodedJSValue encodedThisValue, JSC::EncodedJSValue encodedCallback) { + auto* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); + JSValue callback = JSValue::decode(encodedCallback); +#if ASSERT_ENABLED + if (!callback.isEmpty()) { + ASSERT(callback.isObject()); + ASSERT(callback.isCallable()); + } +#endif + if (callback.isEmpty()) { + thisObject->m_callback_${name}.clear(); + } else { + thisObject->m_callback_${name}.set(thisObject->vm(), thisObject, callback.getObject()); + } + } + `, + ); + } + + rows.push(` + extern "C" void ${symbolName(typeName, "_setAllCallbacks")}(JSC::EncodedJSValue encodedThisValue, ${Object.keys( + callbacks, + ) + .map((_, i) => `JSC::EncodedJSValue encodedCallback${i}`) + .join(", ")}) { + auto* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); + ${Object.keys(callbacks) + .map( + (name, i) => ` + JSValue callback${i} = JSValue::decode(encodedCallback${i}); + if (!callback${i}.isEmpty()) { + thisObject->m_callback_${name}.set(thisObject->vm(), thisObject, callback${i}.getObject()); + } + `, + ) + .join("\n")} + } + +`); + + return rows.join("\n"); +} + +function renderCallbacksZig(typeName, callbacks: Record) { + if (Object.keys(callbacks).length === 0) return ""; + + var out = + "\n" + + `pub const Callbacks = struct { + instance: JSC.JSValue,` + + "\n"; + + for (const name in callbacks) { + const get = symbolName(typeName, "_callback_get_" + name); + const set = symbolName(typeName, "_callback_set_" + name); + out += ` + extern fn ${get}(JSC.JSValue) JSC.JSValue; + extern fn ${set}(JSC.JSValue, JSC.JSValue) void; + pub const ${pascalCase(name)}Callback = JSC.Codegen.CallbackWrapper(${get}, ${set}); + pub fn ${camelCase( + name, + )}(cb: @This(), thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, args: []const JSC.JSValue) ?JSC.JSValue { + return ${pascalCase(name)}Callback.call(.{.instance = cb.instance}, thisValue, globalObject, args); + } + `; + } + + out = out.trim(); + + out += ` + extern fn ${symbolName(typeName, "_setAllCallbacks")}(JSC.JSValue, ${Object.keys(callbacks) + .map((a, i) => `callback${i}: JSC.JSValue`) + .join(", ")}) void; + + pub inline fn set(this: @This(), values: struct { + ${Object.keys(callbacks) + .map((name, i) => `${camelCase(name)}: JSC.JSValue = .zero,`) + .join("\n")} + }) void { + ${symbolName(typeName, "_setAllCallbacks")}(this.instance, ${Object.keys(callbacks) + .map((name, i) => `values.${camelCase(name)}`) + .join(", ")},); + } + `; + + out += "\n};\n"; + + out += ` + + pub fn callbacks(_: *const ${typeName}, instance: JSC.JSValue) Callbacks { + return .{.instance = instance }; + } + +`; + + return "\n" + out; +} + +function renderDecls(symbolName, typeName, proto, supportsObjectCreate = false) { const rows = []; for (const name in proto) { @@ -598,6 +735,10 @@ function renderDecls(symbolName, typeName, proto) { `.trim(), "\n", ); + + if (supportsObjectCreate && !("setter" in proto[name])) { + rows.push("\n" + `static JSC_DECLARE_CUSTOM_SETTER(${symbolName(typeName, name)}SetterWrap);` + "\n"); + } } if ("setter" in proto[name] || ("accessor" in proto[name] && proto[name].setter)) { @@ -606,7 +747,7 @@ function renderDecls(symbolName, typeName, proto) { !!proto[name].this ? " JSC::EncodedJSValue thisValue, " : "" } JSC::JSGlobalObject* lexicalGlobalObject, JSC::EncodedJSValue value);`, ` - JSC_DECLARE_CUSTOM_SETTER(${symbolName(typeName, name)}SetterWrap); + static JSC_DECLARE_CUSTOM_SETTER(${symbolName(typeName, name)}SetterWrap); `.trim(), "\n", ); @@ -647,7 +788,7 @@ function renderDecls(symbolName, typeName, proto) { return rows.join("\n"); } -function renderStaticDecls(symbolName, typeName, fields) { +function renderStaticDecls(symbolName, typeName, fields, supportsObjectCreate = false) { const rows = []; for (const name in fields) { @@ -706,6 +847,8 @@ function renderFieldsImpl( ) { const rows: string[] = []; + const supportsObjectCreate = obj.supportsObjectCreate || false; + if (obj.construct) { rows.push(` @@ -728,16 +871,17 @@ JSC_DEFINE_CUSTOM_GETTER(js${typeName}Constructor, (JSGlobalObject * lexicalGlob if ("cache" in proto[name] || proto[name]?.internal) { const cacheName = typeof proto[name].cache === "string" ? `m_${proto[name].cache}` : `m_${name}`; if ("cache" in proto[name]) { - rows.push(` + if (!supportsObjectCreate) { + rows.push(` JSC_DEFINE_CUSTOM_GETTER(${symbolName( - typeName, - name, - )}GetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) + typeName, + name, + )}GetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, PropertyName attributeName)) { auto& vm = lexicalGlobalObject->vm(); Zig::GlobalObject *globalObject = reinterpret_cast(lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); - ${className(typeName)}* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(thisValue)); + ${className(typeName)}* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); if (JSValue cachedValue = thisObject->${cacheName}.get()) @@ -745,34 +889,91 @@ JSC_DEFINE_CUSTOM_GETTER(${symbolName( JSC::JSValue result = JSC::JSValue::decode( ${symbolName(typeName, proto[name].getter)}(thisObject->wrapped(),${ - proto[name].this!! ? " thisValue, " : "" - } globalObject) + proto[name].this!! ? " encodedThisValue, " : "" + } globalObject) ); RETURN_IF_EXCEPTION(throwScope, {}); thisObject->${cacheName}.set(vm, thisObject, result); RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); }`); + } else { + rows.push(` + JSC_DEFINE_CUSTOM_GETTER(${symbolName( + typeName, + name, + )}GetterWrap, (JSGlobalObject * globalObject, EncodedJSValue encodedThisValue, PropertyName attributeName)) + { + auto& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + ${className(typeName)}* thisObject = jsDynamicCast<${className( + typeName, + )}*>(JSValue::decode(encodedThisValue)); + if (UNLIKELY(!thisObject)) { + return JSValue::encode(jsUndefined()); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + if (JSValue cachedValue = thisObject->${cacheName}.get()) + return JSValue::encode(cachedValue); + + JSC::JSValue result = JSC::JSValue::decode( + ${symbolName(typeName, proto[name].getter)}(thisObject->wrapped(),${ + proto[name].this!! ? " thisValue, " : "" + } globalObject) + ); + RETURN_IF_EXCEPTION(throwScope, {}); + thisObject->${cacheName}.set(vm, thisObject, result); + RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); + }`); + } } rows.push(writeBarrier(symbolName, typeName, name, cacheName)); } else if ("getter" in proto[name] || ("accessor" in proto[name] && proto[name].getter)) { - rows.push(` + if (!supportsObjectCreate) { + rows.push(` JSC_DEFINE_CUSTOM_GETTER(${symbolName( - typeName, - name, - )}GetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) + typeName, + name, + )}GetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, PropertyName attributeName)) { auto& vm = lexicalGlobalObject->vm(); Zig::GlobalObject *globalObject = reinterpret_cast(lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); - ${className(typeName)}* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(thisValue)); + ${className(typeName)}* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); JSC::EncodedJSValue result = ${symbolName(typeName, proto[name].getter)}(thisObject->wrapped(),${ - !!proto[name].this ? " thisValue, " : "" - } globalObject); + !!proto[name].this ? " encodedThisValue, " : "" + } globalObject); RETURN_IF_EXCEPTION(throwScope, {}); RELEASE_AND_RETURN(throwScope, result); } `); + } else { + rows.push(` + JSC_DEFINE_CUSTOM_GETTER(${symbolName( + typeName, + name, + )}GetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, PropertyName attributeName)) + { + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + ${className(typeName)}* thisObject = jsDynamicCast<${className( + typeName, + )}*>(JSValue::decode(encodedThisValue)); + if (UNLIKELY(!thisObject)) { + return JSValue::encode(jsUndefined()); + } + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + JSC::EncodedJSValue result = ${symbolName(typeName, proto[name].getter)}(thisObject->wrapped(),${ + !!proto[name].this ? " encodedThisValue, " : "" + } globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + RELEASE_AND_RETURN(throwScope, result); + } + `); + } } if ("setter" in proto[name] || ("accessor" in proto[name] && proto[name].setter)) { @@ -781,20 +982,41 @@ JSC_DEFINE_CUSTOM_GETTER(${symbolName( JSC_DEFINE_CUSTOM_SETTER(${symbolName( typeName, name, - )}SetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) + )}SetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, EncodedJSValue encodedValue, PropertyName attributeName)) { auto& vm = lexicalGlobalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); - ${className(typeName)}* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(thisValue)); + ${className(typeName)}* thisObject = jsCast<${className(typeName)}*>(JSValue::decode(encodedThisValue)); JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); auto result = ${symbolName(typeName, proto[name].setter || proto[name].accessor.setter)}(thisObject->wrapped(),${ - !!proto[name].this ? " thisValue, " : "" + !!proto[name].this ? " encodedThisValue, " : "" } lexicalGlobalObject, encodedValue); RELEASE_AND_RETURN(throwScope, result); } `, ); + } else if (supportsObjectCreate) { + rows.push( + ` + JSC_DEFINE_CUSTOM_SETTER(${symbolName( + typeName, + name, + )}SetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue encodedThisValue, EncodedJSValue encodedValue, PropertyName attributeName)) + { + auto& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSValue thisValue = JSValue::decode(encodedThisValue); + if (!thisValue.isObject()) { + return false; + } + + JSObject *thisObject = asObject(thisValue); + thisObject->putDirect(vm, attributeName, JSValue::decode(encodedValue), 0); + return true; + } + `, + ); } if ("fn" in proto[name]) { @@ -844,12 +1066,13 @@ JSC_DEFINE_CUSTOM_SETTER(${symbolName( var extraIncludes = []; function generateClassHeader(typeName, obj: ClassDefinition) { - var { klass, proto, JSType = "ObjectType", values = [] } = obj; + var { klass, proto, JSType = "ObjectType", values = [], callbacks = {} } = obj; const name = className(typeName); const DECLARE_VISIT_CHILDREN = values.length || obj.estimatedSize || + Object.keys(callbacks).length || obj.hasPendingActivity || [...Object.values(klass), ...Object.values(proto)].find(a => !!a.cache) ? "DECLARE_VISIT_CHILDREN;\ntemplate void visitAdditionalChildren(Visitor&);\nDECLARE_VISIT_OUTPUT_CONSTRAINTS;\n" @@ -975,6 +1198,7 @@ function generateClassHeader(typeName, obj: ClassDefinition) { ${DECLARE_VISIT_CHILDREN} ${renderCachedFieldsHeader(typeName, klass, proto, values)} + ${callbacks ? renderCallbacksHeader(typeName, obj.callbacks) : ""} }; ${suffix} `; @@ -989,14 +1213,19 @@ function generateClassImpl(typeName, obj: ClassDefinition) { estimatedSize, hasPendingActivity = false, getInternalProperties = false, + callbacks = {}, } = obj; const name = className(typeName); - const DEFINE_VISIT_CHILDREN_LIST = [...Object.entries(fields), ...Object.entries(proto)] + let DEFINE_VISIT_CHILDREN_LIST = [...Object.entries(fields), ...Object.entries(proto)] .filter(([name, { cache = false, internal = false }]) => (cache || internal) === true) - .map(([name]) => ` visitor.append(thisObject->m_${name});`) + .map(([name]) => `visitor.append(thisObject->m_${name});`) .join("\n"); + for (const name in callbacks) { + DEFINE_VISIT_CHILDREN_LIST += "\n" + ` visitor.append(thisObject->m_callback_${name});`; + } + const values = (obj.values || []) .map(val => { return `visitor.append(thisObject->m_${val});`; @@ -1045,6 +1274,8 @@ void ${name}::visitOutputConstraintsImpl(JSCell *cell, Visitor& visitor) DEFINE_VISIT_OUTPUT_CONSTRAINTS(${name}); +${renderCallbacksCppImpl(typeName, callbacks)} + `.trim(); @@ -1234,6 +1465,7 @@ function generateZig( hasPendingActivity = false, structuredClone = false, getInternalProperties = false, + callbacks = {}, } = {} as ClassDefinition, ) { const exports = new Map(); @@ -1306,6 +1538,11 @@ function generateZig( ) .join("\n"); + var renderedCallbacks = ""; + if (Object.keys(callbacks).length) { + renderedCallbacks = renderCallbacksZig(typeName, callbacks); + } + function typeCheck() { var output = ""; @@ -1521,6 +1758,8 @@ pub const ${className(typeName)} = struct { extern fn ${typeName}__dangerouslySetPtr(JSC.JSValue, ?*${typeName}) bool; + ${renderedCallbacks} + comptime { ${typeCheck()} if (!JSC.is_bindgen) { diff --git a/src/deps/zig-clap/build.zig b/src/deps/zig-clap/build.zig deleted file mode 100644 index 5ab66da8aa..0000000000 --- a/src/deps/zig-clap/build.zig +++ /dev/null @@ -1,55 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); - -const Builder = std.build.Builder; -const Mode = std.builtin.Mode; - -pub fn build(b: *Builder) void { - const mode = b.standardReleaseOptions(); - const target = b.standardTargetOptions(.{}); - - const test_all_step = b.step("test", "Run all tests in all modes."); - inline for ([_]Mode{ Mode.Debug, Mode.ReleaseFast, Mode.ReleaseSafe, Mode.ReleaseSmall }) |test_mode| { - const mode_str = comptime modeToString(test_mode); - - const tests = b.addTest("clap.zig"); - tests.setBuildMode(test_mode); - tests.setTarget(target); - tests.setNamePrefix(mode_str ++ " "); - - const test_step = b.step("test-" ++ mode_str, "Run all tests in " ++ mode_str ++ "."); - test_step.dependOn(&tests.step); - test_all_step.dependOn(test_step); - } - - const example_step = b.step("examples", "Build examples"); - inline for ([_][]const u8{ - "simple", - "simple-ex", - //"simple-error", - "streaming-clap", - "help", - "usage", - }) |example_name| { - const example = b.addExecutable(example_name, "example/" ++ example_name ++ ".zig"); - example.addPackagePath("clap", "clap.zig"); - example.setBuildMode(mode); - example.setTarget(target); - example.install(); - example_step.dependOn(&example.step); - } - - const all_step = b.step("all", "Build everything and runs all tests"); - all_step.dependOn(test_all_step); - - b.default_step.dependOn(all_step); -} - -fn modeToString(mode: Mode) []const u8 { - return switch (mode) { - Mode.Debug => "debug", - Mode.ReleaseFast => "release-fast", - Mode.ReleaseSafe => "release-safe", - Mode.ReleaseSmall => "release-small", - }; -} diff --git a/src/env.zig b/src/env.zig index 86d481e4b9..a74ea2d1e1 100644 --- a/src/env.zig +++ b/src/env.zig @@ -24,7 +24,7 @@ pub const isLinux = @import("builtin").target.os.tag == .linux; pub const isAarch64 = @import("builtin").target.cpu.arch.isAARCH64(); pub const isX86 = @import("builtin").target.cpu.arch.isX86(); pub const isX64 = @import("builtin").target.cpu.arch == .x86_64; -pub const allow_assert = isDebug or isTest; +pub const allow_assert = isDebug or isTest or std.builtin.Mode.ReleaseSafe == @import("builtin").mode; pub const analytics_url = if (isDebug) "http://localhost:4000/events" else "http://i.bun.sh/events"; const BuildOptions = if (isTest) struct { diff --git a/src/exact_size_matcher.zig b/src/exact_size_matcher.zig index 67d0b8fe8c..84594ccac4 100644 --- a/src/exact_size_matcher.zig +++ b/src/exact_size_matcher.zig @@ -27,10 +27,10 @@ pub fn ExactSizeMatcher(comptime max_bytes: usize) type { @memset(tmp[str.len..], 0); } - return std.mem.readIntNative(T, &tmp); + return std.mem.readInt(T, &tmp, .little); }, max_bytes => { - return std.mem.readIntSliceNative(T, str[0..]); + return std.mem.readInt(T, str[0..max_bytes], .little); }, 0 => { return 0; @@ -49,10 +49,10 @@ pub fn ExactSizeMatcher(comptime max_bytes: usize) type { tmp[i] = std.ascii.toLower(char); } @memset(tmp[str.len..], 0); - return std.mem.readIntNative(T, &tmp); + return std.mem.readInt(T, &tmp, .little); }, max_bytes => { - return std.mem.readIntSliceNative(T, str); + return std.mem.readInt(T, str[0..max_bytes], .little); }, 0 => { return 0; @@ -67,9 +67,9 @@ pub fn ExactSizeMatcher(comptime max_bytes: usize) type { if (str.len < max_bytes) { var bytes = std.mem.zeroes([max_bytes]u8); bytes[0..str.len].* = str[0..str.len].*; - return std.mem.readIntNative(T, &bytes); + return std.mem.readInt(T, &bytes, .little); } else if (str.len == max_bytes) { - return std.mem.readIntNative(T, str[0..str.len]); + return std.mem.readInt(T, str[0..str.len], .little); } else { @compileError("str: \"" ++ str ++ "\" too long"); } diff --git a/src/fs.zig b/src/fs.zig index a7948ff2a2..c17ee8968b 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -842,9 +842,9 @@ pub const FileSystem = struct { // so if we're not going to do a full content hash, we should use mtime and size. // even mtime is debatable. var hash_bytes_remain: []u8 = hash_bytes[0..]; - std.mem.writeIntNative(@TypeOf(this.size), hash_bytes_remain[0..@sizeOf(@TypeOf(this.size))], this.size); + std.mem.writeInt(@TypeOf(this.size), hash_bytes_remain[0..@sizeOf(@TypeOf(this.size))], this.size, .little); hash_bytes_remain = hash_bytes_remain[@sizeOf(@TypeOf(this.size))..]; - std.mem.writeIntNative(@TypeOf(this.mtime), hash_bytes_remain[0..@sizeOf(@TypeOf(this.mtime))], this.mtime); + std.mem.writeInt(@TypeOf(this.mtime), hash_bytes_remain[0..@sizeOf(@TypeOf(this.mtime))], this.mtime, .little); hash_bytes_remain = hash_bytes_remain[@sizeOf(@TypeOf(this.mtime))..]; std.debug.assert(hash_bytes_remain.len == 8); hash_bytes_remain[0..8].* = @as([8]u8, @bitCast(@as(u64, 0))); diff --git a/src/http/websocket.zig b/src/http/websocket.zig index 2fdd284519..dd1f9a3269 100644 --- a/src/http/websocket.zig +++ b/src/http/websocket.zig @@ -53,14 +53,14 @@ pub const WebsocketHeader = packed struct { if (comptime Environment.allow_assert) { var buf_ = [2]u8{ 0, 0 }; var stream = std.io.fixedBufferStream(&buf_); - stream.writer().writeIntBig(u16, @as(u16, @bitCast(header))) catch unreachable; + stream.writer().writeInt(u16, @as(u16, @bitCast(header)), .big) catch unreachable; stream.pos = 0; - const casted = stream.reader().readIntBig(u16) catch unreachable; + const casted = stream.reader().readInt(u16, .big) catch unreachable; std.debug.assert(casted == @as(u16, @bitCast(header))); std.debug.assert(std.meta.eql(@as(WebsocketHeader, @bitCast(casted)), header)); } - try writer.writeIntBig(u16, @as(u16, @bitCast(header))); + try writer.writeInt(u16, @as(u16, @bitCast(header)), .big); std.debug.assert(header.len == packLength(n)); } @@ -230,14 +230,14 @@ pub const Websocket = struct { if (!dataframe.isValid()) return error.InvalidMessage; - try stream.writeIntBig(u16, @as(u16, @bitCast(dataframe.header))); + try stream.writeInt(u16, @as(u16, @bitCast(dataframe.header)), .big); // Write extended length if needed const n = dataframe.data.len; switch (n) { 0...126 => {}, // Included in header - 127...0xFFFF => try stream.writeIntBig(u16, @as(u16, @truncate(n))), - else => try stream.writeIntBig(u64, n), + 127...0xFFFF => try stream.writeInt(u16, @as(u16, @truncate(n)), .big), + else => try stream.writeInt(u64, n, .big), } // TODO: Handle compression @@ -301,11 +301,11 @@ pub const Websocket = struct { switch (header.len) { 126 => { - length = std.mem.readIntBig(u16, buf[2..4]); + length = std.mem.readInt(u16, buf[2..4], .big); buf = buf[4..]; }, 127 => { - length = std.mem.readIntBig(u64, buf[2..10]); + length = std.mem.readInt(u64, buf[2..10], .big); // Most significant bit must be 0 if (length >> 63 == 1) { return error.InvalidMessage; diff --git a/src/http/websocket_http_client.zig b/src/http/websocket_http_client.zig index 27af3e74dc..f6188d5904 100644 --- a/src/http/websocket_http_client.zig +++ b/src/http/websocket_http_client.zig @@ -836,8 +836,8 @@ const Copy = union(enum) { // Write extended length if needed switch (how_big_is_the_length_integer) { 0 => {}, - 2 => std.mem.writeIntBig(u16, buf[2..][0..2], @as(u16, @truncate(content_byte_len))), - 8 => std.mem.writeIntBig(u64, buf[2..][0..8], @as(u64, @truncate(content_byte_len))), + 2 => std.mem.writeInt(u16, buf[2..][0..2], @as(u16, @truncate(content_byte_len)), .big), + 8 => std.mem.writeInt(u64, buf[2..][0..8], @as(u64, @truncate(content_byte_len)), .big), else => unreachable, } @@ -1266,8 +1266,8 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { // Multibyte length quantities are expressed in network byte order receive_body_remain = switch (byte_size) { - 8 => @as(usize, std.mem.readIntBig(u64, data[0..8])), - 2 => @as(usize, std.mem.readIntBig(u16, data[0..2])), + 8 => @as(usize, std.mem.readInt(u64, data[0..8], .big)), + 2 => @as(usize, std.mem.readInt(u16, data[0..2], .big)), else => unreachable, }; data = data[byte_size..]; @@ -1483,7 +1483,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { header.len = @as(u7, @truncate(body_len + 2)); final_body_bytes[0..2].* = @as([2]u8, @bitCast(@as(u16, @bitCast(header)))); var mask_buf: *[4]u8 = final_body_bytes[2..6]; - std.mem.writeIntSliceBig(u16, final_body_bytes[6..8], code); + final_body_bytes[6..8].* = @bitCast(@byteSwap(code)); var reason = bun.String.empty; if (body) |data| { diff --git a/src/install/install.zig b/src/install/install.zig index 0b9d086133..701c43da33 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -3151,7 +3151,7 @@ pub const PackageManager = struct { tmpname_buf[0..8].* = "tmplock-".*; var tmpfile = FileSystem.RealFS.Tmpfile{}; var secret: [32]u8 = undefined; - std.mem.writeIntNative(u64, secret[0..8], @as(u64, @intCast(std.time.milliTimestamp()))); + std.mem.writeInt(u64, secret[0..8], @as(u64, @intCast(std.time.milliTimestamp())), .little); var base64_bytes: [64]u8 = undefined; std.crypto.random.bytes(&base64_bytes); diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index a57a6f5bea..c6ae6a3585 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -1582,7 +1582,7 @@ pub fn saveToDisk(this: *Lockfile, filename: stringZ) void { tmpname_buf[0..8].* = "bunlock-".*; var tmpfile = FileSystem.RealFS.Tmpfile{}; var secret: [32]u8 = undefined; - std.mem.writeIntNative(u64, secret[0..8], @as(u64, @intCast(std.time.milliTimestamp()))); + std.mem.writeInt(u64, secret[0..8], @as(u64, @intCast(std.time.milliTimestamp())), .little); var base64_bytes: [16]u8 = undefined; std.crypto.random.bytes(&base64_bytes); @@ -1596,12 +1596,38 @@ pub fn saveToDisk(this: *Lockfile, filename: stringZ) void { }; var file = tmpfile.file(); + { + var bytes = std.ArrayList(u8).init(bun.default_allocator); + defer bytes.deinit(); + var total_size: usize = 0; + var end_pos: usize = 0; + Lockfile.Serializer.save(this, &bytes, &total_size, &end_pos) catch |err| { + tmpfile.dir().deleteFileZ(tmpname) catch {}; + Output.prettyErrorln("error: failed to serialize lockfile: {s}", .{@errorName(err)}); + Global.crash(); + }; + if (bytes.items.len >= end_pos) + bytes.items[end_pos..][0..@sizeOf(usize)].* = @bitCast(total_size); - Lockfile.Serializer.save(this, std.fs.File, file) catch |err| { - tmpfile.dir().deleteFileZ(tmpname) catch {}; - Output.prettyErrorln("error: failed to serialize lockfile: {s}", .{@errorName(err)}); - Global.crash(); - }; + var node_fs: bun.JSC.Node.NodeFS = undefined; + switch (node_fs.writeFile( + .{ + .file = .{ + .fd = bun.toFD(file.handle), + }, + .dirfd = bun.invalid_fd, + .data = .{ .string = bytes.items }, + }, + .sync, + )) { + .err => |e| { + tmpfile.dir().deleteFileZ(tmpname) catch {}; + Output.prettyErrorln("error: failed to write lockfile\n{}", .{e}); + Global.crash(); + }, + .result => {}, + } + } if (comptime Environment.isWindows) { // TODO: make this executable @@ -4288,13 +4314,13 @@ pub const Package = extern struct { const AlignmentType = sizes.Types[sizes.fields[0]]; pub fn save(list: Lockfile.Package.List, comptime StreamType: type, stream: StreamType, comptime Writer: type, writer: Writer) !void { - try writer.writeIntLittle(u64, list.len); - try writer.writeIntLittle(u64, @alignOf(@TypeOf(list.bytes))); - try writer.writeIntLittle(u64, sizes.Types.len); + try writer.writeInt(u64, list.len, .little); + try writer.writeInt(u64, @alignOf(@TypeOf(list.bytes)), .little); + try writer.writeInt(u64, sizes.Types.len, .little); const begin_at = try stream.getPos(); - try writer.writeIntLittle(u64, 0); + try writer.writeInt(u64, 0, .little); const end_at = try stream.getPos(); - try writer.writeIntLittle(u64, 0); + try writer.writeInt(u64, 0, .little); _ = try Aligner.write(@TypeOf(list.bytes), Writer, writer, try stream.getPos()); @@ -4312,8 +4338,8 @@ pub const Package = extern struct { const really_end_at = try stream.getPos(); - _ = try std.os.pwrite(stream.handle, std.mem.asBytes(&really_begin_at), begin_at); - _ = try std.os.pwrite(stream.handle, std.mem.asBytes(&really_end_at), end_at); + _ = stream.pwrite(std.mem.asBytes(&really_begin_at), begin_at); + _ = stream.pwrite(std.mem.asBytes(&really_end_at), end_at); } pub fn load( @@ -4323,11 +4349,11 @@ pub const Package = extern struct { ) !Lockfile.Package.List { var reader = stream.reader(); - const list_len = try reader.readIntLittle(u64); + const list_len = try reader.readInt(u64, .little); if (list_len > std.math.maxInt(u32) - 1) return error.@"Lockfile validation failed: list is impossibly long"; - const input_alignment = try reader.readIntLittle(u64); + const input_alignment = try reader.readInt(u64, .little); var list = Lockfile.Package.List{}; const Alingee = @TypeOf(list.bytes); @@ -4336,7 +4362,7 @@ pub const Package = extern struct { return error.@"Lockfile validation failed: alignment mismatch"; } - const field_count = try reader.readIntLittle(u64); + const field_count = try reader.readInt(u64, .little); switch (field_count) { sizes.Types.len => {}, // "scripts" field is absent before v0.6.8 @@ -4347,8 +4373,8 @@ pub const Package = extern struct { }, } - const begin_at = try reader.readIntLittle(u64); - const end_at = try reader.readIntLittle(u64); + const begin_at = try reader.readInt(u64, .little); + const end_at = try reader.readInt(u64, .little); if (begin_at > end or end_at > end or begin_at > end_at) { return error.@"Lockfile validation failed: invalid package list range"; } @@ -4472,8 +4498,8 @@ const Buffers = struct { const PointerType = std.meta.Child(@TypeOf(arraylist.items.ptr)); var reader = stream.reader(); - const start_pos = try reader.readIntLittle(u64); - const end_pos = try reader.readIntLittle(u64); + const start_pos = try reader.readInt(u64, .little); + const end_pos = try reader.readInt(u64, .little); stream.pos = end_pos; const byte_len = end_pos - start_pos; @@ -4500,8 +4526,8 @@ const Buffers = struct { const bytes = std.mem.sliceAsBytes(array); const start_pos = try stream.getPos(); - try writer.writeIntLittle(u64, 0xDEADBEEF); - try writer.writeIntLittle(u64, 0xDEADBEEF); + try writer.writeInt(u64, 0xDEADBEEF, .little); + try writer.writeInt(u64, 0xDEADBEEF, .little); const prefix = comptime std.fmt.comptimePrint( "\n<{s}> {d} sizeof, {d} alignof\n", @@ -4522,14 +4548,14 @@ const Buffers = struct { const positioned = [2]u64{ real_start_pos, real_end_pos }; var written: usize = 0; while (written < 16) { - written += try std.os.pwrite(stream.handle, std.mem.asBytes(&positioned)[written..], start_pos + written); + written += stream.pwrite(std.mem.asBytes(&positioned)[written..], start_pos + written); } } else { const real_end_pos = try stream.getPos(); const positioned = [2]u64{ real_end_pos, real_end_pos }; var written: usize = 0; while (written < 16) { - written += try std.os.pwrite(stream.handle, std.mem.asBytes(&positioned)[written..], start_pos + written); + written += stream.pwrite(std.mem.asBytes(&positioned)[written..], start_pos + written); } } } @@ -4693,23 +4719,40 @@ pub const Serializer = struct { const has_trusted_dependencies_tag: u64 = @bitCast([_]u8{ 't', 'R', 'u', 'S', 't', 'E', 'D', 'd' }); const has_overrides_tag: u64 = @bitCast([_]u8{ 'o', 'V', 'e', 'R', 'r', 'i', 'D', 's' }); - pub fn save(this: *Lockfile, comptime StreamType: type, stream: StreamType) !void { + pub fn save(this: *Lockfile, bytes: *std.ArrayList(u8), total_size: *usize, end_pos: *usize) !void { var old_package_list = this.packages; this.packages = try this.packages.clone(z_allocator); old_package_list.deinit(this.allocator); - var writer = stream.writer(); + var writer = bytes.writer(); try writer.writeAll(header_bytes); - try writer.writeIntLittle(u32, @intFromEnum(this.format)); + try writer.writeInt(u32, @intFromEnum(this.format), .little); try writer.writeAll(&this.meta_hash); - const pos = try stream.getPos(); - try writer.writeIntLittle(u64, 0); + end_pos.* = bytes.items.len; + try writer.writeInt(u64, 0, .little); - try Lockfile.Package.Serializer.save(this.packages, StreamType, stream, @TypeOf(&writer), &writer); - try Lockfile.Buffers.save(this.buffers, z_allocator, StreamType, stream, @TypeOf(&writer), &writer); - try writer.writeIntLittle(u64, 0); + const StreamType = struct { + bytes: *std.ArrayList(u8), + pub inline fn getPos(s: @This()) anyerror!usize { + return s.bytes.items.len; + } + + pub fn pwrite( + s: @This(), + data: []const u8, + index: usize, + ) usize { + @memcpy(s.bytes.items[index..][0..data.len], data); + return data.len; + } + }; + const stream = StreamType{ .bytes = bytes }; + + try Lockfile.Package.Serializer.save(this.packages, StreamType, stream, @TypeOf(writer), writer); + try Lockfile.Buffers.save(this.buffers, z_allocator, StreamType, stream, @TypeOf(writer), writer); + try writer.writeInt(u64, 0, .little); // < Bun v1.0.4 stopped right here when reading the lockfile // So we add an extra 8 byte tag to say "hey, there's more data here" @@ -4721,16 +4764,16 @@ pub const Serializer = struct { try Lockfile.Buffers.writeArray( StreamType, stream, - @TypeOf(&writer), - &writer, + @TypeOf(writer), + writer, []PackageNameHash, this.workspace_versions.keys(), ); try Lockfile.Buffers.writeArray( StreamType, stream, - @TypeOf(&writer), - &writer, + @TypeOf(writer), + writer, []Semver.Version, this.workspace_versions.values(), ); @@ -4738,16 +4781,16 @@ pub const Serializer = struct { try Lockfile.Buffers.writeArray( StreamType, stream, - @TypeOf(&writer), - &writer, + @TypeOf(writer), + writer, []PackageNameHash, this.workspace_paths.keys(), ); try Lockfile.Buffers.writeArray( StreamType, stream, - @TypeOf(&writer), - &writer, + @TypeOf(writer), + writer, []String, this.workspace_paths.values(), ); @@ -4759,8 +4802,8 @@ pub const Serializer = struct { try Lockfile.Buffers.writeArray( StreamType, stream, - @TypeOf(&writer), - &writer, + @TypeOf(writer), + writer, []u32, this.trusted_dependencies.keys(), ); @@ -4772,8 +4815,8 @@ pub const Serializer = struct { try Lockfile.Buffers.writeArray( StreamType, stream, - @TypeOf(&writer), - &writer, + @TypeOf(writer), + writer, []PackageNameHash, this.overrides.map.keys(), ); @@ -4787,19 +4830,16 @@ pub const Serializer = struct { try Lockfile.Buffers.writeArray( StreamType, stream, - @TypeOf(&writer), - &writer, + @TypeOf(writer), + writer, []Dependency.External, external_overrides.items, ); } - const end = try stream.getPos(); + total_size.* = try stream.getPos(); try writer.writeAll(&alignment_bytes_to_repeat_buffer); - - _ = try std.os.pwrite(stream.handle, std.mem.asBytes(&end), pos); - try std.os.ftruncate(stream.handle, try stream.getPos()); } pub fn load( lockfile: *Lockfile, @@ -4815,7 +4855,7 @@ pub const Serializer = struct { return error.InvalidLockfile; } - var format = try reader.readIntLittle(u32); + var format = try reader.readInt(u32, .little); if (format != @intFromEnum(Lockfile.FormatVersion.current)) { return error.@"Outdated lockfile version"; } @@ -4825,7 +4865,7 @@ pub const Serializer = struct { _ = try reader.readAll(&lockfile.meta_hash); - const total_buffer_size = try reader.readIntLittle(u64); + const total_buffer_size = try reader.readInt(u64, .little); if (total_buffer_size > stream.buffer.len) { return error.@"Lockfile is missing data"; } @@ -4836,7 +4876,7 @@ pub const Serializer = struct { allocator, ); lockfile.buffers = try Lockfile.Buffers.load(stream, allocator, log); - if ((try stream.reader().readIntLittle(u64)) != 0) { + if ((try stream.reader().readInt(u64, .little)) != 0) { return error.@"Lockfile is malformed (expected 0 at the end)"; } @@ -4847,7 +4887,7 @@ pub const Serializer = struct { const remaining_in_buffer = total_buffer_size -| stream.pos; if (remaining_in_buffer > 8 and total_buffer_size <= stream.buffer.len) { - const next_num = try reader.readIntLittle(u64); + const next_num = try reader.readInt(u64, .little); if (next_num == has_workspace_package_ids_tag) { { var workspace_package_name_hashes = try Lockfile.Buffers.readArray( @@ -4909,7 +4949,7 @@ pub const Serializer = struct { const remaining_in_buffer = total_buffer_size -| stream.pos; if (remaining_in_buffer > 8 and total_buffer_size <= stream.buffer.len) { - const next_num = try reader.readIntLittle(u64); + const next_num = try reader.readInt(u64, .little); if (next_num == has_trusted_dependencies_tag) { var trusted_dependencies_hashes = try Lockfile.Buffers.readArray( stream, @@ -4933,7 +4973,7 @@ pub const Serializer = struct { const remaining_in_buffer = total_buffer_size -| stream.pos; if (remaining_in_buffer > 8 and total_buffer_size <= stream.buffer.len) { - const next_num = try reader.readIntLittle(u64); + const next_num = try reader.readInt(u64, .little); if (next_num == has_overrides_tag) { var overrides_name_hashes = try Lockfile.Buffers.readArray( stream, @@ -4995,7 +5035,7 @@ pub const Serializer = struct { if (comptime Environment.allow_assert) std.debug.assert(stream.pos == total_buffer_size); - // const end = try reader.readIntLittle(u64); + // const end = try reader.readInt(u64, .little); } }; diff --git a/src/install/npm.zig b/src/install/npm.zig index 49ce8fff4b..5590074f4f 100644 --- a/src/install/npm.zig +++ b/src/install/npm.zig @@ -587,12 +587,12 @@ pub const PackageManifest = struct { pub fn writeArray(comptime Writer: type, writer: Writer, comptime Type: type, array: []const Type, pos: *u64) !void { const bytes = std.mem.sliceAsBytes(array); if (bytes.len == 0) { - try writer.writeIntNative(u64, 0); + try writer.writeInt(u64, 0, .little); pos.* += 8; return; } - try writer.writeIntNative(u64, bytes.len); + try writer.writeInt(u64, bytes.len, .little); pos.* += 8; pos.* += try Aligner.write(Type, Writer, writer, pos.*); @@ -604,7 +604,7 @@ pub const PackageManifest = struct { pub fn readArray(stream: *std.io.FixedBufferStream([]const u8), comptime Type: type) ![]const Type { var reader = stream.reader(); - const byte_len = try reader.readIntNative(u64); + const byte_len = try reader.readInt(u64, .little); if (byte_len == 0) { return &[_]Type{}; } diff --git a/src/js/builtins/ProcessObjectInternals.ts b/src/js/builtins/ProcessObjectInternals.ts index 5ba0752cb2..5dc537e62a 100644 --- a/src/js/builtins/ProcessObjectInternals.ts +++ b/src/js/builtins/ProcessObjectInternals.ts @@ -401,5 +401,17 @@ export function initializeNextTickQueue(process, nextTickQueue, drainMicrotasksF $getter; export function mainModule() { + var existing = $getByIdDirectPrivate(this, "main"); + // note: this doesn't handle "process.mainModule = undefined" + if (typeof existing !== "undefined") { + return existing; + } + return $requireMap.$get(Bun.main); } + +$overriddenName = "set mainModule"; +export function setMainModule(value) { + $putByIdDirectPrivate(this, "main", value); + return true; +} diff --git a/src/js_ast.zig b/src/js_ast.zig index 05df6ff5a1..78bcad7611 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -108,10 +108,10 @@ pub fn NewBaseStore(comptime Union: anytype, comptime count: usize) type { overflow: Overflow = Overflow{}, - pub threadlocal var _self: *Self = undefined; + pub threadlocal var _self: ?*Self = null; pub fn reclaim() []*Block { - var overflow = &_self.overflow; + var overflow = &_self.?.overflow; if (overflow.used == 0) { if (overflow.allocated == 0 or overflow.ptrs[0].used == 0) { @@ -153,7 +153,7 @@ pub fn NewBaseStore(comptime Union: anytype, comptime count: usize) type { /// Nested parsing should either use the same store, or call /// Store.reclaim. pub fn reset() void { - const blocks = _self.overflow.slice(); + const blocks = _self.?.overflow.slice(); for (blocks) |b| { if (comptime Environment.isDebug) { // ensure we crash if we use a freed value @@ -162,7 +162,7 @@ pub fn NewBaseStore(comptime Union: anytype, comptime count: usize) type { } b.used = 0; } - _self.overflow.used = 0; + _self.?.overflow.used = 0; } pub fn init(allocator: std.mem.Allocator) *Self { @@ -174,33 +174,39 @@ pub fn NewBaseStore(comptime Union: anytype, comptime count: usize) type { _self = instance; - return _self; + return _self.?; + } + + pub fn onThreadExit(_: *anyopaque) callconv(.C) void { + deinit(); } fn deinit() void { - var sliced = _self.overflow.slice(); - var allocator = _self.overflow.allocator; + if (_self) |this| { + _self = null; + var sliced = this.overflow.slice(); + var allocator = this.overflow.allocator; - if (sliced.len > 1) { - var i: usize = 1; - const end = sliced.len; - while (i < end) { - var ptrs = @as(*[2]Block, @ptrCast(sliced[i])); - allocator.free(ptrs); - i += 2; + if (sliced.len > 1) { + var i: usize = 1; + const end = sliced.len; + while (i < end) { + var ptrs = @as(*[2]Block, @ptrCast(sliced[i])); + allocator.free(ptrs); + i += 2; + } + this.overflow.allocated = 1; + } + var base_store = @fieldParentPtr(WithBase, "store", this); + if (this.overflow.ptrs[0] == &base_store.head) { + allocator.destroy(base_store); } - _self.overflow.allocated = 1; } - var base_store = @fieldParentPtr(WithBase, "store", _self); - if (_self.overflow.ptrs[0] == &base_store.head) { - allocator.destroy(base_store); - } - _self = undefined; } pub fn append(comptime Disabler: type, comptime ValueType: type, value: ValueType) *ValueType { Disabler.assert(); - return _self._append(ValueType, value); + return _self.?._append(ValueType, value); } inline fn _append(self: *Self, comptime ValueType: type, value: ValueType) *ValueType { @@ -3006,7 +3012,7 @@ pub const Stmt = struct { } pub fn toOwnedSlice() []*Store.All.Block { - if (!has_inited or Store.All._self.overflow.used == 0 or disable_reset) return &[_]*Store.All.Block{}; + if (!has_inited or Store.All._self.?.overflow.used == 0 or disable_reset) return &[_]*Store.All.Block{}; return Store.All.reclaim(); } }; @@ -5392,7 +5398,7 @@ pub const Expr = struct { } pub fn toOwnedSlice() []*Store.All.Block { - if (!has_inited or Store.All._self.overflow.used == 0 or disable_reset or memory_allocator != null) return &[_]*Store.All.Block{}; + if (!has_inited or Store.All._self.?.overflow.used == 0 or disable_reset or memory_allocator != null) return &[_]*Store.All.Block{}; return Store.All.reclaim(); } }; @@ -5432,7 +5438,7 @@ pub const S = struct { pub const Comment = struct { text: string }; pub const Directive = struct { - value: []const u16, + value: []const u8, }; pub const ExportClause = struct { items: []ClauseItem, is_single_line: bool = false }; diff --git a/src/js_parser.zig b/src/js_parser.zig index 49e1a74791..92363b24e1 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -958,7 +958,6 @@ pub const ImportScanner = struct { // var did_remove_star_loc = false; const keep_unused_imports = !p.options.features.trim_unused_imports; - // TypeScript always trims unused imports. This is important for // correctness since some imports might be fake (only in the type // system and used for type-only imports). @@ -10951,6 +10950,10 @@ fn NewParser_( } else if (str.eqlComptime("use asm")) { skip = true; stmt.data = Prefill.Data.SEmpty; + } else { + stmt = Stmt.alloc(S.Directive, S.Directive{ + .value = str.slice(p.allocator), + }, stmt.loc); } } }, @@ -16553,7 +16556,7 @@ fn NewParser_( continue; }, .s_directive => |dir| { - if (strings.utf16EqlString(dir.value, "use strict")) { + if (strings.eqlComptime(dir.value, "use strict")) { return stmt.loc; } }, @@ -17583,10 +17586,6 @@ fn NewParser_( }, .s_directive => { p.current_scope.is_after_const_local_prefix = was_after_after_const_local_prefix; - // if p.isStrictMode() && s.LegacyOctalLoc.Start > 0 { - // p.markStrictModeFeature(legacyOctalEscape, p.source.RangeOfLegacyOctalEscape(s.LegacyOctalLoc), "") - // } - return; }, .s_import => |data| { try p.recordDeclaredSymbol(data.namespace_ref); diff --git a/src/js_printer.zig b/src/js_printer.zig index f0ea20c346..fc75226a2f 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -343,7 +343,7 @@ pub fn writeJSONString(input: []const u8, comptime Writer: type, writer: Writer, const remain = text[@as(usize, width)..]; if (encoding != .utf8 and width > 0) { var codepoint_bytes: [4]u8 = undefined; - std.mem.writeIntNative(i32, &codepoint_bytes, c); + std.mem.writeInt(i32, &codepoint_bytes, c, .little); try writer.writeAll( codepoint_bytes[0..strings.encodeWTF8Rune(codepoint_bytes[0..4], c)], ); @@ -4601,12 +4601,9 @@ fn NewPrinter( if (comptime is_json) unreachable; - const c = bestQuoteCharForString(u16, s.value, false); p.printIndent(); p.printSpaceBeforeIdentifier(); - p.print(c); - p.printQuotedUTF16(s.value, c); - p.print(c); + p.printQuotedUTF8(s.value, false); p.printSemicolonAfterStatement(); }, .s_break => |s| { diff --git a/src/jsc.zig b/src/jsc.zig index 9f02d6a781..7e7e3defcb 100644 --- a/src/jsc.zig +++ b/src/jsc.zig @@ -85,6 +85,10 @@ pub const Subprocess = @import("./bun.js/api/bun.zig").Subprocess; /// - pub usingnamespace JSC.Codegen.JSMyClassName; /// 5. make clean-bindings && make bindings -j10 /// -pub const Codegen = @import("generated/ZigGeneratedClasses.zig"); +pub const Codegen = struct { + pub const GeneratedClasses = @import("ZigGeneratedClasses"); + pub usingnamespace GeneratedClasses; + pub usingnamespace @import("./bun.js/bindings/codegen.zig"); +}; pub const GeneratedClassesList = @import("./bun.js/bindings/generated_classes_list.zig").Classes; diff --git a/src/napi/napi.zig b/src/napi/napi.zig index b45ffdb5a4..ffda175f28 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -67,11 +67,10 @@ pub const Ref = opaque { pub const napi_handle_scope = napi_env; pub const napi_escapable_handle_scope = napi_env; pub const napi_callback_info = *JSC.CallFrame; -pub const napi_deferred = *JSC.napi.Ref; +pub const napi_deferred = *JSC.JSPromise.Strong; pub const napi_value = JSC.JSValue; pub const struct_napi_escapable_handle_scope__ = opaque {}; -pub const struct_napi_deferred__ = opaque {}; const char16_t = u16; pub const napi_default: c_int = 0; @@ -808,24 +807,25 @@ pub export fn napi_get_version(_: napi_env, result: *u32) napi_status { } pub export fn napi_create_promise(env: napi_env, deferred: *napi_deferred, promise: *napi_value) napi_status { log("napi_create_promise", .{}); - var js_promise = JSC.JSPromise.create(env); - var promise_value = js_promise.asValue(env); - deferred.* = Ref.create(env, promise_value); - promise.* = promise_value; + deferred.* = bun.default_allocator.create(JSC.JSPromise.Strong) catch @panic("failed to allocate napi_deferred"); + deferred.*.* = JSC.JSPromise.Strong.init(env); + promise.* = deferred.*.get().asValue(env); return .ok; } pub export fn napi_resolve_deferred(env: napi_env, deferred: napi_deferred, resolution: napi_value) napi_status { log("napi_resolve_deferred", .{}); - var prom = deferred.get().asPromise() orelse return .object_expected; + var prom = deferred.get(); prom.resolve(env, resolution); - deferred.destroy(); + deferred.*.strong.deinit(); + bun.default_allocator.destroy(deferred); return .ok; } pub export fn napi_reject_deferred(env: napi_env, deferred: napi_deferred, rejection: napi_value) napi_status { log("napi_reject_deferred", .{}); - var prom = deferred.get().asPromise() orelse return .object_expected; + var prom = deferred.get(); prom.reject(env, rejection); - deferred.destroy(); + deferred.*.strong.deinit(); + bun.default_allocator.destroy(deferred); return .ok; } pub export fn napi_is_promise(_: napi_env, value: napi_value, is_promise: *bool) napi_status { diff --git a/src/resolver/resolve_path.zig b/src/resolver/resolve_path.zig index 34ed35a5a5..0e3f9950e0 100644 --- a/src/resolver/resolve_path.zig +++ b/src/resolver/resolve_path.zig @@ -25,11 +25,11 @@ const IsSeparatorFunc = fn (char: u8) bool; const LastSeparatorFunction = fn (slice: []const u8) ?usize; inline fn @"is .."(slice: []const u8) bool { - return slice.len >= 2 and @as(u16, @bitCast(slice[0..2].*)) == comptime std.mem.readIntNative(u16, ".."); + return slice.len >= 2 and @as(u16, @bitCast(slice[0..2].*)) == comptime std.mem.readInt(u16, "..", .little); } inline fn isDotSlash(slice: []const u8) bool { - return @as(u16, @bitCast(slice[0..2].*)) == comptime std.mem.readIntNative(u16, "./"); + return @as(u16, @bitCast(slice[0..2].*)) == comptime std.mem.readInt(u16, "./", .little); } inline fn @"is ../"(slice: []const u8) bool { diff --git a/src/string_immutable.zig b/src/string_immutable.zig index 831974cceb..e289f2c07c 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -418,7 +418,7 @@ pub const StringOrTinyString = struct { // This is a switch expression instead of a statement to make sure it uses the faster assembly return switch (this.is_tiny_string) { 1 => this.remainder_buf[0..this.remainder_len], - 0 => @as([*]const u8, @ptrFromInt(std.mem.readIntNative(usize, this.remainder_buf[0..@sizeOf(usize)])))[0..std.mem.readIntNative(usize, this.remainder_buf[@sizeOf(usize) .. @sizeOf(usize) * 2])], + 0 => @as([*]const u8, @ptrFromInt(std.mem.readInt(usize, this.remainder_buf[0..@sizeOf(usize)], .little)))[0..std.mem.readInt(usize, this.remainder_buf[@sizeOf(usize) .. @sizeOf(usize) * 2], .little)], }; } @@ -464,8 +464,8 @@ pub const StringOrTinyString = struct { .is_tiny_string = 0, .remainder_len = 0, }; - std.mem.writeIntNative(usize, tiny.remainder_buf[0..@sizeOf(usize)], @intFromPtr(stringy.ptr)); - std.mem.writeIntNative(usize, tiny.remainder_buf[@sizeOf(usize) .. @sizeOf(usize) * 2], stringy.len); + std.mem.writeInt(usize, tiny.remainder_buf[0..@sizeOf(usize)], @intFromPtr(stringy.ptr), .little); + std.mem.writeInt(usize, tiny.remainder_buf[@sizeOf(usize) .. @sizeOf(usize) * 2], stringy.len, .little); return tiny; }, } @@ -490,8 +490,8 @@ pub const StringOrTinyString = struct { .is_tiny_string = 0, .remainder_len = 0, }; - std.mem.writeIntNative(usize, tiny.remainder_buf[0..@sizeOf(usize)], @intFromPtr(stringy.ptr)); - std.mem.writeIntNative(usize, tiny.remainder_buf[@sizeOf(usize) .. @sizeOf(usize) * 2], stringy.len); + std.mem.writeInt(usize, tiny.remainder_buf[0..@sizeOf(usize)], @intFromPtr(stringy.ptr), .little); + std.mem.writeInt(usize, tiny.remainder_buf[@sizeOf(usize) .. @sizeOf(usize) * 2], stringy.len, .little); return tiny; }, } diff --git a/src/url.zig b/src/url.zig index 8190b59ca3..0cce41a4e6 100644 --- a/src/url.zig +++ b/src/url.zig @@ -313,7 +313,7 @@ pub const URL = struct { url.pathname = "/"; } - while (url.pathname.len > 1 and @as(u16, @bitCast(url.pathname[0..2].*)) == comptime std.mem.readIntNative(u16, "//")) { + while (url.pathname.len > 1 and @as(u16, @bitCast(url.pathname[0..2].*)) == comptime std.mem.readInt(u16, "//", .little)) { url.pathname = url.pathname[1..]; } @@ -380,7 +380,7 @@ pub const URL = struct { '@' => { // we found a password, everything before this point in the slice is a password url.password = str[0..i]; - if (Environment.allow_assert) std.debug.assert(str[i..].len < 2 or std.mem.readIntNative(u16, str[i..][0..2]) != std.mem.readIntNative(u16, "//")); + if (Environment.allow_assert) std.debug.assert(str[i..].len < 2 or std.mem.readInt(u16, str[i..][0..2], .little) != std.mem.readInt(u16, "//", .little)); return i + 1; }, // if we reach a slash or "?", there's no password diff --git a/src/wyhash.zig b/src/wyhash.zig index acb1006821..7c4b67c9b6 100644 --- a/src/wyhash.zig +++ b/src/wyhash.zig @@ -15,7 +15,7 @@ const primes = [_]u64{ fn read_bytes(comptime bytes: u8, data: []const u8) u64 { const T = std.meta.Int(.unsigned, 8 * bytes); - return mem.readIntLittle(T, data[0..bytes]); + return mem.readInt(T, data[0..bytes], .little); } fn read_8bytes_swapped(data: []const u8) u64 { diff --git a/test/js/node/fs/fs.test.ts b/test/js/node/fs/fs.test.ts index 153c2736e1..63da5fc5ed 100644 --- a/test/js/node/fs/fs.test.ts +++ b/test/js/node/fs/fs.test.ts @@ -57,6 +57,13 @@ function mkdirForce(path: string) { if (!existsSync(path)) mkdirSync(path, { recursive: true }); } +it("Dirent.name setter", () => { + const dirent = Object.create(Dirent.prototype); + expect(dirent.name).toBeUndefined(); + dirent.name = "hello"; + expect(dirent.name).toBe("hello"); +}); + it("writeFileSync in append should not truncate the file", () => { const path = join(tmpdir(), "writeFileSync-should-not-append-" + (Date.now() * 10000).toString(16)); var str = ""; diff --git a/test/js/web/worker.test.ts b/test/js/web/worker.test.ts index 9f146456dc..3d9f481114 100644 --- a/test/js/web/worker.test.ts +++ b/test/js/web/worker.test.ts @@ -177,6 +177,7 @@ test("worker_threads with process.exit", done => { expect(event).toBe(2); } catch (e) { done(e); + return; } done(); }); @@ -199,7 +200,7 @@ test("worker_threads with process.exit (delay) and terminate", async () => { expect(code2).toBe(2); }); -test.skip("terminating forcefully properly interrupts", async () => { +test.todo("terminating forcefully properly interrupts", async () => { const worker2 = new wt.Worker(new URL("worker-fixture-while-true.js", import.meta.url).href, {}); await new Promise(done => { worker2.on("message", () => done()); diff --git a/test/transpiler/transpiler.test.js b/test/transpiler/transpiler.test.js index 0539d95ecc..7522980332 100644 --- a/test/transpiler/transpiler.test.js +++ b/test/transpiler/transpiler.test.js @@ -3223,4 +3223,26 @@ console.log(foo, array); it("scanImports on empty file does not segfault", () => { new Bun.Transpiler().scanImports(""); }); + + it("preserves exotic directives", () => { + expect( + new Bun.Transpiler().transformSync(`"use client"; +console.log("boop"); +`), + ).toBe( + `"use client"; +console.log("boop"); +`, + ); + }); + it("does not preserve use strict (for now)", () => { + expect( + new Bun.Transpiler().transformSync(`"use strict"; + console.log("boop"); + `), + ).toBe( + `console.log("boop"); +`, + ); + }); });