char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somgenerate ffi typedefs from typescript implf68be3e

main
3.9 KiB125 linesraw
1import {
2  dlopen,
3  DynamicLibrary,
4  Pointer,
5  UnsafeFnPointer,
6  UnsafePointer,
7  UnsafePointerView,
8  type Callable,
9  type CallArgs,
10  type ForeignFunction,
11  type JsArg,
12  type JsResult,
13  type 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", {
25  add: { parameters: ["i32", "i32"], result: "i32" },
26  wide: { parameters: ["u64"], result: "u64" },
27  address: { parameters: ["pointer"], result: "pointer" },
28  fill: {
29    parameters: ["buffer", "usize"],
30    result: "void",
31    nonblocking: true,
32  },
33  clear: { 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"), {
52  add: { 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) {
62  const narrowed: Pointer = unknownValue;
63  void narrowed;
64}
65if (unknownValue instanceof DynamicLibrary) {
66  const closed: boolean = unknownValue.closed;
67  void closed;
68}
69if (unknownValue instanceof UnsafePointerView) {
70  const seen: UnsafePointerView = unknownValue;
71  void seen;
72}
73if (unknownValue instanceof UnsafeFnPointer) {
74  const seen: UnsafeFnPointer = unknownValue;
75  void 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, {
89  parameters: ["i32"],
90  result: "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", {
104  add: { 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;