Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion scripts/build/buildOptionsRs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export function generateBuildOptionsRs(cfg: Config): string {
"pub const ENABLE_LOGS: bool = cfg!(bun_debug);",
"pub const ENABLE_ASAN: bool = cfg!(bun_asan);",
"pub const ENABLE_TINYCC: bool = !cfg!(any(",
` all(windows, target_arch = "aarch64"),`,
` target_os = "android",`,
` target_os = "freebsd",`,
"));",
Expand Down
7 changes: 3 additions & 4 deletions scripts/build/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -863,10 +863,9 @@ export function resolveConfig(partial: PartialConfig, toolchain: Toolchain): Con
// failure is loud ("cannot find -l:libatomic.a") and the fix is obvious.
const staticLibatomic = partial.staticLibatomic ?? true;

// TinyCC: off on Windows ARM64 (not supported), Android (no upstream
// bionic support; FFI cc() falls back to dlopen-only), and FreeBSD
// (oven-sh/tinycc has no FreeBSD target).
const tinycc = partial.tinycc ?? !((windows && arm64) || abi === "android" || freebsd);
// TinyCC: off on Android (no upstream bionic support; FFI cc() falls back
// to dlopen-only) and FreeBSD (oven-sh/tinycc has no FreeBSD target).
const tinycc = partial.tinycc ?? !(abi === "android" || freebsd);
Comment thread
claude[bot] marked this conversation as resolved.

const valgrind = partial.valgrind ?? false;
const fuzzilli = partial.fuzzilli ?? false;
Expand Down
6 changes: 1 addition & 5 deletions scripts/build/deps/tinycc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
* TinyCC — small embeddable C compiler. Powers bun:ffi's JIT-compile path,
* where user-provided C gets compiled and linked at runtime.
*
* Disabled on windows-arm64 (tinycc doesn't have an arm64-coff backend).
*
* Built via DirectBuild — no cmake sub-process. The old overlay
* CMakeLists.txt had two recurring ASAN workarounds for the c2str host
* tool (Linux ASLR/shadow-map, macOS 26.4 dyld deadlock); DirectBuild's
Expand All @@ -12,14 +10,12 @@

import type { Dependency, DirectBuild } from "../source.ts";

const TINYCC_COMMIT = "12882eee073cfe5c7621bcfadf679e1372d4537b";
const TINYCC_COMMIT = "c49c2204b07c526c9ad935fabfcaf3802cae1346";

export const tinycc: Dependency = {
name: "tinycc",
versionMacro: "TINYCC",

// The cfg.tinycc flag already encodes the windows-arm64 exclusion
// (see config.ts: `tinycc ?? !(windows && arm64)`).
enabled: cfg => cfg.tinycc,

source: () => ({
Expand Down
2 changes: 1 addition & 1 deletion scripts/build/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ export interface Dependency {

/**
* Whether this dep participates in the build at all. Defaults to always-on.
* E.g. libuv is windows-only, tinycc is disabled on windows-arm64.
* E.g. libuv is windows-only.
*/
enabled?: (cfg: Config) => boolean;

Expand Down
9 changes: 4 additions & 5 deletions src/tcc_sys/tcc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ pub type TCCErrorFunc = Option<unsafe extern "C" fn(opaque: *mut c_void, msg: *c
pub type ErrorFunc<Ctx> = unsafe extern "C" fn(ctx: *mut Ctx, msg: *const c_char);

// `libtcc.a` is only built where `cfg.tinycc` is true (`scripts/build/config.ts`):
// not Windows/aarch64 (TinyCC has no aarch64-pe-coff backend), not Android, not
// FreeBSD (the vendored fork doesn't support those targets). On those platforms
// these `extern "C"` decls would be undefined at link:
// not Android, not FreeBSD (the vendored fork doesn't support those targets).
// On those platforms these `extern "C"` decls would be undefined at link:
// `bun_runtime::ffi::ffi_body::{Source::add,
// CompileC::compile}` are reachable from `extern "C"` JS bindings and the
// monomorphized refs land in `libbun_rust.a` regardless of any
Expand All @@ -27,12 +26,12 @@ pub type ErrorFunc<Ctx> = unsafe extern "C" fn(ctx: *mut Ctx, msg: *const c_char
// Keep this predicate in sync with `cfg.tinycc` in `scripts/build/config.ts`.
macro_rules! tcc_externs {
($($(#[$attr:meta])* fn $name:ident($($arg:ident: $ty:ty),* $(,)?) $(-> $ret:ty)?;)*) => {
#[cfg(not(any(target_os = "android", target_os = "freebsd", all(windows, target_arch = "aarch64"))))]
#[cfg(not(any(target_os = "android", target_os = "freebsd")))]
unsafe extern "C" {
$($(#[$attr])* fn $name($($arg: $ty),*) $(-> $ret)?;)*
}
$(
#[cfg(any(target_os = "android", target_os = "freebsd", all(windows, target_arch = "aarch64")))]
#[cfg(any(target_os = "android", target_os = "freebsd"))]
#[allow(unused_variables, clippy::missing_safety_doc)]
unsafe extern "C" fn $name($($arg: $ty),*) $(-> $ret)? {
unreachable!(concat!(
Expand Down
14 changes: 5 additions & 9 deletions test/js/bun/ffi/cc.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { cc, CString, JSCallback, ptr, type FFIFunction, type Library } from "bun:ffi";
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
import { promises as fs } from "fs";
import { bunEnv, bunExe, isArm64, isASAN, isWindows, normalizeBunSnapshot, tempDir, tempDirWithFiles } from "harness";
import { bunEnv, bunExe, isASAN, isWindows, normalizeBunSnapshot, tempDir, tempDirWithFiles } from "harness";
import path from "path";
Comment thread
claude[bot] marked this conversation as resolved.

// TinyCC (and all of bun:ffi) is disabled on Windows ARM64
const isFFIUnavailable = isWindows && isArm64;

// TODO: we need to install build-essential and Apple SDK in CI.
// It can't find includes. It can on machines with that enabled.
// TinyCC's setjmp/longjmp error handling conflicts with ASan.
it.todoIf(isWindows || isASAN || isFFIUnavailable)("can run a .c file", () => {
it.todoIf(isWindows || isASAN)("can run a .c file", () => {
const result = Bun.spawnSync({
cmd: [bunExe(), path.join(__dirname, "cc-fixture.js")],
cwd: __dirname,
Expand All @@ -22,8 +19,7 @@ it.todoIf(isWindows || isASAN || isFFIUnavailable)("can run a .c file", () => {
});

// TinyCC's setjmp/longjmp error handling conflicts with ASan.
// TinyCC is disabled on Windows ARM64.
describe.skipIf(isASAN || isFFIUnavailable)("given an add(a, b) function", () => {
describe.skipIf(isASAN)("given an add(a, b) function", () => {
const source = /* c */ `
Comment thread
claude[bot] marked this conversation as resolved.
int add(int a, int b) {
return a + b;
Expand Down Expand Up @@ -391,7 +387,7 @@ describe.skipIf(isWindows || isASAN)("threadsafe JSCallback invoked from a forei
// Pins GC liveness: compiled trampolines survive the library wrapper being
// collected, and a JSCallback's closure stays alive until close().
// TinyCC's setjmp/longjmp error handling conflicts with ASan.
describe.skipIf(isASAN || isFFIUnavailable)("GC liveness of compiled symbols and callbacks", () => {
describe.skipIf(isASAN)("GC liveness of compiled symbols and callbacks", () => {
it("keeps symbol functions and callback closures alive across forced GC", async () => {
using dir = tempDir("bun-ffi-cc-gc-liveness", {
"lib.c": /* c */ `
Expand Down Expand Up @@ -457,7 +453,7 @@ describe.skipIf(isASAN || isFFIUnavailable)("GC liveness of compiled symbols and
});
});

describe.skipIf(isFFIUnavailable)("double <-> JSValue conversions", () => {
describe("double <-> JSValue conversions", () => {
// JSC NaN-boxes doubles, so a NaN whose payload collides with the tag space
// ("impure NaN", see JSC's PureNaN.h) must never be encoded as-is: it would
// decode as a native-chosen JSValue (true, undefined, an Int32, or a cell
Expand Down
7 changes: 2 additions & 5 deletions test/js/bun/ffi/ffi-error-messages.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { dlopen, linkSymbols } from "bun:ffi";
import { describe, expect, test } from "bun:test";
import { isArm64, isMusl, isWindows } from "harness";
import { isMusl } from "harness";

// TinyCC (and all of bun:ffi) is disabled on Windows ARM64
const isFFIUnavailable = isWindows && isArm64;

describe.skipIf(isFFIUnavailable)("FFI error messages", () => {
describe("FFI error messages", () => {
test("dlopen shows library name when library cannot be opened", () => {
// Try to open a non-existent library
try {
Expand Down
5 changes: 1 addition & 4 deletions test/js/bun/ffi/ffi-viewSource-non-object.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { describe, expect, test } from "bun:test";
import { isArm64, isWindows } from "harness";

const isFFIUnavailable = isWindows && isArm64;

describe.skipIf(isFFIUnavailable)("FFI viewSource", () => {
describe("FFI viewSource", () => {
test("rejects non-object symbol descriptor values", () => {
// These should throw a TypeError because each symbol descriptor
// must be an object like { args: [...], returns: "void" }.
Expand Down
9 changes: 3 additions & 6 deletions test/js/bun/ffi/ffi.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { afterAll, describe, expect, it } from "bun:test";
import { existsSync } from "fs";
import { bunEnv, bunExe, isArm64, isGlibcVersionAtLeast, isWindows, tempDir } from "harness";
import { bunEnv, bunExe, isGlibcVersionAtLeast, tempDir } from "harness";
Comment thread
claude[bot] marked this conversation as resolved.
import { platform } from "os";

import {
Expand Down Expand Up @@ -677,12 +677,9 @@ it(".ptr is not leaked", () => {
}
});

// TinyCC, which implements JSCallback and CFunction, is unavailable on Windows ARM64.
const isFFIUnavailable = isWindows && isArm64;

// Runs in a subprocess: `bun test`'s exit path does not finalize the CFunction's native handle,
// which the ASan lane's leak checker then reports against this file.
it.skipIf(isFFIUnavailable)("JSCallback exceptions propagate out of the native call", async () => {
it("JSCallback exceptions propagate out of the native call", async () => {
await using proc = Bun.spawn({
cmd: [
bunExe(),
Expand Down Expand Up @@ -719,7 +716,7 @@ it.skipIf(isFFIUnavailable)("JSCallback exceptions propagate out of the native c
// worker.terminate() delivered inside a threadsafe JSCallback used to trip
// "ASSERTION FAILED: !isTerminationException(exception) || hasTerminationRequest()"
// in JSC::VM::setException on the worker thread and re-enter the terminated VM.
it.skipIf(isFFIUnavailable)("JSCallback tolerates worker.terminate() arriving inside the callback", async () => {
it("JSCallback tolerates worker.terminate() arriving inside the callback", async () => {
using dir = tempDir("ffi-jscallback-terminate", {
"main.js": `
import { join } from "node:path";
Expand Down
6 changes: 3 additions & 3 deletions test/js/node/fs/cp.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, jest, test } from "bun:test";
import fs from "fs";
import { bunEnv, bunExe, isArm64, isLinux, isPosix, isWindows, tempDir, tempDirWithFiles } from "harness";
import { bunEnv, bunExe, isLinux, isPosix, isWindows, tempDir, tempDirWithFiles } from "harness";
import { mkfifo } from "mkfifo";
import { isAbsolute, join } from "path";

Expand Down Expand Up @@ -434,8 +434,8 @@ test("cp with missing callback throws", () => {
// source symlink to resolve its target via GetFinalPathNameByHandleW. Previously
// that handle was never closed, leaking one OS handle per symlink copied. Over a
// large tree (e.g. node_modules with junctions) this eventually exhausts the
// process handle table. bun:ffi (TinyCC) is unavailable on Windows arm64.
test.skipIf(!isWindows || isArm64)("cpSync over symlinks does not leak Windows handles", () => {
// process handle table.
test.skipIf(!isWindows)("cpSync over symlinks does not leak Windows handles", () => {
const { dlopen } = require("bun:ffi");
const k32 = dlopen("kernel32.dll", {
GetCurrentProcess: { args: [], returns: "ptr" },
Expand Down
7 changes: 2 additions & 5 deletions test/js/node/fs/fs-writeSync-stdio-windows.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@
// Now `fromJS`/`fromJSValidated` return `.fromUV(0|1|2)` directly, and
// `FD.uv()` checks the cached stdio handles before `GetStdHandle`.
import { describe, expect, test } from "bun:test";
import { bunEnv, bunExe, isArm64, isWindows, tempDir } from "harness";
import { bunEnv, bunExe, isWindows, tempDir } from "harness";
import { join } from "node:path";

describe.concurrent.skipIf(!isWindows)("fs.writeSync on Windows stdio/handles", () => {
// bun:ffi (TinyCC) is unavailable on Windows arm64, so this repro can only
// run on x64. The second test below covers the plain openSync→writeSync path
// on all Windows arches.
test.skipIf(isArm64)("fs.writeSync(1, ...) does not panic after SetStdHandle swaps stdout", async () => {
test("fs.writeSync(1, ...) does not panic after SetStdHandle swaps stdout", async () => {
const fixture = `
const fs = require("node:fs");
const { dlopen } = require("bun:ffi");
Expand Down
2 changes: 1 addition & 1 deletion test/js/node/process/process.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ it("process.versions", () => {
mimalloc: "afb41757285694f832e7a2f164d35f5717457f96",
picohttpparser: "066d2b1e9ab820703db0837a7255d92d30f0c9f5",
zlib: "12731092979c6d07f42da27da673a9f6c7b13586",
tinycc: "12882eee073cfe5c7621bcfadf679e1372d4537b",
tinycc: "c49c2204b07c526c9ad935fabfcaf3802cae1346",
lolhtml: "77127cd2b8545998756e8d64e36ee2313c4bb312",
ares: "3ac47ee46edd8ea40370222f91613fc16c434853",
libdeflate: "c8c56a20f8f621e6a966b716b31f1dedab6a41e3",
Expand Down
7 changes: 3 additions & 4 deletions test/napi/napi-value-ffi.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { spawnSync } from "bun";
import { cc, dlopen } from "bun:ffi";
import { beforeAll, describe, expect, it } from "bun:test";
import { bunEnv, bunExe, canBuildNodeAddons, isArm64, isASAN, isWindows } from "harness";
import { bunEnv, bunExe, canBuildNodeAddons, isASAN, isWindows } from "harness";
import { join } from "path";

import source from "./napi-app/ffi_addon_1.c" with { type: "file" };

// TinyCC (and all of bun:ffi) is disabled on Windows ARM64; the napi-app
// fixture needs a toolchain that can compile the reported Node headers.
const isFFIUnavailable = (isWindows && isArm64) || !canBuildNodeAddons();
// The napi-app fixture needs a toolchain that can compile the Node headers.
const isFFIUnavailable = !canBuildNodeAddons();

const symbols = {
set_instance_data: {
Expand Down
Loading