@effindomv2/fui-as 0.1.36 → 0.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/routed-app-conventions.ts +1 -1
- package/browser/src/common-harness.ts +1 -27
- package/browser/src/index.ts +19 -26
- package/browser/src/routed-harness.ts +1 -194
- package/browser/src/shared-browser.ts +6 -0
- package/package.json +26 -16
- package/scripts/build.sh +51 -47
- package/scripts/framework-host-services.ts +1 -1
- package/scripts/generate-host-events.ts +15 -241
- package/scripts/generate-host-services.ts +13 -261
- package/scripts/hostgen/assemblyscript-host-events.ts +165 -0
- package/scripts/hostgen/assemblyscript-host-services.ts +207 -0
- package/scripts/hostgen/common.ts +73 -0
- package/scripts/hostgen/registry.ts +159 -0
- package/src/Fui.ts +70 -4
- package/src/FuiExports.ts +13 -1
- package/src/bindings/ui.ts +165 -8
- package/src/controls/Button.ts +98 -71
- package/src/controls/Checkbox.ts +23 -21
- package/src/controls/ComboBox.ts +1201 -0
- package/src/controls/ContextMenu.ts +279 -165
- package/src/controls/Dialog.ts +91 -17
- package/src/controls/Dropdown.ts +101 -333
- package/src/controls/NavLink.ts +29 -6
- package/src/controls/Popup.ts +10 -5
- package/src/controls/ProgressBar.ts +1 -2
- package/src/controls/RadioButton.ts +34 -13
- package/src/controls/RadioGroup.ts +32 -19
- package/src/controls/Slider.ts +21 -23
- package/src/controls/Switch.ts +34 -17
- package/src/controls/index.ts +2 -1
- package/src/controls/internal/ButtonPresenter.ts +5 -2
- package/src/controls/internal/CheckboxIndicatorPresenter.ts +10 -10
- package/src/controls/internal/DropdownChevronPresenter.ts +3 -3
- package/src/controls/internal/DropdownFieldPresenter.ts +6 -6
- package/src/controls/internal/DropdownOptionRowPresenter.ts +5 -4
- package/src/controls/internal/PressableLabeledControl.ts +4 -7
- package/src/controls/internal/RadioIndicatorPresenter.ts +7 -7
- package/src/controls/internal/SelectablePopupList.ts +463 -0
- package/src/controls/internal/SliderPresenter.ts +5 -5
- package/src/controls/internal/SwitchIndicatorPresenter.ts +63 -17
- package/src/controls/internal/TextInputCore.ts +307 -41
- package/src/controls/internal/TextInputPresenter.ts +2 -2
- package/src/controls/templating.ts +10 -0
- package/src/core/Application.ts +19 -9
- package/src/core/Bitmap.ts +219 -1
- package/src/core/ContextMenuManager.ts +128 -44
- package/src/core/DebugApi.ts +17 -2
- package/src/core/DragGesture.ts +8 -7
- package/src/core/EventRouter.ts +332 -12
- package/src/core/Fetch.ts +15 -7
- package/src/core/FetchFfi.ts +1 -15
- package/src/core/File.ts +75 -34
- package/src/core/FocusAdornerManager.ts +3 -3
- package/src/core/FocusVisibility.ts +7 -0
- package/src/core/FrameScheduler.ts +126 -0
- package/src/core/ImageSampling.ts +43 -0
- package/src/core/Logger.ts +2 -0
- package/src/core/MobileTextSelectionToolbarManager.ts +901 -0
- package/src/core/Node.ts +1039 -97
- package/src/core/Platform.ts +16 -16
- package/src/core/SelectionHandleAdornerManager.ts +697 -0
- package/src/core/Theme.ts +34 -20
- package/src/core/ToolTipManager.ts +3 -3
- package/src/core/Typography.ts +411 -19
- package/src/core/Worker.ts +69 -33
- package/src/core/event_exports.ts +123 -5
- package/src/core/ffi.ts +5 -746
- package/src/core/generated/FetchHostAbi.ts +10 -0
- package/src/core/generated/FrameworkHostServices.ts +1 -1
- package/src/core/generated/HostAbi.ts +220 -0
- package/src/core/generated/UiAbi.ts +356 -0
- package/src/core/generated/UiEnums.ts +186 -0
- package/src/drawing/DrawCallback.ts +34 -0
- package/src/drawing/DrawContext.ts +189 -0
- package/src/drawing/Paint.ts +34 -0
- package/src/drawing/Path.ts +40 -0
- package/src/nodes/CustomDrawable.ts +61 -0
- package/src/nodes/FlexBox.ts +57 -11
- package/src/nodes/Grid.ts +5 -5
- package/src/nodes/Image.ts +20 -1
- package/src/nodes/RichText.ts +36 -25
- package/src/nodes/ScrollBar.ts +2 -1
- package/src/nodes/ScrollBox.ts +7 -2
- package/src/nodes/ScrollView.ts +17 -6
- package/src/nodes/Svg.ts +18 -1
- package/src/nodes/Text.ts +1 -1
- package/src/nodes/TextCore.ts +115 -20
- package/src/nodes/index.ts +3 -1
- package/src/text/DynamicTextLayout.ts +275 -0
- package/src/text/TextLayout.ts +308 -0
- package/src/worker/Worker.ts +3 -36
- package/src/worker/WorkerJob.ts +5 -9
- package/src/worker/ffi.ts +0 -6
- package/browser/src/common-harness/host-imports.ts +0 -447
- package/browser/src/common-harness/interop.ts +0 -39
- package/browser/src/common-harness/managed-harness-bitmap-host.ts +0 -92
- package/browser/src/common-harness/managed-harness-fetch-host.ts +0 -201
- package/browser/src/common-harness/managed-harness-file-host.ts +0 -1066
- package/browser/src/common-harness/managed-harness-file-payloads.ts +0 -143
- package/browser/src/common-harness/managed-harness-file-types.ts +0 -71
- package/browser/src/common-harness/managed-harness-session.ts +0 -15
- package/browser/src/common-harness/managed-harness.ts +0 -1451
- package/browser/src/common-harness/managed-history.ts +0 -168
- package/browser/src/common-harness/persisted-restore-policy.ts +0 -50
- package/browser/src/common-harness/persisted-ui-state-controller.ts +0 -309
- package/browser/src/common-harness/text-session-bridge.ts +0 -452
- package/browser/src/common-harness/types.ts +0 -205
- package/browser/src/common-harness/ui-chrome.ts +0 -206
- package/browser/src/common-harness/ui-imports.ts +0 -553
- package/browser/src/common-harness/wasm-module-cache.ts +0 -47
- package/browser/src/file-processing-worker.ts +0 -89
- package/browser/src/host-events.ts +0 -118
- package/browser/src/host-services.ts +0 -483
- package/browser/src/persisted-ui-state.ts +0 -206
- package/browser/src/routed-app-conventions.ts +0 -292
- package/browser/src/worker-bootstrap.ts +0 -916
- package/browser/src/worker-manager.ts +0 -234
- package/browser/src/worker-types.ts +0 -68
|
@@ -1,249 +1,23 @@
|
|
|
1
|
-
import { build } from "esbuild";
|
|
2
|
-
import { promises as fs } from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
1
|
import path from "node:path";
|
|
5
|
-
import {
|
|
6
|
-
import { listHostEventMethods, type NormalizedHostEventMethod } from "../browser/src/host-events";
|
|
7
|
-
import type { HostServiceTypeName } from "../browser/src/host-services";
|
|
8
|
-
|
|
9
|
-
const SCRIPT_PATH = fileURLToPath(import.meta.url);
|
|
10
|
-
const PACKAGE_DIR = path.resolve(path.dirname(SCRIPT_PATH), "..");
|
|
11
|
-
|
|
12
|
-
function toPosix(value: string): string {
|
|
13
|
-
return value.split(path.sep).join("/");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function relativeImport(fromFile: string, targetFile: string): string {
|
|
17
|
-
let relative = path.relative(path.dirname(fromFile), targetFile);
|
|
18
|
-
relative = relative.replace(/\.[^.]+$/, "");
|
|
19
|
-
if (!relative.startsWith(".")) {
|
|
20
|
-
relative = `./${relative}`;
|
|
21
|
-
}
|
|
22
|
-
return toPosix(relative);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function sourcePathForHeader(sourceModulePath: string): string {
|
|
26
|
-
const relative = path.relative(process.cwd(), sourceModulePath);
|
|
27
|
-
return toPosix(relative.startsWith(".") ? relative : `./${relative}`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function asTypeName(type: HostServiceTypeName): string {
|
|
31
|
-
switch (type) {
|
|
32
|
-
case "string":
|
|
33
|
-
return "string";
|
|
34
|
-
case "bool":
|
|
35
|
-
return "bool";
|
|
36
|
-
case "i32":
|
|
37
|
-
return "i32";
|
|
38
|
-
case "u32":
|
|
39
|
-
return "u32";
|
|
40
|
-
case "i64":
|
|
41
|
-
return "i64";
|
|
42
|
-
case "u64":
|
|
43
|
-
return "u64";
|
|
44
|
-
case "f64":
|
|
45
|
-
return "f64";
|
|
46
|
-
case "bytes":
|
|
47
|
-
return "Uint8Array";
|
|
48
|
-
case "i32_array":
|
|
49
|
-
return "Int32Array";
|
|
50
|
-
case "u32_array":
|
|
51
|
-
return "Uint32Array";
|
|
52
|
-
case "i64_array":
|
|
53
|
-
return "Int64Array";
|
|
54
|
-
case "u64_array":
|
|
55
|
-
return "Uint64Array";
|
|
56
|
-
case "f64_array":
|
|
57
|
-
return "Float64Array";
|
|
58
|
-
case "void":
|
|
59
|
-
return "void";
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function callbackTypeFor(method: NormalizedHostEventMethod): string {
|
|
64
|
-
if (method.args.length == 0) {
|
|
65
|
-
return "Callback0";
|
|
66
|
-
}
|
|
67
|
-
if (method.args.length == 1) {
|
|
68
|
-
return `Callback1<${asTypeName(method.args[0])}>`;
|
|
69
|
-
}
|
|
70
|
-
if (method.args.length == 2) {
|
|
71
|
-
return `Callback2<${asTypeName(method.args[0])}, ${asTypeName(method.args[1])}>`;
|
|
72
|
-
}
|
|
73
|
-
throw new Error(`Host event ${method.serviceName}.${method.methodName} uses ${String(method.args.length)} args; only 0-2 are supported right now.`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function emitExportArgs(method: NormalizedHostEventMethod): string {
|
|
77
|
-
const parts: Array<string> = [];
|
|
78
|
-
method.args.forEach((type, index) => {
|
|
79
|
-
if (
|
|
80
|
-
type === "string" ||
|
|
81
|
-
type === "bytes" ||
|
|
82
|
-
type === "i32_array" ||
|
|
83
|
-
type === "u32_array" ||
|
|
84
|
-
type === "i64_array" ||
|
|
85
|
-
type === "u64_array" ||
|
|
86
|
-
type === "f64_array"
|
|
87
|
-
) {
|
|
88
|
-
parts.push(`arg${String(index)}Ptr: usize`, `arg${String(index)}Len: u32`);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
parts.push(`arg${String(index)}: ${asTypeName(type)}`);
|
|
92
|
-
});
|
|
93
|
-
return parts.join(", ");
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function emitDecodedArgs(method: NormalizedHostEventMethod): Array<string> {
|
|
97
|
-
const lines: Array<string> = [];
|
|
98
|
-
method.args.forEach((type, index) => {
|
|
99
|
-
if (type !== "string") {
|
|
100
|
-
if (type === "bytes") {
|
|
101
|
-
lines.push(` const arg${String(index)} = new Uint8Array(<i32>arg${String(index)}Len);`);
|
|
102
|
-
lines.push(` if (arg${String(index)}Len > 0) {`);
|
|
103
|
-
lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len);`);
|
|
104
|
-
lines.push(" }");
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (type === "i32_array") {
|
|
108
|
-
lines.push(` const arg${String(index)} = new Int32Array(<i32>arg${String(index)}Len);`);
|
|
109
|
-
lines.push(` if (arg${String(index)}Len > 0) {`);
|
|
110
|
-
lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len << 2);`);
|
|
111
|
-
lines.push(" }");
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (type === "f64_array") {
|
|
115
|
-
lines.push(` const arg${String(index)} = new Float64Array(<i32>arg${String(index)}Len);`);
|
|
116
|
-
lines.push(` if (arg${String(index)}Len > 0) {`);
|
|
117
|
-
lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len << 3);`);
|
|
118
|
-
lines.push(" }");
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
if (type === "u32_array") {
|
|
122
|
-
lines.push(` const arg${String(index)} = new Uint32Array(<i32>arg${String(index)}Len);`);
|
|
123
|
-
lines.push(` if (arg${String(index)}Len > 0) {`);
|
|
124
|
-
lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len << 2);`);
|
|
125
|
-
lines.push(" }");
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (type === "i64_array") {
|
|
129
|
-
lines.push(` const arg${String(index)} = new Int64Array(<i32>arg${String(index)}Len);`);
|
|
130
|
-
lines.push(` if (arg${String(index)}Len > 0) {`);
|
|
131
|
-
lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len << 3);`);
|
|
132
|
-
lines.push(" }");
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
if (type === "u64_array") {
|
|
136
|
-
lines.push(` const arg${String(index)} = new Uint64Array(<i32>arg${String(index)}Len);`);
|
|
137
|
-
lines.push(` if (arg${String(index)}Len > 0) {`);
|
|
138
|
-
lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len << 3);`);
|
|
139
|
-
lines.push(" }");
|
|
140
|
-
}
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
lines.push(
|
|
144
|
-
` const arg${String(index)} = arg${String(index)}Len == 0 ? "" : String.UTF8.decodeUnsafe(arg${String(index)}Ptr, <usize>arg${String(index)}Len, false);`,
|
|
145
|
-
);
|
|
146
|
-
});
|
|
147
|
-
return lines;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function emitCallbackInvokeArgs(method: NormalizedHostEventMethod): string {
|
|
151
|
-
return method.args
|
|
152
|
-
.map((_type, index) => method.args[index] === "string" ? `arg${String(index)}` : `arg${String(index)}`)
|
|
153
|
-
.join(", ");
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function emitHandlerBlock(method: NormalizedHostEventMethod): string {
|
|
157
|
-
const eventName = method.eventName;
|
|
158
|
-
const publicName = eventName.length == 0 ? eventName : `${eventName[0].toUpperCase()}${eventName.slice(1)}`;
|
|
159
|
-
const callbackType = callbackTypeFor(method);
|
|
160
|
-
const exportArgs = emitExportArgs(method);
|
|
161
|
-
const decodedArgs = emitDecodedArgs(method);
|
|
162
|
-
const invokeArgs = emitCallbackInvokeArgs(method);
|
|
163
|
-
const directSignature = `${callbackType} | null`;
|
|
164
|
-
return [
|
|
165
|
-
`let __${eventName}Handler: ${directSignature} = null;`,
|
|
166
|
-
"",
|
|
167
|
-
`export function on${publicName}(callback: ${directSignature}): void {`,
|
|
168
|
-
` __${eventName}Handler = callback;`,
|
|
169
|
-
`}`,
|
|
170
|
-
"",
|
|
171
|
-
`export function clear${publicName}(): void {`,
|
|
172
|
-
` __${eventName}Handler = null;`,
|
|
173
|
-
`}`,
|
|
174
|
-
"",
|
|
175
|
-
`export function ${method.exportName}(${exportArgs}): void {`,
|
|
176
|
-
` const callback = __${eventName}Handler;`,
|
|
177
|
-
` if (callback === null) {`,
|
|
178
|
-
` return;`,
|
|
179
|
-
` }`,
|
|
180
|
-
...decodedArgs,
|
|
181
|
-
method.args.length == 0
|
|
182
|
-
? ` callback.invoke();`
|
|
183
|
-
: ` callback.invoke(${invokeArgs});`,
|
|
184
|
-
`}`,
|
|
185
|
-
].join("\n");
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
async function loadHostEvents(modulePath: string, exportName: string): Promise<unknown> {
|
|
189
|
-
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "fui-host-events-"));
|
|
190
|
-
const bundledFile = path.join(tempDir, "host-events.mjs");
|
|
191
|
-
try {
|
|
192
|
-
await build({
|
|
193
|
-
entryPoints: [modulePath],
|
|
194
|
-
outfile: bundledFile,
|
|
195
|
-
bundle: true,
|
|
196
|
-
format: "esm",
|
|
197
|
-
platform: "node",
|
|
198
|
-
target: "node20",
|
|
199
|
-
logLevel: "silent",
|
|
200
|
-
});
|
|
201
|
-
const loaded = await import(pathToFileURL(bundledFile).href);
|
|
202
|
-
if (!(exportName in loaded)) {
|
|
203
|
-
throw new Error(`Host-events module does not export "${exportName}".`);
|
|
204
|
-
}
|
|
205
|
-
return loaded[exportName as keyof typeof loaded];
|
|
206
|
-
} finally {
|
|
207
|
-
await fs.rm(tempDir, { recursive: true, force: true });
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function emitBindingsFile(
|
|
212
|
-
sourceModulePath: string,
|
|
213
|
-
exportName: string,
|
|
214
|
-
outputPath: string,
|
|
215
|
-
methods: ReturnType<typeof listHostEventMethods>,
|
|
216
|
-
primitivesImportOverride: string | undefined,
|
|
217
|
-
): string {
|
|
218
|
-
const callbackImport = primitivesImportOverride ?? relativeImport(outputPath, path.resolve(PACKAGE_DIR, "src/FuiPrimitives.ts"));
|
|
219
|
-
const blocks: Array<string> = [
|
|
220
|
-
"// @ts-nocheck",
|
|
221
|
-
`// Generated by scripts/generate-host-events.ts from ${sourcePathForHeader(sourceModulePath)}#${exportName}.`,
|
|
222
|
-
`import { Callback0, Callback1, Callback2 } from "${callbackImport}";`,
|
|
223
|
-
"",
|
|
224
|
-
];
|
|
225
|
-
methods.forEach((method, index) => {
|
|
226
|
-
blocks.push(emitHandlerBlock(method));
|
|
227
|
-
if (index + 1 < methods.length) {
|
|
228
|
-
blocks.push("");
|
|
229
|
-
blocks.push("");
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
return `${blocks.join("\n")}\n`;
|
|
233
|
-
}
|
|
2
|
+
import { generateAssemblyScriptHostEventsFile } from "./hostgen/assemblyscript-host-events";
|
|
234
3
|
|
|
235
4
|
async function main(): Promise<void> {
|
|
236
|
-
const
|
|
237
|
-
if (
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
if (args.length < 3) {
|
|
238
7
|
throw new Error("Usage: generate-host-events <module-path> <export-name> <output-path> [primitives-import]");
|
|
239
8
|
}
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
await
|
|
9
|
+
const [moduleArg, exportName, outputArg, primitivesImportArg] = args as [
|
|
10
|
+
string,
|
|
11
|
+
string,
|
|
12
|
+
string,
|
|
13
|
+
string | undefined,
|
|
14
|
+
];
|
|
15
|
+
await generateAssemblyScriptHostEventsFile(
|
|
16
|
+
path.resolve(process.cwd(), moduleArg),
|
|
17
|
+
exportName,
|
|
18
|
+
path.resolve(process.cwd(), outputArg),
|
|
19
|
+
primitivesImportArg,
|
|
20
|
+
);
|
|
247
21
|
}
|
|
248
22
|
|
|
249
23
|
await main();
|
|
@@ -1,275 +1,27 @@
|
|
|
1
|
-
import { build } from "esbuild";
|
|
2
|
-
import { promises as fs } from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
1
|
import path from "node:path";
|
|
5
|
-
import {
|
|
6
|
-
import { listHostServiceMethods, type HostServiceTypeName } from "../browser/src/host-services";
|
|
7
|
-
|
|
8
|
-
const SCRIPT_PATH = fileURLToPath(import.meta.url);
|
|
9
|
-
const PACKAGE_DIR = path.resolve(path.dirname(SCRIPT_PATH), "..");
|
|
10
|
-
|
|
11
|
-
function toPosix(value: string): string {
|
|
12
|
-
return value.split(path.sep).join("/");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function relativeImport(fromFile: string, targetFile: string): string {
|
|
16
|
-
let relative = path.relative(path.dirname(fromFile), targetFile);
|
|
17
|
-
relative = relative.replace(/\.[^.]+$/, "");
|
|
18
|
-
if (!relative.startsWith(".")) {
|
|
19
|
-
relative = `./${relative}`;
|
|
20
|
-
}
|
|
21
|
-
return toPosix(relative);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function sourcePathForHeader(sourceModulePath: string): string {
|
|
25
|
-
const relative = path.relative(process.cwd(), sourceModulePath);
|
|
26
|
-
return toPosix(relative.startsWith(".") ? relative : `./${relative}`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function asTypeName(type: HostServiceTypeName): string {
|
|
30
|
-
switch (type) {
|
|
31
|
-
case "string":
|
|
32
|
-
return "string";
|
|
33
|
-
case "bool":
|
|
34
|
-
return "bool";
|
|
35
|
-
case "i32":
|
|
36
|
-
return "i32";
|
|
37
|
-
case "u32":
|
|
38
|
-
return "u32";
|
|
39
|
-
case "i64":
|
|
40
|
-
return "i64";
|
|
41
|
-
case "u64":
|
|
42
|
-
return "u64";
|
|
43
|
-
case "f64":
|
|
44
|
-
return "f64";
|
|
45
|
-
case "bytes":
|
|
46
|
-
return "Uint8Array";
|
|
47
|
-
case "i32_array":
|
|
48
|
-
return "Int32Array";
|
|
49
|
-
case "u32_array":
|
|
50
|
-
return "Uint32Array";
|
|
51
|
-
case "i64_array":
|
|
52
|
-
return "Int64Array";
|
|
53
|
-
case "u64_array":
|
|
54
|
-
return "Uint64Array";
|
|
55
|
-
case "f64_array":
|
|
56
|
-
return "Float64Array";
|
|
57
|
-
case "void":
|
|
58
|
-
return "void";
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function isPointerLengthType(type: HostServiceTypeName): boolean {
|
|
63
|
-
return type === "string" ||
|
|
64
|
-
type === "bytes" ||
|
|
65
|
-
type === "i32_array" ||
|
|
66
|
-
type === "u32_array" ||
|
|
67
|
-
type === "i64_array" ||
|
|
68
|
-
type === "u64_array" ||
|
|
69
|
-
type === "f64_array";
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function returnsBufferType(type: HostServiceTypeName): boolean {
|
|
73
|
-
return type === "string" ||
|
|
74
|
-
type === "bytes" ||
|
|
75
|
-
type === "i32_array" ||
|
|
76
|
-
type === "u32_array" ||
|
|
77
|
-
type === "i64_array" ||
|
|
78
|
-
type === "u64_array" ||
|
|
79
|
-
type === "f64_array";
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function emitExternalSignature(
|
|
83
|
-
importName: string,
|
|
84
|
-
args: readonly HostServiceTypeName[],
|
|
85
|
-
returns: HostServiceTypeName,
|
|
86
|
-
moduleName: string,
|
|
87
|
-
): string {
|
|
88
|
-
const signatureParts: Array<string> = [];
|
|
89
|
-
args.forEach((type, index) => {
|
|
90
|
-
if (isPointerLengthType(type)) {
|
|
91
|
-
signatureParts.push(`arg${String(index)}Ptr: usize`, `arg${String(index)}Len: u32`);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
signatureParts.push(`arg${String(index)}: ${asTypeName(type)}`);
|
|
95
|
-
});
|
|
96
|
-
if (returnsBufferType(returns)) {
|
|
97
|
-
signatureParts.push("resultPtr: usize", "resultCap: u32");
|
|
98
|
-
}
|
|
99
|
-
const returnType = returnsBufferType(returns) ? "u32" : asTypeName(returns);
|
|
100
|
-
return [
|
|
101
|
-
`@external("${moduleName}", "${importName}")`,
|
|
102
|
-
`declare function __host_${importName}(${signatureParts.join(", ")}): ${returnType};`,
|
|
103
|
-
].join("\n");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function emitWrapper(importName: string, args: readonly HostServiceTypeName[], returns: HostServiceTypeName): string {
|
|
107
|
-
const wrapperArgs = args.map((type, index) => `arg${String(index)}: ${asTypeName(type)}`);
|
|
108
|
-
const lines: Array<string> = [];
|
|
109
|
-
args.forEach((type, index) => {
|
|
110
|
-
if (type === "string") {
|
|
111
|
-
lines.push(` const arg${String(index)}Bytes = Uint8Array.wrap(String.UTF8.encode(arg${String(index)}, false));`);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (type === "bytes" || type === "i32_array" || type === "u32_array" || type === "i64_array" || type === "u64_array" || type === "f64_array") {
|
|
115
|
-
lines.push(` const arg${String(index)}Bytes = arg${String(index)};`);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
const callArgs: Array<string> = [];
|
|
119
|
-
args.forEach((type, index) => {
|
|
120
|
-
if (type === "string") {
|
|
121
|
-
callArgs.push(`arg${String(index)}Bytes.length > 0 ? arg${String(index)}Bytes.dataStart : 0`);
|
|
122
|
-
callArgs.push(`<u32>arg${String(index)}Bytes.length`);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (type === "bytes") {
|
|
126
|
-
callArgs.push(`arg${String(index)}Bytes.length > 0 ? arg${String(index)}Bytes.dataStart : 0`);
|
|
127
|
-
callArgs.push(`<u32>arg${String(index)}Bytes.length`);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
if (type === "i32_array" || type === "u32_array" || type === "i64_array" || type === "u64_array" || type === "f64_array") {
|
|
131
|
-
callArgs.push(`arg${String(index)}Bytes.length > 0 ? arg${String(index)}Bytes.dataStart : 0`);
|
|
132
|
-
callArgs.push(`<u32>arg${String(index)}Bytes.length`);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
callArgs.push(`arg${String(index)}`);
|
|
136
|
-
});
|
|
137
|
-
if (returnsBufferType(returns)) {
|
|
138
|
-
lines.push(" const resultPtr = hostServiceResultBufferPtr();");
|
|
139
|
-
lines.push(" const resultCap = hostServiceResultBufferSize();");
|
|
140
|
-
callArgs.push("resultPtr", "resultCap");
|
|
141
|
-
lines.push(` const resultLen = __host_${importName}(${callArgs.join(", ")});`);
|
|
142
|
-
if (returns === "string") {
|
|
143
|
-
lines.push(` return decodeHostServiceStringResult(resultPtr, resultLen, "${importName}");`);
|
|
144
|
-
} else if (returns === "bytes") {
|
|
145
|
-
lines.push(` return decodeHostServiceBytesResult(resultPtr, resultLen, "${importName}");`);
|
|
146
|
-
} else if (returns === "i32_array") {
|
|
147
|
-
lines.push(` return decodeHostServiceI32ArrayResult(resultPtr, resultLen, "${importName}");`);
|
|
148
|
-
} else if (returns === "u32_array") {
|
|
149
|
-
lines.push(` return decodeHostServiceU32ArrayResult(resultPtr, resultLen, "${importName}");`);
|
|
150
|
-
} else if (returns === "i64_array") {
|
|
151
|
-
lines.push(` return decodeHostServiceI64ArrayResult(resultPtr, resultLen, "${importName}");`);
|
|
152
|
-
} else if (returns === "u64_array") {
|
|
153
|
-
lines.push(` return decodeHostServiceU64ArrayResult(resultPtr, resultLen, "${importName}");`);
|
|
154
|
-
} else {
|
|
155
|
-
lines.push(` return decodeHostServiceF64ArrayResult(resultPtr, resultLen, "${importName}");`);
|
|
156
|
-
}
|
|
157
|
-
} else if (returns === "void") {
|
|
158
|
-
lines.push(` __host_${importName}(${callArgs.join(", ")});`);
|
|
159
|
-
} else {
|
|
160
|
-
lines.push(` return __host_${importName}(${callArgs.join(", ")});`);
|
|
161
|
-
}
|
|
162
|
-
return [
|
|
163
|
-
`export function ${importName}(${wrapperArgs.join(", ")}): ${returns === "string" ? "string" : asTypeName(returns)} {`,
|
|
164
|
-
...lines,
|
|
165
|
-
"}",
|
|
166
|
-
].join("\n");
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async function loadHostServices(modulePath: string, exportName: string): Promise<unknown> {
|
|
170
|
-
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "fui-host-services-"));
|
|
171
|
-
const bundledFile = path.join(tempDir, "host-services.mjs");
|
|
172
|
-
try {
|
|
173
|
-
await build({
|
|
174
|
-
entryPoints: [modulePath],
|
|
175
|
-
outfile: bundledFile,
|
|
176
|
-
bundle: true,
|
|
177
|
-
format: "esm",
|
|
178
|
-
platform: "node",
|
|
179
|
-
target: "node20",
|
|
180
|
-
logLevel: "silent",
|
|
181
|
-
});
|
|
182
|
-
const loaded = await import(pathToFileURL(bundledFile).href);
|
|
183
|
-
if (!(exportName in loaded)) {
|
|
184
|
-
throw new Error(`Host-services module does not export "${exportName}".`);
|
|
185
|
-
}
|
|
186
|
-
return loaded[exportName as keyof typeof loaded];
|
|
187
|
-
} finally {
|
|
188
|
-
await fs.rm(tempDir, { recursive: true, force: true });
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function emitBindingsFile(
|
|
193
|
-
sourceModulePath: string,
|
|
194
|
-
exportName: string,
|
|
195
|
-
outputPath: string,
|
|
196
|
-
methods: ReturnType<typeof listHostServiceMethods>,
|
|
197
|
-
primitivesImportOverride: string | undefined,
|
|
198
|
-
moduleName: string,
|
|
199
|
-
): string {
|
|
200
|
-
const runtimeImport = primitivesImportOverride ?? relativeImport(outputPath, path.resolve(PACKAGE_DIR, "src/FuiPrimitives.ts"));
|
|
201
|
-
const blocks: Array<string> = [
|
|
202
|
-
"// @ts-nocheck",
|
|
203
|
-
`// Generated by scripts/generate-host-services.ts from ${sourcePathForHeader(sourceModulePath)}#${exportName}.`,
|
|
204
|
-
];
|
|
205
|
-
if (methods.some((method) => returnsBufferType(method.returns))) {
|
|
206
|
-
const helpers = [
|
|
207
|
-
"hostServiceResultBufferPtr",
|
|
208
|
-
"hostServiceResultBufferSize",
|
|
209
|
-
...new Set(
|
|
210
|
-
methods
|
|
211
|
-
.map((method) => method.returns)
|
|
212
|
-
.filter((type) => returnsBufferType(type))
|
|
213
|
-
.map((type) => {
|
|
214
|
-
if (type === "string") {
|
|
215
|
-
return "decodeHostServiceStringResult";
|
|
216
|
-
}
|
|
217
|
-
if (type === "bytes") {
|
|
218
|
-
return "decodeHostServiceBytesResult";
|
|
219
|
-
}
|
|
220
|
-
if (type === "i32_array") {
|
|
221
|
-
return "decodeHostServiceI32ArrayResult";
|
|
222
|
-
}
|
|
223
|
-
if (type === "u32_array") {
|
|
224
|
-
return "decodeHostServiceU32ArrayResult";
|
|
225
|
-
}
|
|
226
|
-
if (type === "i64_array") {
|
|
227
|
-
return "decodeHostServiceI64ArrayResult";
|
|
228
|
-
}
|
|
229
|
-
if (type === "u64_array") {
|
|
230
|
-
return "decodeHostServiceU64ArrayResult";
|
|
231
|
-
}
|
|
232
|
-
return "decodeHostServiceF64ArrayResult";
|
|
233
|
-
}),
|
|
234
|
-
),
|
|
235
|
-
];
|
|
236
|
-
blocks.push(`import { ${helpers.join(", ")} } from "${runtimeImport}";`);
|
|
237
|
-
blocks.push("");
|
|
238
|
-
} else {
|
|
239
|
-
blocks.push("");
|
|
240
|
-
}
|
|
241
|
-
methods.forEach((method, index) => {
|
|
242
|
-
blocks.push(emitExternalSignature(method.importName, method.args, method.returns, moduleName));
|
|
243
|
-
blocks.push("");
|
|
244
|
-
blocks.push(emitWrapper(method.importName, method.args, method.returns));
|
|
245
|
-
if (index + 1 < methods.length) {
|
|
246
|
-
blocks.push("");
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
return `${blocks.join("\n")}\n`;
|
|
250
|
-
}
|
|
2
|
+
import { generateAssemblyScriptHostServicesFile } from "./hostgen/assemblyscript-host-services";
|
|
251
3
|
|
|
252
4
|
async function main(): Promise<void> {
|
|
253
|
-
const
|
|
254
|
-
if (
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
if (args.length < 3) {
|
|
255
7
|
throw new Error(
|
|
256
8
|
"Usage: generate-host-services <module-path> <export-name> <output-path> [primitives-import] [host-import-module]",
|
|
257
9
|
);
|
|
258
10
|
}
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
11
|
+
const [moduleArg, exportName, outputArg, primitivesImportArg, hostModuleArg] = args as [
|
|
12
|
+
string,
|
|
13
|
+
string,
|
|
14
|
+
string,
|
|
15
|
+
string | undefined,
|
|
16
|
+
string | undefined,
|
|
17
|
+
];
|
|
18
|
+
await generateAssemblyScriptHostServicesFile(
|
|
19
|
+
path.resolve(process.cwd(), moduleArg),
|
|
265
20
|
exportName,
|
|
266
|
-
|
|
267
|
-
methods,
|
|
21
|
+
path.resolve(process.cwd(), outputArg),
|
|
268
22
|
primitivesImportArg,
|
|
269
23
|
hostModuleArg ?? "fui_host_service",
|
|
270
24
|
);
|
|
271
|
-
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
272
|
-
await fs.writeFile(outputPath, content, "utf8");
|
|
273
25
|
}
|
|
274
26
|
|
|
275
27
|
await main();
|