Compare commits

...

13 Commits

Author SHA1 Message Date
Sosuke Suzuki
a40e2b857e fix(git): fix resource leak in countCommits on exception
Replace RETURN_IF_EXCEPTION with explicit cleanup that frees the
git_revwalk before returning when toWTFString throws an exception.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:54:24 +09:00
Sosuke Suzuki
48a6082167 fix(git): fix resource leaks and add edge case tests
Fix memory leaks when JS exceptions occur during array construction
in getStatus, listFiles, and diff functions. Add comprehensive tests
for empty repositories, invalid arguments, and error messages.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:41:34 +09:00
Sosuke Suzuki
0b6d896adf test(git): add comprehensive tests for bun:git APIs
Add tests for error handling, edge cases, and temporary repository
scenarios including status detection, diff operations, and detached
HEAD state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:29:45 +09:00
Sosuke Suzuki
c9dc5dd381 feat(git): add status, diff, log, and rev-parse APIs to bun:git
Add read-only Git operations: getStatus, revParse, getCurrentBranch,
aheadBehind, listFiles, diff, countCommits, and log. Includes Status
and DeltaType constants for nodegit compatibility.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:02:28 +09:00
Sosuke Suzuki
d33550ddba docs: add type definitions for bun:git
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:53:39 +09:00
Sosuke Suzuki
f02511d2f8 feat: add bun:git module with libgit2 integration
Adds a new built-in module for Git operations using libgit2 (v1.9.0) statically linked.
Initial implementation includes Repository.open(), head(), and Commit properties (id, message, summary, author, committer, time).
Designed for local-only operations with lazy initialization to avoid overhead for users who don't need Git functionality.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:50:25 +09:00
Andrew Johnston
7f498a2e07 chore: improve markdown to html perf (#26644)
### What does this PR do?

I was looking at the [recent
support](https://github.com/oven-sh/bun/pull/26440) for markdown and did
some benchmarking against
[bindings](https://github.com/just-js/lo/blob/main/lib/md4c/api.js) i
created for my `lo` runtime to `md4c`. In some cases, Bun is quite a bit
slower, so i did a bit of digging and came up with this change. It uses
`indexOfAny` which should utilise `SIMD` where it's available to scan
ahead in the payload for characters that need escaping.

In
[benchmarks](https://gist.github.com/billywhizz/397f7929a8920c826c072139b695bb68#file-results-md)
I have done this results in anywhere from `3%` to `~15%` improvement in
throughput. The bigger the payload and the more space between entities
the bigger the gain afaict, which would make sense.

### How did you verify your code works?

It passes `test/js/bun/md/*.test.ts` running locally. Only tested on
macos. Can test on linux but I assume that will happen in CI anyway?

## main


![bun-main](https://github.com/user-attachments/assets/8b173b34-1f20-4e52-bb67-bb8b7e5658f3)

## patched


![bun-patch](https://github.com/user-attachments/assets/26bb600c-234c-4903-8f70-32f167481156)
2026-02-03 00:51:25 -08:00
Dylan Conway
5d4b1821f3 [publish images] test windows aarch64 CI (#26701)
### What does this PR do?

### How did you verify your code works?
2026-02-02 23:25:13 -08:00
Dylan Conway
41de7a3bfb [publish images] Upgrade LLVM toolchain from 19.1.7 to 21.1.8 (#26667)
## Summary
- Update LLVM version references across build scripts, Dockerfiles, CI,
Nix configs, and documentation
- Fix LLVM 21 `-Wcharacter-conversion` errors in WebKit bindings:
- `EncodingTables.h`: pragma for intentional char32_t/char16_t
comparisons
- `TextCodecCJK.cpp`: widen `gb18030AsymmetricEncode` param to char32_t
- `URLPatternParser`: widen `isValidNameCodepoint` param to char32_t,
cast for `startsWith`
- Fix `__libcpp_verbose_abort` noexcept mismatch (LLVM 21 uses
`_NOEXCEPT`)
- Fix dangling pointer in `BunJSCModule.h` (`toCString` temporary
lifetime)
- Remove `useMathSumPreciseMethod` (removed upstream in JSC)

**Before merging:** Merge https://github.com/oven-sh/WebKit/pull/153
first, then update `WEBKIT_VERSION` in `cmake/tools/SetupWebKit.cmake`
to point to the merged commit.

## Test plan
- [ ] Build bun debug on macOS with LLVM 21
- [ ] Build bun on Linux (glibc)
- [ ] Build bun on Linux (musl)
- [ ] Build bun on Windows
- [ ] Run test suite

Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-02-02 23:12:21 -08:00
Chris Lloyd
d23312d3f6 feat(test): add Symbol.dispose support to mock/spyOn (#26692)
## Summary

- Add `[Symbol.dispose]` to mock function prototype, aliased to
`mockRestore`
- Enables `using spy = spyOn(obj, "method")` to auto-restore when
leaving scope
- Works for both `spyOn()` and `mock()`

Addresses #6040 — gives users a clean way to scope spy lifetimes instead
of manually calling `mockRestore()` or relying on `afterEach`.

### Example

```ts
import { spyOn, expect, test } from "bun:test";

test("auto-restores spy", () => {
  const obj = { method: () => "original" };

  {
    using spy = spyOn(obj, "method").mockReturnValue("mocked");
    expect(obj.method()).toBe("mocked");
  }

  // automatically restored
  expect(obj.method()).toBe("original");
});
```

## Test plan

- `bun bd test test/js/bun/test/mock-disposable.test.ts` — 3 tests pass
- Verified tests fail with `USE_SYSTEM_BUN=1`
2026-02-02 17:39:36 -08:00
SUZUKI Sosuke
de8c754c6a perf(markdown): cache tag strings in React renderer (#26668)
## Summary

Cache frequently-used HTML tag strings (div, p, h1-h6, etc.) in
`GlobalObject` using `LazyProperty<JSGlobalObject, JSString>` instead of
creating new JSStrings on every React element creation in
`Bun.markdown.react()`.

## Changes

- Added `BunMarkdownTagStrings.h/.cpp` with 30 cached tag strings
- Modified `MarkdownObject.zig` to use cached strings via C API
- Integrated with `ZigGlobalObject` for proper GC visiting

## Benchmark Results

All benchmarks performed on Apple M4 Max with release builds.

### mitata Benchmark (Bun.markdown.react)

| Size | Main | Feature | Improvement |
|------|------|---------|-------------|
| small (121 chars) | 3.20 µs | 2.30 µs | **28% faster** |
| medium (1039 chars) | 15.09 µs | 14.02 µs | **7% faster** |
| large (20780 chars) | 288.48 µs | 267.14 µs | **7.4% faster** |

### Heap Profile

| Metric | Main | Feature | Improvement |
|--------|------|---------|-------------|
| Heap size | 500.7 KB | 469.7 KB | **6% reduction** |
| Object count | 12,000 | 10,315 | **14% reduction** |
| String count | 4,248 | 2,563 | **40% reduction** |
| String size | 97.1 KB | 65.8 KB | **32% reduction** |

### HTTP Request Benchmark (ab -n 10000 -c 20)

| Metric | Main | Feature | Improvement |
|--------|------|---------|-------------|
| Requests/sec | 7,710 | 8,174 | **6% faster** |
| Time/request | 2.59 ms | 2.45 ms | **5% faster** |
| p99 latency | 6 ms | 3 ms | **50% improvement** |

## Technical Details

The optimization uses JSC's `LazyProperty` pattern (similar to
`BunCommonStrings` and `BunHttp2CommonStrings`) to lazily initialize and
cache tag strings on first use. This avoids repeated
`bun.String.createUTF8ForJS` calls which allocate new JSStrings for the
same tag names on every markdown element.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-02-02 17:08:39 -08:00
Jarred Sumner
27e1363a66 Make GC release access skip interval configurable (#26699)
## Summary
- Make `defaultRemainingRunsUntilSkipReleaseAccess` configurable at
runtime instead of a compile-time constant
- Add `BUN_GC_RUNS_UNTIL_SKIP_RELEASE_ACCESS` environment variable to
control how many idle event loop iterations pass before skipping JSC
heap `releaseAccess` calls in `onBeforeWait`
- Default remains 10, matching the previous hardcoded value

## Test plan
- [ ] Verify default behavior is unchanged (no env var set, value is 10)
- [ ] Verify `BUN_GC_RUNS_UNTIL_SKIP_RELEASE_ACCESS=0` causes release
access to be skipped every iteration
- [ ] Verify `BUN_GC_RUNS_UNTIL_SKIP_RELEASE_ACCESS=100` delays skipping
for 100 idle iterations
- [ ] Verify negative values are ignored (default is preserved)
- [ ] Verify non-numeric values are ignored (default is preserved)

## Changelog
<!-- CHANGELOG:START -->
<!-- CHANGELOG:END -->

🤖 Generated with [Claude Code](https://claude.com/claude-code) (0%
3-shotted by claude)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 17:07:20 -08:00
SUZUKI Sosuke
eba4da23e6 perf: skip Event creation in AbortSignal.abort() when no listeners (#26686)
## Summary

This is a tiny optimization that skips creating and dispatching an Event
object when `AbortSignal.abort()` is called with no registered
listeners.

## Changes

When there are no listeners (no `addEventListener` or `onabort`), we now
check `hasEventListeners()` before creating the Event, avoiding:
- Event object allocation (~112 bytes)
- EventPath creation
- dispatchEvent overhead (hash map lookups, method calls)

## Performance

Improvement for the no-listener case:
- **~6% faster** in micro-benchmarks
- ~16ms saved per 1M `abort()` calls (271ms → 255ms)

| Case | Before | After | Improvement |
|------|--------|-------|-------------|
| no listener | 271 ms | 255 ms | ~6% |
| with listener | 368 ms | 370 ms | (same) |

## Why this is safe

The optimization has no observable side effects because:
- `dispatchEvent` is called from C++, not observable via JS
monkey-patching
- Without listeners, no code can obtain a reference to the Event object
- All internal state (`aborted`, `reason`) is set correctly regardless

## Test Plan

- Existing AbortController/AbortSignal tests pass
- Added mitata benchmark: `bench/snippets/abort-signal.mjs`
2026-02-02 14:01:25 -08:00
64 changed files with 4207 additions and 250 deletions

View File

@@ -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}"

View File

@@ -109,13 +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
// { os: "windows", arch: "aarch64", release: "2019" },
{ os: "windows", arch: "aarch64", release: "2019" },
];
/**
@@ -133,9 +132,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 +303,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 +332,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 +353,7 @@ function getZigPlatform() {
arch: "aarch64",
abi: "musl",
distro: "alpine",
release: "3.22",
release: "3.23",
};
}
@@ -456,6 +464,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 -DSKIP_CODEGEN=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl";
}
return "";
}
/**
* @param {Platform} platform
* @param {PipelineOptions} options
@@ -463,6 +482,7 @@ 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 +496,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 +553,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 +1199,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])
: [],
);

View File

@@ -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() {

View File

@@ -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`

View File

@@ -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:

View File

@@ -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 %}
@@ -299,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.
```

View 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();

View File

@@ -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`, () => {

View File

@@ -46,6 +46,7 @@
"src/io/*.cpp",
"src/bun.js/modules/*.cpp",
"src/bun.js/bindings/*.cpp",
"src/bun.js/bindings/git/*.cpp",
"src/bun.js/bindings/webcore/*.cpp",
"src/bun.js/bindings/sqlite/*.cpp",
"src/bun.js/bindings/webcrypto/*.cpp",

View File

@@ -54,6 +54,7 @@ set(BUN_DEPENDENCIES
Cares
Highway
LibDeflate
Libgit2
LolHtml
Lshpack
Mimalloc
@@ -1016,6 +1017,7 @@ if(NOT WIN32)
-Wno-unused-function
-Wno-c++23-lambda-attributes
-Wno-nullability-completeness
-Wno-character-conversion
-Werror
)
else()
@@ -1033,6 +1035,7 @@ if(NOT WIN32)
-Werror=sometimes-uninitialized
-Wno-c++23-lambda-attributes
-Wno-nullability-completeness
-Wno-character-conversion
-Werror
)
@@ -1061,6 +1064,7 @@ else()
-Wno-inconsistent-dllimport
-Wno-incompatible-pointer-types
-Wno-deprecated-declarations
-Wno-character-conversion
)
endif()
@@ -1136,6 +1140,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")
@@ -1310,7 +1323,7 @@ list(TRANSFORM BUN_DEPENDENCIES TOLOWER OUTPUT_VARIABLE BUN_TARGETS)
add_custom_target(dependencies DEPENDS ${BUN_TARGETS})
if(APPLE)
target_link_libraries(${bun} PRIVATE icucore resolv)
target_link_libraries(${bun} PRIVATE icucore resolv iconv)
target_compile_definitions(${bun} PRIVATE U_DISABLE_RENAMING=1)
endif()

View File

@@ -0,0 +1,40 @@
register_repository(
NAME
libgit2
REPOSITORY
libgit2/libgit2
TAG
v1.9.0
)
register_cmake_command(
TARGET
libgit2
TARGETS
libgit2package
ARGS
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DBUILD_SHARED_LIBS=OFF
-DBUILD_TESTS=OFF
-DBUILD_CLI=OFF
-DBUILD_EXAMPLES=OFF
-DBUILD_FUZZERS=OFF
# Network disabled - local operations only
-DUSE_HTTPS=OFF
-DUSE_SSH=OFF
# Use bundled dependencies to avoid symbol conflicts with Bun's libraries
-DUSE_BUNDLED_ZLIB=ON
-DUSE_HTTP_PARSER=builtin
-DREGEX_BACKEND=builtin
-DUSE_SHA1=CollisionDetection
# Enable threading
-DUSE_THREADS=ON
# Disable authentication features (not needed for local operations)
-DUSE_GSSAPI=OFF
LIB_PATH
.
LIBRARIES
git2
INCLUDES
include
)

View File

@@ -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)
@@ -78,14 +74,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()

View File

@@ -6,7 +6,7 @@ option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of down
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 7bc2f97e28353062bb54776ce01e4c2ff24c35cc)
endif()
# Use preview build URL for Windows ARM64 until the fix is merged to main

View File

@@ -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):

View File

@@ -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>
@@ -309,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.
```

View File

@@ -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

View File

@@ -0,0 +1,3 @@
# Bun
This is the Windows ARM64 binary for Bun, a fast all-in-one JavaScript runtime. https://bun.com

View File

@@ -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

View File

@@ -2438,7 +2438,7 @@ 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}`;
}

642
packages/bun-types/git.d.ts vendored Normal file
View File

@@ -0,0 +1,642 @@
/**
* Fast Git operations for Bun.js powered by libgit2.
*
* This module provides read-only Git repository operations.
* Network operations (HTTPS/SSH) are not supported - local operations only.
*
* @example
* ```ts
* import { Repository } from 'bun:git';
*
* const repo = Repository.open('.');
* const head = repo.head();
* console.log(`HEAD: ${head.id} - ${head.summary}`);
* console.log(`Author: ${head.author.name} <${head.author.email}>`);
* ```
*
* @module bun:git
*/
declare module "bun:git" {
/**
* Represents a Git signature (author or committer information).
*/
export interface Signature {
/**
* The name of the person.
* @example "John Doe"
*/
readonly name: string;
/**
* The email address of the person.
* @example "john@example.com"
*/
readonly email: string;
/**
* Unix timestamp of when the signature was created.
* @example 1704067200
*/
readonly time: number;
}
/**
* Status flags for working directory entries.
* These are bit flags that can be combined with bitwise OR.
*
* @example
* ```ts
* import { Status } from 'bun:git';
*
* const entries = repo.getStatus();
* for (const entry of entries) {
* if (entry.status & Status.WT_MODIFIED) {
* console.log('Modified in workdir:', entry.path);
* }
* if (entry.status & Status.INDEX_NEW) {
* console.log('New in index:', entry.path);
* }
* }
* ```
*/
export const Status: {
/** Entry is current and unchanged */
readonly CURRENT: 0;
/** Entry is new in the index */
readonly INDEX_NEW: 1;
/** Entry is modified in the index */
readonly INDEX_MODIFIED: 2;
/** Entry is deleted in the index */
readonly INDEX_DELETED: 4;
/** Entry is renamed in the index */
readonly INDEX_RENAMED: 8;
/** Entry type changed in the index */
readonly INDEX_TYPECHANGE: 16;
/** Entry is new in the working tree */
readonly WT_NEW: 128;
/** Entry is modified in the working tree */
readonly WT_MODIFIED: 256;
/** Entry is deleted in the working tree */
readonly WT_DELETED: 512;
/** Entry type changed in the working tree */
readonly WT_TYPECHANGE: 1024;
/** Entry is renamed in the working tree */
readonly WT_RENAMED: 2048;
/** Entry is ignored */
readonly IGNORED: 16384;
/** Entry is conflicted */
readonly CONFLICTED: 32768;
};
/**
* Delta types for diff entries.
*
* @example
* ```ts
* import { DeltaType } from 'bun:git';
*
* const diff = repo.diff();
* for (const file of diff.files) {
* if (file.status === DeltaType.ADDED) {
* console.log('Added:', file.newPath);
* }
* }
* ```
*/
export const DeltaType: {
/** No changes */
readonly UNMODIFIED: 0;
/** Entry does not exist in old version */
readonly ADDED: 1;
/** Entry does not exist in new version */
readonly DELETED: 2;
/** Entry content changed between old and new */
readonly MODIFIED: 3;
/** Entry was renamed between old and new */
readonly RENAMED: 4;
/** Entry was copied from another old entry */
readonly COPIED: 5;
/** Entry is ignored item in workdir */
readonly IGNORED: 6;
/** Entry is untracked item in workdir */
readonly UNTRACKED: 7;
/** Entry type changed between old and new */
readonly TYPECHANGE: 8;
/** Entry is unreadable */
readonly CONFLICTED: 10;
};
/**
* Options for getting repository status.
*/
export interface StatusOptions {
/**
* Include untracked files in the status.
* @default true
*/
includeUntracked?: boolean;
/**
* Include ignored files in the status.
* @default false
*/
includeIgnored?: boolean;
/**
* Recurse into untracked directories.
* @default true
*/
recurseUntrackedDirs?: boolean;
/**
* Detect renamed files.
* @default false
*/
detectRenames?: boolean;
}
/**
* Represents a status entry for a file in the working directory.
*/
export class StatusEntry {
/**
* The path of the file relative to the repository root.
*/
readonly path: string;
/**
* Status flags (combination of Status values).
*/
readonly status: number;
/**
* Check if the entry is new (untracked or staged as new).
*/
isNew(): boolean;
/**
* Check if the entry is modified.
*/
isModified(): boolean;
/**
* Check if the entry is deleted.
*/
isDeleted(): boolean;
/**
* Check if the entry is renamed.
*/
isRenamed(): boolean;
/**
* Check if the entry is ignored.
*/
isIgnored(): boolean;
/**
* Check if the entry has changes staged in the index.
*/
inIndex(): boolean;
/**
* Check if the entry has changes in the working tree.
*/
inWorkingTree(): boolean;
}
/**
* Represents an entry in the Git index.
*/
export interface IndexEntry {
/**
* The path of the file relative to the repository root.
*/
readonly path: string;
/**
* The file mode (e.g., 0o100644 for regular files).
*/
readonly mode: number;
/**
* The blob OID (SHA-1 hash) of the file content.
*/
readonly oid: string;
/**
* The stage number (0 for normal, 1-3 for conflict stages).
*/
readonly stage: number;
/**
* The file size in bytes.
*/
readonly size: number;
}
/**
* Options for getting diff information.
*/
export interface DiffOptions {
/**
* If true, compare HEAD to index (staged changes).
* If false, compare HEAD to working directory.
* @default false
*/
cached?: boolean;
}
/**
* Represents a changed file in a diff.
*/
export interface DiffFile {
/**
* The type of change (see DeltaType).
*/
readonly status: number;
/**
* The old path (null for added files).
*/
readonly oldPath: string | null;
/**
* The new path.
*/
readonly newPath: string;
/**
* Similarity percentage for renamed/copied files (0-100).
*/
readonly similarity?: number;
}
/**
* Result of a diff operation.
*/
export interface DiffResult {
/**
* List of changed files.
*/
readonly files: DiffFile[];
/**
* Statistics about the diff.
*/
readonly stats: {
/** Number of files changed */
readonly filesChanged: number;
/** Total lines inserted */
readonly insertions: number;
/** Total lines deleted */
readonly deletions: number;
};
}
/**
* Options for getting commit history.
*/
export interface LogOptions {
/**
* Starting point for history traversal.
* @default "HEAD"
*/
from?: string;
/**
* Range specification (e.g., "origin/main..HEAD").
* If provided, `from` is ignored.
*/
range?: string;
/**
* Maximum number of commits to return.
* @default unlimited
*/
limit?: number;
}
/**
* Represents a Git commit object.
*
* A commit contains information about a snapshot of the repository,
* including the author, committer, message, and parent commits.
*
* @example
* ```ts
* const head = repo.head();
* console.log(head.id); // "abc123..."
* console.log(head.message); // "feat: add new feature\n\nDetailed description..."
* console.log(head.summary); // "feat: add new feature"
* ```
*/
export class Commit {
/**
* The full 40-character hexadecimal SHA-1 hash of the commit.
* @example "a1b2c3d4e5f6..."
*/
readonly id: string;
/**
* The full commit message, including the body.
* @example "feat: add new feature\n\nThis commit adds..."
*/
readonly message: string;
/**
* The first line of the commit message (the summary/title).
* Does not include any trailing newline.
* @example "feat: add new feature"
*/
readonly summary: string;
/**
* The author of the commit (who wrote the changes).
*/
readonly author: Signature;
/**
* The committer of the commit (who committed the changes).
* This may differ from the author in cases like cherry-picks or rebases.
*/
readonly committer: Signature;
/**
* Unix timestamp of when the commit was created.
* This is the committer's timestamp.
* @example 1704067200
*/
readonly time: number;
}
/**
* Represents a Git repository.
*
* Use {@link Repository.open} to open an existing repository.
*
* @example
* ```ts
* import { Repository } from 'bun:git';
*
* // Open the repository at the current directory
* const repo = Repository.open('.');
*
* // Get repository info
* console.log('Path:', repo.path); // "/path/to/repo/.git/"
* console.log('Workdir:', repo.workdir); // "/path/to/repo/"
* console.log('Is bare:', repo.isBare); // false
*
* // Get the HEAD commit
* const head = repo.head();
* console.log('HEAD:', head.id.slice(0, 7), head.summary);
* ```
*/
export class Repository {
/**
* Opens an existing Git repository.
*
* The path can point to either a working directory or a bare repository.
* If the path points to a working directory, the `.git` directory will be located automatically.
*
* @param path Path to the repository (working directory or .git directory)
* @returns A Repository instance
* @throws Error if the path is not a valid Git repository
*
* @example
* ```ts
* // Open by working directory
* const repo = Repository.open('/path/to/project');
*
* // Open by .git directory
* const repo2 = Repository.open('/path/to/project/.git');
*
* // Open current directory
* const repo3 = Repository.open('.');
* ```
*/
static open(path: string): Repository;
/**
* Gets the commit that HEAD currently points to.
*
* @returns The commit that HEAD references
* @throws Error if HEAD is unborn (new repository with no commits)
*
* @example
* ```ts
* const head = repo.head();
* console.log(`Current commit: ${head.summary}`);
* console.log(`Author: ${head.author.name}`);
* ```
*/
head(): Commit;
/**
* The path to the `.git` directory.
* Always ends with a trailing slash.
*
* @example "/Users/me/project/.git/"
*/
readonly path: string;
/**
* The path to the working directory.
* Returns `null` for bare repositories.
* When present, always ends with a trailing slash.
*
* @example "/Users/me/project/"
*/
readonly workdir: string | null;
/**
* Whether this is a bare repository.
* Bare repositories have no working directory.
*
* @example
* ```ts
* if (repo.isBare) {
* console.log('This is a bare repository');
* }
* ```
*/
readonly isBare: boolean;
/**
* Gets the working directory status.
*
* Returns an array of status entries for all changed files in the
* working directory and index.
*
* @param options Options to control which files are included
* @returns Array of status entries
*
* @example
* ```ts
* import { Repository, Status } from 'bun:git';
*
* const repo = Repository.open('.');
* const status = repo.getStatus();
*
* for (const entry of status) {
* if (entry.isModified()) {
* console.log('Modified:', entry.path);
* }
* if (entry.isNew()) {
* console.log('New:', entry.path);
* }
* }
* ```
*/
getStatus(options?: StatusOptions): StatusEntry[];
/**
* Resolves a revision specification to a commit OID.
*
* Supports standard Git revision syntax including:
* - Branch names: "main", "feature/foo"
* - Tag names: "v1.0.0"
* - SHA prefixes: "abc123"
* - Special refs: "HEAD", "HEAD~1", "HEAD^2"
* - Upstream: "@{u}", "main@{u}"
*
* @param spec The revision specification to resolve
* @returns The 40-character hex OID
* @throws Error if the spec cannot be resolved
*
* @example
* ```ts
* const headOid = repo.revParse('HEAD');
* const parentOid = repo.revParse('HEAD~1');
* const branchOid = repo.revParse('main');
* ```
*/
revParse(spec: string): string;
/**
* Gets the name of the current branch.
*
* @returns The branch name, or null if HEAD is detached or unborn
*
* @example
* ```ts
* const branch = repo.getCurrentBranch();
* if (branch) {
* console.log('On branch:', branch);
* } else {
* console.log('HEAD is detached');
* }
* ```
*/
getCurrentBranch(): string | null;
/**
* Gets the ahead/behind counts between two commits.
*
* This is useful for comparing a local branch to its upstream.
*
* @param local The local ref (default: "HEAD")
* @param upstream The upstream ref (default: "@{u}")
* @returns Object with ahead and behind counts
*
* @example
* ```ts
* const { ahead, behind } = repo.aheadBehind();
* console.log(`${ahead} ahead, ${behind} behind`);
*
* // Compare specific refs
* const { ahead, behind } = repo.aheadBehind('feature', 'origin/main');
* ```
*/
aheadBehind(local?: string, upstream?: string): { ahead: number; behind: number };
/**
* Gets the list of files tracked in the index.
*
* @returns Array of index entries
*
* @example
* ```ts
* const files = repo.listFiles();
* console.log(`Tracking ${files.length} files`);
*
* for (const file of files) {
* console.log(`${file.path} (mode: ${file.mode.toString(8)})`);
* }
* ```
*/
listFiles(): IndexEntry[];
/**
* Gets diff information between HEAD and working directory or index.
*
* @param options Options to control the diff behavior
* @returns Diff result with file list and statistics
*
* @example
* ```ts
* import { Repository, DeltaType } from 'bun:git';
*
* const repo = Repository.open('.');
*
* // Unstaged changes (HEAD vs workdir)
* const diff = repo.diff();
* console.log(`${diff.stats.filesChanged} files changed`);
* console.log(`+${diff.stats.insertions} -${diff.stats.deletions}`);
*
* // Staged changes (HEAD vs index)
* const staged = repo.diff({ cached: true });
*
* for (const file of diff.files) {
* if (file.status === DeltaType.MODIFIED) {
* console.log('Modified:', file.newPath);
* }
* }
* ```
*/
diff(options?: DiffOptions): DiffResult;
/**
* Counts the number of commits in a range.
*
* @param range Optional range specification (e.g., "origin/main..HEAD")
* @returns Number of commits
*
* @example
* ```ts
* // Total commits
* const total = repo.countCommits();
*
* // Commits since origin/main
* const since = repo.countCommits('origin/main..HEAD');
* ```
*/
countCommits(range?: string): number;
/**
* Gets the commit history.
*
* @param options Options to control the log behavior
* @returns Array of commits
*
* @example
* ```ts
* // Last 10 commits
* const commits = repo.log({ limit: 10 });
*
* for (const commit of commits) {
* console.log(`${commit.id.slice(0, 7)} ${commit.summary}`);
* }
*
* // Commits in a range
* const range = repo.log({ range: 'origin/main..HEAD' });
*
* // Commits from a specific ref
* const fromTag = repo.log({ from: 'v1.0.0', limit: 5 });
* ```
*/
log(options?: LogOptions): Commit[];
}
export default Repository;
}

View File

@@ -14,6 +14,7 @@
/// <reference path="./html-rewriter.d.ts" />
/// <reference path="./jsc.d.ts" />
/// <reference path="./sqlite.d.ts" />
/// <reference path="./git.d.ts" />
/// <reference path="./test.d.ts" />
/// <reference path="./wasm.d.ts" />
/// <reference path="./overrides.d.ts" />

View File

@@ -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> = {

View File

@@ -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"
}

View File

@@ -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}"

View File

@@ -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;

View File

@@ -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}"

View File

@@ -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}"
'' + ''

View File

@@ -10,6 +10,7 @@ pub const HardcodedModule = enum {
@"bun:test",
@"bun:wrap",
@"bun:sqlite",
@"bun:git",
@"node:assert",
@"node:assert/strict",
@"node:async_hooks",
@@ -98,6 +99,7 @@ pub const HardcodedModule = enum {
.{ "bun:main", .@"bun:main" },
.{ "bun:test", .@"bun:test" },
.{ "bun:sqlite", .@"bun:sqlite" },
.{ "bun:git", .@"bun:git" },
.{ "bun:wrap", .@"bun:wrap" },
.{ "bun:internal-for-testing", .@"bun:internal-for-testing" },
// Node.js
@@ -366,6 +368,7 @@ pub const HardcodedModule = enum {
.{ "bun:ffi", .{ .path = "bun:ffi" } },
.{ "bun:jsc", .{ .path = "bun:jsc" } },
.{ "bun:sqlite", .{ .path = "bun:sqlite" } },
.{ "bun:git", .{ .path = "bun:git" } },
.{ "bun:wrap", .{ .path = "bun:wrap" } },
.{ "bun:internal-for-testing", .{ .path = "bun:internal-for-testing" } },
.{ "ffi", .{ .path = "bun:ffi" } },

View File

@@ -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);

View File

@@ -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");

View File

@@ -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;

View 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());
}
}

View 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

View File

@@ -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);

View File

@@ -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.

View File

@@ -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
}

View File

@@ -988,6 +988,10 @@ void JSMockFunctionPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* g
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
this->putDirect(vm, Identifier::fromString(vm, "_isMockFunction"_s), jsBoolean(true), 0);
// Support `using spy = spyOn(...)` — auto-restores when leaving scope.
JSValue restoreFn = this->getDirect(vm, Identifier::fromString(vm, "mockRestore"_s));
this->putDirect(vm, vm.propertyNames->disposeSymbol, restoreFn, static_cast<unsigned>(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontEnum));
}
JSC_DEFINE_HOST_FUNCTION(jsMockFunctionGetMockImplementation, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))

View File

@@ -136,23 +136,23 @@ private:
bool load_functions()
{
CFRelease = (void (*)(CFTypeRef))dlsym(cf_handle, "CFRelease");
CFStringCreateWithCString = (CFStringRef(*)(CFAllocatorRef, const char*, CFStringEncoding))dlsym(cf_handle, "CFStringCreateWithCString");
CFDataCreate = (CFDataRef(*)(CFAllocatorRef, const UInt8*, CFIndex))dlsym(cf_handle, "CFDataCreate");
CFStringCreateWithCString = (CFStringRef (*)(CFAllocatorRef, const char*, CFStringEncoding))dlsym(cf_handle, "CFStringCreateWithCString");
CFDataCreate = (CFDataRef (*)(CFAllocatorRef, const UInt8*, CFIndex))dlsym(cf_handle, "CFDataCreate");
CFDataGetBytePtr = (const UInt8* (*)(CFDataRef))dlsym(cf_handle, "CFDataGetBytePtr");
CFDataGetLength = (CFIndex(*)(CFDataRef))dlsym(cf_handle, "CFDataGetLength");
CFDictionaryCreateMutable = (CFMutableDictionaryRef(*)(CFAllocatorRef, CFIndex, const CFDictionaryKeyCallBacks*, const CFDictionaryValueCallBacks*))dlsym(cf_handle, "CFDictionaryCreateMutable");
CFDataGetLength = (CFIndex (*)(CFDataRef))dlsym(cf_handle, "CFDataGetLength");
CFDictionaryCreateMutable = (CFMutableDictionaryRef (*)(CFAllocatorRef, CFIndex, const CFDictionaryKeyCallBacks*, const CFDictionaryValueCallBacks*))dlsym(cf_handle, "CFDictionaryCreateMutable");
CFDictionaryAddValue = (void (*)(CFMutableDictionaryRef, const void*, const void*))dlsym(cf_handle, "CFDictionaryAddValue");
CFStringGetCString = (Boolean(*)(CFStringRef, char*, CFIndex, CFStringEncoding))dlsym(cf_handle, "CFStringGetCString");
CFStringGetCString = (Boolean (*)(CFStringRef, char*, CFIndex, CFStringEncoding))dlsym(cf_handle, "CFStringGetCString");
CFStringGetCStringPtr = (const char* (*)(CFStringRef, CFStringEncoding))dlsym(cf_handle, "CFStringGetCStringPtr");
CFStringGetLength = (CFIndex(*)(CFStringRef))dlsym(cf_handle, "CFStringGetLength");
CFStringGetMaximumSizeForEncoding = (CFIndex(*)(CFIndex, CFStringEncoding))dlsym(cf_handle, "CFStringGetMaximumSizeForEncoding");
CFStringGetLength = (CFIndex (*)(CFStringRef))dlsym(cf_handle, "CFStringGetLength");
CFStringGetMaximumSizeForEncoding = (CFIndex (*)(CFIndex, CFStringEncoding))dlsym(cf_handle, "CFStringGetMaximumSizeForEncoding");
SecItemAdd = (OSStatus(*)(CFDictionaryRef, CFTypeRef*))dlsym(handle, "SecItemAdd");
SecItemCopyMatching = (OSStatus(*)(CFDictionaryRef, CFTypeRef*))dlsym(handle, "SecItemCopyMatching");
SecItemUpdate = (OSStatus(*)(CFDictionaryRef, CFDictionaryRef))dlsym(handle, "SecItemUpdate");
SecItemDelete = (OSStatus(*)(CFDictionaryRef))dlsym(handle, "SecItemDelete");
SecCopyErrorMessageString = (CFStringRef(*)(OSStatus, void*))dlsym(handle, "SecCopyErrorMessageString");
SecAccessCreate = (OSStatus(*)(CFStringRef, CFArrayRef, SecAccessRef*))dlsym(handle, "SecAccessCreate");
SecItemAdd = (OSStatus (*)(CFDictionaryRef, CFTypeRef*))dlsym(handle, "SecItemAdd");
SecItemCopyMatching = (OSStatus (*)(CFDictionaryRef, CFTypeRef*))dlsym(handle, "SecItemCopyMatching");
SecItemUpdate = (OSStatus (*)(CFDictionaryRef, CFDictionaryRef))dlsym(handle, "SecItemUpdate");
SecItemDelete = (OSStatus (*)(CFDictionaryRef))dlsym(handle, "SecItemDelete");
SecCopyErrorMessageString = (CFStringRef (*)(OSStatus, void*))dlsym(handle, "SecCopyErrorMessageString");
SecAccessCreate = (OSStatus (*)(CFStringRef, CFArrayRef, SecAccessRef*))dlsym(handle, "SecAccessCreate");
return CFRelease && CFStringCreateWithCString && CFDataCreate && CFDataGetBytePtr && CFDataGetLength && CFDictionaryCreateMutable && CFDictionaryAddValue && SecItemAdd && SecItemCopyMatching && SecItemUpdate && SecItemDelete && SecCopyErrorMessageString && SecAccessCreate && CFStringGetCString && CFStringGetCStringPtr && CFStringGetLength && CFStringGetMaximumSizeForEncoding;
}

View File

@@ -199,19 +199,19 @@ private:
g_free = (void (*)(gpointer))dlsym(glib_handle, "g_free");
g_hash_table_new = (GHashTable * (*)(void*, void*)) dlsym(glib_handle, "g_hash_table_new");
g_hash_table_destroy = (void (*)(GHashTable*))dlsym(glib_handle, "g_hash_table_destroy");
g_hash_table_lookup = (gpointer(*)(GHashTable*, gpointer))dlsym(glib_handle, "g_hash_table_lookup");
g_hash_table_lookup = (gpointer (*)(GHashTable*, gpointer))dlsym(glib_handle, "g_hash_table_lookup");
g_hash_table_insert = (void (*)(GHashTable*, gpointer, gpointer))dlsym(glib_handle, "g_hash_table_insert");
g_list_free = (void (*)(GList*))dlsym(glib_handle, "g_list_free");
g_list_free_full = (void (*)(GList*, void (*)(gpointer)))dlsym(glib_handle, "g_list_free_full");
g_str_hash = (guint(*)(gpointer))dlsym(glib_handle, "g_str_hash");
g_str_equal = (gboolean(*)(gpointer, gpointer))dlsym(glib_handle, "g_str_equal");
g_str_hash = (guint (*)(gpointer))dlsym(glib_handle, "g_str_hash");
g_str_equal = (gboolean (*)(gpointer, gpointer))dlsym(glib_handle, "g_str_equal");
// Load libsecret functions
secret_password_store_sync = (gboolean(*)(const SecretSchema*, const gchar*, const gchar*, const gchar*, void*, GError**, ...))
secret_password_store_sync = (gboolean (*)(const SecretSchema*, const gchar*, const gchar*, const gchar*, void*, GError**, ...))
dlsym(secret_handle, "secret_password_store_sync");
secret_password_lookup_sync = (gchar * (*)(const SecretSchema*, void*, GError**, ...))
dlsym(secret_handle, "secret_password_lookup_sync");
secret_password_clear_sync = (gboolean(*)(const SecretSchema*, void*, GError**, ...))
secret_password_clear_sync = (gboolean (*)(const SecretSchema*, void*, GError**, ...))
dlsym(secret_handle, "secret_password_clear_sync");
secret_password_free = (void (*)(gchar*))dlsym(secret_handle, "secret_password_free");
secret_service_search_sync = (GList * (*)(SecretService*, const SecretSchema*, GHashTable*, SecretSearchFlags, void*, GError**))
@@ -220,7 +220,7 @@ private:
secret_value_get_text = (const gchar* (*)(SecretValue*))dlsym(secret_handle, "secret_value_get_text");
secret_value_unref = (void (*)(gpointer))dlsym(secret_handle, "secret_value_unref");
secret_item_get_attributes = (GHashTable * (*)(SecretItem*)) dlsym(secret_handle, "secret_item_get_attributes");
secret_item_load_secret_sync = (gboolean(*)(SecretItem*, void*, GError**))dlsym(secret_handle, "secret_item_load_secret_sync");
secret_item_load_secret_sync = (gboolean (*)(SecretItem*, void*, GError**))dlsym(secret_handle, "secret_item_load_secret_sync");
return g_error_free && g_free && g_hash_table_new && g_hash_table_destroy && g_hash_table_lookup && g_hash_table_insert && g_list_free && secret_password_store_sync && secret_password_lookup_sync && secret_password_clear_sync && secret_password_free;
}

View File

@@ -890,7 +890,7 @@ static const GB18030EncodeIndex& gb18030EncodeIndex()
// https://unicode-org.atlassian.net/browse/ICU-22357
// The 2-byte values are handled correctly by values from gb18030()
// but these need to be exceptions from gb18030Ranges().
static std::optional<uint16_t> gb18030AsymmetricEncode(char16_t codePoint)
static std::optional<uint16_t> gb18030AsymmetricEncode(char32_t codePoint)
{
switch (codePoint) {
case 0xE81E:

View File

@@ -125,6 +125,7 @@
#include "JSSocketAddressDTO.h"
#include "JSReactElement.h"
#include "JSSQLStatement.h"
#include "git/JSGit.h"
#include "JSStringDecoder.h"
#include "JSTextEncoder.h"
#include "JSTextEncoderStream.h"
@@ -302,7 +303,6 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c
JSC::Options::useJITCage() = false;
JSC::Options::useShadowRealm() = true;
JSC::Options::useV8DateParser() = true;
JSC::Options::useMathSumPreciseMethod() = true;
JSC::Options::evalMode() = evalMode;
JSC::Options::heapGrowthSteepnessFactor() = 1.0;
JSC::Options::heapGrowthMaxIncrease() = 2.0;
@@ -1699,6 +1699,7 @@ void GlobalObject::finishCreation(VM& vm)
m_commonStrings.initialize();
m_http2CommonStrings.initialize();
m_bakeAdditions.initialize();
m_markdownTagStrings.initialize();
Bun::addNodeModuleConstructorProperties(vm, this);
m_JSNodeHTTPServerSocketStructure.initLater(
@@ -1868,6 +1869,16 @@ void GlobalObject::finishCreation(VM& vm)
init.set(WebCore::createJSSQLStatementStructure(init.owner));
});
m_JSGitRepositoryStructure.initLater(
[](const Initializer<Structure>& init) {
init.set(WebCore::createJSGitRepositoryStructure(init.owner));
});
m_JSGitCommitStructure.initLater(
[](const Initializer<Structure>& init) {
init.set(WebCore::createJSGitCommitStructure(init.owner));
});
m_V8GlobalInternals.initLater(
[](const JSC::LazyProperty<JSC::JSGlobalObject, v8::shim::GlobalInternals>::Initializer& init) {
init.set(

View File

@@ -58,6 +58,7 @@ struct node_module;
#include "headers-handwritten.h"
#include "BunCommonStrings.h"
#include "BunHttp2CommonStrings.h"
#include "BunMarkdownTagStrings.h"
#include "BunGlobalScope.h"
#include <js_native_api.h>
#include <node_api.h>
@@ -315,6 +316,9 @@ public:
Structure* JSSQLStatementStructure() const { return m_JSSQLStatementStructure.getInitializedOnMainThread(this); }
Structure* JSGitRepositoryStructure() const { return m_JSGitRepositoryStructure.getInitializedOnMainThread(this); }
Structure* JSGitCommitStructure() const { return m_JSGitCommitStructure.getInitializedOnMainThread(this); }
v8::shim::GlobalInternals* V8GlobalInternals() const { return m_V8GlobalInternals.getInitializedOnMainThread(this); }
Bun::BakeAdditionsToGlobalObject& bakeAdditions() { return m_bakeAdditions; }
@@ -526,6 +530,7 @@ public:
V(private, std::unique_ptr<WebCore::DOMConstructors>, m_constructors) \
V(private, Bun::CommonStrings, m_commonStrings) \
V(private, Bun::Http2CommonStrings, m_http2CommonStrings) \
V(private, Bun::MarkdownTagStrings, m_markdownTagStrings) \
\
/* JSC's hashtable code-generator tries to access these properties, so we make them public. */ \
/* However, we'd like it better if they could be protected. */ \
@@ -618,6 +623,8 @@ public:
V(private, LazyPropertyOfGlobalObject<Structure>, m_NapiTypeTagStructure) \
\
V(private, LazyPropertyOfGlobalObject<Structure>, m_JSSQLStatementStructure) \
V(private, LazyPropertyOfGlobalObject<Structure>, m_JSGitRepositoryStructure) \
V(private, LazyPropertyOfGlobalObject<Structure>, m_JSGitCommitStructure) \
V(private, LazyPropertyOfGlobalObject<v8::shim::GlobalInternals>, m_V8GlobalInternals) \
\
V(public, LazyPropertyOfGlobalObject<JSObject>, m_bunObject) \
@@ -716,6 +723,7 @@ public:
Bun::CommonStrings& commonStrings() { return m_commonStrings; }
Bun::Http2CommonStrings& http2CommonStrings() { return m_http2CommonStrings; }
Bun::MarkdownTagStrings& markdownTagStrings() { return m_markdownTagStrings; }
#include "ZigGeneratedClasses+lazyStructureHeader.h"
void finishCreation(JSC::VM&);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
/*
* Copyright (C) 2024 Oven-sh
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "root.h"
#include "ZigGlobalObject.h"
#include <JavaScriptCore/JSFunction.h>
#include <JavaScriptCore/JSDestructibleObject.h>
#include <JavaScriptCore/VM.h>
#include "headers-handwritten.h"
#include "BunClientData.h"
#include <JavaScriptCore/CallFrame.h>
// Forward declarations for libgit2 types
typedef struct git_repository git_repository;
typedef struct git_commit git_commit;
typedef struct git_oid git_oid;
namespace WebCore {
// Forward declarations
class JSGitRepository;
class JSGitCommit;
class JSGitOid;
// JSGitRepository - Wraps git_repository*
class JSGitRepository final : public JSC::JSDestructibleObject {
public:
using Base = JSC::JSDestructibleObject;
static constexpr unsigned StructureFlags = Base::StructureFlags;
static JSGitRepository* create(JSC::VM& vm, JSC::Structure* structure, git_repository* repo);
static void destroy(JSC::JSCell* cell);
DECLARE_INFO;
template<typename CellType, JSC::SubspaceAccess mode>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
return WebCore::subspaceForImpl<JSGitRepository, WebCore::UseCustomHeapCellType::No>(
vm,
[](auto& spaces) { return spaces.m_clientSubspaceForJSGitRepository.get(); },
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSGitRepository = std::forward<decltype(space)>(space); },
[](auto& spaces) { return spaces.m_subspaceForJSGitRepository.get(); },
[](auto& spaces, auto&& space) { spaces.m_subspaceForJSGitRepository = std::forward<decltype(space)>(space); });
}
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
{
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
}
git_repository* repository() const { return m_repo; }
private:
JSGitRepository(JSC::VM& vm, JSC::Structure* structure, git_repository* repo)
: Base(vm, structure)
, m_repo(repo)
{
}
void finishCreation(JSC::VM& vm);
git_repository* m_repo { nullptr };
};
// JSGitCommit - Wraps git_commit*
class JSGitCommit final : public JSC::JSDestructibleObject {
public:
using Base = JSC::JSDestructibleObject;
static constexpr unsigned StructureFlags = Base::StructureFlags;
static JSGitCommit* create(JSC::VM& vm, JSC::Structure* structure, git_commit* commit);
static void destroy(JSC::JSCell* cell);
DECLARE_INFO;
template<typename CellType, JSC::SubspaceAccess mode>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
return WebCore::subspaceForImpl<JSGitCommit, WebCore::UseCustomHeapCellType::No>(
vm,
[](auto& spaces) { return spaces.m_clientSubspaceForJSGitCommit.get(); },
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSGitCommit = std::forward<decltype(space)>(space); },
[](auto& spaces) { return spaces.m_subspaceForJSGitCommit.get(); },
[](auto& spaces, auto&& space) { spaces.m_subspaceForJSGitCommit = std::forward<decltype(space)>(space); });
}
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
{
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
}
git_commit* commit() const { return m_commit; }
private:
JSGitCommit(JSC::VM& vm, JSC::Structure* structure, git_commit* commit)
: Base(vm, structure)
, m_commit(commit)
{
}
void finishCreation(JSC::VM& vm);
git_commit* m_commit { nullptr };
};
// Structure creation functions
JSC::Structure* createJSGitRepositoryStructure(JSC::JSGlobalObject* globalObject);
JSC::Structure* createJSGitCommitStructure(JSC::JSGlobalObject* globalObject);
// Module creation function (called from $cpp)
JSC::JSValue createJSGitModule(Zig::GlobalObject* globalObject);
} // namespace WebCore

View File

@@ -80,11 +80,18 @@ size_t IndexOfAnyCharImpl(const uint8_t* HWY_RESTRICT text, size_t text_len, con
return text_len;
} else {
ASSERT(chars_len <= 16);
constexpr size_t kMaxPreloadedChars = 16;
hn::Vec<D8> char_vecs[kMaxPreloadedChars];
// Use FixedTag to preload search characters into fixed-size vectors.
// ScalableTag vectors (SVE) are sizeless and cannot be stored in arrays.
// FixedTag gives us a known compile-time size that can be stored in arrays,
// then ResizeBitCast converts back to scalable vectors in the inner loop.
static constexpr size_t kMaxPreloadedChars = 16;
const hn::FixedTag<uint8_t, 16> d_fixed;
using VecFixed = hn::Vec<decltype(d_fixed)>;
VecFixed char_vecs[kMaxPreloadedChars];
const size_t num_chars_to_preload = std::min(chars_len, kMaxPreloadedChars);
for (size_t c = 0; c < num_chars_to_preload; ++c) {
char_vecs[c] = hn::Set(d, chars[c]);
char_vecs[c] = hn::Set(d_fixed, chars[c]);
}
const size_t simd_text_len = text_len - (text_len % N);
@@ -95,12 +102,7 @@ size_t IndexOfAnyCharImpl(const uint8_t* HWY_RESTRICT text, size_t text_len, con
auto found_mask = hn::MaskFalse(d);
for (size_t c = 0; c < num_chars_to_preload; ++c) {
found_mask = hn::Or(found_mask, hn::Eq(text_vec, char_vecs[c]));
}
if (chars_len > num_chars_to_preload) {
for (size_t c = num_chars_to_preload; c < chars_len; ++c) {
found_mask = hn::Or(found_mask, hn::Eq(text_vec, hn::Set(d, chars[c])));
}
found_mask = hn::Or(found_mask, hn::Eq(text_vec, hn::ResizeBitCast(d, char_vecs[c])));
}
const intptr_t pos = hn::FindFirstTrue(d, found_mask);

View File

@@ -171,8 +171,8 @@ void AbortSignal::runAbortSteps()
algorithm.second(reason);
// 3. Fire an event named abort at signal.
dispatchEvent(Event::create(eventNames().abortEvent, Event::CanBubble::No, Event::IsCancelable::No));
if (hasEventListeners(eventNames().abortEvent))
dispatchEvent(Event::create(eventNames().abortEvent, Event::CanBubble::No, Event::IsCancelable::No));
setIsFiringEventListeners(false);
}

View File

@@ -24,6 +24,8 @@ public:
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNapiPrototype;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSQLStatement;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSQLStatementConstructor;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSGitRepository;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSGitCommit;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSinkConstructor;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSinkController;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSink;

View File

@@ -24,6 +24,8 @@ public:
std::unique_ptr<IsoSubspace> m_subspaceForNapiPrototype;
std::unique_ptr<IsoSubspace> m_subspaceForJSSQLStatement;
std::unique_ptr<IsoSubspace> m_subspaceForJSSQLStatementConstructor;
std::unique_ptr<IsoSubspace> m_subspaceForJSGitRepository;
std::unique_ptr<IsoSubspace> m_subspaceForJSGitCommit;
std::unique_ptr<IsoSubspace> m_subspaceForJSSinkConstructor;
std::unique_ptr<IsoSubspace> m_subspaceForJSSinkController;
std::unique_ptr<IsoSubspace> m_subspaceForJSSink;

View File

@@ -105,7 +105,7 @@ bool EventTarget::addEventListener(const AtomString& eventType, Ref<EventListene
if (options.signal) {
options.signal->addAlgorithm([weakThis = WeakPtr { *this }, eventType, listener = WeakPtr { listener }, capture = options.capture](JSC::JSValue) {
if (weakThis && listener)
Ref { *weakThis } -> removeEventListener(eventType, *listener, capture);
Ref { *weakThis }->removeEventListener(eventType, *listener, capture);
});
}

View File

@@ -470,7 +470,7 @@ String generatePatternString(const Vector<Part>& partList, const URLPatternStrin
if (!needsGrouping && part.prefix.isEmpty() && previousPart && previousPart->type == PartType::FixedText && !previousPart->value.isEmpty()) {
if (options.prefixCodepoint.length() == 1
&& options.prefixCodepoint.startsWith(*StringView(previousPart->value).codePoints().codePointAt(previousPart->value.length() - 1)))
&& options.prefixCodepoint.startsWith(static_cast<char16_t>(*StringView(previousPart->value).codePoints().codePointAt(previousPart->value.length() - 1))))
needsGrouping = true;
}
@@ -541,7 +541,7 @@ String escapePatternString(StringView input)
}
// https://urlpattern.spec.whatwg.org/#is-a-valid-name-code-point
bool isValidNameCodepoint(char16_t codepoint, URLPatternUtilities::IsFirst first)
bool isValidNameCodepoint(char32_t codepoint, URLPatternUtilities::IsFirst first)
{
if (first == URLPatternUtilities::IsFirst::Yes)
return u_hasBinaryProperty(codepoint, UCHAR_ID_START) || codepoint == '_' || codepoint == '$';

View File

@@ -104,7 +104,7 @@ ASCIILiteral convertModifierToString(Modifier);
std::pair<String, Vector<String>> generateRegexAndNameList(const Vector<Part>& partList, const URLPatternStringOptions&);
String generatePatternString(const Vector<Part>& partList, const URLPatternStringOptions&);
String escapePatternString(StringView input);
bool isValidNameCodepoint(char16_t codepoint, URLPatternUtilities::IsFirst);
bool isValidNameCodepoint(char32_t codepoint, URLPatternUtilities::IsFirst);
} // namespace URLPatternUtilities
} // namespace WebCore

View File

@@ -49,14 +49,14 @@ WebCoreTypedArrayController::WebCoreTypedArrayController(bool allowAtomicsWait)
WebCoreTypedArrayController::~WebCoreTypedArrayController() = default;
JSC::JSArrayBuffer* WebCoreTypedArrayController::toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSGlobalObject* globalObject, JSC::ArrayBuffer* buffer)
JSC::JSArrayBuffer* WebCoreTypedArrayController::toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSGlobalObject* globalObject, JSC::ArrayBuffer& buffer)
{
return JSC::jsCast<JSC::JSArrayBuffer*>(WebCore::toJS(lexicalGlobalObject, getDefaultGlobal(globalObject), buffer));
}
void WebCoreTypedArrayController::registerWrapper(JSC::JSGlobalObject* globalObject, JSC::ArrayBuffer* native, JSC::JSArrayBuffer* wrapper)
void WebCoreTypedArrayController::registerWrapper(JSC::JSGlobalObject* globalObject, JSC::ArrayBuffer& native, JSC::JSArrayBuffer& wrapper)
{
cacheWrapper(static_cast<JSVMClientData*>(JSC::getVM(globalObject).clientData)->normalWorld(), native, wrapper);
cacheWrapper(static_cast<JSVMClientData*>(JSC::getVM(globalObject).clientData)->normalWorld(), &native, &wrapper);
}
bool WebCoreTypedArrayController::isAtomicsWaitAllowedOnCurrentThread()

View File

@@ -35,8 +35,8 @@ public:
WebCoreTypedArrayController(bool allowAtomicsWait);
virtual ~WebCoreTypedArrayController();
JSC::JSArrayBuffer* toJS(JSC::JSGlobalObject*, JSC::JSGlobalObject*, JSC::ArrayBuffer*) override;
void registerWrapper(JSC::JSGlobalObject*, ArrayBuffer*, JSC::JSArrayBuffer*) override;
JSC::JSArrayBuffer* toJS(JSC::JSGlobalObject*, JSC::JSGlobalObject*, JSC::ArrayBuffer&) override;
void registerWrapper(JSC::JSGlobalObject*, ArrayBuffer&, JSC::JSArrayBuffer&) override;
bool isAtomicsWaitAllowedOnCurrentThread() override;
JSC::WeakHandleOwner* wrapperOwner() { return &m_owner; }

View File

@@ -249,32 +249,9 @@ extern "C" __attribute__((used)) char __libc_single_threaded = 0;
#endif
#endif
#ifdef _LIBCPP_VERBOSE_ABORT_NOEXCEPT
// Workaround for this error:
// workaround-missing-symbols.cpp:245:11: error: '__libcpp_verbose_abort' is missing exception specification 'noexcept'
// 2025-07-10 15:59:47 PDT
// 245 | void std::__libcpp_verbose_abort(char const* format, ...)
// 2025-07-10 15:59:47 PDT
// | ^
// 2025-07-10 15:59:47 PDT
// | noexcept
// 2025-07-10 15:59:47 PDT
// /opt/homebrew/Cellar/llvm/20.1.7/bin/../include/c++/v1/__verbose_abort:30:28: note: previous declaration is here
// 2025-07-10 15:59:47 PDT
// 30 | __printf__, 1, 2) void __libcpp_verbose_abort(const char* __format, ...) _LIBCPP_VERBOSE_ABORT_NOEXCEPT;
// 2025-07-10 15:59:47 PDT
// | ^
// 2025-07-10 15:59:47 PDT
// 1 error generated.
// 2025-07-10 15:59:47 PDT
// [515/540] Building CXX
#define BUN_VERBOSE_ABORT_NOEXCEPT _LIBCPP_VERBOSE_ABORT_NOEXCEPT
#else
#define BUN_VERBOSE_ABORT_NOEXCEPT
#endif
// Provide our implementation
void std::__libcpp_verbose_abort(char const* format, ...) BUN_VERBOSE_ABORT_NOEXCEPT
// LLVM 20 used _LIBCPP_VERBOSE_ABORT_NOEXCEPT, LLVM 21+ uses _NOEXCEPT (always noexcept).
void std::__libcpp_verbose_abort(char const* format, ...) noexcept
{
va_list list;
va_start(list, format);

View File

@@ -33,7 +33,7 @@ static char32_t decodeUTF16(const UChar* ptr, size_t available, size_t& outLen)
}
outLen = 1;
return c;
return static_cast<char32_t>(c);
}
static inline uint8_t getVisibleWidth(char32_t cp, bool ambiguousIsWide)

View File

@@ -52,6 +52,14 @@ pub fn init(this: *GarbageCollectionController, vm: *VirtualMachine) void {
}
this.gc_timer_interval = gc_timer_interval;
if (vm.transpiler.env.get("BUN_GC_RUNS_UNTIL_SKIP_RELEASE_ACCESS")) |val| {
if (std.fmt.parseInt(c_int, val, 10)) |parsed| {
if (parsed >= 0) {
VirtualMachine.Bun__defaultRemainingRunsUntilSkipReleaseAccess = parsed;
}
} else |_| {}
}
this.disabled = vm.transpiler.env.has("BUN_GC_TIMER_DISABLE");
if (!this.disabled)

View File

@@ -79,11 +79,13 @@ JSC_DEFINE_HOST_FUNCTION(functionStartRemoteDebugger,
JSC::JSValue hostValue = callFrame->argument(0);
JSC::JSValue portValue = callFrame->argument(1);
const char* host = defaultHost;
WTF::CString hostCString;
if (hostValue.isString()) {
auto str = hostValue.toWTFString(globalObject);
hostCString = toCString(str);
if (!str.isEmpty())
host = toCString(str).span().data();
host = hostCString.span().data();
} else if (!hostValue.isUndefined()) {
throwVMError(globalObject, scope,
createTypeError(globalObject, "host must be a string"_s));

View File

@@ -14,10 +14,15 @@ param(
[Switch]$DownloadWithoutCurl = $false
);
# filter out 32 bit + ARM
if (-not ((Get-CimInstance Win32_ComputerSystem)).SystemType -match "x64-based") {
# Detect system architecture
$SystemType = (Get-CimInstance Win32_ComputerSystem).SystemType
if ($SystemType -match "ARM64-based") {
$IsArm64 = $true
} elseif ($SystemType -match "x64-based") {
$IsArm64 = $false
} else {
Write-Output "Install Failed:"
Write-Output "Bun for Windows is currently only available for x86 64-bit Windows.`n"
Write-Output "Bun for Windows is currently only available for x86 64-bit and ARM64 Windows.`n"
return 1
}
@@ -103,13 +108,18 @@ function Install-Bun {
$Version = "bun-$Version"
}
$Arch = "x64"
$IsBaseline = $ForceBaseline
if (!$IsBaseline) {
$IsBaseline = !( `
Add-Type -MemberDefinition '[DllImport("kernel32.dll")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);' `
-Name 'Kernel32' -Namespace 'Win32' -PassThru `
)::IsProcessorFeaturePresent(40);
if ($IsArm64) {
$Arch = "aarch64"
$IsBaseline = $false
} else {
$Arch = "x64"
$IsBaseline = $ForceBaseline
if (!$IsBaseline) {
$IsBaseline = !( `
Add-Type -MemberDefinition '[DllImport("kernel32.dll")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);' `
-Name 'Kernel32' -Namespace 'Win32' -PassThru `
)::IsProcessorFeaturePresent(40);
}
}
$BunRoot = if ($env:BUN_INSTALL) { $env:BUN_INSTALL } else { "${Home}\.bun" }
@@ -219,7 +229,8 @@ function Install-Bun {
# I want to keep this error message in for a few months to ensure that
# if someone somehow runs into this, it can be reported.
Write-Output "Install Failed - You are missing a DLL required to run bun.exe"
Write-Output "This can be solved by installing the Visual C++ Redistributable from Microsoft:`nSee https://learn.microsoft.com/cpp/windows/latest-supported-vc-redist`nDirect Download -> https://aka.ms/vs/17/release/vc_redist.x64.exe`n`n"
$VCRedistArch = if ($Arch -eq "aarch64") { "arm64" } else { "x64" }
Write-Output "This can be solved by installing the Visual C++ Redistributable from Microsoft:`nSee https://learn.microsoft.com/cpp/windows/latest-supported-vc-redist`nDirect Download -> https://aka.ms/vs/17/release/vc_redist.${VCRedistArch}.exe`n`n"
Write-Output "The error above should be unreachable as Bun does not depend on this library. Please comment in https://github.com/oven-sh/bun/issues/8598 or open a new issue.`n`n"
Write-Output "The command '${BunBin}\bun.exe --revision' exited with code ${LASTEXITCODE}`n"
return 1

View File

@@ -1700,8 +1700,8 @@ pub fn dumpStackTrace(trace: std.builtin.StackTrace, limits: WriteStackTraceLimi
const programs: []const [:0]const u8 = switch (bun.Environment.os) {
.windows => &.{"pdb-addr2line"},
// if `llvm-symbolizer` doesn't work, also try `llvm-symbolizer-19`
else => &.{ "llvm-symbolizer", "llvm-symbolizer-19" },
// if `llvm-symbolizer` doesn't work, also try `llvm-symbolizer-21`
else => &.{ "llvm-symbolizer", "llvm-symbolizer-21" },
};
for (programs) |program| {
var arena = bun.ArenaAllocator.init(bun.default_allocator);

306
src/js/bun/git.ts Normal file
View File

@@ -0,0 +1,306 @@
// Hardcoded module "bun:git"
let Git: any;
function initializeGit() {
Git = $cpp("git/JSGit.cpp", "createJSGitModule");
}
interface Signature {
name: string;
email: string;
time: number; // Unix timestamp in milliseconds
}
interface StatusOptions {
includeUntracked?: boolean;
includeIgnored?: boolean;
recurseUntrackedDirs?: boolean;
detectRenames?: boolean;
}
interface InternalStatusEntry {
path: string;
status: number;
}
interface IndexEntry {
path: string;
mode: number;
oid: string;
stage: number;
size: number;
}
interface DiffOptions {
cached?: boolean;
}
interface DiffFile {
status: number;
oldPath: string | null;
newPath: string;
similarity?: number;
}
interface DiffResult {
files: DiffFile[];
stats: {
filesChanged: number;
insertions: number;
deletions: number;
};
}
interface LogOptions {
from?: string;
range?: string;
limit?: number;
}
// Status constants (nodegit compatible)
const Status = {
CURRENT: 0,
INDEX_NEW: 1,
INDEX_MODIFIED: 2,
INDEX_DELETED: 4,
INDEX_RENAMED: 8,
INDEX_TYPECHANGE: 16,
WT_NEW: 128,
WT_MODIFIED: 256,
WT_DELETED: 512,
WT_TYPECHANGE: 1024,
WT_RENAMED: 2048,
IGNORED: 16384,
CONFLICTED: 32768,
};
// DeltaType constants (nodegit compatible)
const DeltaType = {
UNMODIFIED: 0,
ADDED: 1,
DELETED: 2,
MODIFIED: 3,
RENAMED: 4,
COPIED: 5,
IGNORED: 6,
UNTRACKED: 7,
TYPECHANGE: 8,
CONFLICTED: 10,
};
class StatusEntry {
path: string;
status: number;
constructor(entry: InternalStatusEntry) {
this.path = entry.path;
this.status = entry.status;
}
isNew(): boolean {
return (this.status & (Status.INDEX_NEW | Status.WT_NEW)) !== 0;
}
isModified(): boolean {
return (this.status & (Status.INDEX_MODIFIED | Status.WT_MODIFIED)) !== 0;
}
isDeleted(): boolean {
return (this.status & (Status.INDEX_DELETED | Status.WT_DELETED)) !== 0;
}
isRenamed(): boolean {
return (this.status & (Status.INDEX_RENAMED | Status.WT_RENAMED)) !== 0;
}
isIgnored(): boolean {
return (this.status & Status.IGNORED) !== 0;
}
inIndex(): boolean {
return (
(this.status &
(Status.INDEX_NEW |
Status.INDEX_MODIFIED |
Status.INDEX_DELETED |
Status.INDEX_RENAMED |
Status.INDEX_TYPECHANGE)) !==
0
);
}
inWorkingTree(): boolean {
return (
(this.status &
(Status.WT_NEW | Status.WT_MODIFIED | Status.WT_DELETED | Status.WT_TYPECHANGE | Status.WT_RENAMED)) !==
0
);
}
}
class Repository {
#repo: any;
constructor(repo: any) {
this.#repo = repo;
}
/**
* Open an existing Git repository
*/
static open(path: string): Repository {
if (!Git) {
initializeGit();
}
const repo = Git.Repository.open(path);
return new Repository(repo);
}
/**
* Get the HEAD commit
*/
head(): Commit {
const commit = this.#repo.head();
return new Commit(commit);
}
/**
* Get the .git directory path
*/
get path(): string {
return this.#repo.path;
}
/**
* Get the working directory path (null for bare repositories)
*/
get workdir(): string | null {
return this.#repo.workdir;
}
/**
* Check if this is a bare repository
*/
get isBare(): boolean {
return this.#repo.isBare;
}
/**
* Get the working directory status (nodegit compatible)
*/
getStatus(options?: StatusOptions): StatusEntry[] {
const entries = this.#repo.getStatus(options);
return entries.map((e: InternalStatusEntry) => new StatusEntry(e));
}
/**
* Resolve a revision spec to an OID
*/
revParse(spec: string): string {
return this.#repo.revParse(spec);
}
/**
* Get the name of the current branch (null if detached HEAD or no commits)
*/
getCurrentBranch(): string | null {
return this.#repo.getCurrentBranch();
}
/**
* Get ahead/behind counts between two commits
*/
aheadBehind(local?: string, upstream?: string): { ahead: number; behind: number } {
return this.#repo.aheadBehind(local, upstream);
}
/**
* Get list of files in the index
*/
listFiles(): IndexEntry[] {
return this.#repo.listFiles();
}
/**
* Get diff information
*/
diff(options?: DiffOptions): DiffResult {
return this.#repo.diff(options);
}
/**
* Count commits in a range
*/
countCommits(range?: string): number {
return this.#repo.countCommits(range);
}
/**
* Get commit history
*/
log(options?: LogOptions): Commit[] {
const commits = this.#repo.log(options);
return commits.map((c: any) => new Commit(c));
}
}
class Commit {
#commit: any;
constructor(commit: any) {
this.#commit = commit;
}
/**
* Get the commit OID (SHA-1 hash)
*/
get id(): string {
return this.#commit.id;
}
/**
* Get the full commit message
*/
get message(): string {
return this.#commit.message;
}
/**
* Get the first line of the commit message
*/
get summary(): string {
return this.#commit.summary;
}
/**
* Get the author signature
*/
get author(): Signature {
return this.#commit.author;
}
/**
* Get the committer signature
*/
get committer(): Signature {
return this.#commit.committer;
}
/**
* Get the commit time as Unix timestamp (seconds since epoch)
*/
get time(): number {
return this.#commit.time;
}
}
export default {
__esModule: true,
Repository,
Commit,
StatusEntry,
Status,
DeltaType,
default: Repository,
};

View File

@@ -486,22 +486,27 @@ pub const HtmlRenderer = struct {
}
pub fn writeHtmlEscaped(self: *HtmlRenderer, txt: []const u8) void {
var start: usize = 0;
for (txt, 0..) |c, i| {
const replacement: ?[]const u8 = switch (c) {
'&' => "&amp;",
'<' => "&lt;",
'>' => "&gt;",
'"' => "&quot;",
else => null,
var i: usize = 0;
const needle = "&<>\"";
while (true) {
const next = std.mem.indexOfAny(u8, txt[i..], needle) orelse {
self.write(txt[i..]);
return;
};
if (replacement) |r| {
if (i > start) self.write(txt[start..i]);
self.write(r);
start = i + 1;
const pos = i + next;
if (pos > i)
self.write(txt[i..pos]);
const c = txt[pos];
switch (c) {
'&' => self.write("&amp;"),
'<' => self.write("&lt;"),
'>' => self.write("&gt;"),
'"' => self.write("&quot;"),
else => unreachable,
}
i = pos + 1;
}
if (start < txt.len) self.write(txt[start..]);
}
fn writeUrlEscaped(self: *HtmlRenderer, txt: []const u8) void {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
import { expect, mock, spyOn, test } from "bun:test";
test("spyOn returns a disposable that calls mockRestore", () => {
const obj = { method: () => "original" };
{
using spy = spyOn(obj, "method").mockReturnValue("mocked");
expect(obj.method()).toBe("mocked");
expect(spy).toHaveBeenCalledTimes(1);
}
expect(obj.method()).toBe("original");
});
test("mock() returns a disposable that calls mockRestore", () => {
const fn = mock(() => "original");
fn();
expect(fn).toHaveBeenCalledTimes(1);
expect(fn[Symbol.dispose]).toBeFunction();
fn[Symbol.dispose]();
expect(fn).toHaveBeenCalledTimes(0);
});
test("using with spyOn auto-restores prototype methods", () => {
class Greeter {
greet() {
return "hello";
}
}
{
using spy = spyOn(Greeter.prototype, "greet").mockReturnValue("hola");
expect(new Greeter().greet()).toBe("hola");
}
expect(new Greeter().greet()).toBe("hello");
});

View File

@@ -20,8 +20,8 @@ export async function build(dir: string) {
// so we make it use clang instead
...(process.platform == "linux" && isCI
? {
CC: !isMusl ? "/usr/lib/llvm-19/bin/clang" : "/usr/lib/llvm19/bin/clang",
CXX: !isMusl ? "/usr/lib/llvm-19/bin/clang++" : "/usr/lib/llvm19/bin/clang++",
CC: !isMusl ? "/usr/lib/llvm-21/bin/clang" : "/usr/lib/llvm21/bin/clang",
CXX: !isMusl ? "/usr/lib/llvm-21/bin/clang++" : "/usr/lib/llvm21/bin/clang++",
}
: {}),
},