# C Compiler

> Compile and run C from JavaScript with low overhead

`bun:ffi` has experimental support for compiling and running C from JavaScript with low overhead.

---

## Usage (cc in `bun:ffi`)

See the [introduction blog post](https://bun.com/blog/compile-and-run-c-in-js) for background.

JavaScript:

```ts hello.ts icon="file-code"
import { cc } from "bun:ffi";
import source from "./hello.c" with { type: "file" };

const {
  symbols: { hello },
} = cc({
  source,
  symbols: {
    hello: {
      args: [],
      returns: "int",
    },
  },
});

console.log("What is the answer to the universe?", hello());
```

C source:

```c hello.c
int hello() {
  return 42;
}
```

Running `hello.ts` prints:

```sh terminal icon="terminal"
bun hello.ts
What is the answer to the universe? 42
```

`cc` uses [TinyCC](https://bellard.org/tcc/) to compile the C code, then links it with the JavaScript runtime, converting types in-place.

### Primitive types

`cc` supports the same `FFIType` values as [`dlopen`](https://bun.aphrody.com/docs/runtime/ffi), except `buffer_length`. Only `cc` supports `napi_env` and `napi_value`.

| `FFIType`  | C Type         | Aliases                     |
| ---------- | -------------- | --------------------------- |
| buffer     | `char*`        |                             |
| cstring    | `char*`        |                             |
| function   | `(void*)(*)()` | `fn`, `callback`            |
| ptr        | `void*`        | `pointer`, `void*`, `char*` |
| i8         | `int8_t`       | `int8_t`                    |
| i16        | `int16_t`      | `int16_t`                   |
| i32        | `int32_t`      | `int32_t`, `int`            |
| i64        | `int64_t`      | `int64_t`, `isize`          |
| i64_fast   | `int64_t`      |                             |
| u8         | `uint8_t`      | `uint8_t`                   |
| u16        | `uint16_t`     | `uint16_t`                  |
| u32        | `uint32_t`     | `uint32_t`                  |
| u64        | `uint64_t`     | `uint64_t`, `usize`         |
| u64_fast   | `uint64_t`     |                             |
| f32        | `float`        | `float`                     |
| f64        | `double`       | `double`                    |
| bool       | `bool`         |                             |
| char       | `char`         |                             |
| napi_env   | `napi_env`     |                             |
| napi_value | `napi_value`   |                             |

### Strings, objects, and non-primitive types

For strings, objects, and other non-primitive types that don't map 1:1 to C types, `cc` supports N-API.

Use `napi_value` to pass or receive JavaScript values from a C function without any type conversions.

You can also pass a `napi_env` to receive the N-API environment used to call the JavaScript function.

#### Returning a C string to JavaScript

For example, to return a string from C to JavaScript:

```ts hello.ts
import { cc } from "bun:ffi";
import source from "./hello.c" with { type: "file" };

const {
  symbols: { hello },
} = cc({
  source,
  symbols: {
    hello: {
      args: ["napi_env"],
      returns: "napi_value",
    },
  },
});

const result = hello();
```

And in C:

```c hello.c
#include <node/node_api.h>

napi_value hello(napi_env env) {
  napi_value result;
  napi_create_string_utf8(env, "Hello, Napi!", NAPI_AUTO_LENGTH, &result);
  return result;
}
```

The same approach returns other types like objects and arrays:

```c hello.c
#include <node/node_api.h>

napi_value hello(napi_env env) {
  napi_value result;
  napi_create_object(env, &result);
  return result;
}
```

### `cc` Reference

#### `library: string | string[]`

Use `library` to specify the libraries to link with the C code.

```ts
type Library = string | string[];

cc({
  source: "hello.c",
  library: ["sqlite3"],
});
```

#### `symbols`

Use the `symbols` object to specify the functions and variables to expose to JavaScript.

```ts
type Symbols = {
  [key: string]: {
    args: FFIType[];
    returns: FFIType;
  };
};
```

#### `source`

`source` is the path to the C code to compile and link with the JavaScript runtime.

```ts
type Source = string | URL | BunFile;

cc({
  source: "hello.c",
  symbols: {
    hello: {
      args: [],
      returns: "int",
    },
  },
});
```

`source` can also be an array of paths, which are compiled into one library.

#### `code: string`

Instead of `source`, pass the C source as a string. Diagnostics name it `<inline>`.

```ts
const { symbols } = cc({
  code: "int twice(int x) { return x * 2; }",
  symbols: { twice: { args: ["int"], returns: "int" } },
});
```

#### `flags: string | string[]`

`flags` is an optional string or array of strings passed to the TinyCC compiler.

```ts
type Flags = string | string[];
```

These are flags like `-I` for include directories and `-D` for preprocessor definitions. They are appended after the defaults (`-std=c11 -Wl,--export-all-symbols -g -O2`), so a later `-std=` or `-O` wins. `-std=` accepts `c99`, `c11`, `c17` and `c23`, and their `gnu` variants; `-std=c23` defines `bool`, `true`, `false`, `nullptr`, `static_assert`, `alignas`, `alignof` and `thread_local`.

### Language support

TinyCC compiles C11: `_Static_assert`, `_Generic`, `_Alignas`, `_Thread_local` and `_Atomic`. Bun ships the freestanding headers (`<stdbool.h>`, `<stddef.h>`, `<stdarg.h>`, `<stdalign.h>`, `<stdnoreturn.h>`, `<float.h>`, `<stdatomic.h>`, `<tgmath.h>`). Atomic operations are sequentially consistent whatever memory order is requested. Other headers (`<stdio.h>`, `<string.h>`, …) come from the system: on Linux, including musl distributions such as Alpine, Bun looks in `/usr/include` and links against the C library in `/usr/lib`.

### Arguments and errors

Every argument is checked against its declared type before the C function runs. A value of the wrong type, or a missing argument, throws a `TypeError` (`ERR_INVALID_ARG_TYPE`) naming `arguments[i]`. `ptr` and `cstring` arguments accept a `TypedArray`, `DataView`, `ArrayBuffer`, `CString`, number, `bigint` or `null`.

When compilation fails, `cc()` throws an `Error` whose `errors` property lists each compiler diagnostic as `{ file, line, severity, message }`.

```ts
try {
  cc({ code: "int f(void) { return 1 +; }", symbols: { f: { returns: "int" } } });
} catch (error) {
  console.log(error.errors[0]); // { file: "<inline>", line: 1, severity: "error", message: "..." }
}
```

#### `define: Record<string, string>`

`define` is an optional object of preprocessor definitions passed to the TinyCC compiler.

```ts
type Defines = Record<string, string>;

cc({
  source: "hello.c",
  define: {
    NDEBUG: "1",
  },
});
```

### Disabling `cc`

Pass `--no-ffi-cc` to disable the C compiler for a process. Any call to `cc()` then throws an error with the code `ERR_FFI_CC_DISABLED`. The `--no-addons` flag also disables `cc()`, in addition to `process.dlopen`.

```sh
bun --no-ffi-cc ./app.ts
```

Workers inherit the setting from their parent. A Worker can also set it for itself with `execArgv: ["--no-ffi-cc"]`. Standalone executables can bake it in with `bun build --compile --compile-exec-argv="--no-ffi-cc"`.
