Add a Clojure FFI so libraries can bind native code (jolt.ffi)
A jolt library can now bind its own native dependencies and expose a Clojure API over them — no jolt built-in required. This is the foundation for moving the http-client / db / adapter functionality out of the host and into real libraries. - jolt.ffi/foreign-fn (sugar: defcfn) is a compiler special form: a compile-time -typed C signature lowers to a real Chez foreign-procedure (analyzer :ffi-fn -> backend foreign-procedure), so calls are typed and marshaled, not eval'd. - host/chez/ffi.ss provides the rest under jolt.ffi: load-library, alloc/free, read/write/sizeof, ptr<->string, null/null?. Loaded after the loader snapshot so a library's (require '[jolt.ffi]) still loads the macro side. - Types: int/uint/long/ulong/int64/uint64/size_t/ssize_t/iptr/uptr/double/float/ pointer/string/void/uint8/char. Validated end to end: a pure-Clojure file binds libc (getpid/strlen/abs) and libsqlite3 (open/prepare/step/column/finalize over out-param pointers) and runs a query. Gate test test/chez/ffi-binding-test.ss (make ffi); selfhost holds.
This commit is contained in:
parent
ccf93c896a
commit
537cb360b4
8 changed files with 282 additions and 62 deletions
|
|
@ -393,6 +393,20 @@
|
|||
(defn- analyze-ctor [ctx class args env]
|
||||
(host-new class (mapv #(analyze ctx % env) args)))
|
||||
|
||||
;; jolt.ffi/__cfn (jolt-ffi): the low-level foreign-function form a jolt library
|
||||
;; uses (via the jolt.ffi/foreign-fn macro) to bind native code. Shape:
|
||||
;; (jolt.ffi/__cfn "c_symbol" [:argtype ...] :rettype)
|
||||
;; The C symbol is a string literal and the types are literal keywords, read here
|
||||
;; at compile time; the Chez back end lowers it to a real `foreign-procedure`
|
||||
;; (typed marshaling, no runtime eval). A leaf IR node.
|
||||
(defn- analyze-ffi-fn [ctx items env]
|
||||
(when (not= 4 (count items))
|
||||
(throw (str "jolt.ffi/foreign-fn expects (foreign-fn \"sym\" [argtypes] rettype)")))
|
||||
{:op :ffi-fn
|
||||
:csym (nth items 1)
|
||||
:argtypes (mapv name (form-vec-items (nth items 2)))
|
||||
:rettype (name (nth items 3))})
|
||||
|
||||
;; The `.` special form: `(. target member arg*)` — member access / method call.
|
||||
;; A symbol member whose name starts with "-" is a field read; otherwise it is a
|
||||
;; method (call with the trailing args). Both lower to a :host-call carrying the
|
||||
|
|
@ -476,6 +490,11 @@
|
|||
;; read -> macroexpand -> analyze. A local shadows both.
|
||||
(and (form-sym? head) (not shadowed) (form-macro? ctx head))
|
||||
(analyze ctx (form-expand-1 ctx form) env)
|
||||
;; jolt.ffi/__cfn — the foreign-function special form (always emitted
|
||||
;; fully-qualified by the jolt.ffi/foreign-fn macro, so aliases resolve).
|
||||
(and (form-sym? head) (= "jolt.ffi" (form-sym-ns head))
|
||||
(= "__cfn" (form-sym-name head)))
|
||||
(analyze-ffi-fn ctx items env)
|
||||
;; special-form heads are NOT shadowable (unlike macros): a local named
|
||||
;; `if` does not change the meaning of (if …) in operator position, per
|
||||
;; spec §3 and the reference. No (not shadowed) guard here.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue