mirror of
https://github.com/oven-sh/bun
synced 2026-02-25 02:57:27 +01:00
Compare commits
86 Commits
claude/fix
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d29531527 | ||
|
|
9fbe6a5826 | ||
|
|
c0d97ebd88 | ||
|
|
0b580054a7 | ||
|
|
b817abe55e | ||
|
|
9256b3d777 | ||
|
|
6763fe5a8a | ||
|
|
7848648e09 | ||
|
|
379daff22d | ||
|
|
5b0db0191e | ||
|
|
9ef9ac1db1 | ||
|
|
f5d98191b7 | ||
|
|
83bca9bea8 | ||
|
|
7794cc866e | ||
|
|
70b354aa04 | ||
|
|
9d5a800c3d | ||
|
|
77ca318336 | ||
|
|
337a9f7f2b | ||
|
|
38f41dccdf | ||
|
|
883e43c371 | ||
|
|
cd17934207 | ||
|
|
f6d4ff6779 | ||
|
|
2c173529fa | ||
|
|
243fa45bec | ||
|
|
c19dcb3181 | ||
|
|
c57af9df38 | ||
|
|
3debd0a2d2 | ||
|
|
7afead629c | ||
|
|
9a72bbfae2 | ||
|
|
7a801fcf93 | ||
|
|
44541eb574 | ||
|
|
993be3f931 | ||
|
|
a68393926b | ||
|
|
e8a5f23385 | ||
|
|
16b3e7cde7 | ||
|
|
4c32f15339 | ||
|
|
635034ee33 | ||
|
|
3e792d0d2e | ||
|
|
b7d505b6c1 | ||
|
|
50e478dcdc | ||
|
|
e8f73601c0 | ||
|
|
ba6e84fecd | ||
|
|
e29e830a25 | ||
|
|
4c205486d6 | ||
|
|
099b5e430c | ||
|
|
746771d495 | ||
|
|
ae50b05b50 | ||
|
|
ead4a8c45d | ||
|
|
ce715b5a0f | ||
|
|
fa78d2b408 | ||
|
|
b7475d8768 | ||
|
|
4494170f74 | ||
|
|
9484218ba4 | ||
|
|
2a5e8ef38c | ||
|
|
a84f12b816 | ||
|
|
0f43ea9bec | ||
|
|
0889897a1c | ||
|
|
68f2ea4b95 | ||
|
|
d4ebfd9771 | ||
|
|
e3c25260ed | ||
|
|
1bded85718 | ||
|
|
cf6cdbbbad | ||
|
|
89d2b1cd0b | ||
|
|
2019a1b11d | ||
|
|
6c70ce2485 | ||
|
|
0e386c4168 | ||
|
|
e5cd034e9a | ||
|
|
45b9d1baba | ||
|
|
0ad562d3bd | ||
|
|
63a323a511 | ||
|
|
af76296637 | ||
|
|
d1047c2cf1 | ||
|
|
315e822866 | ||
|
|
7f498a2e07 | ||
|
|
5d4b1821f3 | ||
|
|
41de7a3bfb | ||
|
|
d23312d3f6 | ||
|
|
de8c754c6a | ||
|
|
27e1363a66 | ||
|
|
eba4da23e6 | ||
|
|
ddefa11070 | ||
|
|
35f8154319 | ||
|
|
9d68ec882a | ||
|
|
1337f5dba4 | ||
|
|
56b5be4ba4 | ||
|
|
6c119d608e |
@@ -1,5 +1,5 @@
|
||||
ARG LLVM_VERSION="19"
|
||||
ARG REPORTED_LLVM_VERSION="19.1.7"
|
||||
ARG LLVM_VERSION="21"
|
||||
ARG REPORTED_LLVM_VERSION="21.1.8"
|
||||
ARG OLD_BUN_VERSION="1.1.38"
|
||||
ARG BUILDKITE_AGENT_TAGS="queue=linux,os=linux,arch=${TARGETARCH}"
|
||||
|
||||
|
||||
@@ -109,12 +109,12 @@ const buildPlatforms = [
|
||||
{ os: "linux", arch: "x64", distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "x64", profile: "asan", distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.22" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.22" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.22" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.23" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.23" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.23" },
|
||||
{ os: "windows", arch: "x64", release: "2019" },
|
||||
{ os: "windows", arch: "x64", baseline: true, release: "2019" },
|
||||
// TODO: Enable when Windows ARM64 CI runners are ready
|
||||
// TODO: Re-enable when Windows ARM64 VS component installation is resolved on Buildkite runners
|
||||
// { os: "windows", arch: "aarch64", release: "2019" },
|
||||
];
|
||||
|
||||
@@ -133,9 +133,9 @@ const testPlatforms = [
|
||||
{ os: "linux", arch: "aarch64", distro: "ubuntu", release: "25.04", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", distro: "ubuntu", release: "25.04", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "25.04", tier: "latest" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.22", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.22", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.22", tier: "latest" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.23", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.23", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.23", tier: "latest" },
|
||||
{ os: "windows", arch: "x64", release: "2019", tier: "oldest" },
|
||||
{ os: "windows", arch: "x64", release: "2019", baseline: true, tier: "oldest" },
|
||||
// TODO: Enable when Windows ARM64 CI runners are ready
|
||||
@@ -304,6 +304,13 @@ function getCppAgent(platform, options) {
|
||||
};
|
||||
}
|
||||
|
||||
// Cross-compile Windows ARM64 from x64 runners
|
||||
if (os === "windows" && arch === "aarch64") {
|
||||
return getEc2Agent({ ...platform, arch: "x64" }, options, {
|
||||
instanceType: "c7i.4xlarge",
|
||||
});
|
||||
}
|
||||
|
||||
return getEc2Agent(platform, options, {
|
||||
instanceType: arch === "aarch64" ? "c8g.4xlarge" : "c7i.4xlarge",
|
||||
});
|
||||
@@ -326,8 +333,10 @@ function getLinkBunAgent(platform, options) {
|
||||
}
|
||||
|
||||
if (os === "windows") {
|
||||
return getEc2Agent(platform, options, {
|
||||
instanceType: arch === "aarch64" ? "r8g.large" : "r7i.large",
|
||||
// Cross-compile Windows ARM64 from x64 runners
|
||||
const agentPlatform = arch === "aarch64" ? { ...platform, arch: "x64" } : platform;
|
||||
return getEc2Agent(agentPlatform, options, {
|
||||
instanceType: "r7i.large",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -345,7 +354,7 @@ function getZigPlatform() {
|
||||
arch: "aarch64",
|
||||
abi: "musl",
|
||||
distro: "alpine",
|
||||
release: "3.22",
|
||||
release: "3.23",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -456,6 +465,17 @@ function getBuildCommand(target, options, label) {
|
||||
return `bun run build:${buildProfile}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra flags needed when cross-compiling Windows ARM64 from x64.
|
||||
* Applied to C++ and link steps (not Zig, which has its own toolchain handling).
|
||||
*/
|
||||
function getWindowsArm64CrossFlags(target) {
|
||||
if (target.os === "windows" && target.arch === "aarch64") {
|
||||
return " --toolchain windows-aarch64";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @param {PipelineOptions} options
|
||||
@@ -463,6 +483,8 @@ function getBuildCommand(target, options, label) {
|
||||
*/
|
||||
function getBuildCppStep(platform, options) {
|
||||
const command = getBuildCommand(platform, options);
|
||||
const crossFlags = getWindowsArm64CrossFlags(platform);
|
||||
|
||||
return {
|
||||
key: `${getTargetKey(platform)}-build-cpp`,
|
||||
label: `${getTargetLabel(platform)} - build-cpp`,
|
||||
@@ -476,7 +498,7 @@ function getBuildCppStep(platform, options) {
|
||||
// We used to build the C++ dependencies and bun in separate steps.
|
||||
// However, as long as the zig build takes longer than both sequentially,
|
||||
// it's cheaper to run them in the same step. Can be revisited in the future.
|
||||
command: [`${command} --target bun`, `${command} --target dependencies`],
|
||||
command: [`${command}${crossFlags} --target bun`, `${command}${crossFlags} --target dependencies`],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -533,7 +555,7 @@ function getLinkBunStep(platform, options) {
|
||||
ASAN_OPTIONS: "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=0",
|
||||
...getBuildEnv(platform, options),
|
||||
},
|
||||
command: `${getBuildCommand(platform, options, "build-bun")} --target bun`,
|
||||
command: `${getBuildCommand(platform, options, "build-bun")}${getWindowsArm64CrossFlags(platform)} --target bun`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1179,6 +1201,8 @@ async function getPipeline(options = {}) {
|
||||
buildImages || publishImages
|
||||
? [...buildPlatforms, ...testPlatforms]
|
||||
.filter(({ os }) => os !== "darwin")
|
||||
// Windows ARM64 cross-compiles from x64 runners, no separate image needed
|
||||
.filter(({ os, arch }) => !(os === "windows" && arch === "aarch64"))
|
||||
.map(platform => [getImageKey(platform), platform])
|
||||
: [],
|
||||
);
|
||||
|
||||
@@ -219,9 +219,8 @@ function create_release() {
|
||||
bun-windows-x64-profile.zip
|
||||
bun-windows-x64-baseline.zip
|
||||
bun-windows-x64-baseline-profile.zip
|
||||
# TODO: Enable when Windows ARM64 CI runners are ready
|
||||
# bun-windows-aarch64.zip
|
||||
# bun-windows-aarch64-profile.zip
|
||||
bun-windows-aarch64.zip
|
||||
bun-windows-aarch64-profile.zip
|
||||
)
|
||||
|
||||
function upload_artifact() {
|
||||
|
||||
4
.github/workflows/CLAUDE.md
vendored
4
.github/workflows/CLAUDE.md
vendored
@@ -33,8 +33,8 @@ The workflow runs all three formatters simultaneously:
|
||||
|
||||
#### 3. Tool Installation
|
||||
|
||||
##### Clang-format-19
|
||||
- Installs ONLY `clang-format-19` package (not the entire LLVM toolchain)
|
||||
##### Clang-format-21
|
||||
- Installs ONLY `clang-format-21` package (not the entire LLVM toolchain)
|
||||
- Uses `--no-install-recommends --no-install-suggests` to skip unnecessary packages
|
||||
- Quiet installation with `-qq` and `-o=Dpkg::Use-Pty=0`
|
||||
|
||||
|
||||
4
.github/workflows/format.yml
vendored
4
.github/workflows/format.yml
vendored
@@ -10,8 +10,8 @@ on:
|
||||
merge_group:
|
||||
env:
|
||||
BUN_VERSION: "1.3.2"
|
||||
LLVM_VERSION: "19.1.7"
|
||||
LLVM_VERSION_MAJOR: "19"
|
||||
LLVM_VERSION: "21.1.8"
|
||||
LLVM_VERSION_MAJOR: "21"
|
||||
|
||||
jobs:
|
||||
autofix:
|
||||
|
||||
@@ -35,7 +35,7 @@ $ sudo pacman -S base-devel cmake git go libiconv libtool make ninja pkg-config
|
||||
```
|
||||
|
||||
```bash#Fedora
|
||||
$ sudo dnf install cargo clang19 llvm19 lld19 cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
|
||||
$ sudo dnf install cargo clang21 llvm21 lld21 cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
|
||||
```
|
||||
|
||||
```bash#openSUSE Tumbleweed
|
||||
@@ -90,17 +90,17 @@ Our build scripts will automatically detect and use `ccache` if available. You c
|
||||
|
||||
## Install LLVM
|
||||
|
||||
Bun requires LLVM 19 (`clang` is part of LLVM). This version requirement is to match WebKit (precompiled), as mismatching versions will cause memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
|
||||
Bun requires LLVM 21.1.8 (`clang` is part of LLVM). This version is enforced by the build system — mismatching versions will cause memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
|
||||
|
||||
{% codetabs group="os" %}
|
||||
|
||||
```bash#macOS (Homebrew)
|
||||
$ brew install llvm@19
|
||||
$ brew install llvm@21
|
||||
```
|
||||
|
||||
```bash#Ubuntu/Debian
|
||||
$ # LLVM has an automatic installation script that is compatible with all versions of Ubuntu
|
||||
$ wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 19 all
|
||||
$ wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 21 all
|
||||
```
|
||||
|
||||
```bash#Arch
|
||||
@@ -112,17 +112,17 @@ $ sudo dnf install llvm clang lld-devel
|
||||
```
|
||||
|
||||
```bash#openSUSE Tumbleweed
|
||||
$ sudo zypper install clang19 lld19 llvm19
|
||||
$ sudo zypper install clang21 lld21 llvm21
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
If none of the above solutions apply, you will have to install it [manually](https://github.com/llvm/llvm-project/releases/tag/llvmorg-19.1.7).
|
||||
If none of the above solutions apply, you will have to install it [manually](https://github.com/llvm/llvm-project/releases/tag/llvmorg-21.1.8).
|
||||
|
||||
Make sure Clang/LLVM 19 is in your path:
|
||||
Make sure Clang/LLVM 21 is in your path:
|
||||
|
||||
```bash
|
||||
$ which clang-19
|
||||
$ which clang-21
|
||||
```
|
||||
|
||||
If not, run this to manually add it:
|
||||
@@ -131,13 +131,13 @@ If not, run this to manually add it:
|
||||
|
||||
```bash#macOS (Homebrew)
|
||||
# use fish_add_path if you're using fish
|
||||
# use path+="$(brew --prefix llvm@19)/bin" if you are using zsh
|
||||
$ export PATH="$(brew --prefix llvm@19)/bin:$PATH"
|
||||
# use path+="$(brew --prefix llvm@21)/bin" if you are using zsh
|
||||
$ export PATH="$(brew --prefix llvm@21)/bin:$PATH"
|
||||
```
|
||||
|
||||
```bash#Arch
|
||||
# use fish_add_path if you're using fish
|
||||
$ export PATH="$PATH:/usr/lib/llvm19/bin"
|
||||
$ export PATH="$PATH:/usr/lib/llvm21/bin"
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
@@ -259,18 +259,13 @@ $ git clone https://github.com/oven-sh/WebKit vendor/WebKit
|
||||
# Check out the commit hash specified in `set(WEBKIT_VERSION <commit_hash>)` in cmake/tools/SetupWebKit.cmake
|
||||
$ git -C vendor/WebKit checkout <commit_hash>
|
||||
|
||||
# Make a debug build of JSC. This will output build artifacts in ./vendor/WebKit/WebKitBuild/Debug
|
||||
# Optionally, you can use `bun run jsc:build` for a release build
|
||||
$ bun run jsc:build:debug && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# After an initial run of `make jsc-debug`, you can rebuild JSC with:
|
||||
$ cmake --build vendor/WebKit/WebKitBuild/Debug --target jsc && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# Build bun with the local JSC build
|
||||
# Build bun with the local JSC build — this automatically configures and builds JSC
|
||||
$ bun run build:local
|
||||
```
|
||||
|
||||
Using `bun run build:local` will build Bun in the `./build/debug-local` directory (instead of `./build/debug`), you'll have to change a couple of places to use this new directory:
|
||||
`bun run build:local` handles everything: configuring JSC, building JSC, and building Bun. On subsequent runs, JSC will incrementally rebuild if any WebKit sources changed. `ninja -Cbuild/debug-local` also works after the first build, and will build Bun+JSC.
|
||||
|
||||
The build output goes to `./build/debug-local` (instead of `./build/debug`), so you'll need to update a couple of places:
|
||||
|
||||
- The first line in [`src/js/builtins.d.ts`](/src/js/builtins.d.ts)
|
||||
- The `CompilationDatabase` line in [`.clangd` config](/.clangd) should be `CompilationDatabase: build/debug-local`
|
||||
@@ -281,7 +276,7 @@ Note that the WebKit folder, including build artifacts, is 8GB+ in size.
|
||||
|
||||
If you are using a JSC debug build and using VScode, make sure to run the `C/C++: Select a Configuration` command to configure intellisense to find the debug headers.
|
||||
|
||||
Note that if you change make changes to our [WebKit fork](https://github.com/oven-sh/WebKit), you will also have to change [`SetupWebKit.cmake`](/cmake/tools/SetupWebKit.cmake) to point to the commit hash.
|
||||
Note that if you make changes to our [WebKit fork](https://github.com/oven-sh/WebKit), you will also have to change [`SetupWebKit.cmake`](/cmake/tools/SetupWebKit.cmake) to point to the commit hash.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -304,7 +299,7 @@ The issue may manifest when initially running `bun setup` as Clang being unable
|
||||
```
|
||||
The C++ compiler
|
||||
|
||||
"/usr/bin/clang++-19"
|
||||
"/usr/bin/clang++-21"
|
||||
|
||||
is not able to compile a simple test program.
|
||||
```
|
||||
|
||||
110
bench/snippets/abort-signal.mjs
Normal file
110
bench/snippets/abort-signal.mjs
Normal file
@@ -0,0 +1,110 @@
|
||||
// Benchmark for AbortController/AbortSignal abort() performance
|
||||
// Tests the optimization of skipping Event creation when no listeners are registered
|
||||
|
||||
import { bench, group, run } from "../runner.mjs";
|
||||
|
||||
// Warmup: ensure JIT compilation
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
}
|
||||
|
||||
group("AbortController.abort()", () => {
|
||||
bench("no listener", () => {
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
});
|
||||
|
||||
bench("with addEventListener", () => {
|
||||
const controller = new AbortController();
|
||||
controller.signal.addEventListener("abort", () => {});
|
||||
controller.abort();
|
||||
});
|
||||
|
||||
bench("with onabort property", () => {
|
||||
const controller = new AbortController();
|
||||
controller.signal.onabort = () => {};
|
||||
controller.abort();
|
||||
});
|
||||
|
||||
bench("with 3 listeners", () => {
|
||||
const controller = new AbortController();
|
||||
controller.signal.addEventListener("abort", () => {});
|
||||
controller.signal.addEventListener("abort", () => {});
|
||||
controller.signal.addEventListener("abort", () => {});
|
||||
controller.abort();
|
||||
});
|
||||
});
|
||||
|
||||
group("AbortSignal static methods", () => {
|
||||
bench("AbortSignal.abort() - pre-aborted", () => {
|
||||
const signal = AbortSignal.abort();
|
||||
// Signal is already aborted, no event dispatch needed
|
||||
});
|
||||
|
||||
bench("AbortSignal.any([]) - empty array", () => {
|
||||
const signal = AbortSignal.any([]);
|
||||
});
|
||||
|
||||
bench("AbortSignal.any([signal, signal]) - 2 signals", () => {
|
||||
const a = new AbortController();
|
||||
const b = new AbortController();
|
||||
const signal = AbortSignal.any([a.signal, b.signal]);
|
||||
});
|
||||
|
||||
bench("AbortSignal.any() then abort - no listener", () => {
|
||||
const a = new AbortController();
|
||||
const b = new AbortController();
|
||||
const signal = AbortSignal.any([a.signal, b.signal]);
|
||||
a.abort();
|
||||
});
|
||||
|
||||
bench("AbortSignal.any() then abort - with listener", () => {
|
||||
const a = new AbortController();
|
||||
const b = new AbortController();
|
||||
const signal = AbortSignal.any([a.signal, b.signal]);
|
||||
signal.addEventListener("abort", () => {});
|
||||
a.abort();
|
||||
});
|
||||
});
|
||||
|
||||
group("AbortController creation only", () => {
|
||||
bench("new AbortController()", () => {
|
||||
const controller = new AbortController();
|
||||
});
|
||||
|
||||
bench("new AbortController() + access signal", () => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
});
|
||||
});
|
||||
|
||||
group("AbortSignal.timeout()", () => {
|
||||
// Note: These don't actually wait for timeout, just measure creation overhead
|
||||
bench("AbortSignal.timeout(1000) creation", () => {
|
||||
const signal = AbortSignal.timeout(1000);
|
||||
});
|
||||
|
||||
bench("AbortSignal.timeout(0) creation", () => {
|
||||
const signal = AbortSignal.timeout(0);
|
||||
});
|
||||
});
|
||||
|
||||
group("abort with reason", () => {
|
||||
bench("abort() with no reason", () => {
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
});
|
||||
|
||||
bench("abort() with string reason", () => {
|
||||
const controller = new AbortController();
|
||||
controller.abort("cancelled");
|
||||
});
|
||||
|
||||
bench("abort() with Error reason", () => {
|
||||
const controller = new AbortController();
|
||||
controller.abort(new Error("cancelled"));
|
||||
});
|
||||
});
|
||||
|
||||
await run();
|
||||
38
bench/snippets/buffer-slice.mjs
Normal file
38
bench/snippets/buffer-slice.mjs
Normal file
@@ -0,0 +1,38 @@
|
||||
// @runtime bun,node
|
||||
import { bench, group, run } from "../runner.mjs";
|
||||
|
||||
const small = Buffer.alloc(64, 0x42);
|
||||
const medium = Buffer.alloc(1024, 0x42);
|
||||
const large = Buffer.alloc(1024 * 1024, 0x42);
|
||||
|
||||
group("slice - no args", () => {
|
||||
bench("Buffer(64).slice()", () => small.slice());
|
||||
bench("Buffer(1024).slice()", () => medium.slice());
|
||||
bench("Buffer(1M).slice()", () => large.slice());
|
||||
});
|
||||
|
||||
group("slice - one int arg", () => {
|
||||
bench("Buffer(64).slice(10)", () => small.slice(10));
|
||||
bench("Buffer(1024).slice(10)", () => medium.slice(10));
|
||||
bench("Buffer(1M).slice(1024)", () => large.slice(1024));
|
||||
});
|
||||
|
||||
group("slice - two int args", () => {
|
||||
bench("Buffer(64).slice(10, 50)", () => small.slice(10, 50));
|
||||
bench("Buffer(1024).slice(10, 100)", () => medium.slice(10, 100));
|
||||
bench("Buffer(1M).slice(1024, 4096)", () => large.slice(1024, 4096));
|
||||
});
|
||||
|
||||
group("slice - negative args", () => {
|
||||
bench("Buffer(64).slice(-10)", () => small.slice(-10));
|
||||
bench("Buffer(1024).slice(-100, -10)", () => medium.slice(-100, -10));
|
||||
bench("Buffer(1M).slice(-4096, -1024)", () => large.slice(-4096, -1024));
|
||||
});
|
||||
|
||||
group("subarray - two int args", () => {
|
||||
bench("Buffer(64).subarray(10, 50)", () => small.subarray(10, 50));
|
||||
bench("Buffer(1024).subarray(10, 100)", () => medium.subarray(10, 100));
|
||||
bench("Buffer(1M).subarray(1024, 4096)", () => large.subarray(1024, 4096));
|
||||
});
|
||||
|
||||
await run();
|
||||
@@ -105,6 +105,10 @@ summary(() => {
|
||||
bench(`small (${small.length} chars) - Bun.markdown.render`, () => {
|
||||
return Bun.markdown.render(small, renderCallbacks);
|
||||
});
|
||||
|
||||
bench(`small (${small.length} chars) - Bun.markdown.react`, () => {
|
||||
return Bun.markdown.react(small);
|
||||
});
|
||||
}
|
||||
|
||||
bench(`small (${small.length} chars) - marked`, () => {
|
||||
@@ -125,6 +129,10 @@ summary(() => {
|
||||
bench(`medium (${medium.length} chars) - Bun.markdown.render`, () => {
|
||||
return Bun.markdown.render(medium, renderCallbacks);
|
||||
});
|
||||
|
||||
bench(`medium (${medium.length} chars) - Bun.markdown.react`, () => {
|
||||
return Bun.markdown.react(medium);
|
||||
});
|
||||
}
|
||||
|
||||
bench(`medium (${medium.length} chars) - marked`, () => {
|
||||
@@ -145,6 +153,10 @@ summary(() => {
|
||||
bench(`large (${large.length} chars) - Bun.markdown.render`, () => {
|
||||
return Bun.markdown.render(large, renderCallbacks);
|
||||
});
|
||||
|
||||
bench(`large (${large.length} chars) - Bun.markdown.react`, () => {
|
||||
return Bun.markdown.react(large);
|
||||
});
|
||||
}
|
||||
|
||||
bench(`large (${large.length} chars) - marked`, () => {
|
||||
|
||||
18
bench/snippets/path-parse.mjs
Normal file
18
bench/snippets/path-parse.mjs
Normal file
@@ -0,0 +1,18 @@
|
||||
import { posix, win32 } from "path";
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
const paths = ["/home/user/dir/file.txt", "/home/user/dir/", "file.txt", "/root", ""];
|
||||
|
||||
paths.forEach(p => {
|
||||
bench(`posix.parse(${JSON.stringify(p)})`, () => {
|
||||
globalThis.abc = posix.parse(p);
|
||||
});
|
||||
});
|
||||
|
||||
paths.forEach(p => {
|
||||
bench(`win32.parse(${JSON.stringify(p)})`, () => {
|
||||
globalThis.abc = win32.parse(p);
|
||||
});
|
||||
});
|
||||
|
||||
await run();
|
||||
@@ -33,7 +33,30 @@ var testArray = [
|
||||
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
bench("structuredClone(array)", () => structuredClone(testArray));
|
||||
bench("structuredClone(nested array)", () => structuredClone(testArray));
|
||||
bench("structuredClone(123)", () => structuredClone(123));
|
||||
bench("structuredClone({a: 123})", () => structuredClone({ a: 123 }));
|
||||
|
||||
// Array fast path targets
|
||||
var numbersSmall = Array.from({ length: 10 }, (_, i) => i);
|
||||
var numbersMedium = Array.from({ length: 100 }, (_, i) => i);
|
||||
var numbersLarge = Array.from({ length: 1000 }, (_, i) => i);
|
||||
var stringsSmall = Array.from({ length: 10 }, (_, i) => `item-${i}`);
|
||||
var stringsMedium = Array.from({ length: 100 }, (_, i) => `item-${i}`);
|
||||
var mixed = [1, "hello", true, null, undefined, 3.14, "world", false, 42, "test"];
|
||||
|
||||
bench("structuredClone([10 numbers])", () => structuredClone(numbersSmall));
|
||||
bench("structuredClone([100 numbers])", () => structuredClone(numbersMedium));
|
||||
bench("structuredClone([1000 numbers])", () => structuredClone(numbersLarge));
|
||||
bench("structuredClone([10 strings])", () => structuredClone(stringsSmall));
|
||||
bench("structuredClone([100 strings])", () => structuredClone(stringsMedium));
|
||||
bench("structuredClone([10 mixed])", () => structuredClone(mixed));
|
||||
|
||||
// Array of objects (DenseArray fast path target)
|
||||
var objectsSmall = Array.from({ length: 10 }, (_, i) => ({ id: i, name: `item-${i}`, active: true }));
|
||||
var objectsMedium = Array.from({ length: 100 }, (_, i) => ({ id: i, name: `item-${i}`, active: true }));
|
||||
|
||||
bench("structuredClone([10 objects])", () => structuredClone(objectsSmall));
|
||||
bench("structuredClone([100 objects])", () => structuredClone(objectsMedium));
|
||||
|
||||
await run();
|
||||
|
||||
@@ -7,6 +7,13 @@ register_repository(
|
||||
4f4f5ef8ebc6e23cbf393428f0ab1b526773f7ac
|
||||
)
|
||||
|
||||
set(BORINGSSL_CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF)
|
||||
|
||||
# Disable ASM on Windows ARM64 to avoid mixing non-ARM object files into ARM64 libs
|
||||
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
|
||||
list(APPEND BORINGSSL_CMAKE_ARGS -DOPENSSL_NO_ASM=1)
|
||||
endif()
|
||||
|
||||
register_cmake_command(
|
||||
TARGET
|
||||
boringssl
|
||||
@@ -15,7 +22,7 @@ register_cmake_command(
|
||||
ssl
|
||||
decrepit
|
||||
ARGS
|
||||
-DBUILD_SHARED_LIBS=OFF
|
||||
${BORINGSSL_CMAKE_ARGS}
|
||||
INCLUDES
|
||||
include
|
||||
)
|
||||
|
||||
@@ -1016,6 +1016,7 @@ if(NOT WIN32)
|
||||
-Wno-unused-function
|
||||
-Wno-c++23-lambda-attributes
|
||||
-Wno-nullability-completeness
|
||||
-Wno-character-conversion
|
||||
-Werror
|
||||
)
|
||||
else()
|
||||
@@ -1033,6 +1034,7 @@ if(NOT WIN32)
|
||||
-Werror=sometimes-uninitialized
|
||||
-Wno-c++23-lambda-attributes
|
||||
-Wno-nullability-completeness
|
||||
-Wno-character-conversion
|
||||
-Werror
|
||||
)
|
||||
|
||||
@@ -1061,6 +1063,7 @@ else()
|
||||
-Wno-inconsistent-dllimport
|
||||
-Wno-incompatible-pointer-types
|
||||
-Wno-deprecated-declarations
|
||||
-Wno-character-conversion
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -1136,6 +1139,15 @@ if(LINUX)
|
||||
-Wl,--wrap=pow
|
||||
-Wl,--wrap=powf
|
||||
)
|
||||
|
||||
# Disable LTO for workaround-missing-symbols.cpp to prevent LLD 21 from emitting
|
||||
# glibc versioned symbol names (e.g. exp@GLIBC_2.17) from .symver directives into
|
||||
# the .lto_discard assembler directive, which fails to parse the '@' character.
|
||||
if(ENABLE_LTO)
|
||||
set_source_files_properties(${CWD}/src/bun.js/bindings/workaround-missing-symbols.cpp
|
||||
PROPERTIES COMPILE_OPTIONS "-fno-lto"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT ABI STREQUAL "musl")
|
||||
@@ -1273,13 +1285,18 @@ else()
|
||||
${WEBKIT_LIB_PATH}/libWTF.a
|
||||
${WEBKIT_LIB_PATH}/libJavaScriptCore.a
|
||||
)
|
||||
if(NOT APPLE OR EXISTS ${WEBKIT_LIB_PATH}/libbmalloc.a)
|
||||
if(WEBKIT_LOCAL OR NOT APPLE OR EXISTS ${WEBKIT_LIB_PATH}/libbmalloc.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libbmalloc.a)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include_directories(${WEBKIT_INCLUDE_PATH})
|
||||
|
||||
# When building with a local WebKit, ensure JSC is built before compiling Bun's C++ sources.
|
||||
if(WEBKIT_LOCAL AND TARGET jsc)
|
||||
add_dependencies(${bun} jsc)
|
||||
endif()
|
||||
|
||||
# Include the generated dependency versions header
|
||||
include_directories(${CMAKE_BINARY_DIR})
|
||||
|
||||
@@ -1324,9 +1341,14 @@ if(LINUX)
|
||||
target_link_libraries(${bun} PUBLIC libatomic.so)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicudata.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicui18n.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicuuc.a)
|
||||
if(WEBKIT_LOCAL)
|
||||
find_package(ICU REQUIRED COMPONENTS data i18n uc)
|
||||
target_link_libraries(${bun} PRIVATE ICU::data ICU::i18n ICU::uc)
|
||||
else()
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicudata.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicui18n.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicuuc.a)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
@@ -1435,6 +1457,8 @@ if(NOT BUN_CPP_ONLY)
|
||||
# ==856230==See https://github.com/google/sanitizers/issues/856 for possible workarounds.
|
||||
# the linked issue refers to very old kernels but this still happens to us on modern ones.
|
||||
# disabling ASLR to run the binary works around it
|
||||
# Skip post-build test/features when cross-compiling (can't run the target binary on the host)
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
set(TEST_BUN_COMMAND_BASE ${BUILD_PATH}/${bunExe} --revision)
|
||||
set(TEST_BUN_COMMAND_ENV_WRAP
|
||||
${CMAKE_COMMAND} -E env BUN_DEBUG_QUIET_LOGS=1)
|
||||
@@ -1483,6 +1507,7 @@ if(NOT BUN_CPP_ONLY)
|
||||
${BUILD_PATH}/features.json
|
||||
)
|
||||
endif()
|
||||
endif() # NOT CMAKE_CROSSCOMPILING
|
||||
|
||||
if(CMAKE_HOST_APPLE AND bunStrip)
|
||||
register_command(
|
||||
@@ -1529,7 +1554,10 @@ if(NOT BUN_CPP_ONLY)
|
||||
string(REPLACE bun ${bunTriplet} bunPath ${bun})
|
||||
endif()
|
||||
|
||||
set(bunFiles ${bunExe} features.json)
|
||||
set(bunFiles ${bunExe})
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
list(APPEND bunFiles features.json)
|
||||
endif()
|
||||
if(WIN32)
|
||||
list(APPEND bunFiles ${bun}.pdb)
|
||||
elseif(APPLE)
|
||||
|
||||
@@ -26,6 +26,12 @@ if(RELEASE)
|
||||
list(APPEND LOLHTML_BUILD_ARGS --release)
|
||||
endif()
|
||||
|
||||
# Cross-compilation: tell cargo to target ARM64
|
||||
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
|
||||
list(APPEND LOLHTML_BUILD_ARGS --target aarch64-pc-windows-msvc)
|
||||
set(LOLHTML_LIBRARY ${LOLHTML_BUILD_PATH}/aarch64-pc-windows-msvc/${LOLHTML_BUILD_TYPE}/${CMAKE_STATIC_LIBRARY_PREFIX}lolhtml${CMAKE_STATIC_LIBRARY_SUFFIX})
|
||||
endif()
|
||||
|
||||
# Windows requires unwind tables, apparently.
|
||||
if (NOT WIN32)
|
||||
# The encoded escape sequences are intentional. They're how you delimit multiple arguments in a single environment variable.
|
||||
@@ -51,11 +57,18 @@ if(WIN32)
|
||||
if(MSVC_VERSIONS)
|
||||
list(GET MSVC_VERSIONS -1 MSVC_LATEST) # Get the latest version
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64")
|
||||
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/HostARM64/arm64/link.exe")
|
||||
# Use Hostx64/arm64 for cross-compilation from x64, fall back to native
|
||||
if(EXISTS "${MSVC_LATEST}/bin/Hostx64/arm64/link.exe")
|
||||
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/Hostx64/arm64/link.exe")
|
||||
else()
|
||||
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/HostARM64/arm64/link.exe")
|
||||
endif()
|
||||
set(CARGO_LINKER_VAR "CARGO_TARGET_AARCH64_PC_WINDOWS_MSVC_LINKER")
|
||||
set(MSVC_LIB_ARCH "arm64")
|
||||
else()
|
||||
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/Hostx64/x64/link.exe")
|
||||
set(CARGO_LINKER_VAR "CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER")
|
||||
set(MSVC_LIB_ARCH "x64")
|
||||
endif()
|
||||
if(EXISTS "${MSVC_LINK_PATH}")
|
||||
list(APPEND LOLHTML_ENV "${CARGO_LINKER_VAR}=${MSVC_LINK_PATH}")
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
oven-sh/mimalloc
|
||||
COMMIT
|
||||
ffa38ab8ac914f9eb7af75c1f8ad457643dc14f2
|
||||
1beadf9651a7bfdec6b5367c380ecc3fe1c40d1a
|
||||
)
|
||||
|
||||
set(MIMALLOC_CMAKE_ARGS
|
||||
@@ -14,7 +14,7 @@ set(MIMALLOC_CMAKE_ARGS
|
||||
-DMI_BUILD_TESTS=OFF
|
||||
-DMI_USE_CXX=ON
|
||||
-DMI_SKIP_COLLECT_ON_EXIT=ON
|
||||
|
||||
|
||||
# ```
|
||||
# ❯ mimalloc_allow_large_os_pages=0 BUN_PORT=3004 mem bun http-hello.js
|
||||
# Started development server: http://localhost:3004
|
||||
@@ -51,7 +51,7 @@ if(ENABLE_ASAN)
|
||||
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_DEBUG_UBSAN=ON)
|
||||
elseif(APPLE OR LINUX)
|
||||
if(APPLE)
|
||||
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OVERRIDE=OFF)
|
||||
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OVERRIDE=OFF)
|
||||
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_ZONE=OFF)
|
||||
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_INTERPOSE=OFF)
|
||||
else()
|
||||
@@ -87,9 +87,9 @@ endif()
|
||||
|
||||
if(WIN32)
|
||||
if(DEBUG)
|
||||
set(MIMALLOC_LIBRARY mimalloc-debug)
|
||||
set(MIMALLOC_LIBRARY mimalloc-static-debug)
|
||||
else()
|
||||
set(MIMALLOC_LIBRARY mimalloc)
|
||||
set(MIMALLOC_LIBRARY mimalloc-static)
|
||||
endif()
|
||||
elseif(DEBUG)
|
||||
if (ENABLE_ASAN)
|
||||
|
||||
@@ -3,18 +3,35 @@ set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
set(CMAKE_CROSSCOMPILING ON)
|
||||
|
||||
# Force ARM64 architecture ID - this is what CMake uses to determine /machine: flag
|
||||
set(MSVC_C_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
|
||||
set(MSVC_CXX_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
|
||||
# The rest only applies when building on Windows (C++ and link steps).
|
||||
# The Zig step runs on Linux and only needs CMAKE_SYSTEM_NAME/PROCESSOR above.
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
|
||||
|
||||
# CMake 4.0+ policy CMP0197 controls how MSVC machine type flags are handled
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0197 NEW CACHE INTERNAL "")
|
||||
# Ensure clang/clang-cl targets Windows ARM64 (otherwise ARM64-specific flags like
|
||||
# -march=armv8-a are rejected as x86-only).
|
||||
set(CMAKE_C_COMPILER_TARGET aarch64-pc-windows-msvc CACHE STRING "" FORCE)
|
||||
set(CMAKE_CXX_COMPILER_TARGET aarch64-pc-windows-msvc CACHE STRING "" FORCE)
|
||||
|
||||
# Clear any inherited static linker flags that might have wrong machine types
|
||||
set(CMAKE_STATIC_LINKER_FLAGS "" CACHE STRING "" FORCE)
|
||||
# ARM64 has lock-free atomics (highway's FindAtomics check can't run ARM64 test binary on x64)
|
||||
set(ATOMICS_LOCK_FREE_INSTRUCTIONS TRUE CACHE BOOL "" FORCE)
|
||||
set(HAVE_CXX_ATOMICS_WITHOUT_LIB TRUE CACHE BOOL "" FORCE)
|
||||
set(HAVE_CXX_ATOMICS64_WITHOUT_LIB TRUE CACHE BOOL "" FORCE)
|
||||
|
||||
# Use wrapper script for llvm-lib that strips /machine:x64 flags
|
||||
# This works around CMake 4.1.0 bug where both ARM64 and x64 machine flags are added
|
||||
get_filename_component(_TOOLCHAIN_DIR "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY)
|
||||
set(CMAKE_AR "${_TOOLCHAIN_DIR}/scripts/llvm-lib-wrapper.bat" CACHE FILEPATH "" FORCE)
|
||||
# Force ARM64 architecture ID - this is what CMake uses to determine /machine: flag
|
||||
set(MSVC_C_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
|
||||
set(MSVC_CXX_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
|
||||
|
||||
# CMake 4.0+ policy CMP0197 controls how MSVC machine type flags are handled
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0197 NEW CACHE INTERNAL "")
|
||||
|
||||
# Clear any inherited static linker flags that might have wrong machine types
|
||||
set(CMAKE_STATIC_LINKER_FLAGS "" CACHE STRING "" FORCE)
|
||||
|
||||
# Use wrapper script for llvm-lib that strips /machine:x64 flags
|
||||
# This works around CMake 4.1.0 bug where both ARM64 and x64 machine flags are added
|
||||
get_filename_component(_TOOLCHAIN_DIR "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY)
|
||||
set(CMAKE_AR "${_TOOLCHAIN_DIR}/scripts/llvm-lib-wrapper.bat" CACHE FILEPATH "" FORCE)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -12,13 +12,7 @@ if(NOT ENABLE_LLVM)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# LLVM 21 is required for Windows ARM64 (first version with ARM64 Windows builds)
|
||||
# Other platforms use LLVM 19.1.7
|
||||
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
|
||||
set(DEFAULT_LLVM_VERSION "21.1.8")
|
||||
else()
|
||||
set(DEFAULT_LLVM_VERSION "19.1.7")
|
||||
endif()
|
||||
set(DEFAULT_LLVM_VERSION "21.1.8")
|
||||
|
||||
optionx(LLVM_VERSION STRING "The version of LLVM to use" DEFAULT ${DEFAULT_LLVM_VERSION})
|
||||
|
||||
@@ -27,6 +21,8 @@ if(USE_LLVM_VERSION)
|
||||
set(LLVM_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
set(LLVM_VERSION_MINOR ${CMAKE_MATCH_2})
|
||||
set(LLVM_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||
# Accept any LLVM version within the same major.minor range (e.g. Alpine 3.23 ships 21.1.2)
|
||||
set(LLVM_VERSION_RANGE ">=${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.0 <${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.99")
|
||||
endif()
|
||||
|
||||
set(LLVM_PATHS)
|
||||
@@ -54,6 +50,11 @@ if(APPLE)
|
||||
list(APPEND LLVM_PATHS ${HOMEBREW_PREFIX}/opt/llvm/bin)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# Prefer standalone LLVM over VS-bundled (standalone supports cross-compilation)
|
||||
list(APPEND LLVM_PATHS "C:/Program Files/LLVM/bin")
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
list(APPEND LLVM_PATHS /usr/lib/llvm/bin)
|
||||
|
||||
@@ -78,14 +79,12 @@ macro(find_llvm_command variable command)
|
||||
)
|
||||
endif()
|
||||
|
||||
math(EXPR LLVM_VERSION_NEXT_MAJOR "${LLVM_VERSION_MAJOR} + 1")
|
||||
|
||||
find_command(
|
||||
VARIABLE ${variable}
|
||||
VERSION_VARIABLE LLVM_VERSION
|
||||
COMMAND ${commands}
|
||||
PATHS ${LLVM_PATHS}
|
||||
VERSION ">=${LLVM_VERSION_MAJOR}.1.0 <${LLVM_VERSION_NEXT_MAJOR}.0.0"
|
||||
VERSION "${LLVM_VERSION_RANGE}"
|
||||
)
|
||||
list(APPEND CMAKE_ARGS -D${variable}=${${variable}})
|
||||
endmacro()
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
option(WEBKIT_VERSION "The version of WebKit to use")
|
||||
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
|
||||
option(WEBKIT_BUILD_TYPE "The build type for local WebKit (defaults to CMAKE_BUILD_TYPE)")
|
||||
|
||||
if(NOT WEBKIT_VERSION)
|
||||
set(WEBKIT_VERSION 515344bc5d65aa2d4f9ff277b5fb944f0e051dcd)
|
||||
set(WEBKIT_VERSION 8af7958ff0e2a4787569edf64641a1ae7cfe074a)
|
||||
endif()
|
||||
|
||||
# Use preview build URL for Windows ARM64 until the fix is merged to main
|
||||
@@ -15,7 +16,10 @@ string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)
|
||||
string(SUBSTRING ${WEBKIT_VERSION} 0 8 WEBKIT_VERSION_SHORT)
|
||||
|
||||
if(WEBKIT_LOCAL)
|
||||
set(DEFAULT_WEBKIT_PATH ${VENDOR_PATH}/WebKit/WebKitBuild/${CMAKE_BUILD_TYPE})
|
||||
if(NOT WEBKIT_BUILD_TYPE)
|
||||
set(WEBKIT_BUILD_TYPE ${CMAKE_BUILD_TYPE})
|
||||
endif()
|
||||
set(DEFAULT_WEBKIT_PATH ${VENDOR_PATH}/WebKit/WebKitBuild/${WEBKIT_BUILD_TYPE})
|
||||
else()
|
||||
set(DEFAULT_WEBKIT_PATH ${CACHE_PATH}/webkit-${WEBKIT_VERSION_PREFIX})
|
||||
endif()
|
||||
@@ -30,35 +34,153 @@ set(WEBKIT_INCLUDE_PATH ${WEBKIT_PATH}/include)
|
||||
set(WEBKIT_LIB_PATH ${WEBKIT_PATH}/lib)
|
||||
|
||||
if(WEBKIT_LOCAL)
|
||||
if(EXISTS ${WEBKIT_PATH}/cmakeconfig.h)
|
||||
# You may need to run:
|
||||
# make jsc-compile-debug jsc-copy-headers
|
||||
include_directories(
|
||||
${WEBKIT_PATH}
|
||||
${WEBKIT_PATH}/JavaScriptCore/Headers
|
||||
${WEBKIT_PATH}/JavaScriptCore/Headers/JavaScriptCore
|
||||
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders
|
||||
${WEBKIT_PATH}/bmalloc/Headers
|
||||
${WEBKIT_PATH}/WTF/Headers
|
||||
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders/JavaScriptCore
|
||||
${WEBKIT_PATH}/JavaScriptCore/DerivedSources/inspector
|
||||
)
|
||||
set(WEBKIT_SOURCE_DIR ${VENDOR_PATH}/WebKit)
|
||||
|
||||
# On Windows, add ICU include path from vcpkg
|
||||
if(WIN32)
|
||||
# Auto-detect vcpkg triplet
|
||||
set(VCPKG_ARM64_PATH ${VENDOR_PATH}/WebKit/vcpkg_installed/arm64-windows-static)
|
||||
set(VCPKG_X64_PATH ${VENDOR_PATH}/WebKit/vcpkg_installed/x64-windows-static)
|
||||
if(EXISTS ${VCPKG_ARM64_PATH})
|
||||
set(VCPKG_ICU_PATH ${VCPKG_ARM64_PATH})
|
||||
if(WIN32)
|
||||
# --- Build ICU from source (Windows only) ---
|
||||
# On macOS, ICU is found automatically (Homebrew icu4c for headers, system for libs).
|
||||
# On Linux, ICU is found automatically from system packages (e.g. libicu-dev).
|
||||
# On Windows, there is no system ICU, so we build it from source.
|
||||
set(ICU_LOCAL_ROOT ${VENDOR_PATH}/WebKit/WebKitBuild/icu)
|
||||
if(NOT EXISTS ${ICU_LOCAL_ROOT}/lib/sicudt.lib)
|
||||
message(STATUS "Building ICU from source...")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
|
||||
set(ICU_PLATFORM "ARM64")
|
||||
else()
|
||||
set(VCPKG_ICU_PATH ${VCPKG_X64_PATH})
|
||||
set(ICU_PLATFORM "x64")
|
||||
endif()
|
||||
if(EXISTS ${VCPKG_ICU_PATH}/include)
|
||||
include_directories(${VCPKG_ICU_PATH}/include)
|
||||
message(STATUS "Using ICU from vcpkg: ${VCPKG_ICU_PATH}/include")
|
||||
execute_process(
|
||||
COMMAND powershell -ExecutionPolicy Bypass -File
|
||||
${WEBKIT_SOURCE_DIR}/build-icu.ps1
|
||||
-Platform ${ICU_PLATFORM}
|
||||
-BuildType ${WEBKIT_BUILD_TYPE}
|
||||
-OutputDir ${ICU_LOCAL_ROOT}
|
||||
RESULT_VARIABLE ICU_BUILD_RESULT
|
||||
)
|
||||
if(NOT ICU_BUILD_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to build ICU (exit code: ${ICU_BUILD_RESULT}).")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Copy ICU libs to WEBKIT_LIB_PATH with the names BuildBun.cmake expects.
|
||||
# Prebuilt WebKit uses 's' prefix (static) and 'd' suffix (debug).
|
||||
file(MAKE_DIRECTORY ${WEBKIT_LIB_PATH})
|
||||
if(WEBKIT_BUILD_TYPE STREQUAL "Debug")
|
||||
set(ICU_SUFFIX "d")
|
||||
else()
|
||||
set(ICU_SUFFIX "")
|
||||
endif()
|
||||
file(COPY_FILE ${ICU_LOCAL_ROOT}/lib/sicudt.lib ${WEBKIT_LIB_PATH}/sicudt${ICU_SUFFIX}.lib ONLY_IF_DIFFERENT)
|
||||
file(COPY_FILE ${ICU_LOCAL_ROOT}/lib/icuin.lib ${WEBKIT_LIB_PATH}/sicuin${ICU_SUFFIX}.lib ONLY_IF_DIFFERENT)
|
||||
file(COPY_FILE ${ICU_LOCAL_ROOT}/lib/icuuc.lib ${WEBKIT_LIB_PATH}/sicuuc${ICU_SUFFIX}.lib ONLY_IF_DIFFERENT)
|
||||
endif()
|
||||
|
||||
# --- Configure JSC ---
|
||||
message(STATUS "Configuring JSC from local WebKit source at ${WEBKIT_SOURCE_DIR}...")
|
||||
|
||||
set(JSC_CMAKE_ARGS
|
||||
-S ${WEBKIT_SOURCE_DIR}
|
||||
-B ${WEBKIT_PATH}
|
||||
-G ${CMAKE_GENERATOR}
|
||||
-DPORT=JSCOnly
|
||||
-DENABLE_STATIC_JSC=ON
|
||||
-DUSE_THIN_ARCHIVES=OFF
|
||||
-DENABLE_FTL_JIT=ON
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||
-DUSE_BUN_JSC_ADDITIONS=ON
|
||||
-DUSE_BUN_EVENT_LOOP=ON
|
||||
-DENABLE_BUN_SKIP_FAILING_ASSERTIONS=ON
|
||||
-DALLOW_LINE_AND_COLUMN_NUMBER_IN_BUILTINS=ON
|
||||
-DCMAKE_BUILD_TYPE=${WEBKIT_BUILD_TYPE}
|
||||
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
|
||||
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
|
||||
-DENABLE_REMOTE_INSPECTOR=ON
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
# ICU paths and Windows-specific compiler/linker settings
|
||||
list(APPEND JSC_CMAKE_ARGS
|
||||
-DICU_ROOT=${ICU_LOCAL_ROOT}
|
||||
-DICU_LIBRARY=${ICU_LOCAL_ROOT}/lib
|
||||
-DICU_INCLUDE_DIR=${ICU_LOCAL_ROOT}/include
|
||||
-DCMAKE_LINKER=lld-link
|
||||
)
|
||||
# Static CRT and U_STATIC_IMPLEMENTATION
|
||||
if(WEBKIT_BUILD_TYPE STREQUAL "Debug")
|
||||
set(JSC_MSVC_RUNTIME "MultiThreadedDebug")
|
||||
else()
|
||||
set(JSC_MSVC_RUNTIME "MultiThreaded")
|
||||
endif()
|
||||
list(APPEND JSC_CMAKE_ARGS
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=${JSC_MSVC_RUNTIME}
|
||||
"-DCMAKE_C_FLAGS=/DU_STATIC_IMPLEMENTATION"
|
||||
"-DCMAKE_CXX_FLAGS=/DU_STATIC_IMPLEMENTATION /clang:-fno-c++-static-destructors"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_ASAN)
|
||||
list(APPEND JSC_CMAKE_ARGS -DENABLE_SANITIZERS=address)
|
||||
endif()
|
||||
|
||||
# Pass through ccache if available
|
||||
if(CMAKE_C_COMPILER_LAUNCHER)
|
||||
list(APPEND JSC_CMAKE_ARGS -DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER})
|
||||
endif()
|
||||
if(CMAKE_CXX_COMPILER_LAUNCHER)
|
||||
list(APPEND JSC_CMAKE_ARGS -DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER})
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} ${JSC_CMAKE_ARGS}
|
||||
RESULT_VARIABLE JSC_CONFIGURE_RESULT
|
||||
)
|
||||
if(NOT JSC_CONFIGURE_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to configure JSC (exit code: ${JSC_CONFIGURE_RESULT}). "
|
||||
"Check the output above for errors.")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(JSC_BYPRODUCTS
|
||||
${WEBKIT_LIB_PATH}/JavaScriptCore.lib
|
||||
${WEBKIT_LIB_PATH}/WTF.lib
|
||||
${WEBKIT_LIB_PATH}/bmalloc.lib
|
||||
)
|
||||
else()
|
||||
set(JSC_BYPRODUCTS
|
||||
${WEBKIT_LIB_PATH}/libJavaScriptCore.a
|
||||
${WEBKIT_LIB_PATH}/libWTF.a
|
||||
${WEBKIT_LIB_PATH}/libbmalloc.a
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_custom_target(jsc ALL
|
||||
COMMAND ${CMAKE_COMMAND} --build ${WEBKIT_PATH} --config ${WEBKIT_BUILD_TYPE} --target jsc
|
||||
BYPRODUCTS ${JSC_BYPRODUCTS}
|
||||
COMMENT "Building JSC (${WEBKIT_PATH})"
|
||||
)
|
||||
else()
|
||||
add_custom_target(jsc ALL
|
||||
COMMAND ${CMAKE_COMMAND} --build ${WEBKIT_PATH} --config ${WEBKIT_BUILD_TYPE} --target jsc
|
||||
BYPRODUCTS ${JSC_BYPRODUCTS}
|
||||
COMMENT "Building JSC (${WEBKIT_PATH})"
|
||||
USES_TERMINAL
|
||||
)
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
${WEBKIT_PATH}
|
||||
${WEBKIT_PATH}/JavaScriptCore/Headers
|
||||
${WEBKIT_PATH}/JavaScriptCore/Headers/JavaScriptCore
|
||||
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders
|
||||
${WEBKIT_PATH}/bmalloc/Headers
|
||||
${WEBKIT_PATH}/WTF/Headers
|
||||
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders/JavaScriptCore
|
||||
)
|
||||
|
||||
# On Windows, add ICU headers from the local ICU build
|
||||
if(WIN32)
|
||||
include_directories(${ICU_LOCAL_ROOT}/include)
|
||||
endif()
|
||||
|
||||
# After this point, only prebuilt WebKit is supported
|
||||
|
||||
@@ -20,12 +20,7 @@
|
||||
"completionType": "javascript_files"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun run ./index.js",
|
||||
"bun run ./index.tsx",
|
||||
"bun run dev",
|
||||
"bun run lint"
|
||||
],
|
||||
"examples": ["bun run ./index.js", "bun run ./index.tsx", "bun run dev", "bun run lint"],
|
||||
"usage": "Usage: bun run [flags] <file or script>",
|
||||
"documentationUrl": "https://bun.com/docs/cli/run",
|
||||
"dynamicCompletions": {
|
||||
@@ -63,6 +58,14 @@
|
||||
"required": false,
|
||||
"multiple": false
|
||||
},
|
||||
{
|
||||
"name": "retry",
|
||||
"description": "Default retry count for all tests, overridden by per-test { retry: N }",
|
||||
"hasValue": true,
|
||||
"valueType": "val",
|
||||
"required": false,
|
||||
"multiple": false
|
||||
},
|
||||
{
|
||||
"name": "only",
|
||||
"description": "Only run tests that are marked with \"test.only()\"",
|
||||
@@ -152,11 +155,7 @@
|
||||
"completionType": "test_files"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun test",
|
||||
"bun test foo bar",
|
||||
"bun test --test-name-pattern baz"
|
||||
],
|
||||
"examples": ["bun test", "bun test foo bar", "bun test --test-name-pattern baz"],
|
||||
"usage": "Usage: bun test [flags] [<patterns>]",
|
||||
"documentationUrl": "https://bun.com/docs/cli/test",
|
||||
"dynamicCompletions": {
|
||||
@@ -199,9 +198,7 @@
|
||||
"examples": [],
|
||||
"usage": "Usage: bunx [flags] <package><@version> [flags and arguments for the package]",
|
||||
"dynamicCompletions": {},
|
||||
"aliases": [
|
||||
"bunx"
|
||||
]
|
||||
"aliases": ["bunx"]
|
||||
},
|
||||
"repl": {
|
||||
"name": "repl",
|
||||
@@ -232,10 +229,7 @@
|
||||
"completionType": "script"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun exec \"echo hi\"",
|
||||
"bun exec \"echo \\\"hey friends\\\"!\""
|
||||
],
|
||||
"examples": ["bun exec \"echo hi\"", "bun exec \"echo \\\"hey friends\\\"!\""],
|
||||
"usage": "Usage: bun exec <script>",
|
||||
"dynamicCompletions": {}
|
||||
},
|
||||
@@ -545,14 +539,9 @@
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun install",
|
||||
"bun install --production"
|
||||
],
|
||||
"examples": ["bun install", "bun install --production"],
|
||||
"usage": "Usage: bun install [flags] <name>@<version>",
|
||||
"aliases": [
|
||||
"i"
|
||||
],
|
||||
"aliases": ["i"],
|
||||
"documentationUrl": "https://bun.com/docs/cli/install.",
|
||||
"dynamicCompletions": {}
|
||||
},
|
||||
@@ -864,9 +853,7 @@
|
||||
"bun add --peer esbuild"
|
||||
],
|
||||
"usage": "Usage: bun add [flags] <package><@version>",
|
||||
"aliases": [
|
||||
"a"
|
||||
],
|
||||
"aliases": ["a"],
|
||||
"documentationUrl": "https://bun.com/docs/cli/add.",
|
||||
"dynamicCompletions": {
|
||||
"packages": true
|
||||
@@ -1126,13 +1113,9 @@
|
||||
"completionType": "installed_package"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun remove ts-node"
|
||||
],
|
||||
"examples": ["bun remove ts-node"],
|
||||
"usage": "Usage: bun remove [flags] [<packages>]",
|
||||
"aliases": [
|
||||
"rm"
|
||||
],
|
||||
"aliases": ["rm"],
|
||||
"documentationUrl": "https://bun.com/docs/cli/remove.",
|
||||
"dynamicCompletions": {
|
||||
"packages": true
|
||||
@@ -1422,12 +1405,7 @@
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun update",
|
||||
"bun update --latest",
|
||||
"bun update -i",
|
||||
"bun update zod jquery@3"
|
||||
],
|
||||
"examples": ["bun update", "bun update --latest", "bun update -i", "bun update zod jquery@3"],
|
||||
"usage": "Usage: bun update [flags] <name>@<version>",
|
||||
"documentationUrl": "https://bun.com/docs/cli/update.",
|
||||
"dynamicCompletions": {}
|
||||
@@ -1452,10 +1430,7 @@
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun audit",
|
||||
"bun audit --json"
|
||||
],
|
||||
"examples": ["bun audit", "bun audit --json"],
|
||||
"usage": "Usage: bun audit [flags]",
|
||||
"documentationUrl": "https://bun.com/docs/install/audit.",
|
||||
"dynamicCompletions": {}
|
||||
@@ -1997,10 +1972,7 @@
|
||||
"completionType": "package"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun link",
|
||||
"bun link <package>"
|
||||
],
|
||||
"examples": ["bun link", "bun link <package>"],
|
||||
"usage": "Usage: bun link [flags] [<packages>]",
|
||||
"documentationUrl": "https://bun.com/docs/cli/link.",
|
||||
"dynamicCompletions": {}
|
||||
@@ -2252,9 +2224,7 @@
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun unlink"
|
||||
],
|
||||
"examples": ["bun unlink"],
|
||||
"usage": "Usage: bun unlink [flags]",
|
||||
"documentationUrl": "https://bun.com/docs/cli/unlink.",
|
||||
"dynamicCompletions": {}
|
||||
@@ -3248,11 +3218,7 @@
|
||||
"completionType": "package"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun info react",
|
||||
"bun info react@18.0.0",
|
||||
"bun info react version --json"
|
||||
],
|
||||
"examples": ["bun info react", "bun info react@18.0.0", "bun info react version --json"],
|
||||
"usage": "Usage: bun info [flags] <package>[@<version>]",
|
||||
"documentationUrl": "https://bun.com/docs/cli/info.",
|
||||
"dynamicCompletions": {}
|
||||
@@ -3603,12 +3569,7 @@
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun init",
|
||||
"bun init --yes",
|
||||
"bun init --react",
|
||||
"bun init --react=tailwind my-app"
|
||||
],
|
||||
"examples": ["bun init", "bun init --yes", "bun init --react", "bun init --react=tailwind my-app"],
|
||||
"usage": "Usage: bun init [flags] [<folder>]",
|
||||
"dynamicCompletions": {}
|
||||
},
|
||||
@@ -3621,9 +3582,7 @@
|
||||
"usage": "Usage:",
|
||||
"documentationUrl": "https://bun.com/docs/cli/bun-create",
|
||||
"dynamicCompletions": {},
|
||||
"aliases": [
|
||||
"c"
|
||||
]
|
||||
"aliases": ["c"]
|
||||
},
|
||||
"upgrade": {
|
||||
"name": "upgrade",
|
||||
@@ -3637,10 +3596,7 @@
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"examples": [
|
||||
"bun upgrade",
|
||||
"bun upgrade --stable"
|
||||
],
|
||||
"examples": ["bun upgrade", "bun upgrade --stable"],
|
||||
"usage": "Usage: bun upgrade [flags]",
|
||||
"documentationUrl": "https://bun.com/docs/installation#upgrading",
|
||||
"dynamicCompletions": {}
|
||||
@@ -3743,9 +3699,7 @@
|
||||
"description": "Configure auto-install behavior. One of \"auto\" (default, auto-installs when no node_modules), \"fallback\" (missing packages only), \"force\" (always).",
|
||||
"hasValue": true,
|
||||
"valueType": "val",
|
||||
"choices": [
|
||||
"auto"
|
||||
],
|
||||
"choices": ["auto"],
|
||||
"required": false,
|
||||
"multiple": false
|
||||
},
|
||||
@@ -3827,12 +3781,7 @@
|
||||
"description": "Set the default order of DNS lookup results. Valid orders: verbatim (default), ipv4first, ipv6first",
|
||||
"hasValue": true,
|
||||
"valueType": "val",
|
||||
"choices": [
|
||||
"verbatim",
|
||||
"(default)",
|
||||
"ipv4first",
|
||||
"ipv6first"
|
||||
],
|
||||
"choices": ["verbatim", "(default)", "ipv4first", "ipv6first"],
|
||||
"required": false,
|
||||
"multiple": false
|
||||
},
|
||||
@@ -3898,9 +3847,7 @@
|
||||
"description": "One of \"strict\", \"throw\", \"warn\", \"none\", or \"warn-with-error-code\"",
|
||||
"hasValue": true,
|
||||
"valueType": "val",
|
||||
"choices": [
|
||||
"strict"
|
||||
],
|
||||
"choices": ["strict"],
|
||||
"required": false,
|
||||
"multiple": false
|
||||
},
|
||||
@@ -4023,4 +3970,4 @@
|
||||
"files": "bun getcompletes j"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,6 +665,7 @@ _bun_test_completion() {
|
||||
'--timeout[Set the per-test timeout in milliseconds, default is 5000.]:timeout' \
|
||||
'--update-snapshots[Update snapshot files]' \
|
||||
'--rerun-each[Re-run each test file <NUMBER> times, helps catch certain bugs]:rerun' \
|
||||
'--retry[Default retry count for all tests]:retry' \
|
||||
'--todo[Include tests that are marked with "test.todo()"]' \
|
||||
'--coverage[Generate a coverage profile]' \
|
||||
'--bail[Exit the test suite after <NUMBER> failures. If you do not specify a number, it defaults to 1.]:bail' \
|
||||
|
||||
@@ -7,9 +7,9 @@ Bytecode caching is a build-time optimization that dramatically improves applica
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic usage
|
||||
### Basic usage (CommonJS)
|
||||
|
||||
Enable bytecode caching with the `--bytecode` flag:
|
||||
Enable bytecode caching with the `--bytecode` flag. Without `--format`, this defaults to CommonJS:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./index.ts --target=bun --bytecode --outdir=./dist
|
||||
@@ -17,7 +17,7 @@ bun build ./index.ts --target=bun --bytecode --outdir=./dist
|
||||
|
||||
This generates two files:
|
||||
|
||||
- `dist/index.js` - Your bundled JavaScript
|
||||
- `dist/index.js` - Your bundled JavaScript (CommonJS)
|
||||
- `dist/index.jsc` - The bytecode cache file
|
||||
|
||||
At runtime, Bun automatically detects and uses the `.jsc` file:
|
||||
@@ -28,14 +28,24 @@ bun ./dist/index.js # Automatically uses index.jsc
|
||||
|
||||
### With standalone executables
|
||||
|
||||
When creating executables with `--compile`, bytecode is embedded into the binary:
|
||||
When creating executables with `--compile`, bytecode is embedded into the binary. Both ESM and CommonJS formats are supported:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
# ESM (requires --compile)
|
||||
bun build ./cli.ts --compile --bytecode --format=esm --outfile=mycli
|
||||
|
||||
# CommonJS (works with or without --compile)
|
||||
bun build ./cli.ts --compile --bytecode --outfile=mycli
|
||||
```
|
||||
|
||||
The resulting executable contains both the code and bytecode, giving you maximum performance in a single file.
|
||||
|
||||
### ESM bytecode
|
||||
|
||||
ESM bytecode requires `--compile` because Bun embeds module metadata (import/export information) in the compiled binary. This metadata allows the JavaScript engine to skip parsing entirely at runtime.
|
||||
|
||||
Without `--compile`, ESM bytecode would still require parsing the source to analyze module dependencies—defeating the purpose of bytecode caching.
|
||||
|
||||
### Combining with other optimizations
|
||||
|
||||
Bytecode works great with minification and source maps:
|
||||
@@ -90,35 +100,9 @@ Larger applications benefit more because they have more code to parse.
|
||||
- ❌ **Code that runs once**
|
||||
- ❌ **Development builds**
|
||||
- ❌ **Size-constrained environments**
|
||||
- ❌ **Code with top-level await** (not supported)
|
||||
|
||||
## Limitations
|
||||
|
||||
### CommonJS only
|
||||
|
||||
Bytecode caching currently works with CommonJS output format. Bun's bundler automatically converts most ESM code to CommonJS, but **top-level await** is the exception:
|
||||
|
||||
```js
|
||||
// This prevents bytecode caching
|
||||
const data = await fetch("https://api.example.com");
|
||||
export default data;
|
||||
```
|
||||
|
||||
**Why**: Top-level await requires async module evaluation, which can't be represented in CommonJS. The module graph becomes asynchronous, and the CommonJS wrapper function model breaks down.
|
||||
|
||||
**Workaround**: Move async initialization into a function:
|
||||
|
||||
```js
|
||||
async function init() {
|
||||
const data = await fetch("https://api.example.com");
|
||||
return data;
|
||||
}
|
||||
|
||||
export default init;
|
||||
```
|
||||
|
||||
Now the module exports a function that the consumer can await when needed.
|
||||
|
||||
### Version compatibility
|
||||
|
||||
Bytecode is **not portable across Bun versions**. The bytecode format is tied to JavaScriptCore's internal representation, which changes between versions.
|
||||
@@ -236,8 +220,6 @@ It's normal for it it to log a cache miss multiple times since Bun doesn't curre
|
||||
- Compressing `.jsc` files for network transfer (gzip/brotli)
|
||||
- Evaluating if the startup performance gain is worth the size increase
|
||||
|
||||
**Top-level await**: Not supported. Refactor to use async initialization functions.
|
||||
|
||||
## What is bytecode?
|
||||
|
||||
When you run JavaScript, the JavaScript engine doesn't execute your source code directly. Instead, it goes through several steps:
|
||||
|
||||
@@ -198,13 +198,16 @@ const myPlugin: BunPlugin = {
|
||||
};
|
||||
```
|
||||
|
||||
The builder object provides some methods for hooking into parts of the bundling process. Bun implements `onResolve` and `onLoad`; it does not yet implement the esbuild hooks `onStart`, `onEnd`, and `onDispose`, and `resolve` utilities. `initialOptions` is partially implemented, being read-only and only having a subset of esbuild's options; use `config` (same thing but with Bun's `BuildConfig` format) instead.
|
||||
The builder object provides some methods for hooking into parts of the bundling process. Bun implements `onStart`, `onEnd`, `onResolve`, and `onLoad`. It does not yet implement the esbuild hooks `onDispose` and `resolve`. `initialOptions` is partially implemented, being read-only and only having a subset of esbuild's options; use `config` (same thing but with Bun's `BuildConfig` format) instead.
|
||||
|
||||
```ts title="myPlugin.ts" icon="/icons/typescript.svg"
|
||||
import type { BunPlugin } from "bun";
|
||||
const myPlugin: BunPlugin = {
|
||||
name: "my-plugin",
|
||||
setup(builder) {
|
||||
builder.onStart(() => {
|
||||
/* called when the bundle starts */
|
||||
});
|
||||
builder.onResolve(
|
||||
{
|
||||
/* onResolve.options */
|
||||
@@ -225,6 +228,9 @@ const myPlugin: BunPlugin = {
|
||||
};
|
||||
},
|
||||
);
|
||||
builder.onEnd(result => {
|
||||
/* called when the bundle is complete */
|
||||
});
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
@@ -322,10 +322,7 @@ Using bytecode compilation, `tsc` starts 2x faster:
|
||||
|
||||
Bytecode compilation moves parsing overhead for large input files from runtime to bundle time. Your app starts faster, in exchange for making the `bun build` command a little slower. It doesn't obscure source code.
|
||||
|
||||
<Warning>
|
||||
**Experimental:** Bytecode compilation is an experimental feature. Only `cjs` format is supported (which means no
|
||||
top-level-await). Let us know if you run into any issues!
|
||||
</Warning>
|
||||
<Note>Bytecode compilation supports both `cjs` and `esm` formats when used with `--compile`.</Note>
|
||||
|
||||
### What do these flags do?
|
||||
|
||||
@@ -1187,7 +1184,8 @@ Currently, the `--compile` flag can only accept a single entrypoint at a time an
|
||||
|
||||
- `--outdir` — use `outfile` instead (except when using with `--splitting`).
|
||||
- `--public-path`
|
||||
- `--target=node` or `--target=browser`
|
||||
- `--target=node`
|
||||
- `--target=browser` (without HTML entrypoints — see [Standalone HTML](/bundler/standalone-html) for `--compile --target=browser` with `.html` files)
|
||||
- `--no-bundle` - we always bundle everything into the executable.
|
||||
|
||||
---
|
||||
|
||||
@@ -481,6 +481,16 @@ All paths are resolved relative to your HTML file, making it easy to organize yo
|
||||
|
||||
This is a small wrapper around Bun's support for HTML imports in JavaScript.
|
||||
|
||||
## Standalone HTML
|
||||
|
||||
You can bundle your entire frontend into a **single self-contained `.html` file** with no external dependencies using `--compile --target=browser`. All JavaScript, CSS, and images are inlined directly into the HTML.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile --target=browser ./index.html --outdir=dist
|
||||
```
|
||||
|
||||
Learn more in the [Standalone HTML docs](/bundler/standalone-html).
|
||||
|
||||
## Adding a backend to your frontend
|
||||
|
||||
To add a backend to your frontend, you can use the "routes" option in `Bun.serve`.
|
||||
|
||||
@@ -1508,22 +1508,43 @@ BuildArtifact (entry-point) {
|
||||
|
||||
## Bytecode
|
||||
|
||||
The `bytecode: boolean` option can be used to generate bytecode for any JavaScript/TypeScript entrypoints. This can greatly improve startup times for large applications. Only supported for `"cjs"` format, only supports `"target": "bun"` and dependent on a matching version of Bun. This adds a corresponding `.jsc` file for each entrypoint.
|
||||
The `bytecode: boolean` option can be used to generate bytecode for any JavaScript/TypeScript entrypoints. This can greatly improve startup times for large applications. Requires `"target": "bun"` and is dependent on a matching version of Bun.
|
||||
|
||||
- **CommonJS**: Works with or without `compile: true`. Generates a `.jsc` file alongside each entrypoint.
|
||||
- **ESM**: Requires `compile: true`. Bytecode and module metadata are embedded in the standalone executable.
|
||||
|
||||
Without an explicit `format`, bytecode defaults to CommonJS.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="JavaScript">
|
||||
```ts title="build.ts" icon="/icons/typescript.svg"
|
||||
// CommonJS bytecode (generates .jsc files)
|
||||
await Bun.build({
|
||||
entrypoints: ["./index.tsx"],
|
||||
outdir: "./out",
|
||||
bytecode: true,
|
||||
})
|
||||
|
||||
// ESM bytecode (requires compile)
|
||||
await Bun.build({
|
||||
entrypoints: ["./index.tsx"],
|
||||
outfile: "./mycli",
|
||||
bytecode: true,
|
||||
format: "esm",
|
||||
compile: true,
|
||||
})
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="CLI">
|
||||
```bash terminal icon="terminal"
|
||||
# CommonJS bytecode
|
||||
bun build ./index.tsx --outdir ./out --bytecode
|
||||
|
||||
# ESM bytecode (requires --compile)
|
||||
bun build ./index.tsx --outfile ./mycli --bytecode --format=esm --compile
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1690,7 +1711,10 @@ interface BuildConfig {
|
||||
* start times, but will make the final output larger and slightly increase
|
||||
* memory usage.
|
||||
*
|
||||
* Bytecode is currently only supported for CommonJS (`format: "cjs"`).
|
||||
* - CommonJS: works with or without `compile: true`
|
||||
* - ESM: requires `compile: true`
|
||||
*
|
||||
* Without an explicit `format`, defaults to CommonJS.
|
||||
*
|
||||
* Must be `target: "bun"`
|
||||
* @default false
|
||||
|
||||
@@ -15,6 +15,7 @@ Plugins can register callbacks to be run at various points in the lifecycle of a
|
||||
- `onResolve()`: Run before a module is resolved
|
||||
- `onLoad()`: Run before a module is loaded
|
||||
- `onBeforeParse()`: Run zero-copy native addons in the parser thread before a file is parsed
|
||||
- `onEnd()`: Run after the bundle is complete
|
||||
|
||||
## Reference
|
||||
|
||||
@@ -39,6 +40,7 @@ type PluginBuilder = {
|
||||
exports?: Record<string, any>;
|
||||
},
|
||||
) => void;
|
||||
onEnd(callback: (result: BuildOutput) => void | Promise<void>): void;
|
||||
config: BuildConfig;
|
||||
};
|
||||
|
||||
@@ -423,3 +425,53 @@ This lifecycle callback is run immediately before a file is parsed by Bun's bund
|
||||
As input, it receives the file's contents and can optionally return new source code.
|
||||
|
||||
<Info>This callback can be called from any thread and so the napi module implementation must be thread-safe.</Info>
|
||||
|
||||
### onEnd
|
||||
|
||||
```ts
|
||||
onEnd(callback: (result: BuildOutput) => void | Promise<void>): void;
|
||||
```
|
||||
|
||||
Registers a callback to be run after the bundle is complete. The callback receives the [`BuildOutput`](/docs/bundler#outputs) object containing the build results, including output files and any build messages.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
plugins: [
|
||||
{
|
||||
name: "onEnd example",
|
||||
setup(build) {
|
||||
build.onEnd(result => {
|
||||
console.log(`Build completed with ${result.outputs.length} files`);
|
||||
for (const log of result.logs) {
|
||||
console.log(log);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
The callback can return a `Promise`. The build output promise from `Bun.build()` will not resolve until all `onEnd()` callbacks have completed.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
plugins: [
|
||||
{
|
||||
name: "Upload to S3",
|
||||
setup(build) {
|
||||
build.onEnd(async result => {
|
||||
if (!result.success) return;
|
||||
for (const output of result.outputs) {
|
||||
await uploadToS3(output);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
314
docs/bundler/standalone-html.mdx
Normal file
314
docs/bundler/standalone-html.mdx
Normal file
@@ -0,0 +1,314 @@
|
||||
---
|
||||
title: Standalone HTML
|
||||
description: Bundle a single-page app into a single self-contained .html file with no external dependencies
|
||||
---
|
||||
|
||||
Bun can bundle your entire frontend into a **single `.html` file** with zero external dependencies. JavaScript, TypeScript, JSX, CSS, images, fonts, videos, WASM — everything gets inlined into one file.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile --target=browser ./index.html --outdir=dist
|
||||
```
|
||||
|
||||
The output is a completely self-contained HTML document. No relative paths. No external files. No server required. Just one `.html` file that works anywhere a browser can open it.
|
||||
|
||||
## One file. Upload anywhere. It just works.
|
||||
|
||||
The output is a single `.html` file you can put anywhere:
|
||||
|
||||
- **Upload it to S3** or any static file host — no directory structure to maintain, just one file
|
||||
- **Double-click it from your desktop** — it opens in the browser and works offline, no localhost server needed
|
||||
- **Embed it in your webview** — No need to deal with relative files
|
||||
- **Insert it in an `<iframe>`** — embed interactive content in another page with a single file URL
|
||||
- **Serve it from anywhere** — any HTTP server, CDN, or file share. One file, zero configuration.
|
||||
|
||||
There's nothing to install, no `node_modules` to deploy, no build artifacts to coordinate, no relative paths to think about. The entire app — framework code, stylesheets, images, everything — lives in that one file.
|
||||
|
||||
## Truly one file
|
||||
|
||||
Normally, distributing a web page means managing a folder of assets — the HTML, the JavaScript bundles, the CSS files, the images. Move the HTML without the rest and everything breaks. Browsers have tried to solve this before: Safari's `.webarchive` and `.mhtml` are supposed to save a page as a single file, but in practice they unpack into a folder of loose files on your computer — defeating the purpose.
|
||||
|
||||
Standalone HTML is different. The output is a plain `.html` file. Not an archive. Not a folder. One file, with everything inside it. Every image, every font, every line of CSS and JavaScript is embedded directly in the HTML using standard `<style>` tags, `<script>` tags, and `data:` URIs. Any browser can open it. Any server can host it. Any file host can store it.
|
||||
|
||||
This makes it practical to distribute web pages the same way you'd distribute a PDF — as a single file you can move, copy, upload, or share without worrying about broken paths or missing assets.
|
||||
|
||||
## Quick start
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```html index.html icon="file-code"
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="./app.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```tsx app.tsx icon="/icons/typescript.svg"
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
function App() {
|
||||
return <h1>Hello from a single HTML file!</h1>;
|
||||
}
|
||||
|
||||
createRoot(document.getElementById("root")!).render(<App />);
|
||||
```
|
||||
|
||||
```css styles.css icon="file-code"
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: system-ui, sans-serif;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile --target=browser ./index.html --outdir=dist
|
||||
```
|
||||
|
||||
Open `dist/index.html` — the React app works with no server.
|
||||
|
||||
## Everything gets inlined
|
||||
|
||||
Bun inlines every local asset it finds in your HTML. If it has a relative path, it gets embedded into the output file. This isn't limited to images and stylesheets — it works with any file type.
|
||||
|
||||
### What gets inlined
|
||||
|
||||
| In your source | In the output |
|
||||
| ------------------------------------------------ | ------------------------------------------------------------------------ |
|
||||
| `<script src="./app.tsx">` | `<script type="module">...bundled code...</script>` |
|
||||
| `<link rel="stylesheet" href="./styles.css">` | `<style>...bundled CSS...</style>` |
|
||||
| `<img src="./logo.png">` | `<img src="data:image/png;base64,...">` |
|
||||
| `<img src="./icon.svg">` | `<img src="data:image/svg+xml;base64,...">` |
|
||||
| `<video src="./demo.mp4">` | `<video src="data:video/mp4;base64,...">` |
|
||||
| `<audio src="./click.wav">` | `<audio src="data:audio/wav;base64,...">` |
|
||||
| `<source src="./clip.webm">` | `<source src="data:video/webm;base64,...">` |
|
||||
| `<video poster="./thumb.jpg">` | `<video poster="data:image/jpeg;base64,...">` |
|
||||
| `<link rel="icon" href="./favicon.ico">` | `<link rel="icon" href="data:image/x-icon;base64,...">` |
|
||||
| `<link rel="manifest" href="./app.webmanifest">` | `<link rel="manifest" href="data:application/manifest+json;base64,...">` |
|
||||
| CSS `url("./bg.png")` | CSS `url(data:image/png;base64,...)` |
|
||||
| CSS `@import "./reset.css"` | Flattened into the `<style>` tag |
|
||||
| CSS `url("./font.woff2")` | CSS `url(data:font/woff2;base64,...)` |
|
||||
| JS `import "./styles.css"` | Merged into the `<style>` tag |
|
||||
|
||||
Images, fonts, WASM binaries, videos, audio files, SVGs — any file referenced by a relative path gets base64-encoded into a `data:` URI and embedded directly in the HTML. The MIME type is automatically detected from the file extension.
|
||||
|
||||
External URLs (like CDN links or absolute URLs) are left untouched.
|
||||
|
||||
## Using with React
|
||||
|
||||
React apps work out of the box. Bun handles JSX transpilation and npm package resolution automatically.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun install react react-dom
|
||||
```
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```html index.html icon="file-code"
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>My App</title>
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="./app.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```tsx app.tsx icon="/icons/typescript.svg"
|
||||
import React, { useState } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { Counter } from "./components/Counter.tsx";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<main>
|
||||
<h1>Single-file React App</h1>
|
||||
<Counter />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
createRoot(document.getElementById("root")!).render(<App />);
|
||||
```
|
||||
|
||||
```tsx components/Counter.tsx icon="/icons/typescript.svg"
|
||||
import React, { useState } from "react";
|
||||
|
||||
export function Counter() {
|
||||
const [count, setCount] = useState(0);
|
||||
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile --target=browser ./index.html --outdir=dist
|
||||
```
|
||||
|
||||
All of React, your components, and your CSS are bundled into `dist/index.html`. Upload that one file anywhere and it works.
|
||||
|
||||
## Using with Tailwind CSS
|
||||
|
||||
Install the plugin and reference Tailwind in your HTML or CSS:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun install --dev bun-plugin-tailwind
|
||||
```
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```html index.html icon="file-code"
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="tailwindcss" />
|
||||
</head>
|
||||
<body class="bg-gray-100 flex items-center justify-center min-h-screen">
|
||||
<div id="root"></div>
|
||||
<script src="./app.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```tsx app.tsx icon="/icons/typescript.svg"
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow-lg p-8 max-w-md">
|
||||
<h1 className="text-2xl font-bold text-gray-800">Hello Tailwind</h1>
|
||||
<p className="text-gray-600 mt-2">This is a single HTML file.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
createRoot(document.getElementById("root")!).render(<App />);
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
Build with the plugin using the JavaScript API:
|
||||
|
||||
```ts build.ts icon="/icons/typescript.svg"
|
||||
await Bun.build({
|
||||
entrypoints: ["./index.html"],
|
||||
compile: true,
|
||||
target: "browser",
|
||||
outdir: "./dist",
|
||||
plugins: [require("bun-plugin-tailwind")],
|
||||
});
|
||||
```
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun run build.ts
|
||||
```
|
||||
|
||||
The generated Tailwind CSS is inlined directly into the HTML file as a `<style>` tag.
|
||||
|
||||
## How it works
|
||||
|
||||
When you pass `--compile --target=browser` with an HTML entrypoint, Bun:
|
||||
|
||||
1. Parses the HTML and discovers all `<script>`, `<link>`, `<img>`, `<video>`, `<audio>`, `<source>`, and other asset references
|
||||
2. Bundles all JavaScript/TypeScript/JSX into a single module
|
||||
3. Bundles all CSS (including `@import` chains and CSS imported from JS) into a single stylesheet
|
||||
4. Converts every relative asset reference into a base64 `data:` URI
|
||||
5. Inlines the bundled JS as `<script type="module">` before `</body>`
|
||||
6. Inlines the bundled CSS as `<style>` in `<head>`
|
||||
7. Outputs a single `.html` file with no external dependencies
|
||||
|
||||
## Minification
|
||||
|
||||
Add `--minify` to minify the JavaScript and CSS:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile --target=browser --minify ./index.html --outdir=dist
|
||||
```
|
||||
|
||||
Or via the API:
|
||||
|
||||
```ts build.ts icon="/icons/typescript.svg"
|
||||
await Bun.build({
|
||||
entrypoints: ["./index.html"],
|
||||
compile: true,
|
||||
target: "browser",
|
||||
outdir: "./dist",
|
||||
minify: true,
|
||||
});
|
||||
```
|
||||
|
||||
## JavaScript API
|
||||
|
||||
You can use `Bun.build()` to produce standalone HTML programmatically:
|
||||
|
||||
```ts build.ts icon="/icons/typescript.svg"
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["./index.html"],
|
||||
compile: true,
|
||||
target: "browser",
|
||||
outdir: "./dist", // optional — omit to get output as BuildArtifact
|
||||
minify: true,
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
console.error("Build failed:");
|
||||
for (const log of result.logs) {
|
||||
console.error(log);
|
||||
}
|
||||
} else {
|
||||
console.log("Built:", result.outputs[0].path);
|
||||
}
|
||||
```
|
||||
|
||||
When `outdir` is omitted, the output is available as a `BuildArtifact` in `result.outputs`:
|
||||
|
||||
```ts icon="/icons/typescript.svg"
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["./index.html"],
|
||||
compile: true,
|
||||
target: "browser",
|
||||
});
|
||||
|
||||
const html = await result.outputs[0].text();
|
||||
await Bun.write("output.html", html);
|
||||
```
|
||||
|
||||
## Multiple HTML files
|
||||
|
||||
You can pass multiple HTML files as entrypoints. Each produces its own standalone HTML file:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile --target=browser ./index.html ./about.html --outdir=dist
|
||||
```
|
||||
|
||||
## Environment variables
|
||||
|
||||
Use `--env` to inline environment variables into the bundled JavaScript:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
API_URL=https://api.example.com bun build --compile --target=browser --env=inline ./index.html --outdir=dist
|
||||
```
|
||||
|
||||
References to `process.env.API_URL` in your JavaScript are replaced with the literal value at build time.
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Code splitting** is not supported — `--splitting` cannot be used with `--compile --target=browser`
|
||||
- **Large assets** increase file size since they're base64-encoded (33% overhead vs the raw binary)
|
||||
- **External URLs** (CDN links, absolute URLs) are left as-is — only relative paths are inlined
|
||||
@@ -234,7 +234,7 @@
|
||||
{
|
||||
"group": "Asset Processing",
|
||||
"icon": "image",
|
||||
"pages": ["/bundler/html-static", "/bundler/css", "/bundler/loaders"]
|
||||
"pages": ["/bundler/html-static", "/bundler/standalone-html", "/bundler/css", "/bundler/loaders"]
|
||||
},
|
||||
{
|
||||
"group": "Single File Executable",
|
||||
|
||||
@@ -35,7 +35,7 @@ winget install "Visual Studio Community 2022" --override "--add Microsoft.Visual
|
||||
|
||||
After Visual Studio, you need the following:
|
||||
|
||||
- LLVM (19.1.7 for x64, 21.1.8 for ARM64)
|
||||
- LLVM 21.1.8
|
||||
- Go
|
||||
- Rust
|
||||
- NASM
|
||||
@@ -51,7 +51,7 @@ After Visual Studio, you need the following:
|
||||
irm https://get.scoop.sh | iex
|
||||
scoop install nodejs-lts go rust nasm ruby perl ccache
|
||||
# scoop seems to be buggy if you install llvm and the rest at the same time
|
||||
scoop install llvm@19.1.7
|
||||
scoop install llvm@21.1.8
|
||||
```
|
||||
|
||||
For Windows ARM64, download LLVM 21.1.8 directly from GitHub releases (first version with ARM64 Windows builds):
|
||||
|
||||
@@ -40,7 +40,7 @@ sudo pacman -S base-devel cmake git go libiconv libtool make ninja pkg-config py
|
||||
```
|
||||
|
||||
```bash Fedora
|
||||
sudo dnf install cargo clang19 llvm19 lld19 cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
|
||||
sudo dnf install cargo clang21 llvm21 lld21 cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
|
||||
```
|
||||
|
||||
```bash openSUSE Tumbleweed
|
||||
@@ -95,17 +95,17 @@ Our build scripts will automatically detect and use `ccache` if available. You c
|
||||
|
||||
## Install LLVM
|
||||
|
||||
Bun requires LLVM 19 (`clang` is part of LLVM). This version requirement is to match WebKit (precompiled), as mismatching versions will cause memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
|
||||
Bun requires LLVM 21.1.8 (`clang` is part of LLVM). This version is enforced by the build system — mismatching versions will cause memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash macOS (Homebrew)
|
||||
brew install llvm@19
|
||||
brew install llvm@21
|
||||
```
|
||||
|
||||
```bash Ubuntu/Debian
|
||||
# LLVM has an automatic installation script that is compatible with all versions of Ubuntu
|
||||
wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 19 all
|
||||
wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 21 all
|
||||
```
|
||||
|
||||
```bash Arch
|
||||
@@ -117,17 +117,17 @@ sudo dnf install llvm clang lld-devel
|
||||
```
|
||||
|
||||
```bash openSUSE Tumbleweed
|
||||
sudo zypper install clang19 lld19 llvm19
|
||||
sudo zypper install clang21 lld21 llvm21
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
If none of the above solutions apply, you will have to install it [manually](https://github.com/llvm/llvm-project/releases/tag/llvmorg-19.1.7).
|
||||
If none of the above solutions apply, you will have to install it [manually](https://github.com/llvm/llvm-project/releases/tag/llvmorg-21.1.8).
|
||||
|
||||
Make sure Clang/LLVM 19 is in your path:
|
||||
Make sure Clang/LLVM 21 is in your path:
|
||||
|
||||
```bash
|
||||
which clang-19
|
||||
which clang-21
|
||||
```
|
||||
|
||||
If not, run this to manually add it:
|
||||
@@ -136,13 +136,13 @@ If not, run this to manually add it:
|
||||
|
||||
```bash macOS (Homebrew)
|
||||
# use fish_add_path if you're using fish
|
||||
# use path+="$(brew --prefix llvm@19)/bin" if you are using zsh
|
||||
export PATH="$(brew --prefix llvm@19)/bin:$PATH"
|
||||
# use path+="$(brew --prefix llvm@21)/bin" if you are using zsh
|
||||
export PATH="$(brew --prefix llvm@21)/bin:$PATH"
|
||||
```
|
||||
|
||||
```bash Arch
|
||||
# use fish_add_path if you're using fish
|
||||
export PATH="$PATH:/usr/lib/llvm19/bin"
|
||||
export PATH="$PATH:/usr/lib/llvm21/bin"
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -266,18 +266,13 @@ git clone https://github.com/oven-sh/WebKit vendor/WebKit
|
||||
# Check out the commit hash specified in `set(WEBKIT_VERSION <commit_hash>)` in cmake/tools/SetupWebKit.cmake
|
||||
git -C vendor/WebKit checkout <commit_hash>
|
||||
|
||||
# Make a debug build of JSC. This will output build artifacts in ./vendor/WebKit/WebKitBuild/Debug
|
||||
# Optionally, you can use `bun run jsc:build` for a release build
|
||||
bun run jsc:build:debug && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# After an initial run of `make jsc-debug`, you can rebuild JSC with:
|
||||
cmake --build vendor/WebKit/WebKitBuild/Debug --target jsc && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# Build bun with the local JSC build
|
||||
# Build bun with the local JSC build — this automatically configures and builds JSC
|
||||
bun run build:local
|
||||
```
|
||||
|
||||
Using `bun run build:local` will build Bun in the `./build/debug-local` directory (instead of `./build/debug`), you'll have to change a couple of places to use this new directory:
|
||||
`bun run build:local` handles everything: configuring JSC, building JSC, and building Bun. On subsequent runs, JSC will incrementally rebuild if any WebKit sources changed. `ninja -Cbuild/debug-local` also works after the first build, and will build Bun+JSC.
|
||||
|
||||
The build output goes to `./build/debug-local` (instead of `./build/debug`), so you'll need to update a couple of places:
|
||||
|
||||
- The first line in `src/js/builtins.d.ts`
|
||||
- The `CompilationDatabase` line in `.clangd` config should be `CompilationDatabase: build/debug-local`
|
||||
@@ -288,7 +283,7 @@ Note that the WebKit folder, including build artifacts, is 8GB+ in size.
|
||||
|
||||
If you are using a JSC debug build and using VScode, make sure to run the `C/C++: Select a Configuration` command to configure intellisense to find the debug headers.
|
||||
|
||||
Note that if you change make changes to our [WebKit fork](https://github.com/oven-sh/WebKit), you will also have to change `SetupWebKit.cmake` to point to the commit hash.
|
||||
Note that if you make changes to our [WebKit fork](https://github.com/oven-sh/WebKit), you will also have to change `SetupWebKit.cmake` to point to the commit hash.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -314,7 +309,7 @@ The issue may manifest when initially running `bun setup` as Clang being unable
|
||||
```txt
|
||||
The C++ compiler
|
||||
|
||||
"/usr/bin/clang++-19"
|
||||
"/usr/bin/clang++-21"
|
||||
|
||||
is not able to compile a simple test program.
|
||||
```
|
||||
|
||||
@@ -298,6 +298,17 @@ This is useful for catching flaky tests or non-deterministic behavior. Each test
|
||||
|
||||
The `--rerun-each` CLI flag will override this setting when specified.
|
||||
|
||||
### `test.retry`
|
||||
|
||||
Default retry count for all tests. Failed tests will be retried up to this many times. Per-test `{ retry: N }` overrides this value. Default `0` (no retries).
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[test]
|
||||
retry = 3
|
||||
```
|
||||
|
||||
The `--retry` CLI flag will override this setting when specified.
|
||||
|
||||
### `test.concurrentTestGlob`
|
||||
|
||||
Specify a glob pattern to automatically run matching test files with concurrent test execution enabled. Test files matching this pattern will behave as if the `--concurrent` flag was passed, running all tests within those files concurrently.
|
||||
|
||||
@@ -3,9 +3,9 @@ title: Markdown
|
||||
description: Parse and render Markdown with Bun's built-in Markdown API, supporting GFM extensions and custom rendering callbacks
|
||||
---
|
||||
|
||||
{% callout type="note" %}
|
||||
**Unstable API** — This API is under active development and may change in future versions of Bun.
|
||||
{% /callout %}
|
||||
<Callout type="note">
|
||||
**Unstable API** — This API is under active development and may change in future versions of Bun.
|
||||
</Callout>
|
||||
|
||||
Bun includes a fast, built-in Markdown parser written in Zig. It supports GitHub Flavored Markdown (GFM) extensions and provides three APIs:
|
||||
|
||||
|
||||
@@ -50,7 +50,8 @@ bun build <entry points>
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="--format" type="string" default="esm">
|
||||
Module format of the output bundle. One of <code>esm</code>, <code>cjs</code>, or <code>iife</code>
|
||||
Module format of the output bundle. One of <code>esm</code>, <code>cjs</code>, or <code>iife</code>. Defaults to{" "}
|
||||
<code>cjs</code> when <code>--bytecode</code> is used.
|
||||
</ParamField>
|
||||
|
||||
### File Naming
|
||||
|
||||
@@ -14,6 +14,11 @@ bun test <patterns>
|
||||
Re-run each test file <code>NUMBER</code> times, helps catch certain bugs
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="--retry" type="number">
|
||||
Default retry count for all tests. Failed tests will be retried up to <code>NUMBER</code> times. Overridden by
|
||||
per-test <code>{`{ retry: N }`}</code>
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="--concurrent" type="boolean">
|
||||
Treat all tests as <code>test.concurrent()</code> tests
|
||||
</ParamField>
|
||||
|
||||
@@ -222,6 +222,17 @@ randomize = true
|
||||
seed = 2444615283
|
||||
```
|
||||
|
||||
#### retry
|
||||
|
||||
Default retry count for all tests. Failed tests will be retried up to this many times. Per-test `{ retry: N }` overrides this value. Default `0` (no retries).
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[test]
|
||||
retry = 3
|
||||
```
|
||||
|
||||
The `--retry` CLI flag will override this setting when specified.
|
||||
|
||||
#### rerunEach
|
||||
|
||||
Re-run each test file multiple times to identify flaky tests:
|
||||
|
||||
@@ -201,6 +201,35 @@ test.failing.each([1, 2, 3])("chained qualifiers %d", input => {
|
||||
});
|
||||
```
|
||||
|
||||
## Retry failed tests
|
||||
|
||||
Use the `--retry` flag to automatically retry failed tests up to a given number of times. If a test fails and then passes on a subsequent attempt, it is reported as passing.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bun test --retry 3
|
||||
```
|
||||
|
||||
Per-test `{ retry: N }` overrides the global `--retry` value:
|
||||
|
||||
```ts
|
||||
// Uses the global --retry value
|
||||
test("uses global retry", () => {
|
||||
/* ... */
|
||||
});
|
||||
|
||||
// Overrides --retry with its own value
|
||||
test("custom retry", { retry: 1 }, () => {
|
||||
/* ... */
|
||||
});
|
||||
```
|
||||
|
||||
You can also set this in `bunfig.toml`:
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[test]
|
||||
retry = 3
|
||||
```
|
||||
|
||||
## Rerun tests
|
||||
|
||||
Use the `--rerun-each` flag to run each test multiple times. This is useful for detecting flaky or non-deterministic test failures.
|
||||
|
||||
10
flake.nix
10
flake.nix
@@ -26,10 +26,10 @@
|
||||
};
|
||||
};
|
||||
|
||||
# LLVM 19 - matching the bootstrap script (targets 19.1.7, actual version from nixpkgs-unstable)
|
||||
llvm = pkgs.llvm_19;
|
||||
clang = pkgs.clang_19;
|
||||
lld = pkgs.lld_19;
|
||||
# LLVM 21 - matching the bootstrap script (targets 21.1.8, actual version from nixpkgs-unstable)
|
||||
llvm = pkgs.llvm_21;
|
||||
clang = pkgs.clang_21;
|
||||
lld = pkgs.lld_21;
|
||||
|
||||
# Node.js 24 - matching the bootstrap script (targets 24.3.0, actual version from nixpkgs-unstable)
|
||||
nodejs = pkgs.nodejs_24;
|
||||
@@ -42,7 +42,7 @@
|
||||
pkgs.pkg-config
|
||||
pkgs.ccache
|
||||
|
||||
# Compilers and toolchain - version pinned to LLVM 19
|
||||
# Compilers and toolchain - version pinned to LLVM 21
|
||||
clang
|
||||
llvm
|
||||
lld
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "bun",
|
||||
"version": "1.3.8",
|
||||
"version": "1.3.10",
|
||||
"workspaces": [
|
||||
"./packages/bun-types",
|
||||
"./packages/@types/bun"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Bun
|
||||
|
||||
This is the Windows ARM64 binary for Bun, a fast all-in-one JavaScript runtime. https://bun.com
|
||||
@@ -95,6 +95,12 @@ export const platforms: Platform[] = [
|
||||
bin: "bun-windows-x64-baseline",
|
||||
exe: "bin/bun.exe",
|
||||
},
|
||||
// {
|
||||
// os: "win32",
|
||||
// arch: "arm64",
|
||||
// bin: "bun-windows-aarch64",
|
||||
// exe: "bin/bun.exe",
|
||||
// },
|
||||
];
|
||||
|
||||
export const supportedPlatforms: Platform[] = platforms
|
||||
|
||||
107
packages/bun-types/bun.d.ts
vendored
107
packages/bun-types/bun.d.ts
vendored
@@ -2154,7 +2154,7 @@ declare module "bun" {
|
||||
interface Hash {
|
||||
wyhash: (data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, seed?: bigint) => bigint;
|
||||
adler32: (data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer) => number;
|
||||
crc32: (data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer) => number;
|
||||
crc32: (data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, seed?: number) => number;
|
||||
cityHash32: (data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer) => number;
|
||||
cityHash64: (data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, seed?: bigint) => bigint;
|
||||
xxHash32: (data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, seed?: number) => number;
|
||||
@@ -2438,14 +2438,19 @@ declare module "bun" {
|
||||
| `bun-linux-${Architecture}-${Libc}`
|
||||
| `bun-linux-${Architecture}-${SIMD}`
|
||||
| `bun-linux-${Architecture}-${SIMD}-${Libc}`
|
||||
| "bun-windows-x64"
|
||||
| `bun-windows-${Architecture}`
|
||||
| `bun-windows-x64-${SIMD}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [Bun.build API docs](https://bun.com/docs/bundler#api)
|
||||
*/
|
||||
interface BuildConfigBase {
|
||||
interface BuildConfig {
|
||||
/**
|
||||
* Enable code splitting
|
||||
*/
|
||||
splitting?: boolean;
|
||||
|
||||
/**
|
||||
* List of entrypoints, usually file paths
|
||||
*/
|
||||
@@ -2594,7 +2599,10 @@ declare module "bun" {
|
||||
* start times, but will make the final output larger and slightly increase
|
||||
* memory usage.
|
||||
*
|
||||
* Bytecode is currently only supported for CommonJS (`format: "cjs"`).
|
||||
* - CommonJS: works with or without `compile: true`
|
||||
* - ESM: requires `compile: true`
|
||||
*
|
||||
* Without an explicit `format`, defaults to CommonJS.
|
||||
*
|
||||
* Must be `target: "bun"`
|
||||
* @default false
|
||||
@@ -2771,6 +2779,46 @@ declare module "bun" {
|
||||
metafile?: boolean;
|
||||
|
||||
outdir?: string;
|
||||
|
||||
/**
|
||||
* Create a standalone executable or self-contained HTML.
|
||||
*
|
||||
* When `true`, creates an executable for the current platform.
|
||||
* When a target string, creates an executable for that platform.
|
||||
*
|
||||
* When used with `target: "browser"`, produces self-contained HTML files
|
||||
* with all scripts, styles, and assets inlined. All `<script>` tags become
|
||||
* inline `<script>` with bundled code, all `<link rel="stylesheet">` tags
|
||||
* become inline `<style>` tags, and all asset references become `data:` URIs.
|
||||
* All entrypoints must be HTML files. Cannot be used with `splitting`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Create executable for current platform
|
||||
* await Bun.build({
|
||||
* entrypoints: ['./app.js'],
|
||||
* compile: {
|
||||
* target: 'linux-x64',
|
||||
* },
|
||||
* outfile: './my-app'
|
||||
* });
|
||||
*
|
||||
* // Cross-compile for Linux x64
|
||||
* await Bun.build({
|
||||
* entrypoints: ['./app.js'],
|
||||
* compile: 'linux-x64',
|
||||
* outfile: './my-app'
|
||||
* });
|
||||
*
|
||||
* // Produce self-contained HTML
|
||||
* await Bun.build({
|
||||
* entrypoints: ['./index.html'],
|
||||
* target: 'browser',
|
||||
* compile: true,
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
compile?: boolean | Bun.Build.CompileTarget | CompileBuildOptions;
|
||||
}
|
||||
|
||||
interface CompileBuildOptions {
|
||||
@@ -2829,57 +2877,6 @@ declare module "bun" {
|
||||
};
|
||||
}
|
||||
|
||||
// Compile build config - uses outfile for executable output
|
||||
interface CompileBuildConfig extends BuildConfigBase {
|
||||
/**
|
||||
* Create a standalone executable
|
||||
*
|
||||
* When `true`, creates an executable for the current platform.
|
||||
* When a target string, creates an executable for that platform.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Create executable for current platform
|
||||
* await Bun.build({
|
||||
* entrypoints: ['./app.js'],
|
||||
* compile: {
|
||||
* target: 'linux-x64',
|
||||
* },
|
||||
* outfile: './my-app'
|
||||
* });
|
||||
*
|
||||
* // Cross-compile for Linux x64
|
||||
* await Bun.build({
|
||||
* entrypoints: ['./app.js'],
|
||||
* compile: 'linux-x64',
|
||||
* outfile: './my-app'
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
compile: boolean | Bun.Build.CompileTarget | CompileBuildOptions;
|
||||
|
||||
/**
|
||||
* Splitting is not currently supported with `.compile`
|
||||
*/
|
||||
splitting?: never;
|
||||
}
|
||||
|
||||
interface NormalBuildConfig extends BuildConfigBase {
|
||||
/**
|
||||
* Enable code splitting
|
||||
*
|
||||
* This does not currently work with {@link CompileBuildConfig.compile `compile`}
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
splitting?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [Bun.build API docs](https://bun.com/docs/bundler#api)
|
||||
*/
|
||||
type BuildConfig = CompileBuildConfig | NormalBuildConfig;
|
||||
|
||||
/**
|
||||
* Hash and verify passwords using argon2 or bcrypt
|
||||
*
|
||||
|
||||
1
packages/bun-types/test.d.ts
vendored
1
packages/bun-types/test.d.ts
vendored
@@ -2179,6 +2179,7 @@ declare module "bun:test" {
|
||||
mockResolvedValueOnce(value: ResolveType<T>): this;
|
||||
mockRejectedValue(value: RejectType<T>): this;
|
||||
mockRejectedValueOnce(value: RejectType<T>): this;
|
||||
[Symbol.dispose](): void;
|
||||
}
|
||||
|
||||
// export type MockMetadata<T, MetadataType = MockMetadataType> = {
|
||||
|
||||
@@ -23196,557 +23196,6 @@ CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
|
||||
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
|
||||
|
||||
#
|
||||
# Certificate "CommScope Public Trust ECC Root-01"
|
||||
#
|
||||
# Issuer: CN=CommScope Public Trust ECC Root-01,O=CommScope,C=US
|
||||
# Serial Number:43:70:82:77:cf:4d:5d:34:f1:ca:ae:32:2f:37:f7:f4:7f:75:a0:9e
|
||||
# Subject: CN=CommScope Public Trust ECC Root-01,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:35:43 2021
|
||||
# Not Valid After : Sat Apr 28 17:35:42 2046
|
||||
# Fingerprint (SHA-256): 11:43:7C:DA:7B:B4:5E:41:36:5F:45:B3:9A:38:98:6B:0D:E0:0D:EF:34:8E:0C:7B:B0:87:36:33:80:0B:C3:8B
|
||||
# Fingerprint (SHA1): 07:86:C0:D8:DD:8E:C0:80:98:06:98:D0:58:7A:EF:DE:A6:CC:A2:5D
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust ECC Root-01"
|
||||
CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
|
||||
CKA_SUBJECT MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_ID UTF8 "0"
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\103\160\202\167\317\115\135\064\361\312\256\062\057\067
|
||||
\367\364\177\165\240\236
|
||||
END
|
||||
CKA_VALUE MULTILINE_OCTAL
|
||||
\060\202\002\035\060\202\001\243\240\003\002\001\002\002\024\103
|
||||
\160\202\167\317\115\135\064\361\312\256\062\057\067\367\364\177
|
||||
\165\240\236\060\012\006\010\052\206\110\316\075\004\003\003\060
|
||||
\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061\022
|
||||
\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143\157
|
||||
\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157\155
|
||||
\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124\162
|
||||
\165\163\164\040\105\103\103\040\122\157\157\164\055\060\061\060
|
||||
\036\027\015\062\061\060\064\062\070\061\067\063\065\064\063\132
|
||||
\027\015\064\066\060\064\062\070\061\067\063\065\064\062\132\060
|
||||
\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061\022
|
||||
\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143\157
|
||||
\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157\155
|
||||
\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124\162
|
||||
\165\163\164\040\105\103\103\040\122\157\157\164\055\060\061\060
|
||||
\166\060\020\006\007\052\206\110\316\075\002\001\006\005\053\201
|
||||
\004\000\042\003\142\000\004\113\066\351\256\127\136\250\160\327
|
||||
\320\217\164\142\167\303\136\172\252\345\266\242\361\170\375\002
|
||||
\176\127\335\221\171\234\154\271\122\210\124\274\057\004\276\270
|
||||
\315\366\020\321\051\354\265\320\240\303\360\211\160\031\273\121
|
||||
\145\305\103\234\303\233\143\235\040\203\076\006\013\246\102\104
|
||||
\205\021\247\112\072\055\351\326\150\057\110\116\123\053\007\077
|
||||
\115\275\271\254\167\071\127\243\102\060\100\060\017\006\003\125
|
||||
\035\023\001\001\377\004\005\060\003\001\001\377\060\016\006\003
|
||||
\125\035\017\001\001\377\004\004\003\002\001\006\060\035\006\003
|
||||
\125\035\016\004\026\004\024\216\007\142\300\120\335\306\031\006
|
||||
\000\106\164\004\367\363\256\175\165\115\060\060\012\006\010\052
|
||||
\206\110\316\075\004\003\003\003\150\000\060\145\002\061\000\234
|
||||
\063\337\101\343\043\250\102\066\046\227\065\134\173\353\333\113
|
||||
\370\252\213\163\125\025\134\254\170\051\017\272\041\330\304\240
|
||||
\330\321\003\335\155\321\071\075\304\223\140\322\343\162\262\002
|
||||
\060\174\305\176\210\323\120\365\036\045\350\372\116\165\346\130
|
||||
\226\244\065\137\033\145\352\141\232\160\043\265\015\243\233\222
|
||||
\122\157\151\240\214\215\112\320\356\213\016\313\107\216\320\215
|
||||
\021
|
||||
END
|
||||
CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
|
||||
CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
|
||||
# Trust for "CommScope Public Trust ECC Root-01"
|
||||
# Issuer: CN=CommScope Public Trust ECC Root-01,O=CommScope,C=US
|
||||
# Serial Number:43:70:82:77:cf:4d:5d:34:f1:ca:ae:32:2f:37:f7:f4:7f:75:a0:9e
|
||||
# Subject: CN=CommScope Public Trust ECC Root-01,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:35:43 2021
|
||||
# Not Valid After : Sat Apr 28 17:35:42 2046
|
||||
# Fingerprint (SHA-256): 11:43:7C:DA:7B:B4:5E:41:36:5F:45:B3:9A:38:98:6B:0D:E0:0D:EF:34:8E:0C:7B:B0:87:36:33:80:0B:C3:8B
|
||||
# Fingerprint (SHA1): 07:86:C0:D8:DD:8E:C0:80:98:06:98:D0:58:7A:EF:DE:A6:CC:A2:5D
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust ECC Root-01"
|
||||
CKA_CERT_SHA1_HASH MULTILINE_OCTAL
|
||||
\007\206\300\330\335\216\300\200\230\006\230\320\130\172\357\336
|
||||
\246\314\242\135
|
||||
END
|
||||
CKA_CERT_MD5_HASH MULTILINE_OCTAL
|
||||
\072\100\247\374\003\214\234\070\171\057\072\242\154\266\012\026
|
||||
END
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\103\160\202\167\317\115\135\064\361\312\256\062\057\067
|
||||
\367\364\177\165\240\236
|
||||
END
|
||||
CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
|
||||
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
|
||||
|
||||
#
|
||||
# Certificate "CommScope Public Trust ECC Root-02"
|
||||
#
|
||||
# Issuer: CN=CommScope Public Trust ECC Root-02,O=CommScope,C=US
|
||||
# Serial Number:28:fd:99:60:41:47:a6:01:3a:ca:14:7b:1f:ef:f9:68:08:83:5d:7d
|
||||
# Subject: CN=CommScope Public Trust ECC Root-02,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:44:54 2021
|
||||
# Not Valid After : Sat Apr 28 17:44:53 2046
|
||||
# Fingerprint (SHA-256): 2F:FB:7F:81:3B:BB:B3:C8:9A:B4:E8:16:2D:0F:16:D7:15:09:A8:30:CC:9D:73:C2:62:E5:14:08:75:D1:AD:4A
|
||||
# Fingerprint (SHA1): 3C:3F:EF:57:0F:FE:65:93:86:9E:A0:FE:B0:F6:ED:8E:D1:13:C7:E5
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust ECC Root-02"
|
||||
CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
|
||||
CKA_SUBJECT MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_ID UTF8 "0"
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\050\375\231\140\101\107\246\001\072\312\024\173\037\357
|
||||
\371\150\010\203\135\175
|
||||
END
|
||||
CKA_VALUE MULTILINE_OCTAL
|
||||
\060\202\002\034\060\202\001\243\240\003\002\001\002\002\024\050
|
||||
\375\231\140\101\107\246\001\072\312\024\173\037\357\371\150\010
|
||||
\203\135\175\060\012\006\010\052\206\110\316\075\004\003\003\060
|
||||
\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061\022
|
||||
\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143\157
|
||||
\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157\155
|
||||
\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124\162
|
||||
\165\163\164\040\105\103\103\040\122\157\157\164\055\060\062\060
|
||||
\036\027\015\062\061\060\064\062\070\061\067\064\064\065\064\132
|
||||
\027\015\064\066\060\064\062\070\061\067\064\064\065\063\132\060
|
||||
\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061\022
|
||||
\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143\157
|
||||
\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157\155
|
||||
\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124\162
|
||||
\165\163\164\040\105\103\103\040\122\157\157\164\055\060\062\060
|
||||
\166\060\020\006\007\052\206\110\316\075\002\001\006\005\053\201
|
||||
\004\000\042\003\142\000\004\170\060\201\350\143\036\345\353\161
|
||||
\121\017\367\007\007\312\071\231\174\116\325\017\314\060\060\013
|
||||
\217\146\223\076\317\275\305\206\275\371\261\267\264\076\264\007
|
||||
\310\363\226\061\363\355\244\117\370\243\116\215\051\025\130\270
|
||||
\325\157\177\356\154\042\265\260\257\110\105\012\275\250\111\224
|
||||
\277\204\103\260\333\204\112\003\043\031\147\152\157\301\156\274
|
||||
\006\071\067\321\210\042\367\243\102\060\100\060\017\006\003\125
|
||||
\035\023\001\001\377\004\005\060\003\001\001\377\060\016\006\003
|
||||
\125\035\017\001\001\377\004\004\003\002\001\006\060\035\006\003
|
||||
\125\035\016\004\026\004\024\346\030\165\377\357\140\336\204\244
|
||||
\365\106\307\336\112\125\343\062\066\171\365\060\012\006\010\052
|
||||
\206\110\316\075\004\003\003\003\147\000\060\144\002\060\046\163
|
||||
\111\172\266\253\346\111\364\175\122\077\324\101\004\256\200\103
|
||||
\203\145\165\271\205\200\070\073\326\157\344\223\206\253\217\347
|
||||
\211\310\177\233\176\153\012\022\125\141\252\021\340\171\002\060
|
||||
\167\350\061\161\254\074\161\003\326\204\046\036\024\270\363\073
|
||||
\073\336\355\131\374\153\114\060\177\131\316\105\351\163\140\025
|
||||
\232\114\360\346\136\045\042\025\155\302\207\131\320\262\216\152
|
||||
END
|
||||
CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
|
||||
CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
|
||||
# Trust for "CommScope Public Trust ECC Root-02"
|
||||
# Issuer: CN=CommScope Public Trust ECC Root-02,O=CommScope,C=US
|
||||
# Serial Number:28:fd:99:60:41:47:a6:01:3a:ca:14:7b:1f:ef:f9:68:08:83:5d:7d
|
||||
# Subject: CN=CommScope Public Trust ECC Root-02,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:44:54 2021
|
||||
# Not Valid After : Sat Apr 28 17:44:53 2046
|
||||
# Fingerprint (SHA-256): 2F:FB:7F:81:3B:BB:B3:C8:9A:B4:E8:16:2D:0F:16:D7:15:09:A8:30:CC:9D:73:C2:62:E5:14:08:75:D1:AD:4A
|
||||
# Fingerprint (SHA1): 3C:3F:EF:57:0F:FE:65:93:86:9E:A0:FE:B0:F6:ED:8E:D1:13:C7:E5
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust ECC Root-02"
|
||||
CKA_CERT_SHA1_HASH MULTILINE_OCTAL
|
||||
\074\077\357\127\017\376\145\223\206\236\240\376\260\366\355\216
|
||||
\321\023\307\345
|
||||
END
|
||||
CKA_CERT_MD5_HASH MULTILINE_OCTAL
|
||||
\131\260\104\325\145\115\270\134\125\031\222\002\266\321\224\262
|
||||
END
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\105\103\103\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\050\375\231\140\101\107\246\001\072\312\024\173\037\357
|
||||
\371\150\010\203\135\175
|
||||
END
|
||||
CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
|
||||
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
|
||||
|
||||
#
|
||||
# Certificate "CommScope Public Trust RSA Root-01"
|
||||
#
|
||||
# Issuer: CN=CommScope Public Trust RSA Root-01,O=CommScope,C=US
|
||||
# Serial Number:3e:03:49:81:75:16:74:31:8e:4c:ab:d5:c5:90:29:96:c5:39:10:dd
|
||||
# Subject: CN=CommScope Public Trust RSA Root-01,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 16:45:54 2021
|
||||
# Not Valid After : Sat Apr 28 16:45:53 2046
|
||||
# Fingerprint (SHA-256): 02:BD:F9:6E:2A:45:DD:9B:F1:8F:C7:E1:DB:DF:21:A0:37:9B:A3:C9:C2:61:03:44:CF:D8:D6:06:FE:C1:ED:81
|
||||
# Fingerprint (SHA1): 6D:0A:5F:F7:B4:23:06:B4:85:B3:B7:97:64:FC:AC:75:F5:33:F2:93
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust RSA Root-01"
|
||||
CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
|
||||
CKA_SUBJECT MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_ID UTF8 "0"
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\076\003\111\201\165\026\164\061\216\114\253\325\305\220
|
||||
\051\226\305\071\020\335
|
||||
END
|
||||
CKA_VALUE MULTILINE_OCTAL
|
||||
\060\202\005\154\060\202\003\124\240\003\002\001\002\002\024\076
|
||||
\003\111\201\165\026\164\061\216\114\253\325\305\220\051\226\305
|
||||
\071\020\335\060\015\006\011\052\206\110\206\367\015\001\001\013
|
||||
\005\000\060\116\061\013\060\011\006\003\125\004\006\023\002\125
|
||||
\123\061\022\060\020\006\003\125\004\012\014\011\103\157\155\155
|
||||
\123\143\157\160\145\061\053\060\051\006\003\125\004\003\014\042
|
||||
\103\157\155\155\123\143\157\160\145\040\120\165\142\154\151\143
|
||||
\040\124\162\165\163\164\040\122\123\101\040\122\157\157\164\055
|
||||
\060\061\060\036\027\015\062\061\060\064\062\070\061\066\064\065
|
||||
\065\064\132\027\015\064\066\060\064\062\070\061\066\064\065\065
|
||||
\063\132\060\116\061\013\060\011\006\003\125\004\006\023\002\125
|
||||
\123\061\022\060\020\006\003\125\004\012\014\011\103\157\155\155
|
||||
\123\143\157\160\145\061\053\060\051\006\003\125\004\003\014\042
|
||||
\103\157\155\155\123\143\157\160\145\040\120\165\142\154\151\143
|
||||
\040\124\162\165\163\164\040\122\123\101\040\122\157\157\164\055
|
||||
\060\061\060\202\002\042\060\015\006\011\052\206\110\206\367\015
|
||||
\001\001\001\005\000\003\202\002\017\000\060\202\002\012\002\202
|
||||
\002\001\000\260\110\145\243\015\035\102\343\221\155\235\204\244
|
||||
\141\226\022\302\355\303\332\043\064\031\166\366\352\375\125\132
|
||||
\366\125\001\123\017\362\314\214\227\117\271\120\313\263\001\104
|
||||
\126\226\375\233\050\354\173\164\013\347\102\153\125\316\311\141
|
||||
\262\350\255\100\074\272\271\101\012\005\117\033\046\205\217\103
|
||||
\265\100\265\205\321\324\161\334\203\101\363\366\105\307\200\242
|
||||
\204\120\227\106\316\240\014\304\140\126\004\035\007\133\106\245
|
||||
\016\262\113\244\016\245\174\356\370\324\142\003\271\223\152\212
|
||||
\024\270\160\370\056\202\106\070\043\016\164\307\153\101\267\320
|
||||
\051\243\235\200\260\176\167\223\143\102\373\064\203\073\163\243
|
||||
\132\041\066\353\107\372\030\027\331\272\146\302\223\244\217\374
|
||||
\135\244\255\374\120\152\225\254\274\044\063\321\275\210\177\206
|
||||
\365\365\262\163\052\217\174\257\010\362\032\230\077\251\201\145
|
||||
\077\301\214\211\305\226\060\232\012\317\364\324\310\064\355\235
|
||||
\057\274\215\070\206\123\356\227\237\251\262\143\224\027\215\017
|
||||
\334\146\052\174\122\121\165\313\231\216\350\075\134\277\236\073
|
||||
\050\215\203\002\017\251\237\162\342\054\053\263\334\146\227\000
|
||||
\100\320\244\124\216\233\135\173\105\066\046\326\162\103\353\317
|
||||
\300\352\015\334\316\022\346\175\070\237\005\047\250\227\076\351
|
||||
\121\306\154\005\050\301\002\017\351\030\155\354\275\234\006\324
|
||||
\247\111\364\124\005\153\154\060\361\353\003\325\352\075\152\166
|
||||
\302\313\032\050\111\115\177\144\340\372\053\332\163\203\201\377
|
||||
\221\003\275\224\273\344\270\216\234\062\143\315\237\273\150\201
|
||||
\261\204\133\257\066\277\167\356\035\177\367\111\233\122\354\322
|
||||
\167\132\175\221\235\115\302\071\055\344\272\202\370\157\362\116
|
||||
\036\017\116\346\077\131\245\043\334\075\207\250\050\130\050\321
|
||||
\361\033\066\333\117\304\377\341\214\133\162\214\307\046\003\047
|
||||
\243\071\012\001\252\300\262\061\140\203\042\241\117\022\011\001
|
||||
\021\257\064\324\317\327\256\142\323\005\007\264\061\165\340\015
|
||||
\155\127\117\151\207\371\127\251\272\025\366\310\122\155\241\313
|
||||
\234\037\345\374\170\250\065\232\237\101\024\316\245\264\316\224
|
||||
\010\034\011\255\126\345\332\266\111\232\112\352\143\030\123\234
|
||||
\054\056\303\002\003\001\000\001\243\102\060\100\060\017\006\003
|
||||
\125\035\023\001\001\377\004\005\060\003\001\001\377\060\016\006
|
||||
\003\125\035\017\001\001\377\004\004\003\002\001\006\060\035\006
|
||||
\003\125\035\016\004\026\004\024\067\135\246\232\164\062\302\302
|
||||
\371\307\246\025\020\131\270\344\375\345\270\155\060\015\006\011
|
||||
\052\206\110\206\367\015\001\001\013\005\000\003\202\002\001\000
|
||||
\257\247\317\336\377\340\275\102\215\115\345\042\226\337\150\352
|
||||
\175\115\052\175\320\255\075\026\134\103\347\175\300\206\350\172
|
||||
\065\143\361\314\201\310\306\013\350\056\122\065\244\246\111\220
|
||||
\143\121\254\064\254\005\073\127\000\351\323\142\323\331\051\325
|
||||
\124\276\034\020\221\234\262\155\376\131\375\171\367\352\126\320
|
||||
\236\150\124\102\217\046\122\342\114\337\057\227\246\057\322\007
|
||||
\230\250\363\140\135\113\232\130\127\210\357\202\345\372\257\154
|
||||
\201\113\222\217\100\232\223\106\131\313\137\170\026\261\147\076
|
||||
\102\013\337\050\331\260\255\230\040\276\103\174\321\136\032\011
|
||||
\027\044\215\173\135\225\351\253\301\140\253\133\030\144\200\373
|
||||
\255\340\006\175\035\312\131\270\363\170\051\147\306\126\035\257
|
||||
\266\265\164\052\166\241\077\373\165\060\237\224\136\073\245\140
|
||||
\363\313\134\014\342\016\311\140\370\311\037\026\212\046\335\347
|
||||
\047\177\353\045\246\212\275\270\055\066\020\232\261\130\115\232
|
||||
\150\117\140\124\345\366\106\023\216\210\254\274\041\102\022\255
|
||||
\306\112\211\175\233\301\330\055\351\226\003\364\242\164\014\274
|
||||
\000\035\277\326\067\045\147\264\162\213\257\205\275\352\052\003
|
||||
\217\314\373\074\104\044\202\342\001\245\013\131\266\064\215\062
|
||||
\013\022\015\353\047\302\375\101\327\100\074\162\106\051\300\214
|
||||
\352\272\017\361\006\223\056\367\234\250\364\140\076\243\361\070
|
||||
\136\216\023\301\263\072\227\207\077\222\312\170\251\034\257\320
|
||||
\260\033\046\036\276\160\354\172\365\063\230\352\134\377\053\013
|
||||
\004\116\103\335\143\176\016\247\116\170\003\225\076\324\055\060
|
||||
\225\021\020\050\056\277\240\002\076\377\136\131\323\005\016\225
|
||||
\137\123\105\357\153\207\325\110\315\026\246\226\203\341\337\263
|
||||
\006\363\301\024\333\247\354\034\213\135\220\220\015\162\121\347
|
||||
\141\371\024\312\257\203\217\277\257\261\012\131\135\334\134\327
|
||||
\344\226\255\133\140\035\332\256\227\262\071\331\006\365\166\000
|
||||
\023\370\150\114\041\260\065\304\334\125\262\311\301\101\132\034
|
||||
\211\300\214\157\164\240\153\063\115\265\001\050\375\255\255\211
|
||||
\027\073\246\232\204\274\353\214\352\304\161\044\250\272\051\371
|
||||
\010\262\047\126\065\062\137\352\071\373\061\232\325\031\314\360
|
||||
END
|
||||
CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
|
||||
CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
|
||||
# Trust for "CommScope Public Trust RSA Root-01"
|
||||
# Issuer: CN=CommScope Public Trust RSA Root-01,O=CommScope,C=US
|
||||
# Serial Number:3e:03:49:81:75:16:74:31:8e:4c:ab:d5:c5:90:29:96:c5:39:10:dd
|
||||
# Subject: CN=CommScope Public Trust RSA Root-01,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 16:45:54 2021
|
||||
# Not Valid After : Sat Apr 28 16:45:53 2046
|
||||
# Fingerprint (SHA-256): 02:BD:F9:6E:2A:45:DD:9B:F1:8F:C7:E1:DB:DF:21:A0:37:9B:A3:C9:C2:61:03:44:CF:D8:D6:06:FE:C1:ED:81
|
||||
# Fingerprint (SHA1): 6D:0A:5F:F7:B4:23:06:B4:85:B3:B7:97:64:FC:AC:75:F5:33:F2:93
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust RSA Root-01"
|
||||
CKA_CERT_SHA1_HASH MULTILINE_OCTAL
|
||||
\155\012\137\367\264\043\006\264\205\263\267\227\144\374\254\165
|
||||
\365\063\362\223
|
||||
END
|
||||
CKA_CERT_MD5_HASH MULTILINE_OCTAL
|
||||
\016\264\025\274\207\143\135\135\002\163\324\046\070\150\163\330
|
||||
END
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\061
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\076\003\111\201\165\026\164\061\216\114\253\325\305\220
|
||||
\051\226\305\071\020\335
|
||||
END
|
||||
CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
|
||||
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
|
||||
|
||||
#
|
||||
# Certificate "CommScope Public Trust RSA Root-02"
|
||||
#
|
||||
# Issuer: CN=CommScope Public Trust RSA Root-02,O=CommScope,C=US
|
||||
# Serial Number:54:16:bf:3b:7e:39:95:71:8d:d1:aa:00:a5:86:0d:2b:8f:7a:05:4e
|
||||
# Subject: CN=CommScope Public Trust RSA Root-02,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:16:43 2021
|
||||
# Not Valid After : Sat Apr 28 17:16:42 2046
|
||||
# Fingerprint (SHA-256): FF:E9:43:D7:93:42:4B:4F:7C:44:0C:1C:3D:64:8D:53:63:F3:4B:82:DC:87:AA:7A:9F:11:8F:C5:DE:E1:01:F1
|
||||
# Fingerprint (SHA1): EA:B0:E2:52:1B:89:93:4C:11:68:F2:D8:9A:AC:22:4C:A3:8A:57:AE
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust RSA Root-02"
|
||||
CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509
|
||||
CKA_SUBJECT MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_ID UTF8 "0"
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\124\026\277\073\176\071\225\161\215\321\252\000\245\206
|
||||
\015\053\217\172\005\116
|
||||
END
|
||||
CKA_VALUE MULTILINE_OCTAL
|
||||
\060\202\005\154\060\202\003\124\240\003\002\001\002\002\024\124
|
||||
\026\277\073\176\071\225\161\215\321\252\000\245\206\015\053\217
|
||||
\172\005\116\060\015\006\011\052\206\110\206\367\015\001\001\013
|
||||
\005\000\060\116\061\013\060\011\006\003\125\004\006\023\002\125
|
||||
\123\061\022\060\020\006\003\125\004\012\014\011\103\157\155\155
|
||||
\123\143\157\160\145\061\053\060\051\006\003\125\004\003\014\042
|
||||
\103\157\155\155\123\143\157\160\145\040\120\165\142\154\151\143
|
||||
\040\124\162\165\163\164\040\122\123\101\040\122\157\157\164\055
|
||||
\060\062\060\036\027\015\062\061\060\064\062\070\061\067\061\066
|
||||
\064\063\132\027\015\064\066\060\064\062\070\061\067\061\066\064
|
||||
\062\132\060\116\061\013\060\011\006\003\125\004\006\023\002\125
|
||||
\123\061\022\060\020\006\003\125\004\012\014\011\103\157\155\155
|
||||
\123\143\157\160\145\061\053\060\051\006\003\125\004\003\014\042
|
||||
\103\157\155\155\123\143\157\160\145\040\120\165\142\154\151\143
|
||||
\040\124\162\165\163\164\040\122\123\101\040\122\157\157\164\055
|
||||
\060\062\060\202\002\042\060\015\006\011\052\206\110\206\367\015
|
||||
\001\001\001\005\000\003\202\002\017\000\060\202\002\012\002\202
|
||||
\002\001\000\341\372\016\373\150\000\022\310\115\325\254\042\304
|
||||
\065\001\073\305\124\345\131\166\143\245\177\353\301\304\152\230
|
||||
\275\062\215\027\200\353\135\272\321\142\075\045\043\031\065\024
|
||||
\351\177\211\247\033\142\074\326\120\347\064\225\003\062\261\264
|
||||
\223\042\075\247\342\261\355\346\173\116\056\207\233\015\063\165
|
||||
\012\336\252\065\347\176\345\066\230\242\256\045\236\225\263\062
|
||||
\226\244\053\130\036\357\077\376\142\064\110\121\321\264\215\102
|
||||
\255\140\332\111\152\225\160\335\322\000\342\314\127\143\002\173
|
||||
\226\335\111\227\133\222\116\225\323\371\313\051\037\030\112\370
|
||||
\001\052\322\143\011\156\044\351\211\322\345\307\042\114\334\163
|
||||
\206\107\000\252\015\210\216\256\205\175\112\351\273\063\117\016
|
||||
\122\160\235\225\343\174\155\226\133\055\075\137\241\203\106\135
|
||||
\266\343\045\270\174\247\031\200\034\352\145\103\334\221\171\066
|
||||
\054\164\174\362\147\006\311\211\311\333\277\332\150\277\043\355
|
||||
\334\153\255\050\203\171\057\354\070\245\015\067\001\147\047\232
|
||||
\351\063\331\063\137\067\241\305\360\253\075\372\170\260\347\054
|
||||
\237\366\076\237\140\340\357\110\351\220\105\036\005\121\170\032
|
||||
\054\022\054\134\050\254\015\242\043\236\064\217\005\346\242\063
|
||||
\316\021\167\023\324\016\244\036\102\037\206\315\160\376\331\056
|
||||
\025\075\035\273\270\362\123\127\333\314\306\164\051\234\030\263
|
||||
\066\165\070\056\017\124\241\370\222\037\211\226\117\273\324\356
|
||||
\235\351\073\066\102\265\012\073\052\324\144\171\066\020\341\371
|
||||
\221\003\053\173\040\124\315\015\031\032\310\101\062\064\321\260
|
||||
\231\341\220\036\001\100\066\265\267\372\251\345\167\165\244\042
|
||||
\201\135\260\213\344\047\022\017\124\210\306\333\205\164\346\267
|
||||
\300\327\246\051\372\333\336\363\223\227\047\004\125\057\012\157
|
||||
\067\305\075\023\257\012\000\251\054\213\034\201\050\327\357\206
|
||||
\061\251\256\362\156\270\312\152\054\124\107\330\052\210\056\257
|
||||
\301\007\020\170\254\021\242\057\102\360\067\305\362\270\126\335
|
||||
\016\142\055\316\055\126\176\125\362\247\104\366\053\062\364\043
|
||||
\250\107\350\324\052\001\170\317\152\303\067\250\236\145\322\054
|
||||
\345\372\272\063\301\006\104\366\346\317\245\015\247\146\010\064
|
||||
\212\054\363\002\003\001\000\001\243\102\060\100\060\017\006\003
|
||||
\125\035\023\001\001\377\004\005\060\003\001\001\377\060\016\006
|
||||
\003\125\035\017\001\001\377\004\004\003\002\001\006\060\035\006
|
||||
\003\125\035\016\004\026\004\024\107\320\347\261\042\377\235\054
|
||||
\365\331\127\140\263\261\261\160\225\357\141\172\060\015\006\011
|
||||
\052\206\110\206\367\015\001\001\013\005\000\003\202\002\001\000
|
||||
\206\151\261\115\057\351\237\117\042\223\150\216\344\041\231\243
|
||||
\316\105\123\033\163\104\123\000\201\141\315\061\343\010\272\201
|
||||
\050\050\172\222\271\266\250\310\103\236\307\023\046\115\302\330
|
||||
\345\125\234\222\135\120\330\302\053\333\376\346\250\227\317\122
|
||||
\072\044\303\145\144\134\107\061\243\145\065\023\303\223\271\367
|
||||
\371\121\227\273\244\360\142\207\305\326\006\323\227\203\040\251
|
||||
\176\273\266\041\302\245\015\204\000\341\362\047\020\203\272\335
|
||||
\003\201\325\335\150\303\146\020\310\321\166\264\263\157\051\236
|
||||
\000\371\302\051\365\261\223\031\122\151\032\054\114\240\213\340
|
||||
\025\232\061\057\323\210\225\131\156\345\304\263\120\310\024\010
|
||||
\112\233\213\023\203\261\244\162\262\073\166\063\101\334\334\252
|
||||
\246\007\157\035\044\022\237\310\166\275\057\331\216\364\054\356
|
||||
\267\322\070\020\044\066\121\057\343\134\135\201\041\247\332\273
|
||||
\116\377\346\007\250\376\271\015\047\154\273\160\132\125\172\023
|
||||
\351\361\052\111\151\307\137\207\127\114\103\171\155\072\145\351
|
||||
\060\134\101\356\353\167\245\163\022\210\350\277\175\256\345\304
|
||||
\250\037\015\216\034\155\120\002\117\046\030\103\336\217\125\205
|
||||
\261\013\067\005\140\311\125\071\022\004\241\052\317\161\026\237
|
||||
\066\121\111\277\160\073\236\147\234\373\173\171\311\071\034\170
|
||||
\254\167\221\124\232\270\165\012\201\122\227\343\146\141\153\355
|
||||
\076\070\036\226\141\125\341\221\124\214\355\214\044\037\201\311
|
||||
\020\232\163\231\053\026\116\162\000\077\124\033\370\215\272\213
|
||||
\347\024\326\266\105\117\140\354\226\256\303\057\002\116\135\235
|
||||
\226\111\162\000\262\253\165\134\017\150\133\035\145\302\137\063
|
||||
\017\036\017\360\073\206\365\260\116\273\234\367\352\045\005\334
|
||||
\255\242\233\113\027\001\276\102\337\065\041\035\255\253\256\364
|
||||
\277\256\037\033\323\342\073\374\263\162\163\034\233\050\220\211
|
||||
\023\075\035\301\000\107\011\226\232\070\033\335\261\317\015\302
|
||||
\264\104\363\226\225\316\062\072\217\064\234\340\027\307\136\316
|
||||
\256\015\333\207\070\345\077\133\375\233\031\341\061\101\172\160
|
||||
\252\043\153\001\341\105\114\315\224\316\073\236\055\347\210\002
|
||||
\042\364\156\350\310\354\326\074\363\271\262\327\167\172\254\173
|
||||
END
|
||||
CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE
|
||||
CKA_NSS_SERVER_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
CKA_NSS_EMAIL_DISTRUST_AFTER CK_BBOOL CK_FALSE
|
||||
|
||||
# Trust for "CommScope Public Trust RSA Root-02"
|
||||
# Issuer: CN=CommScope Public Trust RSA Root-02,O=CommScope,C=US
|
||||
# Serial Number:54:16:bf:3b:7e:39:95:71:8d:d1:aa:00:a5:86:0d:2b:8f:7a:05:4e
|
||||
# Subject: CN=CommScope Public Trust RSA Root-02,O=CommScope,C=US
|
||||
# Not Valid Before: Wed Apr 28 17:16:43 2021
|
||||
# Not Valid After : Sat Apr 28 17:16:42 2046
|
||||
# Fingerprint (SHA-256): FF:E9:43:D7:93:42:4B:4F:7C:44:0C:1C:3D:64:8D:53:63:F3:4B:82:DC:87:AA:7A:9F:11:8F:C5:DE:E1:01:F1
|
||||
# Fingerprint (SHA1): EA:B0:E2:52:1B:89:93:4C:11:68:F2:D8:9A:AC:22:4C:A3:8A:57:AE
|
||||
CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST
|
||||
CKA_TOKEN CK_BBOOL CK_TRUE
|
||||
CKA_PRIVATE CK_BBOOL CK_FALSE
|
||||
CKA_MODIFIABLE CK_BBOOL CK_FALSE
|
||||
CKA_LABEL UTF8 "CommScope Public Trust RSA Root-02"
|
||||
CKA_CERT_SHA1_HASH MULTILINE_OCTAL
|
||||
\352\260\342\122\033\211\223\114\021\150\362\330\232\254\042\114
|
||||
\243\212\127\256
|
||||
END
|
||||
CKA_CERT_MD5_HASH MULTILINE_OCTAL
|
||||
\341\051\371\142\173\166\342\226\155\363\324\327\017\256\037\252
|
||||
END
|
||||
CKA_ISSUER MULTILINE_OCTAL
|
||||
\060\116\061\013\060\011\006\003\125\004\006\023\002\125\123\061
|
||||
\022\060\020\006\003\125\004\012\014\011\103\157\155\155\123\143
|
||||
\157\160\145\061\053\060\051\006\003\125\004\003\014\042\103\157
|
||||
\155\155\123\143\157\160\145\040\120\165\142\154\151\143\040\124
|
||||
\162\165\163\164\040\122\123\101\040\122\157\157\164\055\060\062
|
||||
END
|
||||
CKA_SERIAL_NUMBER MULTILINE_OCTAL
|
||||
\002\024\124\026\277\073\176\071\225\161\215\321\252\000\245\206
|
||||
\015\053\217\172\005\116
|
||||
END
|
||||
CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR
|
||||
CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST
|
||||
CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE
|
||||
|
||||
#
|
||||
# Certificate "D-Trust SBR Root CA 1 2022"
|
||||
#
|
||||
|
||||
@@ -3182,96 +3182,6 @@ static struct us_cert_string_t root_certs[] = {
|
||||
"MvHVI5TWWA==\n"
|
||||
"-----END CERTIFICATE-----",.len=869},
|
||||
|
||||
/* CommScope Public Trust ECC Root-01 */
|
||||
{.str="-----BEGIN CERTIFICATE-----\n"
|
||||
"MIICHTCCAaOgAwIBAgIUQ3CCd89NXTTxyq4yLzf39H91oJ4wCgYIKoZIzj0EAwMwTjELMAkG\n"
|
||||
"A1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1Ymxp\n"
|
||||
"YyBUcnVzdCBFQ0MgUm9vdC0wMTAeFw0yMTA0MjgxNzM1NDNaFw00NjA0MjgxNzM1NDJaME4x\n"
|
||||
"CzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQ\n"
|
||||
"dWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARLNumuV16o\n"
|
||||
"cNfQj3Rid8NeeqrltqLxeP0CflfdkXmcbLlSiFS8LwS+uM32ENEp7LXQoMPwiXAZu1FlxUOc\n"
|
||||
"w5tjnSCDPgYLpkJEhRGnSjot6dZoL0hOUysHP029uax3OVejQjBAMA8GA1UdEwEB/wQFMAMB\n"
|
||||
"Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSOB2LAUN3GGQYARnQE9/OufXVNMDAKBggq\n"
|
||||
"hkjOPQQDAwNoADBlAjEAnDPfQeMjqEI2Jpc1XHvr20v4qotzVRVcrHgpD7oh2MSg2NED3W3R\n"
|
||||
"OT3Ek2DS43KyAjB8xX6I01D1HiXo+k515liWpDVfG2XqYZpwI7UNo5uSUm9poIyNStDuiw7L\n"
|
||||
"R47QjRE=\n"
|
||||
"-----END CERTIFICATE-----",.len=792},
|
||||
|
||||
/* CommScope Public Trust ECC Root-02 */
|
||||
{.str="-----BEGIN CERTIFICATE-----\n"
|
||||
"MIICHDCCAaOgAwIBAgIUKP2ZYEFHpgE6yhR7H+/5aAiDXX0wCgYIKoZIzj0EAwMwTjELMAkG\n"
|
||||
"A1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1Ymxp\n"
|
||||
"YyBUcnVzdCBFQ0MgUm9vdC0wMjAeFw0yMTA0MjgxNzQ0NTRaFw00NjA0MjgxNzQ0NTNaME4x\n"
|
||||
"CzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQ\n"
|
||||
"dWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAR4MIHoYx7l\n"
|
||||
"63FRD/cHB8o5mXxO1Q/MMDALj2aTPs+9xYa9+bG3tD60B8jzljHz7aRP+KNOjSkVWLjVb3/u\n"
|
||||
"bCK1sK9IRQq9qEmUv4RDsNuESgMjGWdqb8FuvAY5N9GIIvejQjBAMA8GA1UdEwEB/wQFMAMB\n"
|
||||
"Af8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTmGHX/72DehKT1RsfeSlXjMjZ59TAKBggq\n"
|
||||
"hkjOPQQDAwNnADBkAjAmc0l6tqvmSfR9Uj/UQQSugEODZXW5hYA4O9Zv5JOGq4/nich/m35r\n"
|
||||
"ChJVYaoR4HkCMHfoMXGsPHED1oQmHhS48zs73u1Z/GtMMH9ZzkXpc2AVmkzw5l4lIhVtwodZ\n"
|
||||
"0LKOag==\n"
|
||||
"-----END CERTIFICATE-----",.len=792},
|
||||
|
||||
/* CommScope Public Trust RSA Root-01 */
|
||||
{.str="-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIFbDCCA1SgAwIBAgIUPgNJgXUWdDGOTKvVxZAplsU5EN0wDQYJKoZIhvcNAQELBQAwTjEL\n"
|
||||
"MAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1\n"
|
||||
"YmxpYyBUcnVzdCBSU0EgUm9vdC0wMTAeFw0yMTA0MjgxNjQ1NTRaFw00NjA0MjgxNjQ1NTNa\n"
|
||||
"ME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29w\n"
|
||||
"ZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3QtMDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n"
|
||||
"AoICAQCwSGWjDR1C45FtnYSkYZYSwu3D2iM0GXb26v1VWvZVAVMP8syMl0+5UMuzAURWlv2b\n"
|
||||
"KOx7dAvnQmtVzslhsuitQDy6uUEKBU8bJoWPQ7VAtYXR1HHcg0Hz9kXHgKKEUJdGzqAMxGBW\n"
|
||||
"BB0HW0alDrJLpA6lfO741GIDuZNqihS4cPgugkY4Iw50x2tBt9Apo52AsH53k2NC+zSDO3Oj\n"
|
||||
"WiE260f6GBfZumbCk6SP/F2krfxQapWsvCQz0b2If4b19bJzKo98rwjyGpg/qYFlP8GMicWW\n"
|
||||
"MJoKz/TUyDTtnS+8jTiGU+6Xn6myY5QXjQ/cZip8UlF1y5mO6D1cv547KI2DAg+pn3LiLCuz\n"
|
||||
"3GaXAEDQpFSOm117RTYm1nJD68/A6g3czhLmfTifBSeolz7pUcZsBSjBAg/pGG3svZwG1KdJ\n"
|
||||
"9FQFa2ww8esD1eo9anbCyxooSU1/ZOD6K9pzg4H/kQO9lLvkuI6cMmPNn7togbGEW682v3fu\n"
|
||||
"HX/3SZtS7NJ3Wn2RnU3COS3kuoL4b/JOHg9O5j9ZpSPcPYeoKFgo0fEbNttPxP/hjFtyjMcm\n"
|
||||
"AyejOQoBqsCyMWCDIqFPEgkBEa801M/XrmLTBQe0MXXgDW1XT2mH+VepuhX2yFJtocucH+X8\n"
|
||||
"eKg1mp9BFM6ltM6UCBwJrVbl2rZJmkrqYxhTnCwuwwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\n"
|
||||
"AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUN12mmnQywsL5x6YVEFm45P3luG0wDQYJ\n"
|
||||
"KoZIhvcNAQELBQADggIBAK+nz97/4L1CjU3lIpbfaOp9TSp90K09FlxD533Ahuh6NWPxzIHI\n"
|
||||
"xgvoLlI1pKZJkGNRrDSsBTtXAOnTYtPZKdVUvhwQkZyybf5Z/Xn36lbQnmhUQo8mUuJM3y+X\n"
|
||||
"pi/SB5io82BdS5pYV4jvguX6r2yBS5KPQJqTRlnLX3gWsWc+QgvfKNmwrZggvkN80V4aCRck\n"
|
||||
"jXtdlemrwWCrWxhkgPut4AZ9HcpZuPN4KWfGVh2vtrV0KnahP/t1MJ+UXjulYPPLXAziDslg\n"
|
||||
"+MkfFoom3ecnf+slpoq9uC02EJqxWE2aaE9gVOX2RhOOiKy8IUISrcZKiX2bwdgt6ZYD9KJ0\n"
|
||||
"DLwAHb/WNyVntHKLr4W96ioDj8z7PEQkguIBpQtZtjSNMgsSDesnwv1B10A8ckYpwIzqug/x\n"
|
||||
"BpMu95yo9GA+o/E4Xo4TwbM6l4c/ksp4qRyv0LAbJh6+cOx69TOY6lz/KwsETkPdY34Op054\n"
|
||||
"A5U+1C0wlREQKC6/oAI+/15Z0wUOlV9TRe9rh9VIzRamloPh37MG88EU26fsHItdkJANclHn\n"
|
||||
"YfkUyq+Dj7+vsQpZXdxc1+SWrVtgHdqul7I52Qb1dgAT+GhMIbA1xNxVssnBQVocicCMb3Sg\n"
|
||||
"azNNtQEo/a2tiRc7ppqEvOuM6sRxJKi6KfkIsidWNTJf6jn7MZrVGczw\n"
|
||||
"-----END CERTIFICATE-----",.len=1935},
|
||||
|
||||
/* CommScope Public Trust RSA Root-02 */
|
||||
{.str="-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIFbDCCA1SgAwIBAgIUVBa/O345lXGN0aoApYYNK496BU4wDQYJKoZIhvcNAQELBQAwTjEL\n"
|
||||
"MAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1\n"
|
||||
"YmxpYyBUcnVzdCBSU0EgUm9vdC0wMjAeFw0yMTA0MjgxNzE2NDNaFw00NjA0MjgxNzE2NDJa\n"
|
||||
"ME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29w\n"
|
||||
"ZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3QtMDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n"
|
||||
"AoICAQDh+g77aAASyE3VrCLENQE7xVTlWXZjpX/rwcRqmL0yjReA61260WI9JSMZNRTpf4mn\n"
|
||||
"G2I81lDnNJUDMrG0kyI9p+Kx7eZ7Ti6Hmw0zdQreqjXnfuU2mKKuJZ6VszKWpCtYHu8//mI0\n"
|
||||
"SFHRtI1CrWDaSWqVcN3SAOLMV2MCe5bdSZdbkk6V0/nLKR8YSvgBKtJjCW4k6YnS5cciTNxz\n"
|
||||
"hkcAqg2Ijq6FfUrpuzNPDlJwnZXjfG2WWy09X6GDRl224yW4fKcZgBzqZUPckXk2LHR88mcG\n"
|
||||
"yYnJ27/aaL8j7dxrrSiDeS/sOKUNNwFnJ5rpM9kzXzehxfCrPfp4sOcsn/Y+n2Dg70jpkEUe\n"
|
||||
"BVF4GiwSLFworA2iI540jwXmojPOEXcT1A6kHkIfhs1w/tkuFT0du7jyU1fbzMZ0KZwYszZ1\n"
|
||||
"OC4PVKH4kh+Jlk+71O6d6Ts2QrUKOyrUZHk2EOH5kQMreyBUzQ0ZGshBMjTRsJnhkB4BQDa1\n"
|
||||
"t/qp5Xd1pCKBXbCL5CcSD1SIxtuFdOa3wNemKfrb3vOTlycEVS8KbzfFPROvCgCpLIscgSjX\n"
|
||||
"74Yxqa7ybrjKaixUR9gqiC6vwQcQeKwRoi9C8DfF8rhW3Q5iLc4tVn5V8qdE9isy9COoR+jU\n"
|
||||
"KgF4z2rDN6ieZdIs5fq6M8EGRPbmz6UNp2YINIos8wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD\n"
|
||||
"AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUR9DnsSL/nSz12Vdgs7GxcJXvYXowDQYJ\n"
|
||||
"KoZIhvcNAQELBQADggIBAIZpsU0v6Z9PIpNojuQhmaPORVMbc0RTAIFhzTHjCLqBKCh6krm2\n"
|
||||
"qMhDnscTJk3C2OVVnJJdUNjCK9v+5qiXz1I6JMNlZFxHMaNlNRPDk7n3+VGXu6TwYofF1gbT\n"
|
||||
"l4MgqX67tiHCpQ2EAOHyJxCDut0DgdXdaMNmEMjRdrSzbymeAPnCKfWxkxlSaRosTKCL4BWa\n"
|
||||
"MS/TiJVZbuXEs1DIFAhKm4sTg7GkcrI7djNB3NyqpgdvHSQSn8h2vS/ZjvQs7rfSOBAkNlEv\n"
|
||||
"41xdgSGn2rtO/+YHqP65DSdsu3BaVXoT6fEqSWnHX4dXTEN5bTpl6TBcQe7rd6VzEojov32u\n"
|
||||
"5cSoHw2OHG1QAk8mGEPej1WFsQs3BWDJVTkSBKEqz3EWnzZRSb9wO55nnPt7eck5HHisd5FU\n"
|
||||
"mrh1CoFSl+NmYWvtPjgelmFV4ZFUjO2MJB+ByRCac5krFk5yAD9UG/iNuovnFNa2RU9g7Jau\n"
|
||||
"wy8CTl2dlklyALKrdVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670\n"
|
||||
"v64fG9PiO/yzcnMcmyiQiRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17Org3bhzjl\n"
|
||||
"P1v9mxnhMUF6cKojawHhRUzNlM47ni3niAIi9G7oyOzWPPO5std3eqx7\n"
|
||||
"-----END CERTIFICATE-----",.len=1935},
|
||||
|
||||
/* Telekom Security TLS ECC Root 2020 */
|
||||
{.str="-----BEGIN CERTIFICATE-----\n"
|
||||
"MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQswCQYDVQQG\n"
|
||||
|
||||
@@ -188,6 +188,103 @@ struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t
|
||||
return loop;
|
||||
}
|
||||
|
||||
/* Shared dispatch loop for both us_loop_run and us_loop_run_bun_tick */
|
||||
static void us_internal_dispatch_ready_polls(struct us_loop_t *loop) {
|
||||
#ifdef LIBUS_USE_EPOLL
|
||||
for (loop->current_ready_poll = 0; loop->current_ready_poll < loop->num_ready_polls; loop->current_ready_poll++) {
|
||||
struct us_poll_t *poll = GET_READY_POLL(loop, loop->current_ready_poll);
|
||||
if (LIKELY(poll)) {
|
||||
if (CLEAR_POINTER_TAG(poll) != poll) {
|
||||
Bun__internal_dispatch_ready_poll(loop, poll);
|
||||
continue;
|
||||
}
|
||||
int events = loop->ready_polls[loop->current_ready_poll].events;
|
||||
const int error = events & EPOLLERR;
|
||||
const int eof = events & EPOLLHUP;
|
||||
events &= us_poll_events(poll);
|
||||
if (events || error || eof) {
|
||||
us_internal_dispatch_ready_poll(poll, error, eof, events);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Kqueue delivers each filter (READ, WRITE, TIMER, etc.) as a separate kevent,
|
||||
* so the same fd/poll can appear twice in ready_polls. We coalesce them into a
|
||||
* single set of flags per poll before dispatching, matching epoll's behavior
|
||||
* where each fd appears once with a combined bitmask. */
|
||||
struct kevent_flags {
|
||||
uint8_t readable : 1;
|
||||
uint8_t writable : 1;
|
||||
uint8_t error : 1;
|
||||
uint8_t eof : 1;
|
||||
uint8_t skip : 1;
|
||||
uint8_t _pad : 3;
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct kevent_flags) == 1, "kevent_flags must be 1 byte");
|
||||
struct kevent_flags coalesced[LIBUS_MAX_READY_POLLS]; /* no zeroing needed — every index is written in the first pass */
|
||||
|
||||
/* First pass: decode kevents and coalesce same-poll entries */
|
||||
for (int i = 0; i < loop->num_ready_polls; i++) {
|
||||
struct us_poll_t *poll = GET_READY_POLL(loop, i);
|
||||
if (!poll || CLEAR_POINTER_TAG(poll) != poll) {
|
||||
coalesced[i] = (struct kevent_flags){ .skip = 1 };
|
||||
continue;
|
||||
}
|
||||
|
||||
const int16_t filter = loop->ready_polls[i].filter;
|
||||
const uint16_t flags = loop->ready_polls[i].flags;
|
||||
struct kevent_flags bits = {
|
||||
.readable = (filter == EVFILT_READ || filter == EVFILT_TIMER || filter == EVFILT_MACHPORT),
|
||||
.writable = (filter == EVFILT_WRITE),
|
||||
.error = !!(flags & EV_ERROR),
|
||||
.eof = !!(flags & EV_EOF),
|
||||
};
|
||||
|
||||
/* Look backward for a prior entry with the same poll to coalesce into.
|
||||
* Kqueue returns at most 2 kevents per fd (READ + WRITE). */
|
||||
int merged = 0;
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
if (!coalesced[j].skip && GET_READY_POLL(loop, j) == poll) {
|
||||
coalesced[j].readable |= bits.readable;
|
||||
coalesced[j].writable |= bits.writable;
|
||||
coalesced[j].error |= bits.error;
|
||||
coalesced[j].eof |= bits.eof;
|
||||
coalesced[i] = (struct kevent_flags){ .skip = 1 };
|
||||
merged = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!merged) {
|
||||
coalesced[i] = bits;
|
||||
}
|
||||
}
|
||||
|
||||
/* Second pass: dispatch everything in order — tagged pointers and coalesced events */
|
||||
for (loop->current_ready_poll = 0; loop->current_ready_poll < loop->num_ready_polls; loop->current_ready_poll++) {
|
||||
struct us_poll_t *poll = GET_READY_POLL(loop, loop->current_ready_poll);
|
||||
if (!poll) continue;
|
||||
|
||||
/* Tagged pointers (FilePoll) go through Bun's own dispatch */
|
||||
if (CLEAR_POINTER_TAG(poll) != poll) {
|
||||
Bun__internal_dispatch_ready_poll(loop, poll);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct kevent_flags bits = coalesced[loop->current_ready_poll];
|
||||
if (bits.skip) continue;
|
||||
|
||||
int events = (bits.readable ? LIBUS_SOCKET_READABLE : 0)
|
||||
| (bits.writable ? LIBUS_SOCKET_WRITABLE : 0);
|
||||
|
||||
events &= us_poll_events(poll);
|
||||
if (events || bits.error || bits.eof) {
|
||||
us_internal_dispatch_ready_poll(poll, bits.error, bits.eof, events);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void us_loop_run(struct us_loop_t *loop) {
|
||||
us_loop_integrate(loop);
|
||||
|
||||
@@ -205,41 +302,7 @@ void us_loop_run(struct us_loop_t *loop) {
|
||||
} while (IS_EINTR(loop->num_ready_polls));
|
||||
#endif
|
||||
|
||||
/* Iterate ready polls, dispatching them by type */
|
||||
for (loop->current_ready_poll = 0; loop->current_ready_poll < loop->num_ready_polls; loop->current_ready_poll++) {
|
||||
struct us_poll_t *poll = GET_READY_POLL(loop, loop->current_ready_poll);
|
||||
/* Any ready poll marked with nullptr will be ignored */
|
||||
if (LIKELY(poll)) {
|
||||
if (CLEAR_POINTER_TAG(poll) != poll) {
|
||||
Bun__internal_dispatch_ready_poll(loop, poll);
|
||||
continue;
|
||||
}
|
||||
#ifdef LIBUS_USE_EPOLL
|
||||
int events = loop->ready_polls[loop->current_ready_poll].events;
|
||||
const int error = events & EPOLLERR;
|
||||
const int eof = events & EPOLLHUP;
|
||||
#else
|
||||
const struct kevent64_s* current_kevent = &loop->ready_polls[loop->current_ready_poll];
|
||||
const int16_t filter = current_kevent->filter;
|
||||
const uint16_t flags = current_kevent->flags;
|
||||
const uint32_t fflags = current_kevent->fflags;
|
||||
|
||||
// > Multiple events which trigger the filter do not result in multiple kevents being placed on the kqueue
|
||||
// > Instead, the filter will aggregate the events into a single kevent struct
|
||||
// Note: EV_ERROR only sets the error in data as part of changelist. Not in this call!
|
||||
int events = 0
|
||||
| ((filter == EVFILT_READ) ? LIBUS_SOCKET_READABLE : 0)
|
||||
| ((filter == EVFILT_WRITE) ? LIBUS_SOCKET_WRITABLE : 0);
|
||||
const int error = (flags & (EV_ERROR)) ? ((int)fflags || 1) : 0;
|
||||
const int eof = (flags & (EV_EOF));
|
||||
#endif
|
||||
/* Always filter all polls by what they actually poll for (callback polls always poll for readable) */
|
||||
events &= us_poll_events(poll);
|
||||
if (events || error || eof) {
|
||||
us_internal_dispatch_ready_poll(poll, error, eof, events);
|
||||
}
|
||||
}
|
||||
}
|
||||
us_internal_dispatch_ready_polls(loop);
|
||||
|
||||
/* Emit post callback */
|
||||
us_internal_loop_post(loop);
|
||||
@@ -263,57 +326,33 @@ void us_loop_run_bun_tick(struct us_loop_t *loop, const struct timespec* timeout
|
||||
/* Emit pre callback */
|
||||
us_internal_loop_pre(loop);
|
||||
|
||||
|
||||
if (loop->data.jsc_vm)
|
||||
const unsigned int had_wakeups = __atomic_exchange_n(&loop->pending_wakeups, 0, __ATOMIC_ACQUIRE);
|
||||
const int will_idle_inside_event_loop = had_wakeups == 0 && (!timeout || (timeout->tv_nsec != 0 || timeout->tv_sec != 0));
|
||||
if (will_idle_inside_event_loop && loop->data.jsc_vm)
|
||||
Bun__JSC_onBeforeWait(loop->data.jsc_vm);
|
||||
|
||||
/* Fetch ready polls */
|
||||
#ifdef LIBUS_USE_EPOLL
|
||||
/* A zero timespec already has a fast path in ep_poll (fs/eventpoll.c):
|
||||
* it sets timed_out=1 (line 1952) and returns before any scheduler
|
||||
* interaction (line 1975). No equivalent of KEVENT_FLAG_IMMEDIATE needed. */
|
||||
loop->num_ready_polls = bun_epoll_pwait2(loop->fd, loop->ready_polls, 1024, timeout);
|
||||
#else
|
||||
do {
|
||||
loop->num_ready_polls = kevent64(loop->fd, NULL, 0, loop->ready_polls, 1024, 0, timeout);
|
||||
loop->num_ready_polls = kevent64(loop->fd, NULL, 0, loop->ready_polls, 1024,
|
||||
/* When we won't idle (pending wakeups or zero timeout), use KEVENT_FLAG_IMMEDIATE.
|
||||
* In XNU's kqueue_scan (bsd/kern/kern_event.c):
|
||||
* - KEVENT_FLAG_IMMEDIATE: returns immediately after kqueue_process() (line 8031)
|
||||
* - Zero timespec without the flag: falls through to assert_wait_deadline (line 8039)
|
||||
* and thread_block (line 8048), doing a full context switch cycle (~14us) even
|
||||
* though the deadline is already in the past. */
|
||||
will_idle_inside_event_loop ? 0 : KEVENT_FLAG_IMMEDIATE,
|
||||
timeout);
|
||||
} while (IS_EINTR(loop->num_ready_polls));
|
||||
#endif
|
||||
|
||||
|
||||
/* Iterate ready polls, dispatching them by type */
|
||||
for (loop->current_ready_poll = 0; loop->current_ready_poll < loop->num_ready_polls; loop->current_ready_poll++) {
|
||||
struct us_poll_t *poll = GET_READY_POLL(loop, loop->current_ready_poll);
|
||||
/* Any ready poll marked with nullptr will be ignored */
|
||||
if (LIKELY(poll)) {
|
||||
if (CLEAR_POINTER_TAG(poll) != poll) {
|
||||
Bun__internal_dispatch_ready_poll(loop, poll);
|
||||
continue;
|
||||
}
|
||||
#ifdef LIBUS_USE_EPOLL
|
||||
int events = loop->ready_polls[loop->current_ready_poll].events;
|
||||
const int error = events & EPOLLERR;
|
||||
const int eof = events & EPOLLHUP;
|
||||
#else
|
||||
const struct kevent64_s* current_kevent = &loop->ready_polls[loop->current_ready_poll];
|
||||
const int16_t filter = current_kevent->filter;
|
||||
const uint16_t flags = current_kevent->flags;
|
||||
const uint32_t fflags = current_kevent->fflags;
|
||||
|
||||
// > Multiple events which trigger the filter do not result in multiple kevents being placed on the kqueue
|
||||
// > Instead, the filter will aggregate the events into a single kevent struct
|
||||
int events = 0
|
||||
| ((filter & EVFILT_READ) ? LIBUS_SOCKET_READABLE : 0)
|
||||
| ((filter & EVFILT_WRITE) ? LIBUS_SOCKET_WRITABLE : 0);
|
||||
|
||||
// Note: EV_ERROR only sets the error in data as part of changelist. Not in this call!
|
||||
const int error = (flags & (EV_ERROR)) ? ((int)fflags || 1) : 0;
|
||||
const int eof = (flags & (EV_EOF));
|
||||
|
||||
#endif
|
||||
/* Always filter all polls by what they actually poll for (callback polls always poll for readable) */
|
||||
events &= us_poll_events(poll);
|
||||
if (events || error || eof) {
|
||||
us_internal_dispatch_ready_poll(poll, error, eof, events);
|
||||
}
|
||||
}
|
||||
}
|
||||
us_internal_dispatch_ready_polls(loop);
|
||||
|
||||
/* Emit post callback */
|
||||
us_internal_loop_post(loop);
|
||||
@@ -613,7 +652,7 @@ struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int f
|
||||
struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p;
|
||||
cb->loop = loop;
|
||||
cb->cb_expects_the_loop = 1;
|
||||
cb->leave_poll_ready = 0;
|
||||
cb->leave_poll_ready = 1; /* Edge-triggered: skip reading eventfd on wakeup */
|
||||
|
||||
return (struct us_internal_async *) cb;
|
||||
}
|
||||
@@ -635,12 +674,28 @@ void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_int
|
||||
internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb;
|
||||
|
||||
us_poll_start((struct us_poll_t *) a, internal_cb->loop, LIBUS_SOCKET_READABLE);
|
||||
#ifdef LIBUS_USE_EPOLL
|
||||
/* Upgrade to edge-triggered to avoid reading the eventfd on each wakeup */
|
||||
struct epoll_event event;
|
||||
event.events = EPOLLIN | EPOLLET;
|
||||
event.data.ptr = (struct us_poll_t *) a;
|
||||
epoll_ctl(internal_cb->loop->fd, EPOLL_CTL_MOD,
|
||||
us_poll_fd((struct us_poll_t *) a), &event);
|
||||
#endif
|
||||
}
|
||||
|
||||
void us_internal_async_wakeup(struct us_internal_async *a) {
|
||||
uint64_t one = 1;
|
||||
int written = write(us_poll_fd((struct us_poll_t *) a), &one, 8);
|
||||
(void)written;
|
||||
int fd = us_poll_fd((struct us_poll_t *) a);
|
||||
uint64_t val;
|
||||
for (val = 1; ; val = 1) {
|
||||
if (write(fd, &val, 8) >= 0) return;
|
||||
if (errno == EINTR) continue;
|
||||
if (errno == EAGAIN) {
|
||||
/* Counter overflow — drain and retry */
|
||||
if (read(fd, &val, 8) > 0 || errno == EAGAIN || errno == EINTR) continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@ struct us_loop_t {
|
||||
/* Number of polls owned by bun */
|
||||
unsigned int bun_polls;
|
||||
|
||||
/* Incremented atomically by wakeup(), swapped to 0 before epoll/kqueue.
|
||||
* If non-zero, the event loop will return immediately so we can skip the GC safepoint. */
|
||||
unsigned int pending_wakeups;
|
||||
|
||||
/* The list of ready polls */
|
||||
#ifdef LIBUS_USE_EPOLL
|
||||
alignas(LIBUS_EXT_ALIGNMENT) struct epoll_event ready_polls[1024];
|
||||
|
||||
@@ -93,6 +93,9 @@ void us_internal_loop_data_free(struct us_loop_t *loop) {
|
||||
}
|
||||
|
||||
void us_wakeup_loop(struct us_loop_t *loop) {
|
||||
#ifndef LIBUS_USE_LIBUV
|
||||
__atomic_fetch_add(&loop->pending_wakeups, 1, __ATOMIC_RELEASE);
|
||||
#endif
|
||||
us_internal_async_wakeup(loop->data.wakeup_async);
|
||||
}
|
||||
|
||||
@@ -393,8 +396,12 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
|
||||
if (events & LIBUS_SOCKET_WRITABLE && !error) {
|
||||
s->flags.last_write_failed = 0;
|
||||
#ifdef LIBUS_USE_KQUEUE
|
||||
/* Kqueue is one-shot so is not writable anymore */
|
||||
p->state.poll_type = us_internal_poll_type(p) | ((events & LIBUS_SOCKET_READABLE) ? POLL_TYPE_POLLING_IN : 0);
|
||||
/* Kqueue EVFILT_WRITE is one-shot so the filter is removed after delivery.
|
||||
* Clear POLLING_OUT to reflect this.
|
||||
* Keep POLLING_IN from the poll's own state, NOT from `events`: kqueue delivers
|
||||
* each filter as a separate kevent, so a pure EVFILT_WRITE event won't have
|
||||
* LIBUS_SOCKET_READABLE set even though the socket is still registered for reads. */
|
||||
p->state.poll_type = us_internal_poll_type(p) | (p->state.poll_type & POLL_TYPE_POLLING_IN);
|
||||
#endif
|
||||
|
||||
s = s->context->on_writable(s);
|
||||
@@ -412,7 +419,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
|
||||
us_poll_change(&s->p, loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE);
|
||||
} else {
|
||||
#ifdef LIBUS_USE_KQUEUE
|
||||
/* Kqueue one-shot writable needs to be re-enabled */
|
||||
/* Kqueue one-shot writable needs to be re-registered */
|
||||
us_poll_change(&s->p, loop, us_poll_events(&s->p) | LIBUS_SOCKET_WRITABLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -566,8 +566,10 @@ namespace uWS
|
||||
|
||||
|
||||
bool isHTTPMethod = (__builtin_expect(data[1] == '/', 1));
|
||||
bool isConnect = !isHTTPMethod && (isHTTPorHTTPSPrefixForProxies(data + 1, end) == 1 || ((data - start) == 7 && memcmp(start, "CONNECT", 7) == 0));
|
||||
if (isHTTPMethod || isConnect) [[likely]] {
|
||||
bool isConnect = !isHTTPMethod && ((data - start) == 7 && memcmp(start, "CONNECT", 7) == 0);
|
||||
/* Also accept proxy-style absolute URLs (http://... or https://...) as valid request targets */
|
||||
bool isProxyStyleURL = !isHTTPMethod && !isConnect && data[0] == 32 && isHTTPorHTTPSPrefixForProxies(data + 1, end) == 1;
|
||||
if (isHTTPMethod || isConnect || isProxyStyleURL) [[likely]] {
|
||||
header.key = {start, (size_t) (data - start)};
|
||||
data++;
|
||||
if(!isValidMethod(header.key, useStrictMethodValidation)) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Version: 11
|
||||
# Version: 12
|
||||
# A script that installs the dependencies needed to build and test Bun.
|
||||
# This should work on Windows 10 or newer with PowerShell.
|
||||
|
||||
@@ -387,7 +387,7 @@ function Install-PdbAddr2line {
|
||||
function Install-Llvm {
|
||||
Install-Package llvm `
|
||||
-Command clang-cl `
|
||||
-Version "19.1.7"
|
||||
-Version "21.1.8"
|
||||
Add-To-Path "$env:ProgramFiles\LLVM\bin"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Version: 27
|
||||
# Version: 28
|
||||
|
||||
# A script that installs the dependencies needed to build and test Bun.
|
||||
# This should work on macOS and Linux with a POSIX shell.
|
||||
@@ -1096,7 +1096,7 @@ install_build_essentials() {
|
||||
}
|
||||
|
||||
llvm_version_exact() {
|
||||
print "19.1.7"
|
||||
print "21.1.8"
|
||||
}
|
||||
|
||||
llvm_version() {
|
||||
@@ -1106,23 +1106,20 @@ llvm_version() {
|
||||
install_llvm() {
|
||||
case "$pm" in
|
||||
apt)
|
||||
# Debian 13 (Trixie) has LLVM 19 natively, and apt.llvm.org doesn't have a trixie repo
|
||||
if [ "$distro" = "debian" ]; then
|
||||
install_packages \
|
||||
"llvm-$(llvm_version)" \
|
||||
"clang-$(llvm_version)" \
|
||||
"lld-$(llvm_version)" \
|
||||
"llvm-$(llvm_version)-dev" \
|
||||
"llvm-$(llvm_version)-tools" \
|
||||
"libclang-rt-$(llvm_version)-dev"
|
||||
else
|
||||
bash="$(require bash)"
|
||||
llvm_script="$(download_file "https://apt.llvm.org/llvm.sh")"
|
||||
execute_sudo "$bash" "$llvm_script" "$(llvm_version)" all
|
||||
|
||||
# Install llvm-symbolizer explicitly to ensure it's available for ASAN
|
||||
install_packages "llvm-$(llvm_version)-tools"
|
||||
# apt.llvm.org's GPG key uses SHA1, which Debian 13+ (sqv) rejects since 2026-02-01.
|
||||
# Override the sequoia crypto policy to extend the SHA1 deadline.
|
||||
# See: https://github.com/llvm/llvm-project/issues/153385
|
||||
if [ -x /usr/bin/sqv ] && [ -f /usr/share/apt/default-sequoia.config ]; then
|
||||
execute_sudo mkdir -p /etc/crypto-policies/back-ends
|
||||
execute_sudo /usr/bin/sh -c "sed 's/sha1.second_preimage_resistance = 2026-02-01/sha1.second_preimage_resistance = 2028-02-01/' /usr/share/apt/default-sequoia.config > /etc/crypto-policies/back-ends/apt-sequoia.config"
|
||||
fi
|
||||
|
||||
bash="$(require bash)"
|
||||
llvm_script="$(download_file "https://apt.llvm.org/llvm.sh")"
|
||||
execute_sudo "$bash" "$llvm_script" "$(llvm_version)" all
|
||||
|
||||
# Install llvm-symbolizer explicitly to ensure it's available for ASAN
|
||||
install_packages "llvm-$(llvm_version)-tools"
|
||||
;;
|
||||
brew)
|
||||
install_packages "llvm@$(llvm_version)"
|
||||
@@ -1177,7 +1174,7 @@ install_gcc() {
|
||||
;;
|
||||
esac
|
||||
|
||||
llvm_v="19"
|
||||
llvm_v="21"
|
||||
|
||||
append_to_profile "export CC=clang-${llvm_v}"
|
||||
append_to_profile "export CXX=clang++-${llvm_v}"
|
||||
|
||||
@@ -77,10 +77,10 @@ const HAS_CCACHE = CCACHE !== null;
|
||||
// On Windows, use clang-cl for MSVC compatibility
|
||||
const CC_BASE = IS_WINDOWS
|
||||
? findExecutable(["clang-cl.exe", "clang-cl"]) || "clang-cl"
|
||||
: findExecutable(["clang-19", "clang"]) || "clang";
|
||||
: findExecutable(["clang-21", "clang"]) || "clang";
|
||||
const CXX_BASE = IS_WINDOWS
|
||||
? findExecutable(["clang-cl.exe", "clang-cl"]) || "clang-cl"
|
||||
: findExecutable(["clang++-19", "clang++"]) || "clang++";
|
||||
: findExecutable(["clang++-21", "clang++"]) || "clang++";
|
||||
|
||||
const CC = HAS_CCACHE ? CCACHE : CC_BASE;
|
||||
const CXX = HAS_CCACHE ? CCACHE : CXX_BASE;
|
||||
|
||||
@@ -57,7 +57,11 @@ async function build(args) {
|
||||
if (process.platform === "win32" && !process.env["VSINSTALLDIR"]) {
|
||||
const shellPath = join(import.meta.dirname, "vs-shell.ps1");
|
||||
const scriptPath = import.meta.filename;
|
||||
return spawn("pwsh", ["-NoProfile", "-NoLogo", "-File", shellPath, process.argv0, scriptPath, ...args]);
|
||||
// When cross-compiling to ARM64, tell vs-shell.ps1 to set up the x64_arm64 VS environment
|
||||
const toolchainIdx = args.indexOf("--toolchain");
|
||||
const requestedVsArch = toolchainIdx !== -1 && args[toolchainIdx + 1] === "windows-aarch64" ? "arm64" : undefined;
|
||||
const env = requestedVsArch ? { ...process.env, BUN_VS_ARCH: requestedVsArch } : undefined;
|
||||
return spawn("pwsh", ["-NoProfile", "-NoLogo", "-File", shellPath, process.argv0, scriptPath, ...args], { env });
|
||||
}
|
||||
|
||||
if (isCI) {
|
||||
@@ -92,21 +96,9 @@ async function build(args) {
|
||||
generateOptions["--toolchain"] = toolchainPath;
|
||||
}
|
||||
|
||||
// Windows ARM64: automatically set required options
|
||||
// Windows ARM64: log detection (compiler is selected by CMake/toolchain)
|
||||
if (isWindowsARM64) {
|
||||
// Use clang-cl instead of MSVC cl.exe for proper ARM64 flag support
|
||||
if (!generateOptions["-DCMAKE_C_COMPILER"]) {
|
||||
generateOptions["-DCMAKE_C_COMPILER"] = "clang-cl";
|
||||
}
|
||||
if (!generateOptions["-DCMAKE_CXX_COMPILER"]) {
|
||||
generateOptions["-DCMAKE_CXX_COMPILER"] = "clang-cl";
|
||||
}
|
||||
// Skip codegen by default since x64 bun crashes under WoW64 emulation
|
||||
// Can be overridden with -DSKIP_CODEGEN=OFF once ARM64 bun is available
|
||||
if (!generateOptions["-DSKIP_CODEGEN"]) {
|
||||
generateOptions["-DSKIP_CODEGEN"] = "ON";
|
||||
}
|
||||
console.log("Windows ARM64 detected: using clang-cl and SKIP_CODEGEN=ON");
|
||||
console.log("Windows ARM64 detected");
|
||||
}
|
||||
|
||||
const generateArgs = Object.entries(generateOptions).flatMap(([flag, value]) =>
|
||||
|
||||
@@ -12,7 +12,7 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
MODE="${1:-format}"
|
||||
|
||||
# Use LLVM_VERSION_MAJOR from environment or default to 19
|
||||
LLVM_VERSION="${LLVM_VERSION_MAJOR:-19}"
|
||||
LLVM_VERSION="${LLVM_VERSION_MAJOR:-21}"
|
||||
|
||||
# Ensure we have the specific clang-format version
|
||||
CLANG_FORMAT="clang-format-${LLVM_VERSION}"
|
||||
|
||||
@@ -5,7 +5,22 @@ $ErrorActionPreference = "Stop"
|
||||
|
||||
# Detect system architecture
|
||||
$script:IsARM64 = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq [System.Runtime.InteropServices.Architecture]::Arm64
|
||||
$script:VsArch = if ($script:IsARM64) { "arm64" } else { "amd64" }
|
||||
|
||||
# Allow overriding the target arch (useful for cross-compiling on x64 -> ARM64)
|
||||
$script:VsArch = $null
|
||||
if ($env:BUN_VS_ARCH) {
|
||||
switch ($env:BUN_VS_ARCH.ToLowerInvariant()) {
|
||||
"arm64" { $script:VsArch = "arm64" }
|
||||
"aarch64" { $script:VsArch = "arm64" }
|
||||
"amd64" { $script:VsArch = "amd64" }
|
||||
"x64" { $script:VsArch = "amd64" }
|
||||
default { throw "Invalid BUN_VS_ARCH: $env:BUN_VS_ARCH (expected arm64|amd64)" }
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $script:VsArch) {
|
||||
$script:VsArch = if ($script:IsARM64) { "arm64" } else { "amd64" }
|
||||
}
|
||||
|
||||
if($env:VSINSTALLDIR -eq $null) {
|
||||
Write-Host "Loading Visual Studio environment, this may take a second..."
|
||||
@@ -17,17 +32,29 @@ if($env:VSINSTALLDIR -eq $null) {
|
||||
|
||||
$vsDir = (& $vswhere -prerelease -latest -property installationPath)
|
||||
if ($vsDir -eq $null) {
|
||||
$vsDir = Get-ChildItem -Path "C:\Program Files\Microsoft Visual Studio\2022" -Directory
|
||||
# Check common VS installation paths
|
||||
$searchPaths = @(
|
||||
"C:\Program Files\Microsoft Visual Studio\2022",
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2022"
|
||||
)
|
||||
foreach ($searchPath in $searchPaths) {
|
||||
if (Test-Path $searchPath) {
|
||||
$vsDir = (Get-ChildItem -Path $searchPath -Directory | Select-Object -First 1).FullName
|
||||
if ($vsDir -ne $null) { break }
|
||||
}
|
||||
}
|
||||
if ($vsDir -eq $null) {
|
||||
throw "Visual Studio directory not found."
|
||||
}
|
||||
$vsDir = $vsDir.FullName
|
||||
}
|
||||
|
||||
Push-Location $vsDir
|
||||
try {
|
||||
$vsShell = (Join-Path -Path $vsDir -ChildPath "Common7\Tools\Launch-VsDevShell.ps1")
|
||||
. $vsShell -Arch $script:VsArch -HostArch $script:VsArch
|
||||
# Visual Studio's Launch-VsDevShell.ps1 only supports x86/amd64 for HostArch
|
||||
# For ARM64 builds, use amd64 as HostArch since it can cross-compile to ARM64
|
||||
$hostArch = if ($script:VsArch -eq "arm64") { "amd64" } else { $script:VsArch }
|
||||
. $vsShell -Arch $script:VsArch -HostArch $hostArch
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
@@ -61,7 +88,7 @@ if ($args.Count -gt 0) {
|
||||
$displayArgs += $arg
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Write-Host "$ $command $displayArgs"
|
||||
& $command $commandArgs
|
||||
exit $LASTEXITCODE
|
||||
|
||||
16
shell.nix
16
shell.nix
@@ -8,9 +8,9 @@ pkgs.mkShell rec {
|
||||
# Core build tools (matching bootstrap.sh)
|
||||
cmake
|
||||
ninja
|
||||
clang_19
|
||||
llvm_19
|
||||
lld_19
|
||||
clang_21
|
||||
llvm_21
|
||||
lld_21
|
||||
nodejs_24
|
||||
bun
|
||||
rustc
|
||||
@@ -77,10 +77,10 @@ pkgs.mkShell rec {
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
export CC="${pkgs.lib.getExe pkgs.clang_19}"
|
||||
export CXX="${pkgs.lib.getExe' pkgs.clang_19 "clang++"}"
|
||||
export AR="${pkgs.llvm_19}/bin/llvm-ar"
|
||||
export RANLIB="${pkgs.llvm_19}/bin/llvm-ranlib"
|
||||
export CC="${pkgs.lib.getExe pkgs.clang_21}"
|
||||
export CXX="${pkgs.lib.getExe' pkgs.clang_21 "clang++"}"
|
||||
export AR="${pkgs.llvm_21}/bin/llvm-ar"
|
||||
export RANLIB="${pkgs.llvm_21}/bin/llvm-ranlib"
|
||||
export CMAKE_C_COMPILER="$CC"
|
||||
export CMAKE_CXX_COMPILER="$CXX"
|
||||
export CMAKE_AR="$AR"
|
||||
@@ -88,7 +88,7 @@ pkgs.mkShell rec {
|
||||
export CMAKE_SYSTEM_PROCESSOR=$(uname -m)
|
||||
export TMPDIR=''${TMPDIR:-/tmp}
|
||||
'' + pkgs.lib.optionalString pkgs.stdenv.isLinux ''
|
||||
export LD="${pkgs.lib.getExe' pkgs.lld_19 "ld.lld"}"
|
||||
export LD="${pkgs.lib.getExe' pkgs.lld_21 "ld.lld"}"
|
||||
export NIX_CFLAGS_LINK="''${NIX_CFLAGS_LINK:+$NIX_CFLAGS_LINK }-fuse-ld=lld"
|
||||
export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath packages}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
'' + ''
|
||||
|
||||
292
src/CLAUDE.md
292
src/CLAUDE.md
@@ -10,3 +10,295 @@ Conventions:
|
||||
- Prefer `@import` at the **bottom** of the file, but the auto formatter will move them so you don't need to worry about it.
|
||||
- **Never** use `@import()` inline inside of functions. **Always** put them at the bottom of the file or containing struct. Imports in Zig are free of side-effects, so there's no such thing as a "dynamic" import.
|
||||
- You must be patient with the build.
|
||||
|
||||
## Prefer Bun APIs over `std`
|
||||
|
||||
**Always use `bun.*` APIs instead of `std.*`.** The `bun` namespace (`@import("bun")`) provides cross-platform wrappers that preserve OS error info and never use `unreachable`. Using `std.fs`, `std.posix`, or `std.os` directly is wrong in this codebase.
|
||||
|
||||
| Instead of | Use |
|
||||
| ------------------------------------------------------------ | ------------------------------------ |
|
||||
| `std.fs.File` | `bun.sys.File` |
|
||||
| `std.fs.cwd()` | `bun.FD.cwd()` |
|
||||
| `std.posix.open/read/write/stat/mkdir/unlink/rename/symlink` | `bun.sys.*` equivalents |
|
||||
| `std.fs.path.join/dirname/basename` | `bun.path.join/dirname/basename` |
|
||||
| `std.mem.eql/indexOf/startsWith` (for strings) | `bun.strings.eql/indexOf/startsWith` |
|
||||
| `std.posix.O` / `std.posix.mode_t` / `std.posix.fd_t` | `bun.O` / `bun.Mode` / `bun.FD` |
|
||||
| `std.process.Child` | `bun.spawnSync` |
|
||||
| `catch bun.outOfMemory()` | `bun.handleOom(...)` |
|
||||
|
||||
## `bun.sys` — System Calls (`src/sys.zig`)
|
||||
|
||||
All return `Maybe(T)` — a tagged union of `.result: T` or `.err: bun.sys.Error`:
|
||||
|
||||
```zig
|
||||
const fd = switch (bun.sys.open(path, bun.O.RDONLY, 0)) {
|
||||
.result => |fd| fd,
|
||||
.err => |err| return .{ .err = err },
|
||||
};
|
||||
// Or: const fd = try bun.sys.open(path, bun.O.RDONLY, 0).unwrap();
|
||||
```
|
||||
|
||||
Key functions (all take `bun.FileDescriptor`, not `std.posix.fd_t`):
|
||||
|
||||
- `open`, `openat`, `openA` (non-sentinel) → `Maybe(bun.FileDescriptor)`
|
||||
- `read`, `readAll`, `pread` → `Maybe(usize)`
|
||||
- `write`, `pwrite`, `writev` → `Maybe(usize)`
|
||||
- `stat`, `fstat`, `lstat` → `Maybe(bun.Stat)`
|
||||
- `mkdir`, `unlink`, `rename`, `symlink`, `chmod`, `fchmod`, `fchown` → `Maybe(void)`
|
||||
- `readlink`, `getFdPath`, `getcwd` → `Maybe` of path slice
|
||||
- `getFileSize`, `dup`, `sendfile`, `mmap`
|
||||
|
||||
Use `bun.O.RDONLY`, `bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC`, etc. for open flags.
|
||||
|
||||
### `bun.sys.File` (`src/sys/File.zig`)
|
||||
|
||||
Higher-level file handle wrapping `bun.FileDescriptor`:
|
||||
|
||||
```zig
|
||||
// One-shot read: open + read + close
|
||||
const bytes = switch (bun.sys.File.readFrom(bun.FD.cwd(), path, allocator)) {
|
||||
.result => |b| b,
|
||||
.err => |err| return .{ .err = err },
|
||||
};
|
||||
|
||||
// One-shot write: open + write + close
|
||||
switch (bun.sys.File.writeFile(bun.FD.cwd(), path, data)) {
|
||||
.result => {},
|
||||
.err => |err| return .{ .err = err },
|
||||
}
|
||||
```
|
||||
|
||||
Key methods:
|
||||
|
||||
- `File.open/openat/makeOpen` → `Maybe(File)` (`makeOpen` creates parent dirs)
|
||||
- `file.read/readAll/write/writeAll` — single or looped I/O
|
||||
- `file.readToEnd(allocator)` — read entire file into allocated buffer
|
||||
- `File.readFrom(dir_fd, path, allocator)` — open + read + close
|
||||
- `File.writeFile(dir_fd, path, data)` — open + write + close
|
||||
- `file.stat()`, `file.close()`, `file.writer()`, `file.reader()`
|
||||
|
||||
### `bun.FD` (`src/fd.zig`)
|
||||
|
||||
Cross-platform file descriptor. Use `bun.FD.cwd()` for cwd, `bun.invalid_fd` for sentinel, `fd.close()` to close.
|
||||
|
||||
### `bun.sys.Error` (`src/sys/Error.zig`)
|
||||
|
||||
Preserves errno, syscall tag, and file path. Convert to JS: `err.toSystemError().toErrorInstance(globalObject)`.
|
||||
|
||||
## `bun.strings` — String Utilities (`src/string/immutable.zig`)
|
||||
|
||||
SIMD-accelerated string operations. Use instead of `std.mem` for strings.
|
||||
|
||||
```zig
|
||||
// Searching
|
||||
strings.indexOf(haystack, needle) // ?usize
|
||||
strings.contains(haystack, needle) // bool
|
||||
strings.containsChar(haystack, char) // bool
|
||||
strings.indexOfChar(haystack, char) // ?u32
|
||||
strings.indexOfAny(str, comptime chars) // ?OptionalUsize (SIMD-accelerated)
|
||||
|
||||
// Comparison
|
||||
strings.eql(a, b) // bool
|
||||
strings.eqlComptime(str, comptime literal) // bool — optimized
|
||||
strings.eqlCaseInsensitiveASCII(a, b, comptime true) // 3rd arg = check_len
|
||||
|
||||
// Prefix/Suffix
|
||||
strings.startsWith(str, prefix) // bool
|
||||
strings.endsWith(str, suffix) // bool
|
||||
strings.hasPrefixComptime(str, comptime prefix) // bool — optimized
|
||||
strings.hasSuffixComptime(str, comptime suffix) // bool — optimized
|
||||
|
||||
// Trimming
|
||||
strings.trim(str, comptime chars) // strip from both ends
|
||||
strings.trimSpaces(str) // strip whitespace
|
||||
|
||||
// Encoding conversions
|
||||
strings.toUTF8Alloc(allocator, utf16) // ![]u8
|
||||
strings.toUTF16Alloc(allocator, utf8) // !?[]u16
|
||||
strings.toUTF8FromLatin1(allocator, latin1) // !?Managed(u8)
|
||||
strings.firstNonASCII(slice) // ?u32
|
||||
```
|
||||
|
||||
Bun handles UTF-8, Latin-1, and UTF-16/WTF-16 because JSC uses Latin-1 and UTF-16 internally. Latin-1 is NOT UTF-8 — bytes 128-255 are single chars in Latin-1 but invalid UTF-8.
|
||||
|
||||
### `bun.String` (`src/string.zig`)
|
||||
|
||||
Bridges Zig and JavaScriptCore. Prefer over `ZigString` in new code.
|
||||
|
||||
```zig
|
||||
const s = bun.String.cloneUTF8(utf8_slice); // copies into WTFStringImpl
|
||||
const s = bun.String.borrowUTF8(utf8_slice); // no copy, caller keeps alive
|
||||
const utf8 = s.toUTF8(allocator); // ZigString.Slice
|
||||
defer utf8.deinit();
|
||||
const js_value = s.toJS(globalObject);
|
||||
|
||||
// Create a JS string value directly from UTF-8 bytes:
|
||||
const js_str = try bun.String.createUTF8ForJS(globalObject, utf8_slice);
|
||||
```
|
||||
|
||||
## `bun.path` — Path Manipulation (`src/resolver/resolve_path.zig`)
|
||||
|
||||
Use instead of `std.fs.path`. Platform param: `.auto` (current platform), `.posix`, `.windows`, `.loose` (both separators).
|
||||
|
||||
```zig
|
||||
// Join paths — uses threadlocal buffer, result must be copied if it needs to persist
|
||||
bun.path.join(&.{ dir, filename }, .auto)
|
||||
bun.path.joinZ(&.{ dir, filename }, .auto) // null-terminated
|
||||
|
||||
// Join into a caller-provided buffer
|
||||
bun.path.joinStringBuf(&buf, &.{ a, b }, .auto)
|
||||
bun.path.joinStringBufZ(&buf, &.{ a, b }, .auto) // null-terminated
|
||||
|
||||
// Resolve against an absolute base (like Node.js path.resolve)
|
||||
bun.path.joinAbsString(cwd, &.{ relative_path }, .auto)
|
||||
bun.path.joinAbsStringBufZ(cwd, &buf, &.{ relative_path }, .auto)
|
||||
|
||||
// Path components
|
||||
bun.path.dirname(path, .auto)
|
||||
bun.path.basename(path)
|
||||
|
||||
// Relative path between two absolute paths
|
||||
bun.path.relative(from, to)
|
||||
bun.path.relativeAlloc(allocator, from, to)
|
||||
|
||||
// Normalize (resolve `.` and `..`)
|
||||
bun.path.normalizeBuf(path, &buf, .auto)
|
||||
|
||||
// Null-terminate a path into a buffer
|
||||
bun.path.z(path, &buf) // returns [:0]const u8
|
||||
```
|
||||
|
||||
Use `bun.PathBuffer` for path buffers: `var buf: bun.PathBuffer = undefined;`
|
||||
|
||||
For pooled path buffers (avoids 64KB stack allocations on Windows):
|
||||
|
||||
```zig
|
||||
const buf = bun.path_buffer_pool.get();
|
||||
defer bun.path_buffer_pool.put(buf);
|
||||
```
|
||||
|
||||
## URL Parsing
|
||||
|
||||
Prefer `bun.jsc.URL` (WHATWG-compliant, backed by WebKit C++) over `bun.URL.parse` (internal, doesn't properly handle errors or invalid URLs).
|
||||
|
||||
```zig
|
||||
// Parse a URL string (returns null if invalid)
|
||||
const url = bun.jsc.URL.fromUTF8(href_string) orelse return error.InvalidURL;
|
||||
defer url.deinit();
|
||||
|
||||
url.protocol() // bun.String
|
||||
url.pathname() // bun.String
|
||||
url.search() // bun.String
|
||||
url.hash() // bun.String (includes leading '#')
|
||||
url.port() // u32 (maxInt(u32) if not set, otherwise u16 range)
|
||||
|
||||
// NOTE: host/hostname are SWAPPED vs JS:
|
||||
url.host() // hostname WITHOUT port (opposite of JS!)
|
||||
url.hostname() // hostname WITH port (opposite of JS!)
|
||||
|
||||
// Normalize a URL string (percent-encode, punycode, etc.)
|
||||
const normalized = bun.jsc.URL.hrefFromString(bun.String.borrowUTF8(input));
|
||||
if (normalized.tag == .Dead) return error.InvalidURL;
|
||||
defer normalized.deref();
|
||||
|
||||
// Join base + relative URLs
|
||||
const joined = bun.jsc.URL.join(base_str, relative_str);
|
||||
defer joined.deref();
|
||||
|
||||
// Convert between file paths and file:// URLs
|
||||
const file_url = bun.jsc.URL.fileURLFromString(path_str); // path → file://
|
||||
const file_path = bun.jsc.URL.pathFromFileURL(url_str); // file:// → path
|
||||
```
|
||||
|
||||
## MIME Types (`src/http/MimeType.zig`)
|
||||
|
||||
```zig
|
||||
const MimeType = bun.http.MimeType;
|
||||
|
||||
// Look up by file extension (without leading dot)
|
||||
const mime = MimeType.byExtension("html"); // MimeType{ .value = "text/html", .category = .html }
|
||||
const mime = MimeType.byExtensionNoDefault("xyz"); // ?MimeType (null if unknown)
|
||||
|
||||
// Category checks
|
||||
mime.category // .javascript, .css, .html, .json, .image, .text, .wasm, .font, .video, .audio, ...
|
||||
mime.category.isCode()
|
||||
```
|
||||
|
||||
Common constants: `MimeType.javascript`, `MimeType.json`, `MimeType.html`, `MimeType.css`, `MimeType.text`, `MimeType.wasm`, `MimeType.ico`, `MimeType.other`.
|
||||
|
||||
## Memory & Allocators
|
||||
|
||||
**Use `bun.default_allocator` for almost everything.** It's backed by mimalloc.
|
||||
|
||||
`bun.handleOom(expr)` converts `error.OutOfMemory` into a crash without swallowing other errors:
|
||||
|
||||
```zig
|
||||
const buf = bun.handleOom(allocator.alloc(u8, size)); // correct
|
||||
// NOT: allocator.alloc(u8, size) catch bun.outOfMemory() — could swallow non-OOM errors
|
||||
```
|
||||
|
||||
## Environment Variables (`src/env_var.zig`)
|
||||
|
||||
Type-safe, cached environment variable accessors via `bun.env_var`:
|
||||
|
||||
```zig
|
||||
bun.env_var.HOME.get() // ?[]const u8
|
||||
bun.env_var.CI.get() // ?bool
|
||||
bun.env_var.BUN_CONFIG_DNS_TIME_TO_LIVE_SECONDS.get() // u64 (has default: 30)
|
||||
```
|
||||
|
||||
## Logging (`src/output.zig`)
|
||||
|
||||
```zig
|
||||
const log = bun.Output.scoped(.MY_FEATURE, .visible); // .hidden = opt-in via BUN_DEBUG_MY_FEATURE=1
|
||||
log("processing {d} items", .{count});
|
||||
|
||||
// Color output (convenience wrappers auto-detect TTY):
|
||||
bun.Output.pretty("<green>success<r>: {s}\n", .{msg});
|
||||
bun.Output.prettyErrorln("<red>error<r>: {s}", .{msg});
|
||||
```
|
||||
|
||||
## Spawning Subprocesses (`src/bun.js/api/bun/process.zig`)
|
||||
|
||||
Use `bun.spawnSync` instead of `std.process.Child`:
|
||||
|
||||
```zig
|
||||
switch (bun.spawnSync(&.{
|
||||
.argv = argv,
|
||||
.envp = null, // inherit parent env
|
||||
.cwd = cwd,
|
||||
.stdout = .buffer, // capture
|
||||
.stderr = .inherit, // pass through
|
||||
.stdin = .ignore,
|
||||
|
||||
.windows = if (bun.Environment.isWindows) .{
|
||||
.loop = bun.jsc.EventLoopHandle.init(bun.jsc.MiniEventLoop.initGlobal(env, null)),
|
||||
},
|
||||
}) catch return) {
|
||||
.err => |err| { /* bun.sys.Error */ },
|
||||
.result => |result| {
|
||||
defer result.deinit();
|
||||
const stdout = result.stdout.items;
|
||||
if (result.status.isOK()) { ... }
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Options: `argv: []const []const u8`, `envp: ?[*:null]?[*:0]const u8` (null = inherit), `argv0: ?[*:0]const u8`. Stdio: `.inherit`, `.ignore`, `.buffer`.
|
||||
|
||||
## Common Patterns
|
||||
|
||||
```zig
|
||||
// Read a file
|
||||
const contents = switch (bun.sys.File.readFrom(bun.FD.cwd(), path, allocator)) {
|
||||
.result => |bytes| bytes,
|
||||
.err => |err| { globalObject.throwValue(err.toSystemError().toErrorInstance(globalObject)); return .zero; },
|
||||
};
|
||||
|
||||
// Create directories recursively
|
||||
bun.makePath(dir.stdDir(), sub_path) catch |err| { ... };
|
||||
|
||||
// Hashing
|
||||
bun.hash(bytes) // u64 — wyhash
|
||||
bun.hash32(bytes) // u32
|
||||
```
|
||||
|
||||
@@ -935,7 +935,7 @@ pub const StandaloneModuleGraph = struct {
|
||||
|
||||
var remain = bytes;
|
||||
while (remain.len > 0) {
|
||||
switch (Syscall.write(cloned_executable_fd, bytes)) {
|
||||
switch (Syscall.write(cloned_executable_fd, remain)) {
|
||||
.result => |written| remain = remain[written..],
|
||||
.err => |err| {
|
||||
Output.prettyErrorln("<r><red>error<r><d>:<r> failed to write to temporary file\n{f}", .{err});
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const safety_checks = bun.Environment.isDebug or bun.Environment.enable_asan;
|
||||
|
||||
#heap: *mimalloc.Heap,
|
||||
thread_id: if (safety_checks) std.Thread.Id else void,
|
||||
#heap: if (safety_checks) Owned(*DebugHeap) else *mimalloc.Heap,
|
||||
|
||||
/// Uses the default thread-local heap. This type is zero-sized.
|
||||
///
|
||||
@@ -23,18 +20,18 @@ pub const Default = struct {
|
||||
///
|
||||
/// This type is a `GenericAllocator`; see `src/allocators.zig`.
|
||||
pub const Borrowed = struct {
|
||||
#heap: *mimalloc.Heap,
|
||||
#heap: BorrowedHeap,
|
||||
|
||||
pub fn allocator(self: Borrowed) std.mem.Allocator {
|
||||
return .{ .ptr = self.#heap, .vtable = c_allocator_vtable };
|
||||
return .{ .ptr = self.#heap, .vtable = &c_allocator_vtable };
|
||||
}
|
||||
|
||||
pub fn getDefault() Borrowed {
|
||||
return .{ .#heap = mimalloc.mi_heap_main() };
|
||||
return .{ .#heap = getThreadHeap() };
|
||||
}
|
||||
|
||||
pub fn gc(self: Borrowed) void {
|
||||
mimalloc.mi_heap_collect(self.#heap, false);
|
||||
mimalloc.mi_heap_collect(self.getMimallocHeap(), false);
|
||||
}
|
||||
|
||||
pub fn helpCatchMemoryIssues(self: Borrowed) void {
|
||||
@@ -44,17 +41,30 @@ pub const Borrowed = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ownsPtr(self: Borrowed, ptr: *const anyopaque) bool {
|
||||
return mimalloc.mi_heap_check_owned(self.getMimallocHeap(), ptr);
|
||||
}
|
||||
|
||||
fn fromOpaque(ptr: *anyopaque) Borrowed {
|
||||
return .{ .#heap = @ptrCast(@alignCast(ptr)) };
|
||||
}
|
||||
|
||||
fn getMimallocHeap(self: Borrowed) *mimalloc.Heap {
|
||||
return if (comptime safety_checks) self.#heap.inner else self.#heap;
|
||||
}
|
||||
|
||||
fn assertThreadLock(self: Borrowed) void {
|
||||
if (comptime safety_checks) self.#heap.thread_lock.assertLocked();
|
||||
}
|
||||
|
||||
fn alignedAlloc(self: Borrowed, len: usize, alignment: Alignment) ?[*]u8 {
|
||||
log("Malloc: {d}\n", .{len});
|
||||
|
||||
const heap = self.getMimallocHeap();
|
||||
const ptr: ?*anyopaque = if (mimalloc.mustUseAlignedAlloc(alignment))
|
||||
mimalloc.mi_heap_malloc_aligned(self.#heap, len, alignment.toByteUnits())
|
||||
mimalloc.mi_heap_malloc_aligned(heap, len, alignment.toByteUnits())
|
||||
else
|
||||
mimalloc.mi_heap_malloc(self.#heap, len);
|
||||
mimalloc.mi_heap_malloc(heap, len);
|
||||
|
||||
if (comptime bun.Environment.isDebug) {
|
||||
const usable = mimalloc.mi_malloc_usable_size(ptr);
|
||||
@@ -79,17 +89,42 @@ pub const Borrowed = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const BorrowedHeap = if (safety_checks) *DebugHeap else *mimalloc.Heap;
|
||||
|
||||
const DebugHeap = struct {
|
||||
inner: *mimalloc.Heap,
|
||||
thread_lock: bun.safety.ThreadLock,
|
||||
|
||||
pub const deinit = void;
|
||||
};
|
||||
|
||||
threadlocal var thread_heap: if (safety_checks) ?DebugHeap else void = if (safety_checks) null;
|
||||
|
||||
fn getThreadHeap() BorrowedHeap {
|
||||
if (comptime !safety_checks) return mimalloc.mi_heap_get_default();
|
||||
if (thread_heap == null) {
|
||||
thread_heap = .{
|
||||
.inner = mimalloc.mi_heap_get_default(),
|
||||
.thread_lock = .initLocked(),
|
||||
};
|
||||
}
|
||||
return &thread_heap.?;
|
||||
}
|
||||
|
||||
const log = bun.Output.scoped(.mimalloc, .hidden);
|
||||
|
||||
pub fn allocator(self: Self) std.mem.Allocator {
|
||||
self.assertThreadOwnership();
|
||||
return self.borrow().allocator();
|
||||
}
|
||||
|
||||
pub fn borrow(self: Self) Borrowed {
|
||||
return .{ .#heap = self.#heap };
|
||||
return .{ .#heap = if (comptime safety_checks) self.#heap.get() else self.#heap };
|
||||
}
|
||||
|
||||
/// Internally, mimalloc calls mi_heap_get_default()
|
||||
/// to get the default heap.
|
||||
/// It uses pthread_getspecific to do that.
|
||||
/// We can save those extra calls if we just do it once in here
|
||||
pub fn getThreadLocalDefault() std.mem.Allocator {
|
||||
if (bun.Environment.enable_asan) return bun.default_allocator;
|
||||
return Borrowed.getDefault().allocator();
|
||||
@@ -122,15 +157,22 @@ pub fn dumpStats(_: Self) void {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
mimalloc.mi_heap_destroy(self.#heap);
|
||||
const mimalloc_heap = self.borrow().getMimallocHeap();
|
||||
if (comptime safety_checks) {
|
||||
self.#heap.deinit();
|
||||
}
|
||||
mimalloc.mi_heap_destroy(mimalloc_heap);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn init() Self {
|
||||
return .{
|
||||
.#heap = mimalloc.mi_heap_new() orelse bun.outOfMemory(),
|
||||
.thread_id = if (safety_checks) std.Thread.getCurrentId() else {},
|
||||
};
|
||||
const mimalloc_heap = mimalloc.mi_heap_new() orelse bun.outOfMemory();
|
||||
if (comptime !safety_checks) return .{ .#heap = mimalloc_heap };
|
||||
const heap: Owned(*DebugHeap) = .new(.{
|
||||
.inner = mimalloc_heap,
|
||||
.thread_lock = .initLocked(),
|
||||
});
|
||||
return .{ .#heap = heap };
|
||||
}
|
||||
|
||||
pub fn gc(self: Self) void {
|
||||
@@ -141,16 +183,8 @@ pub fn helpCatchMemoryIssues(self: Self) void {
|
||||
self.borrow().helpCatchMemoryIssues();
|
||||
}
|
||||
|
||||
fn assertThreadOwnership(self: Self) void {
|
||||
if (comptime safety_checks) {
|
||||
const current_thread = std.Thread.getCurrentId();
|
||||
if (current_thread != self.thread_id) {
|
||||
std.debug.panic(
|
||||
"MimallocArena used from wrong thread: arena belongs to thread {d}, but current thread is {d}",
|
||||
.{ self.thread_id, current_thread },
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn ownsPtr(self: Self, ptr: *const anyopaque) bool {
|
||||
return self.borrow().ownsPtr(ptr);
|
||||
}
|
||||
|
||||
fn alignedAllocSize(ptr: [*]u8) usize {
|
||||
@@ -159,10 +193,13 @@ fn alignedAllocSize(ptr: [*]u8) usize {
|
||||
|
||||
fn vtable_alloc(ptr: *anyopaque, len: usize, alignment: Alignment, _: usize) ?[*]u8 {
|
||||
const self: Borrowed = .fromOpaque(ptr);
|
||||
self.assertThreadLock();
|
||||
return self.alignedAlloc(len, alignment);
|
||||
}
|
||||
|
||||
fn vtable_resize(_: *anyopaque, buf: []u8, _: Alignment, new_len: usize, _: usize) bool {
|
||||
fn vtable_resize(ptr: *anyopaque, buf: []u8, _: Alignment, new_len: usize, _: usize) bool {
|
||||
const self: Borrowed = .fromOpaque(ptr);
|
||||
self.assertThreadLock();
|
||||
return mimalloc.mi_expand(buf.ptr, new_len) != null;
|
||||
}
|
||||
|
||||
@@ -186,17 +223,39 @@ fn vtable_free(
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to expand or shrink memory, allowing relocation.
|
||||
///
|
||||
/// `memory.len` must equal the length requested from the most recent
|
||||
/// successful call to `alloc`, `resize`, or `remap`. `alignment` must
|
||||
/// equal the same value that was passed as the `alignment` parameter to
|
||||
/// the original `alloc` call.
|
||||
///
|
||||
/// A non-`null` return value indicates the resize was successful. The
|
||||
/// allocation may have same address, or may have been relocated. In either
|
||||
/// case, the allocation now has size of `new_len`. A `null` return value
|
||||
/// indicates that the resize would be equivalent to allocating new memory,
|
||||
/// copying the bytes from the old memory, and then freeing the old memory.
|
||||
/// In such case, it is more efficient for the caller to perform the copy.
|
||||
///
|
||||
/// `new_len` must be greater than zero.
|
||||
///
|
||||
/// `ret_addr` is optionally provided as the first return address of the
|
||||
/// allocation call stack. If the value is `0` it means no return address
|
||||
/// has been provided.
|
||||
fn vtable_remap(ptr: *anyopaque, buf: []u8, alignment: Alignment, new_len: usize, _: usize) ?[*]u8 {
|
||||
const self: Borrowed = .fromOpaque(ptr);
|
||||
const value = mimalloc.mi_heap_realloc_aligned(self.#heap, buf.ptr, new_len, alignment.toByteUnits());
|
||||
self.assertThreadLock();
|
||||
const heap = self.getMimallocHeap();
|
||||
const aligned_size = alignment.toByteUnits();
|
||||
const value = mimalloc.mi_heap_realloc_aligned(heap, buf.ptr, new_len, aligned_size);
|
||||
return @ptrCast(value);
|
||||
}
|
||||
|
||||
pub fn isInstance(alloc: std.mem.Allocator) bool {
|
||||
return alloc.vtable == c_allocator_vtable;
|
||||
return alloc.vtable == &c_allocator_vtable;
|
||||
}
|
||||
|
||||
const c_allocator_vtable = &std.mem.Allocator.VTable{
|
||||
const c_allocator_vtable = std.mem.Allocator.VTable{
|
||||
.alloc = vtable_alloc,
|
||||
.resize = vtable_resize,
|
||||
.remap = vtable_remap,
|
||||
@@ -209,3 +268,5 @@ const Alignment = std.mem.Alignment;
|
||||
const bun = @import("bun");
|
||||
const assert = bun.assert;
|
||||
const mimalloc = bun.mimalloc;
|
||||
const Owned = bun.ptr.Owned;
|
||||
const safety_checks = bun.Environment.ci_assert;
|
||||
|
||||
@@ -60,29 +60,17 @@ pub const Heap = opaque {
|
||||
return mi_heap_realloc(self, p, newsize);
|
||||
}
|
||||
|
||||
pub fn isOwned(self: *Heap, p: ?*const anyopaque) bool {
|
||||
return mi_heap_contains(self, p);
|
||||
pub fn isOwned(self: *Heap, p: ?*anyopaque) bool {
|
||||
return mi_heap_check_owned(self, p);
|
||||
}
|
||||
};
|
||||
pub extern fn mi_heap_new() ?*Heap;
|
||||
pub extern fn mi_heap_delete(heap: *Heap) void;
|
||||
pub extern fn mi_heap_destroy(heap: *Heap) void;
|
||||
pub extern fn mi_heap_set_default(heap: *Heap) *Heap;
|
||||
pub extern fn mi_heap_get_default() *Heap;
|
||||
pub extern fn mi_heap_get_backing() *Heap;
|
||||
pub extern fn mi_heap_collect(heap: *Heap, force: bool) void;
|
||||
pub extern fn mi_heap_main() *Heap;
|
||||
|
||||
// Thread-local heap (theap) API - new in mimalloc v3
|
||||
pub const THeap = opaque {};
|
||||
pub extern fn mi_theap_get_default() *THeap;
|
||||
pub extern fn mi_theap_set_default(theap: *THeap) *THeap;
|
||||
pub extern fn mi_theap_collect(theap: *THeap, force: bool) void;
|
||||
pub extern fn mi_theap_malloc(theap: *THeap, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_zalloc(theap: *THeap, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_calloc(theap: *THeap, count: usize, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_malloc_small(theap: *THeap, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_malloc_aligned(theap: *THeap, size: usize, alignment: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_realloc(theap: *THeap, p: ?*anyopaque, newsize: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_destroy(theap: *THeap) void;
|
||||
pub extern fn mi_heap_theap(heap: *Heap) *THeap;
|
||||
pub extern fn mi_heap_malloc(heap: *Heap, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_heap_zalloc(heap: *Heap, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_heap_calloc(heap: *Heap, count: usize, size: usize) ?*anyopaque;
|
||||
@@ -114,7 +102,8 @@ pub extern fn mi_heap_rezalloc_aligned(heap: *Heap, p: ?*anyopaque, newsize: usi
|
||||
pub extern fn mi_heap_rezalloc_aligned_at(heap: *Heap, p: ?*anyopaque, newsize: usize, alignment: usize, offset: usize) ?*anyopaque;
|
||||
pub extern fn mi_heap_recalloc_aligned(heap: *Heap, p: ?*anyopaque, newcount: usize, size: usize, alignment: usize) ?*anyopaque;
|
||||
pub extern fn mi_heap_recalloc_aligned_at(heap: *Heap, p: ?*anyopaque, newcount: usize, size: usize, alignment: usize, offset: usize) ?*anyopaque;
|
||||
pub extern fn mi_heap_contains(heap: *const Heap, p: ?*const anyopaque) bool;
|
||||
pub extern fn mi_heap_contains_block(heap: *Heap, p: *const anyopaque) bool;
|
||||
pub extern fn mi_heap_check_owned(heap: *Heap, p: *const anyopaque) bool;
|
||||
pub extern fn mi_check_owned(p: ?*const anyopaque) bool;
|
||||
pub const struct_mi_heap_area_s = extern struct {
|
||||
blocks: ?*anyopaque,
|
||||
|
||||
@@ -183,13 +183,14 @@ pub fn addUrlForCss(
|
||||
source: *const logger.Source,
|
||||
mime_type_: ?[]const u8,
|
||||
unique_key: ?[]const u8,
|
||||
force_inline: bool,
|
||||
) void {
|
||||
{
|
||||
const mime_type = if (mime_type_) |m| m else MimeType.byExtension(bun.strings.trimLeadingChar(std.fs.path.extension(source.path.text), '.')).value;
|
||||
const contents = source.contents;
|
||||
// TODO: make this configurable
|
||||
const COPY_THRESHOLD = 128 * 1024; // 128kb
|
||||
const should_copy = contents.len >= COPY_THRESHOLD and unique_key != null;
|
||||
const should_copy = !force_inline and contents.len >= COPY_THRESHOLD and unique_key != null;
|
||||
if (should_copy) return;
|
||||
this.url_for_css = url_for_css: {
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ pub const Class = struct {
|
||||
close_brace_loc: logger.Loc = logger.Loc.Empty,
|
||||
properties: []Property = &([_]Property{}),
|
||||
has_decorators: bool = false,
|
||||
should_lower_standard_decorators: bool = false,
|
||||
|
||||
pub fn canBeMoved(this: *const Class) bool {
|
||||
if (this.extends != null)
|
||||
@@ -134,6 +135,7 @@ pub const Property = struct {
|
||||
declare,
|
||||
abstract,
|
||||
class_static_block,
|
||||
auto_accessor,
|
||||
|
||||
pub fn jsonStringify(self: @This(), writer: anytype) !void {
|
||||
return try writer.write(@tagName(self));
|
||||
|
||||
@@ -99,6 +99,7 @@ pub fn NewParser_(
|
||||
pub const parseStmtsUpTo = parse_zig.parseStmtsUpTo;
|
||||
pub const parseAsyncPrefixExpr = parse_zig.parseAsyncPrefixExpr;
|
||||
pub const parseTypeScriptDecorators = parse_zig.parseTypeScriptDecorators;
|
||||
pub const parseStandardDecorator = parse_zig.parseStandardDecorator;
|
||||
pub const parseTypeScriptNamespaceStmt = parse_zig.parseTypeScriptNamespaceStmt;
|
||||
pub const parseTypeScriptImportEqualsStmt = parse_zig.parseTypeScriptImportEqualsStmt;
|
||||
pub const parseTypescriptEnumStmt = parse_zig.parseTypescriptEnumStmt;
|
||||
@@ -136,6 +137,10 @@ pub fn NewParser_(
|
||||
pub const findSymbol = symbols_zig.findSymbol;
|
||||
pub const findSymbolWithRecordUsage = symbols_zig.findSymbolWithRecordUsage;
|
||||
|
||||
const lowerDecorators_zig = @import("./lowerDecorators.zig").LowerDecorators(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only);
|
||||
pub const lowerStandardDecoratorsStmt = lowerDecorators_zig.lowerStandardDecoratorsStmt;
|
||||
pub const lowerStandardDecoratorsExpr = lowerDecorators_zig.lowerStandardDecoratorsExpr;
|
||||
|
||||
macro: MacroState = undefined,
|
||||
allocator: Allocator,
|
||||
options: Parser.Options,
|
||||
@@ -486,6 +491,10 @@ pub fn NewParser_(
|
||||
/// Used for react refresh, it must be able to insert `const _s = $RefreshSig$();`
|
||||
nearest_stmt_list: ?*ListManaged(Stmt) = null,
|
||||
|
||||
/// Name from assignment context for anonymous decorated class expressions.
|
||||
/// Set before visitExpr, consumed by lowerStandardDecoratorsImpl.
|
||||
decorator_class_name: ?[]const u8 = null,
|
||||
|
||||
const RecentlyVisitedTSNamespace = struct {
|
||||
expr: Expr.Data = Expr.empty.data,
|
||||
map: ?*js_ast.TSNamespaceMemberMap = null,
|
||||
@@ -1110,7 +1119,7 @@ pub fn NewParser_(
|
||||
pub fn handleIdentifier(noalias p: *P, loc: logger.Loc, ident: E.Identifier, original_name: ?string, opts: IdentifierOpts) Expr {
|
||||
const ref = ident.ref;
|
||||
|
||||
if (p.options.features.inlining) {
|
||||
if (p.options.features.inlining or p.should_fold_typescript_constant_expressions) {
|
||||
if (p.const_values.get(ref)) |replacement| {
|
||||
p.ignoreUsage(ref);
|
||||
return replacement;
|
||||
@@ -3759,7 +3768,10 @@ pub fn NewParser_(
|
||||
}
|
||||
},
|
||||
else => {
|
||||
Output.panic("Unexpected type in export default", .{});
|
||||
// Standard decorator lowering can produce non-class
|
||||
// statements as the export default value; conservatively
|
||||
// assume they have side effects.
|
||||
return false;
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -4857,6 +4869,11 @@ pub fn NewParser_(
|
||||
) []Stmt {
|
||||
switch (stmtorexpr) {
|
||||
.stmt => |stmt| {
|
||||
// Standard decorator lowering path (for both JS and TS files)
|
||||
if (stmt.data.s_class.class.should_lower_standard_decorators) {
|
||||
return p.lowerStandardDecoratorsStmt(stmt);
|
||||
}
|
||||
|
||||
if (comptime !is_typescript_enabled) {
|
||||
if (!stmt.data.s_class.class.has_decorators) {
|
||||
var stmts = p.allocator.alloc(Stmt, 1) catch unreachable;
|
||||
@@ -5007,7 +5024,7 @@ pub fn NewParser_(
|
||||
}
|
||||
}
|
||||
},
|
||||
.spread, .declare => {}, // not allowed in a class
|
||||
.spread, .declare, .auto_accessor => {}, // not allowed in a class (auto_accessor is standard decorators only)
|
||||
.class_static_block => {}, // not allowed to decorate this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,6 +533,28 @@ pub const Parser = struct {
|
||||
var preprocessed_enums: std.ArrayListUnmanaged([]js_ast.Part) = .{};
|
||||
var preprocessed_enum_i: usize = 0;
|
||||
if (p.scopes_in_order_for_enum.count() > 0) {
|
||||
// Pre-populate const_values for simple const declarations that
|
||||
// appear before enums. This allows const enum members to reference
|
||||
// const variables with constant initializers, matching TypeScript
|
||||
// behavior. Without this, the enum preprocessing (which runs before
|
||||
// the main statement visiting loop) would not see these values.
|
||||
for (stmts) |*stmt| {
|
||||
if (stmt.data == .s_local) {
|
||||
const local = stmt.data.s_local;
|
||||
if (local.kind == .k_const) {
|
||||
for (local.decls.slice()) |decl| {
|
||||
if (decl.binding.data == .b_identifier) {
|
||||
if (decl.value) |val| {
|
||||
if (val.data.canBeConstValue()) {
|
||||
p.const_values.put(p.allocator, decl.binding.data.b_identifier.ref, val) catch unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (stmts) |*stmt| {
|
||||
if (stmt.data == .s_enum) {
|
||||
const old_scopes_in_order = p.scope_order_to_visit;
|
||||
|
||||
1495
src/ast/lowerDecorators.zig
Normal file
1495
src/ast/lowerDecorators.zig
Normal file
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@ pub fn Parse(
|
||||
pub const parseImportClause = @import("./parseImportExport.zig").ParseImportExport(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only).parseImportClause;
|
||||
pub const parseExportClause = @import("./parseImportExport.zig").ParseImportExport(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only).parseExportClause;
|
||||
pub const parseTypeScriptDecorators = @import("./parseTypescript.zig").ParseTypescript(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only).parseTypeScriptDecorators;
|
||||
pub const parseStandardDecorator = @import("./parseTypescript.zig").ParseTypescript(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only).parseStandardDecorator;
|
||||
pub const parseTypeScriptNamespaceStmt = @import("./parseTypescript.zig").ParseTypescript(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only).parseTypeScriptNamespaceStmt;
|
||||
pub const parseTypeScriptImportEqualsStmt = @import("./parseTypescript.zig").ParseTypescript(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only).parseTypeScriptImportEqualsStmt;
|
||||
pub const parseTypescriptEnumStmt = @import("./parseTypescript.zig").ParseTypescript(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only).parseTypescriptEnumStmt;
|
||||
@@ -102,6 +103,7 @@ pub fn Parse(
|
||||
pub fn parseClass(p: *P, class_keyword: logger.Range, name: ?js_ast.LocRef, class_opts: ParseClassOptions) !G.Class {
|
||||
var extends: ?Expr = null;
|
||||
var has_decorators: bool = false;
|
||||
var has_auto_accessor: bool = false;
|
||||
|
||||
if (p.lexer.token == .t_extends) {
|
||||
try p.lexer.next();
|
||||
@@ -168,6 +170,7 @@ pub fn Parse(
|
||||
// This property may turn out to be a type in TypeScript, which should be ignored
|
||||
if (try p.parseProperty(.normal, &opts, null)) |property| {
|
||||
properties.append(property) catch unreachable;
|
||||
has_auto_accessor = has_auto_accessor or property.kind == .auto_accessor;
|
||||
|
||||
// Forbid decorators on class constructors
|
||||
if (opts.ts_decorators.len > 0) {
|
||||
@@ -196,6 +199,7 @@ pub fn Parse(
|
||||
const close_brace_loc = p.lexer.loc();
|
||||
try p.lexer.expect(.t_close_brace);
|
||||
|
||||
const has_any_decorators = has_decorators or class_opts.ts_decorators.len > 0;
|
||||
return G.Class{
|
||||
.class_name = name,
|
||||
.extends = extends,
|
||||
@@ -204,7 +208,8 @@ pub fn Parse(
|
||||
.class_keyword = class_keyword,
|
||||
.body_loc = body_loc,
|
||||
.properties = properties.items,
|
||||
.has_decorators = has_decorators or class_opts.ts_decorators.len > 0,
|
||||
.has_decorators = has_any_decorators,
|
||||
.should_lower_standard_decorators = p.options.features.standard_decorators and (has_any_decorators or has_auto_accessor),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -390,7 +390,58 @@ pub fn ParsePrefix(
|
||||
_ = try p.skipTypeScriptTypeParameters(.{ .allow_in_out_variance_annotations = true, .allow_const_modifier = true });
|
||||
}
|
||||
|
||||
const class = try p.parseClass(classKeyword, name, ParseClassOptions{});
|
||||
const class = try p.parseClass(classKeyword, name, ParseClassOptions{
|
||||
.allow_ts_decorators = is_typescript_enabled or p.options.features.standard_decorators,
|
||||
});
|
||||
p.popScope();
|
||||
|
||||
return p.newExpr(class, loc);
|
||||
}
|
||||
fn t_at(noalias p: *P) anyerror!Expr {
|
||||
// Parse decorators before a class expression: @dec class { ... }
|
||||
const ts_decorators = try p.parseTypeScriptDecorators();
|
||||
|
||||
// Expect class keyword after decorators
|
||||
if (p.lexer.token != .t_class) {
|
||||
try p.lexer.expected(.t_class);
|
||||
return error.SyntaxError;
|
||||
}
|
||||
|
||||
const loc = p.lexer.loc();
|
||||
const classKeyword = p.lexer.range();
|
||||
try p.lexer.next();
|
||||
var name: ?js_ast.LocRef = null;
|
||||
|
||||
_ = p.pushScopeForParsePass(.class_name, loc) catch unreachable;
|
||||
|
||||
// Parse an optional class name
|
||||
if (p.lexer.token == .t_identifier) {
|
||||
const name_text = p.lexer.identifier;
|
||||
if (!is_typescript_enabled or !strings.eqlComptime(name_text, "implements")) {
|
||||
if (p.fn_or_arrow_data_parse.allow_await != .allow_ident and strings.eqlComptime(name_text, "await")) {
|
||||
p.log.addRangeError(p.source, p.lexer.range(), "Cannot use \"await\" as an identifier here") catch unreachable;
|
||||
}
|
||||
|
||||
name = js_ast.LocRef{
|
||||
.loc = p.lexer.loc(),
|
||||
.ref = p.newSymbol(
|
||||
.other,
|
||||
name_text,
|
||||
) catch unreachable,
|
||||
};
|
||||
try p.lexer.next();
|
||||
}
|
||||
}
|
||||
|
||||
// Even anonymous classes can have TypeScript type parameters
|
||||
if (is_typescript_enabled) {
|
||||
_ = try p.skipTypeScriptTypeParameters(.{ .allow_in_out_variance_annotations = true, .allow_const_modifier = true });
|
||||
}
|
||||
|
||||
const class = try p.parseClass(classKeyword, name, ParseClassOptions{
|
||||
.ts_decorators = ts_decorators,
|
||||
.allow_ts_decorators = true,
|
||||
});
|
||||
p.popScope();
|
||||
|
||||
return p.newExpr(class, loc);
|
||||
@@ -727,6 +778,7 @@ pub fn ParsePrefix(
|
||||
.t_plus_plus => t_plus_plus(p),
|
||||
.t_function => t_function(p),
|
||||
.t_class => t_class(p),
|
||||
.t_at => t_at(p),
|
||||
.t_new => t_new(p, flags),
|
||||
.t_super => t_super(p, level),
|
||||
else => {
|
||||
|
||||
@@ -158,7 +158,7 @@ pub fn ParseProperty(
|
||||
try p.lexer.next();
|
||||
},
|
||||
.t_private_identifier => {
|
||||
if (!opts.is_class or opts.ts_decorators.len > 0) {
|
||||
if (!opts.is_class or (opts.ts_decorators.len > 0 and !p.options.features.standard_decorators)) {
|
||||
try p.lexer.expected(.t_identifier);
|
||||
}
|
||||
|
||||
@@ -299,6 +299,16 @@ pub fn ParseProperty(
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.p_accessor => {
|
||||
// "accessor" keyword for auto-accessor fields (TC39 standard decorators)
|
||||
if (opts.is_class and p.options.features.standard_decorators and
|
||||
(js_lexer.PropertyModifierKeyword.List.get(raw) orelse .p_static) == .p_accessor)
|
||||
{
|
||||
kind = .auto_accessor;
|
||||
errors = null;
|
||||
continue :restart;
|
||||
}
|
||||
},
|
||||
.p_private, .p_protected, .p_public, .p_readonly, .p_override => {
|
||||
// Skip over TypeScript keywords
|
||||
if (opts.is_class and is_typescript_enabled and (js_lexer.PropertyModifierKeyword.List.get(raw) orelse .p_static) == keyword) {
|
||||
@@ -411,7 +421,7 @@ pub fn ParseProperty(
|
||||
try p.lexer.next();
|
||||
} else if (p.lexer.token == .t_exclamation and
|
||||
!p.lexer.has_newline_before and
|
||||
kind == .normal and
|
||||
(kind == .normal or kind == .auto_accessor) and
|
||||
!opts.is_async and
|
||||
!opts.is_generator)
|
||||
{
|
||||
@@ -430,7 +440,7 @@ pub fn ParseProperty(
|
||||
|
||||
// Parse a class field with an optional initial value
|
||||
if (opts.is_class and
|
||||
kind == .normal and !opts.is_async and
|
||||
(kind == .normal or kind == .auto_accessor) and !opts.is_async and
|
||||
!opts.is_generator and
|
||||
p.lexer.token != .t_open_paren and
|
||||
!has_type_parameters and
|
||||
@@ -518,6 +528,12 @@ pub fn ParseProperty(
|
||||
};
|
||||
}
|
||||
|
||||
// Auto-accessor fields cannot be methods
|
||||
if (kind == .auto_accessor and p.lexer.token == .t_open_paren) {
|
||||
p.log.addRangeError(p.source, key_range, "auto-accessor properties cannot have a method body") catch unreachable;
|
||||
return error.SyntaxError;
|
||||
}
|
||||
|
||||
// Parse a method expression
|
||||
if (p.lexer.token == .t_open_paren or kind != .normal or opts.is_class or opts.is_async or opts.is_generator) {
|
||||
return parseMethodExpression(p, kind, opts, is_computed, &key, key_range);
|
||||
|
||||
@@ -445,7 +445,7 @@ pub fn ParseStmt(
|
||||
}
|
||||
fn t_at(p: *P, opts: *ParseStatementOptions) anyerror!Stmt {
|
||||
// Parse decorators before class statements, which are potentially exported
|
||||
if (is_typescript_enabled) {
|
||||
if (is_typescript_enabled or p.options.features.standard_decorators) {
|
||||
const scope_index = p.scopes_in_order.items.len;
|
||||
const ts_decorators = try p.parseTypeScriptDecorators();
|
||||
|
||||
@@ -473,7 +473,10 @@ pub fn ParseStmt(
|
||||
// "@decorator export declare abstract class Foo {}"
|
||||
// "@decorator export default class Foo {}"
|
||||
// "@decorator export default abstract class Foo {}"
|
||||
if (p.lexer.token != .t_class and p.lexer.token != .t_export and !p.lexer.isContextualKeyword("abstract") and !p.lexer.isContextualKeyword("declare")) {
|
||||
if (p.lexer.token != .t_class and p.lexer.token != .t_export and
|
||||
!(is_typescript_enabled and p.lexer.isContextualKeyword("abstract")) and
|
||||
!(is_typescript_enabled and p.lexer.isContextualKeyword("declare")))
|
||||
{
|
||||
try p.lexer.expected(.t_class);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ pub fn ParseTypescript(
|
||||
const is_typescript_enabled = P.is_typescript_enabled;
|
||||
|
||||
pub fn parseTypeScriptDecorators(p: *P) ![]ExprNodeIndex {
|
||||
if (!is_typescript_enabled) {
|
||||
if (!is_typescript_enabled and !p.options.features.standard_decorators) {
|
||||
return &([_]ExprNodeIndex{});
|
||||
}
|
||||
|
||||
@@ -16,22 +16,106 @@ pub fn ParseTypescript(
|
||||
while (p.lexer.token == T.t_at) {
|
||||
try p.lexer.next();
|
||||
|
||||
// Parse a new/call expression with "exprFlagTSDecorator" so we ignore
|
||||
// EIndex expressions, since they may be part of a computed property:
|
||||
//
|
||||
// class Foo {
|
||||
// @foo ['computed']() {}
|
||||
// }
|
||||
//
|
||||
// This matches the behavior of the TypeScript compiler.
|
||||
try decorators.ensureUnusedCapacity(1);
|
||||
try p.parseExprWithFlags(.new, Expr.EFlags.ts_decorator, &decorators.unusedCapacitySlice()[0]);
|
||||
decorators.items.len += 1;
|
||||
if (p.options.features.standard_decorators) {
|
||||
// TC39 standard decorator grammar:
|
||||
// @Identifier
|
||||
// @Identifier.member
|
||||
// @Identifier.member(args)
|
||||
// @(Expression)
|
||||
try decorators.ensureUnusedCapacity(1);
|
||||
decorators.unusedCapacitySlice()[0] = try p.parseStandardDecorator();
|
||||
decorators.items.len += 1;
|
||||
} else {
|
||||
// Parse a new/call expression with "exprFlagTSDecorator" so we ignore
|
||||
// EIndex expressions, since they may be part of a computed property:
|
||||
//
|
||||
// class Foo {
|
||||
// @foo ['computed']() {}
|
||||
// }
|
||||
//
|
||||
// This matches the behavior of the TypeScript compiler.
|
||||
try decorators.ensureUnusedCapacity(1);
|
||||
try p.parseExprWithFlags(.new, Expr.EFlags.ts_decorator, &decorators.unusedCapacitySlice()[0]);
|
||||
decorators.items.len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return decorators.items;
|
||||
}
|
||||
|
||||
/// Parse a standard (TC39) decorator expression following the `@` token.
|
||||
///
|
||||
/// DecoratorExpression:
|
||||
/// @ IdentifierReference
|
||||
/// @ DecoratorMemberExpression
|
||||
/// @ DecoratorCallExpression
|
||||
/// @ DecoratorParenthesizedExpression
|
||||
pub fn parseStandardDecorator(p: *P) !ExprNodeIndex {
|
||||
const loc = p.lexer.loc();
|
||||
|
||||
// @(Expression) — parenthesized, any expression allowed
|
||||
if (p.lexer.token == .t_open_paren) {
|
||||
try p.lexer.next();
|
||||
const expr = try p.parseExpr(.lowest);
|
||||
try p.lexer.expect(.t_close_paren);
|
||||
return expr;
|
||||
}
|
||||
|
||||
// Must start with an identifier
|
||||
if (p.lexer.token != .t_identifier) {
|
||||
try p.lexer.expect(.t_identifier);
|
||||
return error.SyntaxError;
|
||||
}
|
||||
|
||||
var expr = p.newExpr(E.Identifier{ .ref = try p.storeNameInRef(p.lexer.identifier) }, loc);
|
||||
try p.lexer.next();
|
||||
|
||||
// Skip TypeScript type arguments after the identifier (e.g., @foo<T>)
|
||||
if (is_typescript_enabled) {
|
||||
_ = try p.skipTypeScriptTypeArguments(false);
|
||||
}
|
||||
|
||||
// DecoratorMemberExpression: Identifier (.Identifier)*
|
||||
while (p.lexer.token == .t_dot or p.lexer.token == .t_question_dot) {
|
||||
// Forbid optional chaining in decorators
|
||||
if (p.lexer.token == .t_question_dot) {
|
||||
try p.log.addError(p.source, p.lexer.loc(), "Optional chaining is not allowed in decorator expressions");
|
||||
return error.SyntaxError;
|
||||
}
|
||||
|
||||
try p.lexer.next();
|
||||
|
||||
if (!p.lexer.isIdentifierOrKeyword()) {
|
||||
try p.lexer.expect(.t_identifier);
|
||||
return error.SyntaxError;
|
||||
}
|
||||
|
||||
const name = p.lexer.identifier;
|
||||
const name_loc = p.lexer.loc();
|
||||
try p.lexer.next();
|
||||
|
||||
expr = p.newExpr(E.Dot{ .target = expr, .name = name, .name_loc = name_loc }, loc);
|
||||
|
||||
// Skip TypeScript type arguments after member access (e.g., @foo.bar<T>)
|
||||
if (is_typescript_enabled) {
|
||||
_ = try p.skipTypeScriptTypeArguments(false);
|
||||
}
|
||||
}
|
||||
|
||||
// DecoratorCallExpression: DecoratorMemberExpression Arguments
|
||||
// Only a single call is allowed, no chaining after the call
|
||||
if (p.lexer.token == .t_open_paren) {
|
||||
const args = try p.parseCallArgs();
|
||||
expr = p.newExpr(E.Call{
|
||||
.target = expr,
|
||||
.args = args.list,
|
||||
.close_paren_loc = args.loc,
|
||||
}, loc);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
pub fn parseTypeScriptNamespaceStmt(p: *P, loc: logger.Loc, opts: *ParseStatementOptions) anyerror!Stmt {
|
||||
// "namespace foo {}";
|
||||
const name_loc = p.lexer.loc();
|
||||
|
||||
@@ -186,9 +186,15 @@ pub fn Visit(
|
||||
if (only_scan_imports_and_do_not_visit) {
|
||||
@compileError("only_scan_imports_and_do_not_visit must not run this.");
|
||||
}
|
||||
// Propagate name from binding to anonymous decorated class expressions
|
||||
const prev_decorator_class_name = p.decorator_class_name;
|
||||
if (was_anonymous_named_expr and val.data == .e_class and val.data.e_class.should_lower_standard_decorators and decl.binding.data == .b_identifier) {
|
||||
p.decorator_class_name = p.loadNameFromRef(decl.binding.data.b_identifier.ref);
|
||||
}
|
||||
decl.value = p.visitExprInOut(val, .{
|
||||
.is_immediately_assigned_to_decl = true,
|
||||
});
|
||||
p.decorator_class_name = prev_decorator_class_name;
|
||||
|
||||
if (p.options.features.react_fast_refresh) {
|
||||
// When hooks are immediately assigned to something, we need to hash the binding.
|
||||
@@ -407,7 +413,15 @@ pub fn Visit(
|
||||
p.visitBinding(item.binding, duplicate_arg_check);
|
||||
if (item.default_value) |default_value| {
|
||||
const was_anonymous_named_expr = default_value.isAnonymousNamed();
|
||||
const prev_decorator_class_name2 = p.decorator_class_name;
|
||||
if (was_anonymous_named_expr and default_value.data == .e_class and
|
||||
default_value.data.e_class.should_lower_standard_decorators and
|
||||
item.binding.data == .b_identifier)
|
||||
{
|
||||
p.decorator_class_name = p.loadNameFromRef(item.binding.data.b_identifier.ref);
|
||||
}
|
||||
item.default_value = p.visitExpr(default_value);
|
||||
p.decorator_class_name = prev_decorator_class_name2;
|
||||
|
||||
switch (item.binding.data) {
|
||||
.b_identifier => |bind_| {
|
||||
@@ -431,7 +445,15 @@ pub fn Visit(
|
||||
p.visitBinding(property.value, duplicate_arg_check);
|
||||
if (property.default_value) |default_value| {
|
||||
const was_anonymous_named_expr = default_value.isAnonymousNamed();
|
||||
const prev_decorator_class_name3 = p.decorator_class_name;
|
||||
if (was_anonymous_named_expr and default_value.data == .e_class and
|
||||
default_value.data.e_class.should_lower_standard_decorators and
|
||||
property.value.data == .b_identifier)
|
||||
{
|
||||
p.decorator_class_name = p.loadNameFromRef(property.value.data.b_identifier.ref);
|
||||
}
|
||||
property.default_value = p.visitExpr(default_value);
|
||||
p.decorator_class_name = prev_decorator_class_name3;
|
||||
|
||||
switch (property.value.data) {
|
||||
.b_identifier => |bind_| {
|
||||
@@ -627,7 +649,12 @@ pub fn Visit(
|
||||
if (property.value) |val| {
|
||||
if (name_to_keep) |name| {
|
||||
const was_anon = val.isAnonymousNamed();
|
||||
const prev_dcn = p.decorator_class_name;
|
||||
if (val.data == .e_class and val.data.e_class.class_name == null and val.data.e_class.should_lower_standard_decorators) {
|
||||
p.decorator_class_name = name;
|
||||
}
|
||||
property.value = p.maybeKeepExprSymbolName(p.visitExpr(val), name, was_anon);
|
||||
p.decorator_class_name = prev_dcn;
|
||||
} else {
|
||||
property.value = p.visitExpr(val);
|
||||
}
|
||||
@@ -643,7 +670,12 @@ pub fn Visit(
|
||||
// if (property.flags.is_static and )
|
||||
if (name_to_keep) |name| {
|
||||
const was_anon = val.isAnonymousNamed();
|
||||
const prev_dcn2 = p.decorator_class_name;
|
||||
if (val.data == .e_class and val.data.e_class.class_name == null and val.data.e_class.should_lower_standard_decorators) {
|
||||
p.decorator_class_name = name;
|
||||
}
|
||||
property.initializer = p.maybeKeepExprSymbolName(p.visitExpr(val), name, was_anon);
|
||||
p.decorator_class_name = prev_dcn2;
|
||||
} else {
|
||||
property.initializer = p.visitExpr(val);
|
||||
}
|
||||
@@ -765,6 +797,28 @@ pub fn Visit(
|
||||
var preprocessed_enums: std.ArrayListUnmanaged([]Stmt) = .{};
|
||||
defer preprocessed_enums.deinit(p.allocator);
|
||||
if (p.scopes_in_order_for_enum.count() > 0) {
|
||||
// Pre-populate const_values for simple const declarations that
|
||||
// appear before enums. This allows const enum members to reference
|
||||
// const variables with constant initializers, matching TypeScript
|
||||
// behavior. Without this, the enum preprocessing (which runs before
|
||||
// the main statement visiting loop) would not see these values.
|
||||
for (stmts.items) |*stmt| {
|
||||
if (stmt.data == .s_local) {
|
||||
const local = stmt.data.s_local;
|
||||
if (local.kind == .k_const) {
|
||||
for (local.decls.slice()) |decl| {
|
||||
if (decl.binding.data == .b_identifier) {
|
||||
if (decl.value) |val| {
|
||||
if (val.data.canBeConstValue()) {
|
||||
p.const_values.put(p.allocator, decl.binding.data.b_identifier.ref, val) catch unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var found: usize = 0;
|
||||
for (stmts.items) |*stmt| {
|
||||
if (stmt.data == .s_enum) {
|
||||
|
||||
@@ -69,6 +69,15 @@ pub fn CreateBinaryExpressionVisitor(
|
||||
const is_call_target = @as(Expr.Tag, p.call_target) == .e_binary and e_ == p.call_target.e_binary;
|
||||
// const is_stmt_expr = @as(Expr.Tag, p.stmt_expr_value) == .e_binary and expr.data.e_binary == p.stmt_expr_value.e_binary;
|
||||
const was_anonymous_named_expr = e_.right.isAnonymousNamed();
|
||||
const prev_decorator_class_name = p.decorator_class_name;
|
||||
|
||||
// Propagate name for anonymous decorated class expressions in assignments
|
||||
if (e_.op == .bin_assign and was_anonymous_named_expr and
|
||||
e_.right.data == .e_class and e_.right.data.e_class.should_lower_standard_decorators and
|
||||
e_.left.data == .e_identifier)
|
||||
{
|
||||
p.decorator_class_name = p.loadNameFromRef(e_.left.data.e_identifier.ref);
|
||||
}
|
||||
|
||||
// Mark the control flow as dead if the branch is never taken
|
||||
switch (e_.op) {
|
||||
@@ -112,6 +121,7 @@ pub fn CreateBinaryExpressionVisitor(
|
||||
e_.right = p.visitExpr(e_.right);
|
||||
},
|
||||
}
|
||||
p.decorator_class_name = prev_decorator_class_name;
|
||||
|
||||
// Always put constants on the right for equality comparisons to help
|
||||
// reduce the number of cases we have to check during pattern matching. We
|
||||
|
||||
@@ -1015,8 +1015,16 @@ pub fn VisitExpr(
|
||||
.e_binary => |e2| {
|
||||
if (in.assign_target != .none and e2.op == .bin_assign) {
|
||||
const was_anonymous_named_expr = e2.right.isAnonymousNamed();
|
||||
// Propagate name for anonymous decorated class expressions
|
||||
if (was_anonymous_named_expr and e2.right.data == .e_class and
|
||||
e2.right.data.e_class.should_lower_standard_decorators and
|
||||
@as(Expr.Tag, e2.left.data) == .e_identifier)
|
||||
{
|
||||
p.decorator_class_name = p.loadNameFromRef(e2.left.data.e_identifier.ref);
|
||||
}
|
||||
e2.left = p.visitExprInOut(e2.left, ExprIn{ .assign_target = .replace });
|
||||
e2.right = p.visitExpr(e2.right);
|
||||
p.decorator_class_name = null;
|
||||
|
||||
if (@as(Expr.Tag, e2.left.data) == .e_identifier) {
|
||||
e2.right = p.maybeKeepExprSymbolName(
|
||||
@@ -1089,12 +1097,34 @@ pub fn VisitExpr(
|
||||
}
|
||||
|
||||
if (property.value != null) {
|
||||
// Propagate name from property key for decorated anonymous class expressions
|
||||
// e.g., { Foo: @dec class {} } should give the class .name = "Foo"
|
||||
if (in.assign_target == .none and
|
||||
property.value.?.data == .e_class and
|
||||
property.value.?.data.e_class.should_lower_standard_decorators and
|
||||
property.value.?.data.e_class.class_name == null and
|
||||
property.key != null and
|
||||
property.key.?.data == .e_string)
|
||||
{
|
||||
p.decorator_class_name = property.key.?.data.e_string.string(p.allocator) catch null;
|
||||
}
|
||||
property.value = p.visitExprInOut(property.value.?, ExprIn{ .assign_target = in.assign_target });
|
||||
p.decorator_class_name = null;
|
||||
}
|
||||
|
||||
if (property.initializer != null) {
|
||||
const was_anonymous_named_expr = property.initializer.?.isAnonymousNamed();
|
||||
if (was_anonymous_named_expr and property.initializer.?.data == .e_class and
|
||||
property.initializer.?.data.e_class.should_lower_standard_decorators)
|
||||
{
|
||||
if (property.value) |val| {
|
||||
if (@as(Expr.Tag, val.data) == .e_identifier) {
|
||||
p.decorator_class_name = p.loadNameFromRef(val.data.e_identifier.ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
property.initializer = p.visitExpr(property.initializer.?);
|
||||
p.decorator_class_name = null;
|
||||
|
||||
if (property.value) |val| {
|
||||
if (@as(Expr.Tag, val.data) == .e_identifier) {
|
||||
@@ -1627,8 +1657,17 @@ pub fn VisitExpr(
|
||||
return expr;
|
||||
}
|
||||
|
||||
// Save name from assignment context before visiting (nested visits may overwrite it)
|
||||
const decorator_name_from_context = p.decorator_class_name;
|
||||
p.decorator_class_name = null;
|
||||
|
||||
_ = p.visitClass(expr.loc, e_, Ref.None);
|
||||
|
||||
// Lower standard decorators for class expressions
|
||||
if (e_.should_lower_standard_decorators) {
|
||||
return p.lowerStandardDecoratorsExpr(e_, expr.loc, decorator_name_from_context);
|
||||
}
|
||||
|
||||
// Remove unused class names when minifying (only when bundling is enabled)
|
||||
// unless --keep-names is specified
|
||||
if (p.options.features.minify_syntax and p.options.bundle and
|
||||
|
||||
@@ -215,8 +215,12 @@ pub fn VisitStmt(
|
||||
switch (data.value) {
|
||||
.expr => |expr| {
|
||||
const was_anonymous_named_expr = expr.isAnonymousNamed();
|
||||
|
||||
const prev_decorator_class_name = p.decorator_class_name;
|
||||
if (was_anonymous_named_expr and expr.data == .e_class and expr.data.e_class.should_lower_standard_decorators) {
|
||||
p.decorator_class_name = js_ast.ClauseItem.default_alias;
|
||||
}
|
||||
data.value.expr = p.visitExpr(expr);
|
||||
p.decorator_class_name = prev_decorator_class_name;
|
||||
|
||||
if (p.is_control_flow_dead) {
|
||||
return;
|
||||
@@ -457,17 +461,29 @@ pub fn VisitStmt(
|
||||
}
|
||||
}
|
||||
|
||||
// This is to handle TS decorators, mostly.
|
||||
// Lower the class (handles both TS legacy and standard decorators).
|
||||
// Standard decorator lowering may produce prefix statements
|
||||
// (variable declarations) before the class statement.
|
||||
var class_stmts = p.lowerClass(.{ .stmt = s2 });
|
||||
bun.assert(class_stmts[0].data == .s_class);
|
||||
|
||||
if (class_stmts.len > 1) {
|
||||
data.value.stmt = class_stmts[0];
|
||||
stmts.append(stmt.*) catch {};
|
||||
stmts.appendSlice(class_stmts[1..]) catch {};
|
||||
} else {
|
||||
data.value.stmt = class_stmts[0];
|
||||
stmts.append(stmt.*) catch {};
|
||||
// Find the s_class statement in the returned list
|
||||
var class_stmt_idx: usize = 0;
|
||||
for (class_stmts, 0..) |cs, idx| {
|
||||
if (cs.data == .s_class) {
|
||||
class_stmt_idx = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Emit any prefix statements before the export default
|
||||
stmts.appendSlice(class_stmts[0..class_stmt_idx]) catch {};
|
||||
|
||||
data.value.stmt = class_stmts[class_stmt_idx];
|
||||
stmts.append(stmt.*) catch {};
|
||||
|
||||
// Emit any suffix statements after the export default
|
||||
if (class_stmt_idx + 1 < class_stmts.len) {
|
||||
stmts.appendSlice(class_stmts[class_stmt_idx + 1 ..]) catch {};
|
||||
}
|
||||
|
||||
if (p.options.features.server_components.wrapsExports()) {
|
||||
|
||||
@@ -65,13 +65,30 @@ async function performRouteReload() {
|
||||
// HMR payloads are script tags that call this internal function.
|
||||
// A previous version of this runtime used `eval`, but browser support around
|
||||
// mapping stack traces of eval'd frames is poor (the case the error overlay).
|
||||
const scriptTags = new Map<string, [script: HTMLScriptElement, size: number]>();
|
||||
const pendingScriptSymbol = Symbol.for("bun:hmr:pendingScripts");
|
||||
type PendingHmrScript = {
|
||||
script: HTMLScriptElement;
|
||||
size: number;
|
||||
url: string;
|
||||
};
|
||||
type PendingHmrQueue = PendingHmrScript[];
|
||||
const scriptTags: Map<string, PendingHmrQueue> =
|
||||
((globalThis as any)[pendingScriptSymbol] as Map<string, PendingHmrQueue> | undefined) ??
|
||||
new Map<string, PendingHmrQueue>();
|
||||
(globalThis as any)[pendingScriptSymbol] = scriptTags;
|
||||
|
||||
globalThis[Symbol.for("bun:hmr")] = (modules: any, id: string) => {
|
||||
const entry = scriptTags.get(id);
|
||||
if (!entry) throw new Error("Unknown HMR script: " + id);
|
||||
const [script, size] = entry;
|
||||
scriptTags.delete(id);
|
||||
const url = script.src;
|
||||
const queue = scriptTags.get(id);
|
||||
let entry = queue?.shift() ?? null;
|
||||
if (queue && queue.length === 0) {
|
||||
scriptTags.delete(id);
|
||||
}
|
||||
|
||||
if (!entry) {
|
||||
throw new Error("Unknown HMR script: " + id);
|
||||
}
|
||||
|
||||
const { script, size, url } = entry;
|
||||
const map: SourceMapURL = {
|
||||
id,
|
||||
url,
|
||||
@@ -184,7 +201,17 @@ const handlers = {
|
||||
const blob = new Blob([rest], { type: "application/javascript" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const script = document.createElement("script");
|
||||
scriptTags.set(sourceMapId, [script, sourceMapSize]);
|
||||
const pendingScripts = scriptTags.get(sourceMapId);
|
||||
const entry: PendingHmrScript = {
|
||||
script,
|
||||
size: sourceMapSize,
|
||||
url,
|
||||
};
|
||||
if (pendingScripts) {
|
||||
pendingScripts.push(entry);
|
||||
} else {
|
||||
scriptTags.set(sourceMapId, [entry]);
|
||||
}
|
||||
script.className = "bun-hmr-script";
|
||||
script.src = url;
|
||||
script.onerror = onHmrLoadError;
|
||||
|
||||
@@ -285,7 +285,9 @@ pub const Run = struct {
|
||||
.dir = cpu_prof_opts.dir,
|
||||
.md_format = cpu_prof_opts.md_format,
|
||||
.json_format = cpu_prof_opts.json_format,
|
||||
.interval = cpu_prof_opts.interval,
|
||||
};
|
||||
CPUProfiler.setSamplingInterval(cpu_prof_opts.interval);
|
||||
CPUProfiler.startCPUProfiler(vm.jsc_vm);
|
||||
bun.analytics.Features.cpu_profile += 1;
|
||||
}
|
||||
|
||||
@@ -233,6 +233,7 @@ pub fn transpileSourceCode(
|
||||
.macro_remappings = macro_remappings,
|
||||
.jsx = jsc_vm.transpiler.options.jsx,
|
||||
.emit_decorator_metadata = jsc_vm.transpiler.options.emit_decorator_metadata,
|
||||
.experimental_decorators = jsc_vm.transpiler.options.experimental_decorators,
|
||||
.virtual_source = virtual_source,
|
||||
.dont_bundle_twice = true,
|
||||
.allow_commonjs = true,
|
||||
@@ -1139,14 +1140,14 @@ export fn Bun__runVirtualModule(globalObject: *JSGlobalObject, specifier_ptr: *c
|
||||
fn getHardcodedModule(jsc_vm: *VirtualMachine, specifier: bun.String, hardcoded: HardcodedModule) ?ResolvedSource {
|
||||
analytics.Features.builtin_modules.insert(hardcoded);
|
||||
return switch (hardcoded) {
|
||||
.@"bun:main" => .{
|
||||
.@"bun:main" => if (jsc_vm.entry_point.generated) .{
|
||||
.allocator = null,
|
||||
.source_code = bun.String.cloneUTF8(jsc_vm.entry_point.source.contents),
|
||||
.specifier = specifier,
|
||||
.source_url = specifier,
|
||||
.tag = .esm,
|
||||
.source_code_needs_deref = true,
|
||||
},
|
||||
} else null,
|
||||
.@"bun:internal-for-testing" => {
|
||||
if (!Environment.isDebug) {
|
||||
if (!is_allowed_to_use_internal_testing_apis)
|
||||
|
||||
@@ -386,6 +386,7 @@ pub const RuntimeTranspilerStore = struct {
|
||||
.macro_remappings = macro_remappings,
|
||||
.jsx = transpiler.options.jsx,
|
||||
.emit_decorator_metadata = transpiler.options.emit_decorator_metadata,
|
||||
.experimental_decorators = transpiler.options.experimental_decorators,
|
||||
.virtual_source = null,
|
||||
.dont_bundle_twice = true,
|
||||
.allow_commonjs = true,
|
||||
|
||||
@@ -7,6 +7,7 @@ const VirtualMachine = @This();
|
||||
|
||||
export var has_bun_garbage_collector_flag_enabled = false;
|
||||
pub export var isBunTest: bool = false;
|
||||
pub export var Bun__defaultRemainingRunsUntilSkipReleaseAccess: c_int = 10;
|
||||
|
||||
// TODO: evaluate if this has any measurable performance impact.
|
||||
pub var synthetic_allocation_limit: usize = std.math.maxInt(u32);
|
||||
@@ -1615,7 +1616,7 @@ fn _resolve(
|
||||
if (strings.eqlComptime(std.fs.path.basename(specifier), Runtime.Runtime.Imports.alt_name)) {
|
||||
ret.path = Runtime.Runtime.Imports.Name;
|
||||
return;
|
||||
} else if (strings.eqlComptime(specifier, main_file_name)) {
|
||||
} else if (strings.eqlComptime(specifier, main_file_name) and jsc_vm.entry_point.generated) {
|
||||
ret.result = null;
|
||||
ret.path = jsc_vm.entry_point.source.path.text;
|
||||
return;
|
||||
|
||||
@@ -977,45 +977,78 @@ pub const JSBundler = struct {
|
||||
}
|
||||
|
||||
if (this.compile) |*compile| {
|
||||
this.target = .bun;
|
||||
// When compile + target=browser + all HTML entrypoints, produce standalone HTML.
|
||||
// Otherwise, default to bun executable compile.
|
||||
const has_all_html_entrypoints = brk: {
|
||||
if (this.entry_points.count() == 0) break :brk false;
|
||||
for (this.entry_points.keys()) |ep| {
|
||||
if (!strings.hasSuffixComptime(ep, ".html")) break :brk false;
|
||||
}
|
||||
break :brk true;
|
||||
};
|
||||
const is_standalone_html = this.target == .browser and has_all_html_entrypoints;
|
||||
if (!is_standalone_html) {
|
||||
this.target = .bun;
|
||||
|
||||
const define_keys = compile.compile_target.defineKeys();
|
||||
const define_values = compile.compile_target.defineValues();
|
||||
for (define_keys, define_values) |key, value| {
|
||||
try this.define.insert(key, value);
|
||||
const define_keys = compile.compile_target.defineKeys();
|
||||
const define_values = compile.compile_target.defineValues();
|
||||
for (define_keys, define_values) |key, value| {
|
||||
try this.define.insert(key, value);
|
||||
}
|
||||
|
||||
const base_public_path = bun.StandaloneModuleGraph.targetBasePublicPath(this.compile.?.compile_target.os, "root/");
|
||||
try this.public_path.append(base_public_path);
|
||||
|
||||
// When using --compile, only `external` sourcemaps work, as we do not
|
||||
// look at the source map comment. Override any other sourcemap type.
|
||||
if (this.source_map != .none) {
|
||||
this.source_map = .external;
|
||||
}
|
||||
|
||||
if (compile.outfile.isEmpty()) {
|
||||
const entry_point = this.entry_points.keys()[0];
|
||||
var outfile = std.fs.path.basename(entry_point);
|
||||
const ext = std.fs.path.extension(outfile);
|
||||
if (ext.len > 0) {
|
||||
outfile = outfile[0 .. outfile.len - ext.len];
|
||||
}
|
||||
|
||||
if (strings.eqlComptime(outfile, "index")) {
|
||||
outfile = std.fs.path.basename(std.fs.path.dirname(entry_point) orelse "index");
|
||||
}
|
||||
|
||||
if (strings.eqlComptime(outfile, "bun")) {
|
||||
outfile = std.fs.path.basename(std.fs.path.dirname(entry_point) orelse "bun");
|
||||
}
|
||||
|
||||
// If argv[0] is "bun" or "bunx", we don't check if the binary is standalone
|
||||
if (strings.eqlComptime(outfile, "bun") or strings.eqlComptime(outfile, "bunx")) {
|
||||
return globalThis.throwInvalidArguments("cannot use compile with an output file named 'bun' because bun won't realize it's a standalone executable. Please choose a different name for compile.outfile", .{});
|
||||
}
|
||||
|
||||
try compile.outfile.appendSliceExact(outfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const base_public_path = bun.StandaloneModuleGraph.targetBasePublicPath(this.compile.?.compile_target.os, "root/");
|
||||
try this.public_path.append(base_public_path);
|
||||
// ESM bytecode requires compile because module_info (import/export metadata)
|
||||
// is only available in compiled binaries. Without it, JSC must parse the file
|
||||
// twice (once for module analysis, once for bytecode), which is a deopt.
|
||||
if (this.bytecode and this.format == .esm and this.compile == null) {
|
||||
return globalThis.throwInvalidArguments("ESM bytecode requires compile: true. Use format: 'cjs' for bytecode without compile.", .{});
|
||||
}
|
||||
|
||||
// When using --compile, only `external` sourcemaps work, as we do not
|
||||
// look at the source map comment. Override any other sourcemap type.
|
||||
if (this.source_map != .none) {
|
||||
this.source_map = .external;
|
||||
}
|
||||
|
||||
if (compile.outfile.isEmpty()) {
|
||||
const entry_point = this.entry_points.keys()[0];
|
||||
var outfile = std.fs.path.basename(entry_point);
|
||||
const ext = std.fs.path.extension(outfile);
|
||||
if (ext.len > 0) {
|
||||
outfile = outfile[0 .. outfile.len - ext.len];
|
||||
// Validate standalone HTML mode: compile + browser target + all HTML entrypoints
|
||||
if (this.compile != null and this.target == .browser) {
|
||||
const has_all_html = brk: {
|
||||
if (this.entry_points.count() == 0) break :brk false;
|
||||
for (this.entry_points.keys()) |ep| {
|
||||
if (!strings.hasSuffixComptime(ep, ".html")) break :brk false;
|
||||
}
|
||||
|
||||
if (strings.eqlComptime(outfile, "index")) {
|
||||
outfile = std.fs.path.basename(std.fs.path.dirname(entry_point) orelse "index");
|
||||
}
|
||||
|
||||
if (strings.eqlComptime(outfile, "bun")) {
|
||||
outfile = std.fs.path.basename(std.fs.path.dirname(entry_point) orelse "bun");
|
||||
}
|
||||
|
||||
// If argv[0] is "bun" or "bunx", we don't check if the binary is standalone
|
||||
if (strings.eqlComptime(outfile, "bun") or strings.eqlComptime(outfile, "bunx")) {
|
||||
return globalThis.throwInvalidArguments("cannot use compile with an output file named 'bun' because bun won't realize it's a standalone executable. Please choose a different name for compile.outfile", .{});
|
||||
}
|
||||
|
||||
try compile.outfile.appendSliceExact(outfile);
|
||||
break :brk true;
|
||||
};
|
||||
if (has_all_html and this.code_splitting) {
|
||||
return globalThis.throwInvalidArguments("Cannot use compile with target 'browser' and splitting for standalone HTML", .{});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -464,8 +464,8 @@ const ParseRenderer = struct {
|
||||
const entry = self.#stack.pop().?;
|
||||
const g = self.#globalObject;
|
||||
|
||||
// Determine HTML tag name
|
||||
const type_str: []const u8 = blockTypeName(block_type, entry.data);
|
||||
// Determine HTML tag index for cached string
|
||||
const tag_index = getBlockTypeTag(block_type, entry.data);
|
||||
|
||||
// For headings, compute slug before counting props
|
||||
const slug: ?[]const u8 = if (block_type == .h) self.#heading_tracker.leaveHeading(bun.default_allocator) else null;
|
||||
@@ -496,7 +496,7 @@ const ParseRenderer = struct {
|
||||
|
||||
// Build React element — use component override as type if set
|
||||
const component = self.getBlockComponent(block_type, entry.data);
|
||||
const type_val: JSValue = if (component != .zero) component else try bun.String.createUTF8ForJS(g, type_str);
|
||||
const type_val: JSValue = if (component != .zero) component else getCachedTagString(g, tag_index);
|
||||
|
||||
const props = JSValue.createEmptyObject(g, props_count);
|
||||
self.#marked_args.append(props);
|
||||
@@ -572,7 +572,7 @@ const ParseRenderer = struct {
|
||||
const entry = self.#stack.pop().?;
|
||||
const g = self.#globalObject;
|
||||
|
||||
const type_str: []const u8 = spanTypeName(span_type);
|
||||
const tag_index = getSpanTypeTag(span_type);
|
||||
|
||||
// Count props fields: always children (or alt for img) + metadata
|
||||
var props_count: usize = 1; // children (or alt for img)
|
||||
@@ -592,7 +592,7 @@ const ParseRenderer = struct {
|
||||
|
||||
// Build React element: { $$typeof, type, key, ref, props }
|
||||
const component = self.getSpanComponent(span_type);
|
||||
const type_val: JSValue = if (component != .zero) component else try bun.String.createUTF8ForJS(g, type_str);
|
||||
const type_val: JSValue = if (component != .zero) component else getCachedTagString(g, tag_index);
|
||||
|
||||
const props = JSValue.createEmptyObject(g, props_count);
|
||||
self.#marked_args.append(props);
|
||||
@@ -675,7 +675,7 @@ const ParseRenderer = struct {
|
||||
switch (text_type) {
|
||||
.br => {
|
||||
const br_component = self.#components.br;
|
||||
const br_type: JSValue = if (br_component != .zero) br_component else try bun.String.createUTF8ForJS(g, "br");
|
||||
const br_type: JSValue = if (br_component != .zero) br_component else getCachedTagString(g, .br);
|
||||
const empty_props = JSValue.createEmptyObject(g, 0);
|
||||
self.#marked_args.append(empty_props);
|
||||
const obj = self.createElement(br_type, empty_props);
|
||||
@@ -705,53 +705,6 @@ const ParseRenderer = struct {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Type name mappings
|
||||
// ========================================
|
||||
|
||||
fn blockTypeName(block_type: md.BlockType, data: u32) []const u8 {
|
||||
return switch (block_type) {
|
||||
.h => switch (data) {
|
||||
1 => "h1",
|
||||
2 => "h2",
|
||||
3 => "h3",
|
||||
4 => "h4",
|
||||
5 => "h5",
|
||||
else => "h6",
|
||||
},
|
||||
.p => "p",
|
||||
.quote => "blockquote",
|
||||
.ul => "ul",
|
||||
.ol => "ol",
|
||||
.li => "li",
|
||||
.code => "pre",
|
||||
.hr => "hr",
|
||||
.html => "html",
|
||||
.table => "table",
|
||||
.thead => "thead",
|
||||
.tbody => "tbody",
|
||||
.tr => "tr",
|
||||
.th => "th",
|
||||
.td => "td",
|
||||
.doc => "div",
|
||||
};
|
||||
}
|
||||
|
||||
fn spanTypeName(span_type: md.SpanType) []const u8 {
|
||||
return switch (span_type) {
|
||||
.em => "em",
|
||||
.strong => "strong",
|
||||
.a => "a",
|
||||
.img => "img",
|
||||
.code => "code",
|
||||
.del => "del",
|
||||
.latexmath => "math",
|
||||
.latexmath_display => "math",
|
||||
.wikilink => "a",
|
||||
.u => "u",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Renderer that calls JavaScript callbacks for each markdown element.
|
||||
@@ -1125,6 +1078,89 @@ fn extractLanguage(src_text: []const u8, info_beg: u32) []const u8 {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Cached tag string indices - must match BunMarkdownTagStrings.h
|
||||
const TagIndex = enum(u8) {
|
||||
h1 = 0,
|
||||
h2 = 1,
|
||||
h3 = 2,
|
||||
h4 = 3,
|
||||
h5 = 4,
|
||||
h6 = 5,
|
||||
p = 6,
|
||||
blockquote = 7,
|
||||
ul = 8,
|
||||
ol = 9,
|
||||
li = 10,
|
||||
pre = 11,
|
||||
hr = 12,
|
||||
html = 13,
|
||||
table = 14,
|
||||
thead = 15,
|
||||
tbody = 16,
|
||||
tr = 17,
|
||||
th = 18,
|
||||
td = 19,
|
||||
div = 20,
|
||||
em = 21,
|
||||
strong = 22,
|
||||
a = 23,
|
||||
img = 24,
|
||||
code = 25,
|
||||
del = 26,
|
||||
math = 27,
|
||||
u = 28,
|
||||
br = 29,
|
||||
};
|
||||
|
||||
extern fn BunMarkdownTagStrings__getTagString(*jsc.JSGlobalObject, u8) JSValue;
|
||||
|
||||
fn getCachedTagString(globalObject: *jsc.JSGlobalObject, tag: TagIndex) JSValue {
|
||||
return BunMarkdownTagStrings__getTagString(globalObject, @intFromEnum(tag));
|
||||
}
|
||||
|
||||
fn getBlockTypeTag(block_type: md.BlockType, data: u32) TagIndex {
|
||||
return switch (block_type) {
|
||||
.h => switch (data) {
|
||||
1 => .h1,
|
||||
2 => .h2,
|
||||
3 => .h3,
|
||||
4 => .h4,
|
||||
5 => .h5,
|
||||
else => .h6,
|
||||
},
|
||||
.p => .p,
|
||||
.quote => .blockquote,
|
||||
.ul => .ul,
|
||||
.ol => .ol,
|
||||
.li => .li,
|
||||
.code => .pre,
|
||||
.hr => .hr,
|
||||
.html => .html,
|
||||
.table => .table,
|
||||
.thead => .thead,
|
||||
.tbody => .tbody,
|
||||
.tr => .tr,
|
||||
.th => .th,
|
||||
.td => .td,
|
||||
.doc => .div,
|
||||
};
|
||||
}
|
||||
|
||||
fn getSpanTypeTag(span_type: md.SpanType) TagIndex {
|
||||
return switch (span_type) {
|
||||
.em => .em,
|
||||
.strong => .strong,
|
||||
.a => .a,
|
||||
.img => .img,
|
||||
.code => .code,
|
||||
.del => .del,
|
||||
.latexmath => .math,
|
||||
.latexmath_display => .math,
|
||||
.wikilink => .a,
|
||||
.u => .u,
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const bun = @import("bun");
|
||||
|
||||
@@ -245,6 +245,16 @@ pub const All = struct {
|
||||
}
|
||||
|
||||
pub fn getTimeout(this: *All, spec: *timespec, vm: *VirtualMachine) bool {
|
||||
// On POSIX, if there are pending immediate tasks, use a zero timeout
|
||||
// so epoll/kqueue returns immediately without the overhead of writing
|
||||
// to the eventfd via wakeup().
|
||||
if (comptime Environment.isPosix) {
|
||||
if (vm.event_loop.immediate_tasks.items.len > 0) {
|
||||
spec.* = .{ .nsec = 0, .sec = 0 };
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var maybe_now: ?timespec = null;
|
||||
while (this.timers.peek()) |min| {
|
||||
const now = maybe_now orelse now: {
|
||||
|
||||
@@ -4,7 +4,7 @@ const TimerObjectInternals = @This();
|
||||
/// Identifier for this timer that is exposed to JavaScript (by `+timer`)
|
||||
id: i32 = -1,
|
||||
interval: u31 = 0,
|
||||
strong_this: jsc.Strong.Optional = .empty,
|
||||
this_value: jsc.JSRef = .empty(),
|
||||
flags: Flags = .{},
|
||||
|
||||
/// Used by:
|
||||
@@ -76,31 +76,41 @@ pub fn runImmediateTask(this: *TimerObjectInternals, vm: *VirtualMachine) bool {
|
||||
// loop alive other than setImmediates
|
||||
(!this.flags.is_keeping_event_loop_alive and !vm.isEventLoopAliveExcludingImmediates()))
|
||||
{
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.this_value.downgrade();
|
||||
this.deref();
|
||||
return false;
|
||||
}
|
||||
|
||||
const timer = this.strong_this.get() orelse {
|
||||
const timer = this.this_value.tryGet() orelse {
|
||||
if (Environment.isDebug) {
|
||||
@panic("TimerObjectInternals.runImmediateTask: this_object is null");
|
||||
}
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.deref();
|
||||
return false;
|
||||
};
|
||||
const globalThis = vm.global;
|
||||
this.strong_this.deinit();
|
||||
this.this_value.downgrade();
|
||||
this.eventLoopTimer().state = .FIRED;
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
timer.ensureStillAlive();
|
||||
|
||||
vm.eventLoop().enter();
|
||||
const callback = ImmediateObject.js.callbackGetCached(timer).?;
|
||||
const arguments = ImmediateObject.js.argumentsGetCached(timer).?;
|
||||
this.ref();
|
||||
const exception_thrown = this.run(globalThis, timer, callback, arguments, this.asyncID(), vm);
|
||||
this.deref();
|
||||
|
||||
if (this.eventLoopTimer().state == .FIRED) {
|
||||
this.deref();
|
||||
}
|
||||
const exception_thrown = brk: {
|
||||
this.ref();
|
||||
defer {
|
||||
if (this.eventLoopTimer().state == .FIRED) {
|
||||
this.deref();
|
||||
}
|
||||
this.deref();
|
||||
}
|
||||
break :brk this.run(globalThis, timer, callback, arguments, this.asyncID(), vm);
|
||||
};
|
||||
// --- after this point, the timer is no longer guaranteed to be alive ---
|
||||
|
||||
vm.eventLoop().exitMaybeDrainMicrotasks(!exception_thrown) catch return true;
|
||||
|
||||
@@ -120,7 +130,13 @@ pub fn fire(this: *TimerObjectInternals, _: *const timespec, vm: *jsc.VirtualMac
|
||||
this.eventLoopTimer().state = .FIRED;
|
||||
|
||||
const globalThis = vm.global;
|
||||
const this_object = this.strong_this.get().?;
|
||||
const this_object = this.this_value.tryGet() orelse {
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.flags.has_cleared_timer = true;
|
||||
this.this_value.downgrade();
|
||||
this.deref();
|
||||
return;
|
||||
};
|
||||
|
||||
const callback: JSValue, const arguments: JSValue, var idle_timeout: JSValue, var repeat: JSValue = switch (kind) {
|
||||
.setImmediate => .{
|
||||
@@ -143,7 +159,7 @@ pub fn fire(this: *TimerObjectInternals, _: *const timespec, vm: *jsc.VirtualMac
|
||||
}
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.flags.has_cleared_timer = true;
|
||||
this.strong_this.deinit();
|
||||
this.this_value.downgrade();
|
||||
this.deref();
|
||||
|
||||
return;
|
||||
@@ -152,7 +168,7 @@ pub fn fire(this: *TimerObjectInternals, _: *const timespec, vm: *jsc.VirtualMac
|
||||
var time_before_call: timespec = undefined;
|
||||
|
||||
if (kind != .setInterval) {
|
||||
this.strong_this.clearWithoutDeallocation();
|
||||
this.this_value.downgrade();
|
||||
} else {
|
||||
time_before_call = timespec.msFromNow(.allow_mocked_time, this.interval);
|
||||
}
|
||||
@@ -239,7 +255,7 @@ fn convertToInterval(this: *TimerObjectInternals, global: *JSGlobalObject, timer
|
||||
|
||||
// https://github.com/nodejs/node/blob/a7cbb904745591c9a9d047a364c2c188e5470047/lib/internal/timers.js#L613
|
||||
TimeoutObject.js.idleTimeoutSetCached(timer, global, repeat);
|
||||
this.strong_this.set(global, timer);
|
||||
this.this_value.setStrong(timer, global);
|
||||
this.flags.kind = .setInterval;
|
||||
this.interval = new_interval;
|
||||
this.reschedule(timer, vm, global);
|
||||
@@ -297,7 +313,7 @@ pub fn init(
|
||||
this.reschedule(timer, vm, global);
|
||||
}
|
||||
|
||||
this.strong_this.set(global, timer);
|
||||
this.this_value.setStrong(timer, global);
|
||||
}
|
||||
|
||||
pub fn doRef(this: *TimerObjectInternals, _: *jsc.JSGlobalObject, this_value: JSValue) JSValue {
|
||||
@@ -327,7 +343,7 @@ pub fn doRefresh(this: *TimerObjectInternals, globalObject: *jsc.JSGlobalObject,
|
||||
return this_value;
|
||||
}
|
||||
|
||||
this.strong_this.set(globalObject, this_value);
|
||||
this.this_value.setStrong(this_value, globalObject);
|
||||
this.reschedule(this_value, VirtualMachine.get(), globalObject);
|
||||
|
||||
return this_value;
|
||||
@@ -350,12 +366,18 @@ pub fn cancel(this: *TimerObjectInternals, vm: *VirtualMachine) void {
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.flags.has_cleared_timer = true;
|
||||
|
||||
if (this.flags.kind == .setImmediate) return;
|
||||
if (this.flags.kind == .setImmediate) {
|
||||
// Release the strong reference so the GC can collect the JS object.
|
||||
// The immediate task is still in the event loop queue and will be skipped
|
||||
// by runImmediateTask when it sees has_cleared_timer == true.
|
||||
this.this_value.downgrade();
|
||||
return;
|
||||
}
|
||||
|
||||
const was_active = this.eventLoopTimer().state == .ACTIVE;
|
||||
|
||||
this.eventLoopTimer().state = .CANCELLED;
|
||||
this.strong_this.deinit();
|
||||
this.this_value.downgrade();
|
||||
|
||||
if (was_active) {
|
||||
vm.timer.remove(this.eventLoopTimer());
|
||||
@@ -442,12 +464,12 @@ pub fn getDestroyed(this: *TimerObjectInternals) bool {
|
||||
}
|
||||
|
||||
pub fn finalize(this: *TimerObjectInternals) void {
|
||||
this.strong_this.deinit();
|
||||
this.this_value.finalize();
|
||||
this.deref();
|
||||
}
|
||||
|
||||
pub fn deinit(this: *TimerObjectInternals) void {
|
||||
this.strong_this.deinit();
|
||||
this.this_value.deinit();
|
||||
const vm = VirtualMachine.get();
|
||||
const kind = this.flags.kind;
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
jsc.markBinding(@src());
|
||||
if (this.socket.isDetached()) return;
|
||||
const handlers = this.getHandlers();
|
||||
log("onTimeout {s}", .{if (handlers.is_server) "S" else "C"});
|
||||
log("onTimeout {s}", .{if (handlers.mode == .server) "S" else "C"});
|
||||
const callback = handlers.onTimeout;
|
||||
if (callback == .zero or this.flags.finalizing) return;
|
||||
if (handlers.vm.isShuttingDown()) {
|
||||
@@ -281,7 +281,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
|
||||
pub fn handleConnectError(this: *This, errno: c_int) bun.JSError!void {
|
||||
const handlers = this.getHandlers();
|
||||
log("onConnectError {s} ({d}, {d})", .{ if (handlers.is_server) "S" else "C", errno, this.ref_count.get() });
|
||||
log("onConnectError {s} ({d}, {d})", .{ if (handlers.mode == .server) "S" else "C", errno, this.ref_count.get() });
|
||||
// Ensure the socket is still alive for any defer's we have
|
||||
this.ref();
|
||||
defer this.deref();
|
||||
@@ -397,7 +397,8 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
}
|
||||
|
||||
pub fn isServer(this: *const This) bool {
|
||||
return this.getHandlers().is_server;
|
||||
const handlers = this.getHandlers();
|
||||
return handlers.mode.isServer();
|
||||
}
|
||||
|
||||
pub fn onOpen(this: *This, socket: Socket) void {
|
||||
@@ -502,7 +503,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
jsc.markBinding(@src());
|
||||
if (this.socket.isDetached()) return;
|
||||
const handlers = this.getHandlers();
|
||||
log("onEnd {s}", .{if (handlers.is_server) "S" else "C"});
|
||||
log("onEnd {s}", .{if (handlers.mode == .server) "S" else "C"});
|
||||
// Ensure the socket remains alive until this is finished
|
||||
this.ref();
|
||||
defer this.deref();
|
||||
@@ -534,7 +535,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
this.socket = s;
|
||||
if (this.socket.isDetached()) return;
|
||||
const handlers = this.getHandlers();
|
||||
log("onHandshake {s} ({d})", .{ if (handlers.is_server) "S" else "C", success });
|
||||
log("onHandshake {s} ({d})", .{ if (handlers.mode == .server) "S" else "C", success });
|
||||
|
||||
const authorized = if (success == 1) true else false;
|
||||
|
||||
@@ -571,7 +572,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
result = callback.call(globalObject, this_value, &[_]JSValue{this_value}) catch |err| globalObject.takeException(err);
|
||||
|
||||
// only call onOpen once for clients
|
||||
if (!handlers.is_server) {
|
||||
if (handlers.mode != .server) {
|
||||
// clean onOpen callback so only called in the first handshake and not in every renegotiation
|
||||
// on servers this would require a different approach but it's not needed because our servers will not call handshake multiple times
|
||||
// servers don't support renegotiation
|
||||
@@ -600,7 +601,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
pub fn onClose(this: *This, _: Socket, err: c_int, _: ?*anyopaque) bun.JSError!void {
|
||||
jsc.markBinding(@src());
|
||||
const handlers = this.getHandlers();
|
||||
log("onClose {s}", .{if (handlers.is_server) "S" else "C"});
|
||||
log("onClose {s}", .{if (handlers.mode == .server) "S" else "C"});
|
||||
this.detachNativeCallback();
|
||||
this.socket.detach();
|
||||
defer this.deref();
|
||||
@@ -648,7 +649,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
this.socket = s;
|
||||
if (this.socket.isDetached()) return;
|
||||
const handlers = this.getHandlers();
|
||||
log("onData {s} ({d})", .{ if (handlers.is_server) "S" else "C", data.len });
|
||||
log("onData {s} ({d})", .{ if (handlers.mode == .server) "S" else "C", data.len });
|
||||
if (this.native_callback.onData(data)) return;
|
||||
|
||||
const callback = handlers.onData;
|
||||
@@ -691,7 +692,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
pub fn getListener(this: *This, _: *jsc.JSGlobalObject) JSValue {
|
||||
const handlers = this.handlers orelse return .js_undefined;
|
||||
|
||||
if (!handlers.is_server or this.socket.isDetached()) {
|
||||
if (handlers.mode != .server or this.socket.isDetached()) {
|
||||
return .js_undefined;
|
||||
}
|
||||
|
||||
@@ -1352,7 +1353,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
};
|
||||
|
||||
const this_handlers = this.getHandlers();
|
||||
const handlers = try Handlers.fromJS(globalObject, socket_obj, this_handlers.is_server);
|
||||
const handlers = try Handlers.fromJS(globalObject, socket_obj, this_handlers.mode == .server);
|
||||
this_handlers.deinit();
|
||||
this_handlers.* = handlers;
|
||||
|
||||
@@ -1380,6 +1381,9 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
if (this.socket.isDetached() or this.socket.isNamedPipe()) {
|
||||
return .js_undefined;
|
||||
}
|
||||
if (this.isServer()) {
|
||||
return globalObject.throw("Server-side upgradeTLS is not supported. Use upgradeDuplexToTLS with isServer: true instead.", .{});
|
||||
}
|
||||
const args = callframe.arguments_old(1);
|
||||
|
||||
if (args.len < 1) {
|
||||
@@ -1571,7 +1575,7 @@ pub fn NewSocket(comptime ssl: bool) type {
|
||||
this.socket.detach();
|
||||
|
||||
// start TLS handshake after we set extension on the socket
|
||||
new_socket.startTLS(!handlers_ptr.is_server);
|
||||
new_socket.startTLS(handlers_ptr.mode != .server);
|
||||
|
||||
success = true;
|
||||
return array;
|
||||
@@ -1703,6 +1707,15 @@ pub fn NewWrappedHandler(comptime tls: bool) type {
|
||||
|
||||
pub fn onClose(this: WrappedSocket, socket: Socket, err: c_int, data: ?*anyopaque) bun.JSError!void {
|
||||
if (comptime tls) {
|
||||
// Clean up the raw TCP socket from upgradeTLS() — its onClose
|
||||
// never fires because uws closes through the TLS context only.
|
||||
defer {
|
||||
if (!this.tcp.socket.isDetached()) {
|
||||
this.tcp.socket.detach();
|
||||
this.tcp.has_pending_activity.store(false, .release);
|
||||
this.tcp.deref();
|
||||
}
|
||||
}
|
||||
try TLSSocket.onClose(this.tls, socket, err, data);
|
||||
} else {
|
||||
try TLSSocket.onClose(this.tcp, socket, err, data);
|
||||
@@ -1754,6 +1767,23 @@ pub fn NewWrappedHandler(comptime tls: bool) type {
|
||||
};
|
||||
}
|
||||
|
||||
/// Unified socket mode replacing the old is_server bool + TLSMode pair.
|
||||
pub const SocketMode = enum {
|
||||
/// Default — TLS client or non-TLS socket
|
||||
client,
|
||||
/// Listener-owned server. TLS (if any) configured at the listener level.
|
||||
server,
|
||||
/// Duplex upgraded to TLS server role. Not listener-owned —
|
||||
/// markInactive uses client lifecycle path.
|
||||
duplex_server,
|
||||
|
||||
/// Returns true for any mode that acts as a TLS server (ALPN, handshake direction).
|
||||
/// Both .server and .duplex_server present as server to peers.
|
||||
pub fn isServer(this: SocketMode) bool {
|
||||
return this == .server or this == .duplex_server;
|
||||
}
|
||||
};
|
||||
|
||||
pub const DuplexUpgradeContext = struct {
|
||||
upgrade: uws.UpgradedDuplex,
|
||||
// We only us a tls and not a raw socket when upgrading a Duplex, Duplex dont support socketpairs
|
||||
@@ -1764,6 +1794,7 @@ pub const DuplexUpgradeContext = struct {
|
||||
task_event: EventState = .StartTLS,
|
||||
ssl_config: ?jsc.API.ServerConfig.SSLConfig,
|
||||
is_open: bool = false,
|
||||
#mode: SocketMode = .client,
|
||||
|
||||
pub const EventState = enum(u8) {
|
||||
StartTLS,
|
||||
@@ -1846,7 +1877,8 @@ pub const DuplexUpgradeContext = struct {
|
||||
switch (this.task_event) {
|
||||
.StartTLS => {
|
||||
if (this.ssl_config) |config| {
|
||||
this.upgrade.startTLS(config, true) catch |err| {
|
||||
log("DuplexUpgradeContext.startTLS mode={s}", .{@tagName(this.#mode)});
|
||||
this.upgrade.startTLS(config, this.#mode == .client) catch |err| {
|
||||
switch (err) {
|
||||
error.OutOfMemory => {
|
||||
bun.outOfMemory();
|
||||
@@ -1914,8 +1946,15 @@ pub fn jsUpgradeDuplexToTLS(globalObject: *jsc.JSGlobalObject, callframe: *jsc.C
|
||||
return globalObject.throw("Expected \"socket\" option", .{});
|
||||
};
|
||||
|
||||
const is_server = false; // A duplex socket is always handled as a client
|
||||
const handlers = try Handlers.fromJS(globalObject, socket_obj, is_server);
|
||||
var is_server = false;
|
||||
if (try opts.getTruthy(globalObject, "isServer")) |is_server_val| {
|
||||
is_server = is_server_val.toBoolean();
|
||||
}
|
||||
// Note: Handlers.fromJS is_server=false because these handlers are standalone
|
||||
// allocations (not embedded in a Listener). The mode field on Handlers
|
||||
// controls lifecycle (markInactive expects a Listener parent when .server).
|
||||
// The TLS direction (client vs server) is controlled by DuplexUpgradeContext.mode.
|
||||
const handlers = try Handlers.fromJS(globalObject, socket_obj, false);
|
||||
|
||||
var ssl_opts: ?jsc.API.ServerConfig.SSLConfig = null;
|
||||
if (try opts.getTruthy(globalObject, "tls")) |tls| {
|
||||
@@ -1937,6 +1976,9 @@ pub fn jsUpgradeDuplexToTLS(globalObject: *jsc.JSGlobalObject, callframe: *jsc.C
|
||||
|
||||
const handlers_ptr = bun.handleOom(handlers.vm.allocator.create(Handlers));
|
||||
handlers_ptr.* = handlers;
|
||||
// Set mode to duplex_server so TLSSocket.isServer() returns true for ALPN server mode
|
||||
// without affecting markInactive lifecycle (which requires a Listener parent).
|
||||
handlers_ptr.mode = if (is_server) .duplex_server else .client;
|
||||
var tls = bun.new(TLSSocket, .{
|
||||
.ref_count = .init(),
|
||||
.handlers = handlers_ptr,
|
||||
@@ -1963,6 +2005,7 @@ pub fn jsUpgradeDuplexToTLS(globalObject: *jsc.JSGlobalObject, callframe: *jsc.C
|
||||
.vm = globalObject.bunVM(),
|
||||
.task = undefined,
|
||||
.ssl_config = socket_config.*,
|
||||
.#mode = if (is_server) .duplex_server else .client,
|
||||
});
|
||||
tls.ref();
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ binary_type: BinaryType = .Buffer,
|
||||
vm: *jsc.VirtualMachine,
|
||||
globalObject: *jsc.JSGlobalObject,
|
||||
active_connections: u32 = 0,
|
||||
is_server: bool,
|
||||
mode: SocketMode = .client,
|
||||
promise: jsc.Strong.Optional = .empty,
|
||||
|
||||
protection_count: if (Environment.ci_assert) u32 else void = if (Environment.ci_assert) 0,
|
||||
@@ -81,7 +81,7 @@ pub fn markInactive(this: *Handlers) void {
|
||||
Listener.log("markInactive", .{});
|
||||
this.active_connections -= 1;
|
||||
if (this.active_connections == 0) {
|
||||
if (this.is_server) {
|
||||
if (this.mode == .server) {
|
||||
const listen_socket: *Listener = @fieldParentPtr("handlers", this);
|
||||
// allow it to be GC'd once the last connection is closed and it's not listening anymore
|
||||
if (listen_socket.listener == .none) {
|
||||
@@ -133,7 +133,7 @@ pub fn fromGenerated(
|
||||
var result: Handlers = .{
|
||||
.vm = globalObject.bunVM(),
|
||||
.globalObject = globalObject,
|
||||
.is_server = is_server,
|
||||
.mode = if (is_server) .server else .client,
|
||||
.binary_type = switch (generated.binary_type) {
|
||||
.arraybuffer => .ArrayBuffer,
|
||||
.buffer => .Buffer,
|
||||
@@ -217,7 +217,7 @@ pub fn clone(this: *const Handlers) Handlers {
|
||||
.vm = this.vm,
|
||||
.globalObject = this.globalObject,
|
||||
.binary_type = this.binary_type,
|
||||
.is_server = this.is_server,
|
||||
.mode = this.mode,
|
||||
};
|
||||
inline for (callback_fields) |field| {
|
||||
@field(result, field) = @field(this, field);
|
||||
@@ -346,6 +346,7 @@ const strings = bun.strings;
|
||||
const uws = bun.uws;
|
||||
const Listener = bun.api.Listener;
|
||||
const SSLConfig = bun.api.ServerConfig.SSLConfig;
|
||||
const SocketMode = bun.api.socket.SocketMode;
|
||||
|
||||
const jsc = bun.jsc;
|
||||
const JSValue = jsc.JSValue;
|
||||
|
||||
@@ -91,7 +91,7 @@ pub fn reload(this: *Listener, globalObject: *jsc.JSGlobalObject, callframe: *js
|
||||
return globalObject.throw("Expected \"socket\" object", .{});
|
||||
};
|
||||
|
||||
const handlers = try Handlers.fromJS(globalObject, socket_obj, this.handlers.is_server);
|
||||
const handlers = try Handlers.fromJS(globalObject, socket_obj, this.handlers.mode == .server);
|
||||
this.handlers.deinit();
|
||||
this.handlers = handlers;
|
||||
|
||||
@@ -773,7 +773,7 @@ pub fn connectInner(globalObject: *jsc.JSGlobalObject, prev_maybe_tcp: ?*TCPSock
|
||||
|
||||
const handlers_ptr = bun.handleOom(handlers.vm.allocator.create(Handlers));
|
||||
handlers_ptr.* = handlers.*;
|
||||
handlers_ptr.is_server = false;
|
||||
handlers_ptr.mode = .client;
|
||||
|
||||
var promise = jsc.JSPromise.create(globalObject);
|
||||
const promise_value = promise.toJS();
|
||||
|
||||
@@ -173,8 +173,10 @@ pub fn SSLWrapper(comptime T: type) type {
|
||||
|
||||
// flush buffered data and returns amount of pending data to write
|
||||
pub fn flush(this: *This) usize {
|
||||
const ssl = this.ssl orelse return 0;
|
||||
// handleTraffic may trigger a close callback which frees ssl,
|
||||
// so we must not capture the ssl pointer before calling it.
|
||||
this.handleTraffic();
|
||||
const ssl = this.ssl orelse return 0;
|
||||
const pending = BoringSSL.BIO_ctrl_pending(BoringSSL.SSL_get_wbio(ssl));
|
||||
if (pending > 0) return @intCast(pending);
|
||||
return 0;
|
||||
@@ -428,6 +430,8 @@ pub fn SSLWrapper(comptime T: type) type {
|
||||
if (read > 0) {
|
||||
log("triggering data callback (read {d})", .{read});
|
||||
this.triggerDataCallback(buffer[0..read]);
|
||||
// The data callback may have closed the connection
|
||||
if (this.ssl == null or this.flags.closed_notified) return false;
|
||||
}
|
||||
this.triggerCloseCallback();
|
||||
return false;
|
||||
|
||||
@@ -468,6 +468,17 @@ pub fn writeHead(this: *NodeHTTPResponse, globalObject: *jsc.JSGlobalObject, cal
|
||||
return globalObject.ERR(.HTTP_HEADERS_SENT, "Stream already started", .{}).throw();
|
||||
}
|
||||
|
||||
// Validate status message does not contain invalid characters (defense-in-depth
|
||||
// against HTTP response splitting). Matches Node.js checkInvalidHeaderChar:
|
||||
// rejects any char not in [\t\x20-\x7e\x80-\xff].
|
||||
if (status_message_slice.len > 0) {
|
||||
for (status_message_slice.slice()) |c| {
|
||||
if (c != '\t' and (c < 0x20 or c == 0x7f)) {
|
||||
return globalObject.ERR(.INVALID_CHAR, "Invalid character in statusMessage", .{}).throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_it: {
|
||||
if (status_message_slice.len == 0) {
|
||||
if (HTTPStatusText.get(@intCast(status_code))) |status_message| {
|
||||
|
||||
@@ -24,6 +24,18 @@ static inline bool isEscapeCharacter(Char c)
|
||||
}
|
||||
}
|
||||
|
||||
// SIMD comparison against exact escape character values. Used to refine
|
||||
// the broad range match (0x10-0x1F / 0x90-0x9F) to only actual escape
|
||||
// introducers: 0x1B, 0x90, 0x98, 0x9B, 0x9D, 0x9E, 0x9F.
|
||||
template<typename SIMDType>
|
||||
static auto exactEscapeMatch(std::conditional_t<sizeof(SIMDType) == 1, simde_uint8x16_t, simde_uint16x8_t> chunk)
|
||||
{
|
||||
if constexpr (sizeof(SIMDType) == 1)
|
||||
return SIMD::equal<0x1b, 0x90, 0x98, 0x9b, 0x9d, 0x9e, 0x9f>(chunk);
|
||||
else
|
||||
return SIMD::equal<u'\x1b', u'\x90', u'\x98', u'\x9b', u'\x9d', u'\x9e', u'\x9f'>(chunk);
|
||||
}
|
||||
|
||||
// Find the first escape character in a string using SIMD
|
||||
template<typename Char>
|
||||
static const Char* findEscapeCharacter(const Char* start, const Char* end)
|
||||
@@ -43,8 +55,13 @@ static const Char* findEscapeCharacter(const Char* start, const Char* end)
|
||||
const auto chunk = SIMD::load(reinterpret_cast<const SIMDType*>(it));
|
||||
const auto chunkMasked = SIMD::bitAnd(chunk, escMask);
|
||||
const auto chunkIsEsc = SIMD::equal(chunkMasked, escVector);
|
||||
if (const auto index = SIMD::findFirstNonZeroIndex(chunkIsEsc))
|
||||
return it + *index;
|
||||
if (SIMD::findFirstNonZeroIndex(chunkIsEsc)) {
|
||||
// Broad mask matched 0x10-0x1F / 0x90-0x9F. Refine with exact
|
||||
// escape character comparison to filter out false positives.
|
||||
const auto exactMatch = exactEscapeMatch<SIMDType>(chunk);
|
||||
if (const auto exactIndex = SIMD::findFirstNonZeroIndex(exactMatch))
|
||||
return it + *exactIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Check remaining characters
|
||||
|
||||
@@ -19,6 +19,12 @@
|
||||
|
||||
extern "C" void Bun__startCPUProfiler(JSC::VM* vm);
|
||||
extern "C" void Bun__stopCPUProfiler(JSC::VM* vm, BunString* outJSON, BunString* outText);
|
||||
extern "C" void Bun__setSamplingInterval(int intervalMicroseconds);
|
||||
|
||||
void Bun__setSamplingInterval(int intervalMicroseconds)
|
||||
{
|
||||
Bun::setSamplingInterval(intervalMicroseconds);
|
||||
}
|
||||
|
||||
namespace Bun {
|
||||
|
||||
|
||||
@@ -3,11 +3,17 @@ pub const CPUProfilerConfig = struct {
|
||||
dir: []const u8,
|
||||
md_format: bool = false,
|
||||
json_format: bool = false,
|
||||
interval: u32 = 1000,
|
||||
};
|
||||
|
||||
// C++ function declarations
|
||||
extern fn Bun__startCPUProfiler(vm: *jsc.VM) void;
|
||||
extern fn Bun__stopCPUProfiler(vm: *jsc.VM, outJSON: ?*bun.String, outText: ?*bun.String) void;
|
||||
extern fn Bun__setSamplingInterval(intervalMicroseconds: c_int) void;
|
||||
|
||||
pub fn setSamplingInterval(interval: u32) void {
|
||||
Bun__setSamplingInterval(@intCast(interval));
|
||||
}
|
||||
|
||||
pub fn startCPUProfiler(vm: *jsc.VM) void {
|
||||
Bun__startCPUProfiler(vm);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <JavaScriptCore/VM.h>
|
||||
#include <JavaScriptCore/Heap.h>
|
||||
|
||||
extern "C" int Bun__defaultRemainingRunsUntilSkipReleaseAccess;
|
||||
|
||||
extern "C" void Bun__JSC_onBeforeWait(JSC::VM* _Nonnull vm)
|
||||
{
|
||||
ASSERT(vm);
|
||||
@@ -46,7 +48,7 @@ extern "C" void Bun__JSC_onBeforeWait(JSC::VM* _Nonnull vm)
|
||||
// finalizers that might've been waiting to be run is a good idea.
|
||||
// But if you haven't, like if the process is just waiting on I/O
|
||||
// then don't bother.
|
||||
static constexpr int defaultRemainingRunsUntilSkipReleaseAccess = 10;
|
||||
const int defaultRemainingRunsUntilSkipReleaseAccess = Bun__defaultRemainingRunsUntilSkipReleaseAccess;
|
||||
|
||||
static thread_local int remainingRunsUntilSkipReleaseAccess = 0;
|
||||
|
||||
|
||||
59
src/bun.js/bindings/BunMarkdownTagStrings.cpp
Normal file
59
src/bun.js/bindings/BunMarkdownTagStrings.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "root.h"
|
||||
#include "BunMarkdownTagStrings.h"
|
||||
#include <JavaScriptCore/JSString.h>
|
||||
#include <JavaScriptCore/JSGlobalObject.h>
|
||||
#include <JavaScriptCore/LazyProperty.h>
|
||||
#include <JavaScriptCore/LazyPropertyInlines.h>
|
||||
#include "ZigGlobalObject.h"
|
||||
#include <JavaScriptCore/SlotVisitorInlines.h>
|
||||
#include <JavaScriptCore/VMTrapsInlines.h>
|
||||
|
||||
namespace Bun {
|
||||
using namespace JSC;
|
||||
|
||||
#define MARKDOWN_TAG_STRINGS_LAZY_PROPERTY_DEFINITION(name, str, idx) \
|
||||
this->m_strings[idx].initLater( \
|
||||
[](const JSC::LazyProperty<JSGlobalObject, JSString>::Initializer& init) { \
|
||||
init.set(jsOwnedString(init.vm, str)); \
|
||||
});
|
||||
|
||||
#define MARKDOWN_TAG_STRINGS_LAZY_PROPERTY_VISITOR(name, str, idx) \
|
||||
this->m_strings[idx].visit(visitor);
|
||||
|
||||
void MarkdownTagStrings::initialize()
|
||||
{
|
||||
MARKDOWN_TAG_STRINGS_EACH_NAME(MARKDOWN_TAG_STRINGS_LAZY_PROPERTY_DEFINITION)
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void MarkdownTagStrings::visit(Visitor& visitor)
|
||||
{
|
||||
MARKDOWN_TAG_STRINGS_EACH_NAME(MARKDOWN_TAG_STRINGS_LAZY_PROPERTY_VISITOR)
|
||||
}
|
||||
|
||||
template void MarkdownTagStrings::visit(JSC::AbstractSlotVisitor&);
|
||||
template void MarkdownTagStrings::visit(JSC::SlotVisitor&);
|
||||
|
||||
} // namespace Bun
|
||||
|
||||
// C API for Zig bindings
|
||||
extern "C" JSC::EncodedJSValue BunMarkdownTagStrings__getTagString(Zig::GlobalObject* globalObject, uint8_t tagIndex)
|
||||
{
|
||||
if (tagIndex >= MARKDOWN_TAG_STRINGS_COUNT)
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
|
||||
auto& tagStrings = globalObject->markdownTagStrings();
|
||||
|
||||
// Use a switch to call the appropriate accessor
|
||||
switch (tagIndex) {
|
||||
#define MARKDOWN_TAG_STRINGS_CASE(name, str, idx) \
|
||||
case idx: \
|
||||
return JSC::JSValue::encode(tagStrings.name##String(globalObject));
|
||||
|
||||
MARKDOWN_TAG_STRINGS_EACH_NAME(MARKDOWN_TAG_STRINGS_CASE)
|
||||
|
||||
#undef MARKDOWN_TAG_STRINGS_CASE
|
||||
default:
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
}
|
||||
70
src/bun.js/bindings/BunMarkdownTagStrings.h
Normal file
70
src/bun.js/bindings/BunMarkdownTagStrings.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <JavaScriptCore/JSString.h>
|
||||
#include <JavaScriptCore/LazyProperty.h>
|
||||
|
||||
// Markdown HTML tag names cached as JSStrings
|
||||
// These are commonly reused when rendering markdown to React elements
|
||||
|
||||
// clang-format off
|
||||
#define MARKDOWN_TAG_STRINGS_EACH_NAME(macro) \
|
||||
macro(h1, "h1"_s, 0) \
|
||||
macro(h2, "h2"_s, 1) \
|
||||
macro(h3, "h3"_s, 2) \
|
||||
macro(h4, "h4"_s, 3) \
|
||||
macro(h5, "h5"_s, 4) \
|
||||
macro(h6, "h6"_s, 5) \
|
||||
macro(p, "p"_s, 6) \
|
||||
macro(blockquote, "blockquote"_s, 7) \
|
||||
macro(ul, "ul"_s, 8) \
|
||||
macro(ol, "ol"_s, 9) \
|
||||
macro(li, "li"_s, 10) \
|
||||
macro(pre, "pre"_s, 11) \
|
||||
macro(hr, "hr"_s, 12) \
|
||||
macro(html, "html"_s, 13) \
|
||||
macro(table, "table"_s, 14) \
|
||||
macro(thead, "thead"_s, 15) \
|
||||
macro(tbody, "tbody"_s, 16) \
|
||||
macro(tr, "tr"_s, 17) \
|
||||
macro(th, "th"_s, 18) \
|
||||
macro(td, "td"_s, 19) \
|
||||
macro(div, "div"_s, 20) \
|
||||
macro(em, "em"_s, 21) \
|
||||
macro(strong, "strong"_s, 22) \
|
||||
macro(a, "a"_s, 23) \
|
||||
macro(img, "img"_s, 24) \
|
||||
macro(code, "code"_s, 25) \
|
||||
macro(del, "del"_s, 26) \
|
||||
macro(math, "math"_s, 27) \
|
||||
macro(u, "u"_s, 28) \
|
||||
macro(br, "br"_s, 29)
|
||||
// clang-format on
|
||||
|
||||
#define MARKDOWN_TAG_STRINGS_COUNT 30
|
||||
|
||||
namespace Bun {
|
||||
|
||||
using namespace JSC;
|
||||
|
||||
class MarkdownTagStrings {
|
||||
public:
|
||||
#define MARKDOWN_TAG_STRINGS_ACCESSOR_DEFINITION(name, str, idx) \
|
||||
JSC::JSString* name##String(JSC::JSGlobalObject* globalObject) \
|
||||
{ \
|
||||
return m_strings[idx].getInitializedOnMainThread(globalObject); \
|
||||
}
|
||||
|
||||
MARKDOWN_TAG_STRINGS_EACH_NAME(MARKDOWN_TAG_STRINGS_ACCESSOR_DEFINITION)
|
||||
|
||||
#undef MARKDOWN_TAG_STRINGS_ACCESSOR_DEFINITION
|
||||
|
||||
void initialize();
|
||||
|
||||
template<typename Visitor>
|
||||
void visit(Visitor& visitor);
|
||||
|
||||
private:
|
||||
JSC::LazyProperty<JSC::JSGlobalObject, JSC::JSString> m_strings[MARKDOWN_TAG_STRINGS_COUNT];
|
||||
};
|
||||
|
||||
} // namespace Bun
|
||||
@@ -954,6 +954,7 @@ BUN_DEFINE_HOST_FUNCTION(jsFunctionBunPluginClear, (JSC::JSGlobalObject * global
|
||||
global->onResolvePlugins.namespaces.clear();
|
||||
|
||||
delete global->onLoadPlugins.virtualModules;
|
||||
global->onLoadPlugins.virtualModules = nullptr;
|
||||
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ static uint8_t x86_cpu_features()
|
||||
#if CPU(ARM64)
|
||||
|
||||
#if OS(WINDOWS)
|
||||
#include <windows.h>
|
||||
#elif OS(MACOS)
|
||||
#include <sys/sysctl.h>
|
||||
#elif OS(LINUX)
|
||||
@@ -81,7 +82,18 @@ static uint8_t aarch64_cpu_features()
|
||||
uint8_t features = 0;
|
||||
|
||||
#if OS(WINDOWS)
|
||||
#pragma error "TODO: Implement AArch64 CPU features for Windows"
|
||||
// FP is mandatory on AArch64 — no separate PF_ constant exists for it
|
||||
features |= 1 << static_cast<uint8_t>(AArch64CPUFeature::fp);
|
||||
if (IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE))
|
||||
features |= 1 << static_cast<uint8_t>(AArch64CPUFeature::neon);
|
||||
if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE))
|
||||
features |= 1 << static_cast<uint8_t>(AArch64CPUFeature::aes);
|
||||
if (IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE))
|
||||
features |= 1 << static_cast<uint8_t>(AArch64CPUFeature::crc32);
|
||||
if (IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE))
|
||||
features |= 1 << static_cast<uint8_t>(AArch64CPUFeature::atomics);
|
||||
if (IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE))
|
||||
features |= 1 << static_cast<uint8_t>(AArch64CPUFeature::sve);
|
||||
#elif OS(MACOS)
|
||||
int value = 0;
|
||||
size_t size = sizeof(value);
|
||||
|
||||
@@ -39,7 +39,7 @@ static WebCore::ExceptionOr<void> encode(VM& vm, const WTF::BitSet<256>& doNotEs
|
||||
// 4-d-ii-1. Let V be the code unit value of C.
|
||||
char32_t codePoint;
|
||||
if (!U16_IS_LEAD(character))
|
||||
codePoint = character;
|
||||
codePoint = static_cast<char32_t>(character);
|
||||
else {
|
||||
// 4-d-iii. Else,
|
||||
// 4-d-iii-1. Increase k by 1.
|
||||
|
||||
@@ -55,6 +55,10 @@ template<typename CollectionType, typename KeyType> static auto findInSortedPair
|
||||
inline void checkEncodingTableInvariants() {}
|
||||
#endif
|
||||
|
||||
// LLVM 21+ -Wcharacter-conversion flags intentional char32_t/char16_t comparisons
|
||||
// used for Unicode code point range checks in findFirstInSortedPairs.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wcharacter-conversion"
|
||||
struct CompareFirst {
|
||||
template<typename TypeA, typename TypeB> bool operator()(const TypeA& a, const TypeB& b)
|
||||
{
|
||||
@@ -132,5 +136,6 @@ template<typename CollectionType, typename KeyType> static auto findInSortedPair
|
||||
}
|
||||
return std::ranges::equal_range(collection, makeFirstAdapter(key), CompareFirst {});
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
}
|
||||
|
||||
@@ -641,13 +641,16 @@ JSC_DEFINE_CUSTOM_GETTER(errorInstanceLazyStackCustomGetter, (JSGlobalObject * g
|
||||
OrdinalNumber column;
|
||||
String sourceURL;
|
||||
auto stackTrace = errorObject->stackTrace();
|
||||
if (stackTrace == nullptr) {
|
||||
return JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
JSValue result = computeErrorInfoToJSValue(vm, *stackTrace, line, column, sourceURL, errorObject, nullptr);
|
||||
stackTrace->clear();
|
||||
errorObject->setStackFrames(vm, {});
|
||||
JSValue result;
|
||||
if (stackTrace == nullptr) {
|
||||
WTF::Vector<JSC::StackFrame> emptyTrace;
|
||||
result = computeErrorInfoToJSValue(vm, emptyTrace, line, column, sourceURL, errorObject, nullptr);
|
||||
} else {
|
||||
result = computeErrorInfoToJSValue(vm, *stackTrace, line, column, sourceURL, errorObject, nullptr);
|
||||
stackTrace->clear();
|
||||
errorObject->setStackFrames(vm, {});
|
||||
}
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
errorObject->putDirect(vm, vm.propertyNames->stack, result, 0);
|
||||
return JSValue::encode(result);
|
||||
@@ -687,17 +690,27 @@ JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace, (JSC::JSGlobalOb
|
||||
JSCStackTrace::getFramesForCaller(vm, callFrame, errorObject, caller, stackTrace, stackTraceLimit);
|
||||
|
||||
if (auto* instance = jsDynamicCast<JSC::ErrorInstance*>(errorObject)) {
|
||||
// Force materialization before replacing the stack frames, so that JSC's
|
||||
// internal lazy error info mechanism doesn't later see the replaced (possibly empty)
|
||||
// stack trace and fail to create the stack property.
|
||||
if (!instance->hasMaterializedErrorInfo())
|
||||
instance->materializeErrorInfoIfNeeded(vm);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
instance->setStackFrames(vm, WTF::move(stackTrace));
|
||||
if (instance->hasMaterializedErrorInfo()) {
|
||||
|
||||
{
|
||||
const auto& propertyName = vm.propertyNames->stack;
|
||||
VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable);
|
||||
VM::DeletePropertyModeScope deleteScope(vm, VM::DeletePropertyMode::IgnoreConfigurable);
|
||||
DeletePropertySlot slot;
|
||||
JSObject::deleteProperty(instance, globalObject, propertyName, slot);
|
||||
if (auto* zigGlobalObject = jsDynamicCast<Zig::GlobalObject*>(globalObject)) {
|
||||
instance->putDirectCustomAccessor(vm, vm.propertyNames->stack, zigGlobalObject->m_lazyStackCustomGetterSetter.get(zigGlobalObject), JSC::PropertyAttribute::CustomAccessor | 0);
|
||||
} else {
|
||||
instance->putDirectCustomAccessor(vm, vm.propertyNames->stack, CustomGetterSetter::create(vm, errorInstanceLazyStackCustomGetter, errorInstanceLazyStackCustomSetter), JSC::PropertyAttribute::CustomAccessor | 0);
|
||||
}
|
||||
}
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (auto* zigGlobalObject = jsDynamicCast<Zig::GlobalObject*>(globalObject)) {
|
||||
instance->putDirectCustomAccessor(vm, vm.propertyNames->stack, zigGlobalObject->m_lazyStackCustomGetterSetter.get(zigGlobalObject), JSC::PropertyAttribute::CustomAccessor | 0);
|
||||
} else {
|
||||
instance->putDirectCustomAccessor(vm, vm.propertyNames->stack, CustomGetterSetter::create(vm, errorInstanceLazyStackCustomGetter, errorInstanceLazyStackCustomSetter), JSC::PropertyAttribute::CustomAccessor | 0);
|
||||
}
|
||||
} else {
|
||||
OrdinalNumber line;
|
||||
|
||||
@@ -119,6 +119,7 @@ JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_swap16);
|
||||
JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_swap32);
|
||||
JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_swap64);
|
||||
JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_toString);
|
||||
JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_slice);
|
||||
JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_write);
|
||||
JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_writeBigInt64LE);
|
||||
JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_writeBigInt64BE);
|
||||
@@ -1879,6 +1880,103 @@ bool inline parseArrayIndex(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalO
|
||||
return true;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE size_t adjustSliceOffsetInt32(int32_t offset, size_t length)
|
||||
{
|
||||
if (offset < 0) {
|
||||
int64_t adjusted = static_cast<int64_t>(offset) + static_cast<int64_t>(length);
|
||||
return adjusted > 0 ? static_cast<size_t>(adjusted) : 0;
|
||||
}
|
||||
return static_cast<size_t>(offset) < length ? static_cast<size_t>(offset) : length;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE size_t adjustSliceOffsetDouble(double offset, size_t length)
|
||||
{
|
||||
if (std::isnan(offset)) {
|
||||
return 0;
|
||||
}
|
||||
offset = std::trunc(offset);
|
||||
if (offset == 0) {
|
||||
return 0;
|
||||
} else if (offset < 0) {
|
||||
double adjusted = offset + static_cast<double>(length);
|
||||
return adjusted > 0 ? static_cast<size_t>(adjusted) : 0;
|
||||
} else {
|
||||
return offset < static_cast<double>(length) ? static_cast<size_t>(offset) : length;
|
||||
}
|
||||
}
|
||||
|
||||
static JSC::EncodedJSValue jsBufferPrototypeFunction_sliceBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||
|
||||
size_t byteLength = castedThis->byteLength();
|
||||
size_t byteOffset = castedThis->byteOffset();
|
||||
|
||||
size_t startOffset = 0;
|
||||
size_t endOffset = byteLength;
|
||||
|
||||
unsigned argCount = callFrame->argumentCount();
|
||||
|
||||
if (argCount > 0) {
|
||||
JSValue startArg = callFrame->uncheckedArgument(0);
|
||||
if (startArg.isInt32()) {
|
||||
startOffset = adjustSliceOffsetInt32(startArg.asInt32(), byteLength);
|
||||
} else if (!startArg.isUndefined()) {
|
||||
double startD = startArg.toNumber(lexicalGlobalObject);
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
startOffset = adjustSliceOffsetDouble(startD, byteLength);
|
||||
}
|
||||
}
|
||||
|
||||
if (argCount > 1) {
|
||||
JSValue endArg = callFrame->uncheckedArgument(1);
|
||||
if (endArg.isInt32()) {
|
||||
endOffset = adjustSliceOffsetInt32(endArg.asInt32(), byteLength);
|
||||
} else if (!endArg.isUndefined()) {
|
||||
double endD = endArg.toNumber(lexicalGlobalObject);
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
endOffset = adjustSliceOffsetDouble(endD, byteLength);
|
||||
}
|
||||
}
|
||||
|
||||
size_t newLength = endOffset > startOffset ? endOffset - startOffset : 0;
|
||||
|
||||
if (castedThis->isDetached()) [[unlikely]] {
|
||||
throwVMTypeError(lexicalGlobalObject, throwScope, "Buffer is detached"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
RefPtr<ArrayBuffer> buffer = castedThis->possiblySharedBuffer();
|
||||
if (!buffer) {
|
||||
throwOutOfMemoryError(globalObject, throwScope);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (castedThis->isResizableOrGrowableShared()) {
|
||||
auto* subclassStructure = globalObject->JSResizableOrGrowableSharedBufferSubclassStructure();
|
||||
auto* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, WTF::move(buffer), byteOffset + startOffset, newLength);
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
if (!uint8Array) [[unlikely]] {
|
||||
throwOutOfMemoryError(globalObject, throwScope);
|
||||
return {};
|
||||
}
|
||||
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
|
||||
}
|
||||
|
||||
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
||||
auto* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, WTF::move(buffer), byteOffset + startOffset, newLength);
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
if (!uint8Array) [[unlikely]] {
|
||||
throwOutOfMemoryError(globalObject, throwScope);
|
||||
return {};
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
|
||||
}
|
||||
|
||||
// https://github.com/nodejs/node/blob/v22.9.0/lib/buffer.js#L834
|
||||
// using byteLength and byte offsets here is intentional
|
||||
static JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
|
||||
@@ -2430,6 +2528,11 @@ JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_swap64, (JSGlobalObject * lex
|
||||
return IDLOperation<JSArrayBufferView>::call<jsBufferPrototypeFunction_swap64Body>(*lexicalGlobalObject, *callFrame, "swap64");
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_slice, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
return IDLOperation<JSArrayBufferView>::call<jsBufferPrototypeFunction_sliceBody>(*lexicalGlobalObject, *callFrame, "slice");
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsBufferPrototypeFunction_toString, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
return IDLOperation<JSArrayBufferView>::call<jsBufferPrototypeFunction_toStringBody>(*lexicalGlobalObject, *callFrame, "toString");
|
||||
@@ -2711,8 +2814,8 @@ static const HashTableValue JSBufferPrototypeTableValues[]
|
||||
{ "readUIntBE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUIntBECodeGenerator, 1 } },
|
||||
{ "readUIntLE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUIntLECodeGenerator, 1 } },
|
||||
|
||||
{ "slice"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeSliceCodeGenerator, 2 } },
|
||||
{ "subarray"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeSliceCodeGenerator, 2 } },
|
||||
{ "slice"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_slice, 2 } },
|
||||
{ "subarray"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_slice, 2 } },
|
||||
{ "swap16"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_swap16, 0 } },
|
||||
{ "swap32"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_swap32, 0 } },
|
||||
{ "swap64"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_swap64, 0 } },
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user