char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
f68be3e
main
1import { 2dlopen , 3DynamicLibrary , 4Pointer , 5UnsafeFnPointer , 6UnsafePointer , 7UnsafePointerView , 8type Callable , 9type CallArgs , 10type ForeignFunction , 11type JsArg , 12type JsResult , 13type NativeType , 14} from "avril:ffi" ; 15 16// The helper types are part of the public surface. 17const native :NativeType = "pointer" ; 18const def :ForeignFunction = { parameters :[ "i32" ], result :"i32" }; 19const arg :JsArg < "i32" > = 1 ; 20const result :JsResult < "i32" > = 2 ; 21const args :CallArgs <[ "i32" ]> = [ 3 ]; 22const callable :Callable <{ parameters :[ "i32" ]; result :"i32" }> = ( value ) => value + 1 ; 23 24const library = dlopen ( "./native.so" , { 25add :{ parameters :[ "i32" , "i32" ], result :"i32" }, 26wide :{ parameters :[ "u64" ], result :"u64" }, 27address :{ parameters :[ "pointer" ], result :"pointer" }, 28fill :{ 29parameters :[ "buffer" , "usize" ], 30result :"void" , 31nonblocking :true , 32}, 33clear :{ parameters :[ "buffer" ], result :"void" }, 34} as const ); 35 36const sum :number = library . symbols . add ( 1 , 2 ); 37const large :bigint = library . symbols . wide ( 3n ); 38const nullable :Pointer | null = library . symbols . address ( null ); 39const asyncResult :Promise < undefined > = library . symbols . fill ( new Uint8Array (), 0 ); 40const cleared :undefined = library . symbols . clear ( new Uint8Array ()); 41 42// @ts-expect-error A 64-bit result is a bigint. 43const wrongWide :number = library . symbols . wide ( 3n ); 44// @ts-expect-error Nonblocking calls return promises. 45const wrongAsync :undefined = library . symbols . fill ( new Uint8Array (), 0 ); 46// @ts-expect-error A void native result is undefined. 47const wrongResult :number = library . symbols . clear ( new Uint8Array ()); 48// @ts-expect-error The symbols reject wrong arities. 49library . symbols . add ( 1 ); 50 51const byUrl = dlopen ( new URL ( "file:///native.so" ), { 52add :{ parameters :[ "i32" , "i32" ], result :"i32" }, 53}); 54const viaUrl :number = byUrl . symbols . add ( 1 , 2 ); 55 56const address :bigint = UnsafePointer . value ( UnsafePointer . create ( 4 )[ 0 ]); 57const moved :Pointer = UnsafePointer . offset ( UnsafePointer . create ( 4 )[ 0 ], 1 ); 58const stable :boolean = UnsafePointer . equals ( moved , moved ); 59 60declare const unknownValue :unknown ; 61if ( unknownValue instanceof Pointer ) { 62const narrowed :Pointer = unknownValue ; 63void narrowed ; 64} 65if ( unknownValue instanceof DynamicLibrary ) { 66const closed :boolean = unknownValue . closed ; 67void closed ; 68} 69if ( unknownValue instanceof UnsafePointerView ) { 70const seen :UnsafePointerView = unknownValue ; 71void seen ; 72} 73if ( unknownValue instanceof UnsafeFnPointer ) { 74const seen :UnsafeFnPointer = unknownValue ; 75void seen ; 76} 77 78// @ts-expect-error Pointers cannot be constructed. 79new Pointer (); 80// @ts-expect-error Libraries cannot be constructed. 81new DynamicLibrary ( Symbol ( "library" ), 1 , {}); 82// The public `Pointer` is structurally empty, so a forgery passes the 83// checker and fails at runtime instead. 84UnsafePointer . value ({}); 85 86const view = new UnsafePointerView ( moved ); 87const scalar :number = view . getInt32 (); 88const fnPointer :UnsafeFnPointer <{ parameters :[ "i32" ]; result :"i32" }> = new UnsafeFnPointer ( moved , { 89parameters :[ "i32" ], 90result :"i32" , 91}); 92const call :number = fnPointer . call ( 1 ); 93// @ts-expect-error Function pointers check arity. 94fnPointer . call (); 95// @ts-expect-error The native address is not part of the public function-pointer type. 96const hiddenAddress :bigint = fnPointer . address ; 97// @ts-expect-error The symbol table is readonly. 98library . symbols = {}; 99const disposable :Disposable = library ; 100library . close (); 101 102declare const nonblocking :boolean ; 103const selected = dlopen ( "./native.so" , { 104add :{ parameters :[ "i32" , "i32" ], result :"i32" , nonblocking}, 105}); 106const selectedResult :number | Promise < number > = selected . symbols . add ( 1 , 2 ); 107// @ts-expect-error A runtime-selected call mode cannot promise a synchronous result. 108const assumedSync :number = selected . symbols . add ( 1 , 2 ); 109void sum ; 110void large ; 111void nullable ; 112void asyncResult ; 113void cleared ; 114void viaUrl ; 115void address ; 116void stable ; 117void scalar ; 118void call ; 119void disposable ; 120void native ; 121void def ; 122void arg ; 123void result ; 124void args ; 125void callable ;