import { dlopen, DynamicLibrary, Pointer, UnsafeFnPointer, UnsafePointer, UnsafePointerView, type Callable, type CallArgs, type ForeignFunction, type JsArg, type JsResult, type NativeType, } from "avril:ffi"; // The helper types are part of the public surface. const native: NativeType = "pointer"; const def: ForeignFunction = { parameters: ["i32"], result: "i32" }; const arg: JsArg<"i32"> = 1; const result: JsResult<"i32"> = 2; const args: CallArgs<["i32"]> = [3]; const callable: Callable<{ parameters: ["i32"]; result: "i32" }> = (value) => value + 1; const library = dlopen("./native.so", { add: { parameters: ["i32", "i32"], result: "i32" }, wide: { parameters: ["u64"], result: "u64" }, address: { parameters: ["pointer"], result: "pointer" }, fill: { parameters: ["buffer", "usize"], result: "void", nonblocking: true, }, clear: { parameters: ["buffer"], result: "void" }, } as const); const sum: number = library.symbols.add(1, 2); const large: bigint = library.symbols.wide(3n); const nullable: Pointer | null = library.symbols.address(null); const asyncResult: Promise = library.symbols.fill(new Uint8Array(), 0); const cleared: undefined = library.symbols.clear(new Uint8Array()); // @ts-expect-error A 64-bit result is a bigint. const wrongWide: number = library.symbols.wide(3n); // @ts-expect-error Nonblocking calls return promises. const wrongAsync: undefined = library.symbols.fill(new Uint8Array(), 0); // @ts-expect-error A void native result is undefined. const wrongResult: number = library.symbols.clear(new Uint8Array()); // @ts-expect-error The symbols reject wrong arities. library.symbols.add(1); const byUrl = dlopen(new URL("file:///native.so"), { add: { parameters: ["i32", "i32"], result: "i32" }, }); const viaUrl: number = byUrl.symbols.add(1, 2); const address: bigint = UnsafePointer.value(UnsafePointer.create(4)[0]); const moved: Pointer = UnsafePointer.offset(UnsafePointer.create(4)[0], 1); const stable: boolean = UnsafePointer.equals(moved, moved); declare const unknownValue: unknown; if (unknownValue instanceof Pointer) { const narrowed: Pointer = unknownValue; void narrowed; } if (unknownValue instanceof DynamicLibrary) { const closed: boolean = unknownValue.closed; void closed; } if (unknownValue instanceof UnsafePointerView) { const seen: UnsafePointerView = unknownValue; void seen; } if (unknownValue instanceof UnsafeFnPointer) { const seen: UnsafeFnPointer = unknownValue; void seen; } // @ts-expect-error Pointers cannot be constructed. new Pointer(); // @ts-expect-error Libraries cannot be constructed. new DynamicLibrary(Symbol("library"), 1, {}); // The public `Pointer` is structurally empty, so a forgery passes the // checker and fails at runtime instead. UnsafePointer.value({}); const view = new UnsafePointerView(moved); const scalar: number = view.getInt32(); const fnPointer: UnsafeFnPointer<{ parameters: ["i32"]; result: "i32" }> = new UnsafeFnPointer(moved, { parameters: ["i32"], result: "i32", }); const call: number = fnPointer.call(1); // @ts-expect-error Function pointers check arity. fnPointer.call(); // @ts-expect-error The native address is not part of the public function-pointer type. const hiddenAddress: bigint = fnPointer.address; // @ts-expect-error The symbol table is readonly. library.symbols = {}; const disposable: Disposable = library; library.close(); declare const nonblocking: boolean; const selected = dlopen("./native.so", { add: { parameters: ["i32", "i32"], result: "i32", nonblocking }, }); const selectedResult: number | Promise = selected.symbols.add(1, 2); // @ts-expect-error A runtime-selected call mode cannot promise a synchronous result. const assumedSync: number = selected.symbols.add(1, 2); void sum; void large; void nullable; void asyncResult; void cleared; void viaUrl; void address; void stable; void scalar; void call; void disposable; void native; void def; void arg; void result; void args; void callable;