2026-07-15 11:46:05 -03:00
## Compile-time metadata types for FFI binding generation, populated by the
## {.ffiCtor.}/{.ffi.} macros and consumed by codegen.
2026-05-11 23:28:17 +02:00
2026-06-23 11:18:03 -03:00
import std / strutils
2026-05-11 23:28:17 +02:00
type
2026-06-23 11:18:03 -03:00
ABIFormat * {. pure . } = enum
2026-07-15 11:46:05 -03:00
## FFI payload wire format. `Cbor` is wired end-to-end; `C` has a type codec
## but no proc-dispatch path yet.
2026-06-23 11:18:03 -03:00
Cbor = " cbor "
C = " c "
2026-05-11 23:28:17 +02:00
FFIParamMeta * = object
2026-07-15 11:46:05 -03:00
name * : string
typeName * : string
isPtr * : bool
isHandle * : bool # {.ffiHandle.} type, wire form uint64
2026-05-11 23:28:17 +02:00
2026-05-16 01:08:42 +02:00
FFIKind * {. pure . } = enum
FFI
CTOR
DTOR
2026-05-11 23:28:17 +02:00
FFIProcMeta * = object
2026-07-15 11:46:05 -03:00
procName * : string
libName * : string
2026-05-16 01:08:42 +02:00
kind * : FFIKind
2026-07-15 11:46:05 -03:00
libTypeName * : string
2026-07-22 17:04:35 -03:00
doc * : string
2026-05-11 23:28:17 +02:00
extraParams * : seq [ FFIParamMeta ] # all params except the lib param
2026-07-15 11:46:05 -03:00
returnTypeName * : string
returnIsPtr * : bool
returnIsHandle * : bool
abiFormat * : ABIFormat
2026-07-06 11:57:46 -03:00
scalarFastPath * : bool
2026-07-15 11:46:05 -03:00
## `abi = c` proc with an all-scalar signature: uses the CBOR-free fast
2026-07-16 10:58:45 -03:00
## path, and binds only in the `abi = c` C header (see `bindableProcs`).
2026-05-11 23:28:17 +02:00
FFIFieldMeta * = object
2026-07-15 11:46:05 -03:00
name * : string
typeName * : string
2026-05-11 23:28:17 +02:00
2026-07-23 14:00:24 -03:00
FFIEnumValueMeta * = object
## One `{.ffi.}` enum value. `wire` is what `$value` yields — the symbol name,
## or the associated string if the enum declares one — which is exactly what
## cbor_serialization puts on the wire.
name * : string
wire * : string
ord * : int
2026-05-11 23:28:17 +02:00
FFITypeMeta * = object
name * : string
fields * : seq [ FFIFieldMeta ]
2026-07-15 11:46:05 -03:00
abiFormat * : ABIFormat
2026-07-23 14:00:24 -03:00
enumValues * : seq [ FFIEnumValueMeta ] ## non-empty iff the type is an enum
FFIConstMeta * = object
## A `{.ffiConst.}` value. `value` is the compile-time-evaluated result of
## `$theConst`, re-rendered as a literal by each backend.
name * : string
typeName * : string
value * : string
2026-05-11 23:28:17 +02:00
2026-05-25 15:51:56 +02:00
FFIEventMeta * = object
2026-07-15 11:46:05 -03:00
## Library-initiated event from `{.ffiEvent: "wire_name".}`; `wireName` is
## the verbatim CBOR `eventType` the foreign side dispatches on.
2026-05-25 15:51:56 +02:00
wireName * : string
nimProcName * : string
libName * : string
payloadTypeName * : string
2026-07-15 11:46:05 -03:00
abiFormat * : ABIFormat
2026-07-22 17:04:35 -03:00
doc * : string
2026-05-25 15:51:56 +02:00
2026-05-11 23:28:17 +02:00
var ffiProcRegistry * {. compileTime . } : seq [ FFIProcMeta ]
var ffiTypeRegistry * {. compileTime . } : seq [ FFITypeMeta ]
2026-05-25 15:51:56 +02:00
var ffiEventRegistry * {. compileTime . } : seq [ FFIEventMeta ]
2026-07-23 14:00:24 -03:00
var ffiConstRegistry * {. compileTime . } : seq [ FFIConstMeta ]
2026-05-11 23:28:17 +02:00
var currentLibName * {. compileTime . } : string
2026-07-15 11:46:05 -03:00
# Set by `declareLibrary`; the FFI annotations require it.
2026-06-23 11:18:03 -03:00
var libraryDeclared * {. compileTime . } : bool = false
2026-07-15 11:46:05 -03:00
# Set by `genBindings()`. Annotations expanded after it register too late to be emitted, so the macros check this and fail loudly instead of dropping silently.
2026-07-06 12:31:49 -03:00
var genBindingsEmitted * {. compileTime . } : bool = false
2026-06-23 11:18:03 -03:00
# Library-wide default ABI, inherited by each annotation unless it overrides.
var currentDefaultABIFormat * {. compileTime . } : ABIFormat = ABIFormat . Cbor
proc abiCodegenImplemented * ( fmt : ABIFormat ) : bool =
2026-07-15 11:46:05 -03:00
## Whether `fmt` has a working proc-dispatch path (both Cbor and C do).
2026-07-06 13:48:08 -03:00
fmt in { ABIFormat . Cbor , ABIFormat . C }
2026-06-23 11:18:03 -03:00
2026-07-06 11:58:04 -03:00
proc overrideKey * ( override : string ) : string =
2026-07-15 11:46:05 -03:00
## Lowercased key of a `key = value` pragma override, e.g. `"abi = c"` → `"abi"`.
2026-07-06 11:58:04 -03:00
override . split ( ' = ' ) [ 0 ] . strip ( ) . toLowerAscii ( )
2026-06-23 11:18:03 -03:00
proc parseABIFormatName * ( name : string ) : tuple [ ok : bool , fmt : ABIFormat ] =
2026-07-15 11:46:05 -03:00
## Bare format name ("c"/"cbor", case-insensitive) → ABIFormat; else ok=false.
2026-06-23 11:18:03 -03:00
case name . strip ( ) . toLowerAscii ( )
of " cbor " :
( true , ABIFormat . Cbor )
of " c " :
( true , ABIFormat . C )
else :
( false , ABIFormat . Cbor )
2026-07-06 11:58:04 -03:00
proc parseAbiSpec * ( override : string ) : tuple [ ok : bool , fmt : ABIFormat , err : string ] =
2026-07-15 11:46:05 -03:00
## Parse an `"abi = <format>"` override; on bad grammar returns ok=false + err.
2026-07-06 11:58:04 -03:00
let parts = override . split ( ' = ' )
2026-06-23 11:18:03 -03:00
if parts . len ! = 2 :
return (
false ,
ABIFormat . Cbor ,
2026-07-06 11:58:04 -03:00
" invalid ABI override: ' " & override & " ' ; expected `abi = c` or `abi = cbor` " ,
2026-06-23 11:18:03 -03:00
)
if parts [ 0 ] . strip ( ) . toLowerAscii ( ) ! = " abi " :
return (
false ,
ABIFormat . Cbor ,
2026-07-06 11:58:04 -03:00
" invalid ABI override: ' " & override & " ' ; expected `abi = c` or `abi = cbor` " ,
2026-06-23 11:18:03 -03:00
)
let ( ok , fmt ) = parseABIFormatName ( parts [ 1 ] )
if not ok :
return (
false ,
ABIFormat . Cbor ,
2026-07-06 11:58:04 -03:00
" unknown ABI format: ' " & parts [ 1 ] . strip ( ) & " ' ; valid values are `c` and `cbor` " ,
2026-06-23 11:18:03 -03:00
)
( true , fmt , " " )
2026-06-16 14:11:31 -03:00
# Lib type name (set by declareLibrary) so handle-receiver procs resolve the pool.
var currentLibType * {. compileTime . } : string
# Names of types marked `{.ffiHandle.}` (wire form uint64).
var ffiHandleTypeNames * {. compileTime . } : seq [ string ]
proc isFFIHandleTypeName * ( name : string ) : bool {. compileTime . } =
name in ffiHandleTypeNames
2026-07-23 14:00:24 -03:00
func isEnum * ( t : FFITypeMeta ) : bool =
return t . enumValues . len > 0
# Names of `{.ffi.}` enum types; the `abi = c` wire path has to reject them.
var ffiEnumTypeNames * {. compileTime . } : seq [ string ]
proc isFFIEnumTypeName * ( name : string ) : bool {. compileTime . } =
name in ffiEnumTypeNames
2026-06-16 14:11:31 -03:00
proc ridesAsPtr * ( ep : FFIParamMeta ) : bool =
2026-07-15 11:46:05 -03:00
## True if the param crosses the wire as an opaque uint64 (raw ptr or handle).
2026-06-16 14:11:31 -03:00
ep . isPtr or ep . isHandle
proc returnRidesAsPtr * ( p : FFIProcMeta ) : bool =
2026-07-15 11:46:05 -03:00
## True if the return crosses the wire as an opaque uint64 (raw ptr or handle).
2026-06-16 14:11:31 -03:00
p . returnIsPtr or p . returnIsHandle
2026-07-15 11:46:05 -03:00
# Target language(s), override with -d:targetLang=cpp; comma-separated list allowed.
2026-05-11 23:28:17 +02:00
const targetLang * {. strdefine . } = " rust "
2026-07-15 11:46:05 -03:00
# Output dir override (-d:ffiOutputDir); empty derives `<lang>_bindings/` by src.
2026-05-11 23:28:17 +02:00
const ffiOutputDir * {. strdefine . } = " "
2026-07-15 11:46:05 -03:00
# Nim src path override relative to outputDir (-d:ffiSrcPath); empty derives it.
2026-05-18 20:00:57 +02:00
const ffiSrcPath * {. strdefine . } = " "
2026-07-09 16:43:21 -03:00
2026-07-16 10:58:45 -03:00
# When true, targets without scalar codegen silently omit scalar-only `abi = c` procs rather than failing the build. Off by default so the drop is loud; see genBindings().
2026-07-09 16:43:21 -03:00
const ffiAllowScalarSkip * {. booldefine . } = false