Skip to content

Commit

Permalink
Support compiling and running C from JavaScript (oven-sh#13403)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarred-Sumner committed Sep 14, 2024
1 parent b9a5e44 commit f4391e7
Show file tree
Hide file tree
Showing 17 changed files with 1,347 additions and 92 deletions.
60 changes: 60 additions & 0 deletions packages/bun-types/ffi.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,66 @@ declare module "bun:ffi" {
symbols: Fns,
): Library<Fns>;

/**
* **Experimental:** Compile ISO C11 source code using TinyCC, and make {@link symbols} available as functions to JavaScript.
*
* @param options
* @returns Library<Fns>
*
* @example
* ## Hello, World!
*
* JavaScript:
* ```js
* import { cc } from "bun:ffi";
* import hello from "./hello.c" with {type: "file"};
* const {symbols: {hello}} = cc({
* source: hello,
* symbols: {
* hello: {
* returns: "cstring",
* args: [],
* },
* },
* });
* // "Hello, World!"
* console.log(hello());
* ```
*
* `./hello.c`:
* ```c
* #include <stdio.h>
* const char* hello() {
* return "Hello, World!";
* }
* ```
*/
function cc<Fns extends Record<string, FFIFunction>>(options: {
/**
* File path to an ISO C11 source file to compile and link
*/
source: string | import("bun").BunFile | URL;

/**
* Library names to link against
*
* Equivalent to `-l` option in gcc/clang.
*/
library?: string[] | string;

/**
* Include directories to pass to the compiler
*
* Equivalent to `-I` option in gcc/clang.
*/
include?: string[] | string;

/**
* Map of symbols to load where the key is the symbol name and the value is the {@link FFIFunction}
*/
symbols: Fns;
}): Library<Fns>;

/**
* Turn a native library's function pointer into a JavaScript function
*
Expand Down
15 changes: 15 additions & 0 deletions src/bun.js/api/ffi-stdalign.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef _STDALIGN_H
#define _STDALIGN_H

#if __STDC_VERSION__ < 201112L && (defined(__GNUC__) || defined(__TINYC__))
#define _Alignas(t) __attribute__((__aligned__(t)))
#define _Alignof(t) __alignof__(t)
#endif

#define alignas _Alignas
#define alignof _Alignof

#define __alignas_is_defined 1
#define __alignof_is_defined 1

#endif /* _STDALIGN_H */
14 changes: 14 additions & 0 deletions src/bun.js/api/ffi-stdarg.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef _STDARG_H
#define _STDARG_H

typedef __builtin_va_list va_list;
#define va_start __builtin_va_start
#define va_arg __builtin_va_arg
#define va_copy __builtin_va_copy
#define va_end __builtin_va_end

/* fix a buggy dependency on GCC in libio.h */
typedef va_list __gnuc_va_list;
#define _VA_LIST_DEFINED

#endif /* _STDARG_H */
180 changes: 180 additions & 0 deletions src/bun.js/api/ffi-stdatomic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/* This file is derived from clang's stdatomic.h */

/*===---- stdatomic.h - Standard header for atomic types and operations -----===
*
* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
* See https://llvm.org/LICENSE.txt for license information.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*
*===-----------------------------------------------------------------------===
*/

#ifndef _STDATOMIC_H
#define _STDATOMIC_H

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#define __ATOMIC_RELAXED 0
#define __ATOMIC_CONSUME 1
#define __ATOMIC_ACQUIRE 2
#define __ATOMIC_RELEASE 3
#define __ATOMIC_ACQ_REL 4
#define __ATOMIC_SEQ_CST 5

/* Memory ordering */
typedef enum {
memory_order_relaxed = __ATOMIC_RELAXED,
memory_order_consume = __ATOMIC_CONSUME,
memory_order_acquire = __ATOMIC_ACQUIRE,
memory_order_release = __ATOMIC_RELEASE,
memory_order_acq_rel = __ATOMIC_ACQ_REL,
memory_order_seq_cst = __ATOMIC_SEQ_CST,
} memory_order;

/* Atomic typedefs */
typedef _Atomic(_Bool) atomic_bool;
typedef _Atomic(char) atomic_char;
typedef _Atomic(signed char) atomic_schar;
typedef _Atomic(unsigned char) atomic_uchar;
typedef _Atomic(short) atomic_short;
typedef _Atomic(unsigned short) atomic_ushort;
typedef _Atomic(int) atomic_int;
typedef _Atomic(unsigned int) atomic_uint;
typedef _Atomic(long) atomic_long;
typedef _Atomic(unsigned long) atomic_ulong;
typedef _Atomic(long long) atomic_llong;
typedef _Atomic(unsigned long long) atomic_ullong;
typedef _Atomic(uint_least16_t) atomic_char16_t;
typedef _Atomic(uint_least32_t) atomic_char32_t;
typedef _Atomic(wchar_t) atomic_wchar_t;
typedef _Atomic(int_least8_t) atomic_int_least8_t;
typedef _Atomic(uint_least8_t) atomic_uint_least8_t;
typedef _Atomic(int_least16_t) atomic_int_least16_t;
typedef _Atomic(uint_least16_t) atomic_uint_least16_t;
typedef _Atomic(int_least32_t) atomic_int_least32_t;
typedef _Atomic(uint_least32_t) atomic_uint_least32_t;
typedef _Atomic(int_least64_t) atomic_int_least64_t;
typedef _Atomic(uint_least64_t) atomic_uint_least64_t;
typedef _Atomic(int_fast8_t) atomic_int_fast8_t;
typedef _Atomic(uint_fast8_t) atomic_uint_fast8_t;
typedef _Atomic(int_fast16_t) atomic_int_fast16_t;
typedef _Atomic(uint_fast16_t) atomic_uint_fast16_t;
typedef _Atomic(int_fast32_t) atomic_int_fast32_t;
typedef _Atomic(uint_fast32_t) atomic_uint_fast32_t;
typedef _Atomic(int_fast64_t) atomic_int_fast64_t;
typedef _Atomic(uint_fast64_t) atomic_uint_fast64_t;
typedef _Atomic(intptr_t) atomic_intptr_t;
typedef _Atomic(uintptr_t) atomic_uintptr_t;
typedef _Atomic(size_t) atomic_size_t;
typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
typedef _Atomic(intmax_t) atomic_intmax_t;
typedef _Atomic(uintmax_t) atomic_uintmax_t;

/* Atomic flag */
typedef struct {
atomic_bool value;
} atomic_flag;

#define ATOMIC_FLAG_INIT {0}
#define ATOMIC_VAR_INIT(value) (value)

#define atomic_flag_test_and_set_explicit(object, order) \
__atomic_test_and_set((void *)(&((object)->value)), order)
#define atomic_flag_test_and_set(object) \
atomic_flag_test_and_set_explicit(object, __ATOMIC_SEQ_CST)

#define atomic_flag_clear_explicit(object, order) \
__atomic_clear((bool *)(&((object)->value)), order)
#define atomic_flag_clear(object) \
atomic_flag_clear_explicit(object, __ATOMIC_SEQ_CST)

/* Generic routines */
#define atomic_init(object, desired) \
atomic_store_explicit(object, desired, __ATOMIC_RELAXED)

#define atomic_store_explicit(object, desired, order) \
({ \
__typeof__(object) ptr = (object); \
__typeof__(*ptr) tmp = (desired); \
__atomic_store(ptr, &tmp, (order)); \
})
#define atomic_store(object, desired) \
atomic_store_explicit(object, desired, __ATOMIC_SEQ_CST)

#define atomic_load_explicit(object, order) \
({ \
__typeof__(object) ptr = (object); \
__typeof__(*ptr) tmp; \
__atomic_load(ptr, &tmp, (order)); \
tmp; \
})
#define atomic_load(object) atomic_load_explicit(object, __ATOMIC_SEQ_CST)

#define atomic_exchange_explicit(object, desired, order) \
({ \
__typeof__(object) ptr = (object); \
__typeof__(*ptr) val = (desired); \
__typeof__(*ptr) tmp; \
__atomic_exchange(ptr, &val, &tmp, (order)); \
tmp; \
})
#define atomic_exchange(object, desired) \
atomic_exchange_explicit(object, desired, __ATOMIC_SEQ_CST)

#define atomic_compare_exchange_strong_explicit(object, expected, desired, \
success, failure) \
({ \
__typeof__(object) ptr = (object); \
__typeof__(*ptr) tmp = desired; \
__atomic_compare_exchange(ptr, expected, &tmp, 0, success, failure); \
})
#define atomic_compare_exchange_strong(object, expected, desired) \
atomic_compare_exchange_strong_explicit(object, expected, desired, \
__ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)

#define atomic_compare_exchange_weak_explicit(object, expected, desired, \
success, failure) \
({ \
__typeof__(object) ptr = (object); \
__typeof__(*ptr) tmp = desired; \
__atomic_compare_exchange(ptr, expected, &tmp, 1, success, failure); \
})
#define atomic_compare_exchange_weak(object, expected, desired) \
atomic_compare_exchange_weak_explicit(object, expected, desired, \
__ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)

#define atomic_fetch_add(object, operand) \
__atomic_fetch_add(object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_add_explicit __atomic_fetch_add

#define atomic_fetch_sub(object, operand) \
__atomic_fetch_sub(object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_sub_explicit __atomic_fetch_sub

#define atomic_fetch_or(object, operand) \
__atomic_fetch_or(object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_or_explicit __atomic_fetch_or

#define atomic_fetch_xor(object, operand) \
__atomic_fetch_xor(object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_xor_explicit __atomic_fetch_xor

#define atomic_fetch_and(object, operand) \
__atomic_fetch_and(object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_and_explicit __atomic_fetch_and

extern void atomic_thread_fence(memory_order);
extern void __atomic_thread_fence(memory_order);
#define atomic_thread_fence(order) __atomic_thread_fence(order)
extern void atomic_signal_fence(memory_order);
extern void __atomic_signal_fence(memory_order);
#define atomic_signal_fence(order) __atomic_signal_fence(order)
extern bool __atomic_is_lock_free(size_t size, void *ptr);
#define atomic_is_lock_free(OBJ) __atomic_is_lock_free(sizeof(*(OBJ)), (OBJ))

extern bool __atomic_test_and_set(void *, memory_order);
extern void __atomic_clear(bool *, memory_order);

#endif /* _STDATOMIC_H */
11 changes: 11 additions & 0 deletions src/bun.js/api/ffi-stdbool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef _STDBOOL_H
#define _STDBOOL_H

/* ISOC99 boolean */

#define bool _Bool
#define true 1
#define false 0
#define __bool_true_false_are_defined 1

#endif /* _STDBOOL_H */
44 changes: 44 additions & 0 deletions src/bun.js/api/ffi-stddef.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef _STDDEF_H
#define _STDDEF_H

typedef __SIZE_TYPE__ size_t;
typedef __PTRDIFF_TYPE__ ssize_t;
typedef __WCHAR_TYPE__ wchar_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;
typedef __PTRDIFF_TYPE__ intptr_t;
typedef __SIZE_TYPE__ uintptr_t;

#if __STDC_VERSION__ >= 201112L
typedef union {
long long __ll;
long double __ld;
} max_align_t;
#endif

#ifndef NULL
#define NULL ((void *)0)
#endif

#undef offsetof
#define offsetof(type, field) ((size_t) & ((type *)0)->field)

#if defined __i386__ || defined __x86_64__
void *alloca(size_t size);
#endif

#endif

/* Older glibc require a wint_t from <stddef.h> (when requested
by __need_wint_t, as otherwise stddef.h isn't allowed to
define this type). Note that this must be outside the normal
_STDDEF_H guard, so that it works even when we've included the file
already (without requiring wint_t). Some other libs define _WINT_T
if they've already provided that type, so we can use that as guard.
TCC defines __WINT_TYPE__ for us. */
#if defined(__need_wint_t)
#ifndef _WINT_T
#define _WINT_T
typedef __WINT_TYPE__ wint_t;
#endif
#undef __need_wint_t
#endif
7 changes: 7 additions & 0 deletions src/bun.js/api/ffi-stdnoreturn.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef _STDNORETURN_H
#define _STDNORETURN_H

/* ISOC11 noreturn */
#define noreturn _Noreturn

#endif /* _STDNORETURN_H */
Loading

0 comments on commit f4391e7

Please sign in to comment.