@effindomv2/fui-as 0.1.37 → 0.1.41
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 +3 -0
- package/package.json +23 -13
- package/scripts/build.sh +70 -46
- package/scripts/check-runtime-dependency.sh +0 -19
- 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 +65 -5
- package/src/FuiExports.ts +12 -1
- package/src/bindings/ui.ts +157 -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 +17 -8
- package/src/core/Bitmap.ts +192 -3
- 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 +104 -5
- 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 -865
- 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 +17 -1
- package/src/drawing/DrawContext.ts +140 -32
- package/src/nodes/CustomDrawable.ts +8 -1
- package/src/nodes/FlexBox.ts +40 -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 +1 -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-canvas-host.ts +0 -72
- 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 -1472
- 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 -206
- package/browser/src/common-harness/ui-chrome.ts +0 -206
- package/browser/src/common-harness/ui-imports.ts +0 -559
- 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,118 +0,0 @@
|
|
|
1
|
-
import type { HostServiceTypeName } from "./host-services";
|
|
2
|
-
|
|
3
|
-
type HostEventTypeValue<T extends HostServiceTypeName> =
|
|
4
|
-
T extends "string" ? string :
|
|
5
|
-
T extends "bool" ? boolean :
|
|
6
|
-
T extends "bytes" ? Uint8Array :
|
|
7
|
-
T extends "i32_array" ? Int32Array :
|
|
8
|
-
T extends "u32_array" ? Uint32Array :
|
|
9
|
-
T extends "i64_array" ? BigInt64Array :
|
|
10
|
-
T extends "u64_array" ? BigUint64Array :
|
|
11
|
-
T extends "f64_array" ? Float64Array :
|
|
12
|
-
T extends "i64" | "u64" ? bigint :
|
|
13
|
-
T extends "void" ? void :
|
|
14
|
-
number;
|
|
15
|
-
|
|
16
|
-
type HostEventArgsValues<TArgs extends readonly HostServiceTypeName[]> = {
|
|
17
|
-
readonly [K in keyof TArgs]: HostEventTypeValue<TArgs[K] & HostServiceTypeName>;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export interface HostEventMethodDefinition<
|
|
21
|
-
TArgs extends readonly HostServiceTypeName[] = readonly HostServiceTypeName[],
|
|
22
|
-
> {
|
|
23
|
-
readonly args: TArgs;
|
|
24
|
-
readonly subscribe: (emit: (...args: HostEventArgsValues<TArgs>) => void) => (() => void) | void;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type HostEventsDefinition = Record<string, Record<string, HostEventMethodDefinition>>;
|
|
28
|
-
|
|
29
|
-
export interface NormalizedHostEventMethod {
|
|
30
|
-
readonly serviceName: string;
|
|
31
|
-
readonly methodName: string;
|
|
32
|
-
readonly eventName: string;
|
|
33
|
-
readonly exportName: string;
|
|
34
|
-
readonly args: readonly HostServiceTypeName[];
|
|
35
|
-
readonly subscribe: (emit: (...args: readonly unknown[]) => void) => (() => void) | void;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
39
|
-
|
|
40
|
-
export function hostEvent<
|
|
41
|
-
TArgs extends readonly HostServiceTypeName[],
|
|
42
|
-
>(definition: HostEventMethodDefinition<TArgs>): HostEventMethodDefinition<TArgs> {
|
|
43
|
-
return definition;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function defineHostEvents<TEvents extends HostEventsDefinition>(events: TEvents): TEvents {
|
|
47
|
-
return events;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function assertIdentifier(value: string, context: string): void {
|
|
51
|
-
if (!IDENTIFIER_RE.test(value)) {
|
|
52
|
-
throw new Error(`${context} "${value}" must be a valid identifier.`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function capitalize(value: string): string {
|
|
57
|
-
return value.length == 0 ? value : `${value[0].toUpperCase()}${value.slice(1)}`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function buildEventName(serviceName: string, methodName: string): string {
|
|
61
|
-
return `${serviceName}${capitalize(methodName)}`;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function buildExportName(eventName: string): string {
|
|
65
|
-
return `__fui_host_event_${eventName}`;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function validateEventType(type: string, context: string): asserts type is HostServiceTypeName {
|
|
69
|
-
if (
|
|
70
|
-
type === "string" ||
|
|
71
|
-
type === "bool" ||
|
|
72
|
-
type === "i32" ||
|
|
73
|
-
type === "u32" ||
|
|
74
|
-
type === "i64" ||
|
|
75
|
-
type === "u64" ||
|
|
76
|
-
type === "f64" ||
|
|
77
|
-
type === "bytes" ||
|
|
78
|
-
type === "i32_array" ||
|
|
79
|
-
type === "u32_array" ||
|
|
80
|
-
type === "i64_array" ||
|
|
81
|
-
type === "u64_array" ||
|
|
82
|
-
type === "f64_array"
|
|
83
|
-
) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
throw new Error(`${context} uses unsupported host-event type "${type}".`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function listHostEventMethods(events: HostEventsDefinition | undefined): ReadonlyArray<NormalizedHostEventMethod> {
|
|
90
|
-
if (events === undefined) {
|
|
91
|
-
return [];
|
|
92
|
-
}
|
|
93
|
-
const methods: Array<NormalizedHostEventMethod> = [];
|
|
94
|
-
const seenEvents = new Set<string>();
|
|
95
|
-
for (const [serviceName, serviceMethods] of Object.entries(events)) {
|
|
96
|
-
assertIdentifier(serviceName, "Host event service");
|
|
97
|
-
for (const [methodName, definition] of Object.entries(serviceMethods)) {
|
|
98
|
-
assertIdentifier(methodName, `Host event ${serviceName} method`);
|
|
99
|
-
const eventName = buildEventName(serviceName, methodName);
|
|
100
|
-
if (seenEvents.has(eventName)) {
|
|
101
|
-
throw new Error(`Duplicate host-event name "${eventName}".`);
|
|
102
|
-
}
|
|
103
|
-
seenEvents.add(eventName);
|
|
104
|
-
const args = [...definition.args];
|
|
105
|
-
args.forEach((type, index) => validateEventType(type, `Host event ${serviceName}.${methodName} arg ${String(index)}`));
|
|
106
|
-
methods.push({
|
|
107
|
-
serviceName,
|
|
108
|
-
methodName,
|
|
109
|
-
eventName,
|
|
110
|
-
exportName: buildExportName(eventName),
|
|
111
|
-
args,
|
|
112
|
-
subscribe: definition.subscribe as (emit: (...args: readonly unknown[]) => void) => (() => void) | void,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
methods.sort((left, right) => left.eventName.localeCompare(right.eventName));
|
|
117
|
-
return methods;
|
|
118
|
-
}
|
|
@@ -1,483 +0,0 @@
|
|
|
1
|
-
export type HostServiceTypeName =
|
|
2
|
-
| "string"
|
|
3
|
-
| "bool"
|
|
4
|
-
| "i32"
|
|
5
|
-
| "u32"
|
|
6
|
-
| "i64"
|
|
7
|
-
| "u64"
|
|
8
|
-
| "f64"
|
|
9
|
-
| "bytes"
|
|
10
|
-
| "i32_array"
|
|
11
|
-
| "u32_array"
|
|
12
|
-
| "i64_array"
|
|
13
|
-
| "u64_array"
|
|
14
|
-
| "f64_array"
|
|
15
|
-
| "void";
|
|
16
|
-
|
|
17
|
-
type HostServiceTypeValue<T extends HostServiceTypeName> =
|
|
18
|
-
T extends "string" ? string :
|
|
19
|
-
T extends "bool" ? boolean :
|
|
20
|
-
T extends "bytes" ? Uint8Array :
|
|
21
|
-
T extends "i32_array" ? Int32Array :
|
|
22
|
-
T extends "u32_array" ? Uint32Array :
|
|
23
|
-
T extends "i64_array" ? BigInt64Array :
|
|
24
|
-
T extends "u64_array" ? BigUint64Array :
|
|
25
|
-
T extends "f64_array" ? Float64Array :
|
|
26
|
-
T extends "i64" | "u64" ? bigint :
|
|
27
|
-
T extends "void" ? void :
|
|
28
|
-
number;
|
|
29
|
-
|
|
30
|
-
type HostServiceArgsValues<TArgs extends readonly HostServiceTypeName[]> = {
|
|
31
|
-
readonly [K in keyof TArgs]: HostServiceTypeValue<TArgs[K] & HostServiceTypeName>;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export interface HostServiceMethodDefinition<
|
|
35
|
-
TArgs extends readonly HostServiceTypeName[] = readonly HostServiceTypeName[],
|
|
36
|
-
TResult extends HostServiceTypeName = HostServiceTypeName,
|
|
37
|
-
> {
|
|
38
|
-
readonly importName?: string;
|
|
39
|
-
readonly args: TArgs;
|
|
40
|
-
readonly returns: TResult;
|
|
41
|
-
readonly implementation: (...args: HostServiceArgsValues<TArgs>) => HostServiceTypeValue<TResult>;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export type HostServicesDefinition = Record<string, Record<string, HostServiceMethodDefinition>>;
|
|
45
|
-
|
|
46
|
-
export interface NormalizedHostServiceMethod {
|
|
47
|
-
readonly serviceName: string;
|
|
48
|
-
readonly methodName: string;
|
|
49
|
-
readonly importName: string;
|
|
50
|
-
readonly args: readonly HostServiceTypeName[];
|
|
51
|
-
readonly returns: HostServiceTypeName;
|
|
52
|
-
readonly implementation: (...args: readonly unknown[]) => unknown;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface HostServiceImportIo {
|
|
56
|
-
readString(ptr: number, len: number): string;
|
|
57
|
-
writeString(ptr: number, capacity: number, text: string, context: string): number;
|
|
58
|
-
readBytes(ptr: number, len: number): Uint8Array;
|
|
59
|
-
writeBytes(ptr: number, capacity: number, bytes: Uint8Array, context: string): number;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
63
|
-
|
|
64
|
-
export function hostService<
|
|
65
|
-
TArgs extends readonly HostServiceTypeName[],
|
|
66
|
-
TResult extends HostServiceTypeName,
|
|
67
|
-
>(definition: HostServiceMethodDefinition<TArgs, TResult>): HostServiceMethodDefinition<TArgs, TResult> {
|
|
68
|
-
return definition;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function defineHostServices<TServices extends HostServicesDefinition>(services: TServices): TServices {
|
|
72
|
-
return services;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function assertIdentifier(value: string, context: string): void {
|
|
76
|
-
if (!IDENTIFIER_RE.test(value)) {
|
|
77
|
-
throw new Error(`${context} "${value}" must be a valid identifier.`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function capitalize(value: string): string {
|
|
82
|
-
return value.length == 0 ? value : `${value[0].toUpperCase()}${value.slice(1)}`;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function buildImportName(serviceName: string, methodName: string): string {
|
|
86
|
-
return `${serviceName}${capitalize(methodName)}`;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function validateServiceType(type: string, context: string): asserts type is HostServiceTypeName {
|
|
90
|
-
if (
|
|
91
|
-
type === "string" ||
|
|
92
|
-
type === "bool" ||
|
|
93
|
-
type === "i32" ||
|
|
94
|
-
type === "u32" ||
|
|
95
|
-
type === "i64" ||
|
|
96
|
-
type === "u64" ||
|
|
97
|
-
type === "f64" ||
|
|
98
|
-
type === "bytes" ||
|
|
99
|
-
type === "i32_array" ||
|
|
100
|
-
type === "u32_array" ||
|
|
101
|
-
type === "i64_array" ||
|
|
102
|
-
type === "u64_array" ||
|
|
103
|
-
type === "f64_array" ||
|
|
104
|
-
type === "void"
|
|
105
|
-
) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
throw new Error(`${context} uses unsupported host-service type "${type}".`);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function listHostServiceMethods(services: HostServicesDefinition | undefined): ReadonlyArray<NormalizedHostServiceMethod> {
|
|
112
|
-
if (services === undefined) {
|
|
113
|
-
return [];
|
|
114
|
-
}
|
|
115
|
-
const methods: Array<NormalizedHostServiceMethod> = [];
|
|
116
|
-
const seenImports = new Set<string>();
|
|
117
|
-
for (const [serviceName, serviceMethods] of Object.entries(services)) {
|
|
118
|
-
assertIdentifier(serviceName, "Host service");
|
|
119
|
-
for (const [methodName, definition] of Object.entries(serviceMethods)) {
|
|
120
|
-
assertIdentifier(methodName, `Host service ${serviceName} method`);
|
|
121
|
-
const importName = definition.importName ?? buildImportName(serviceName, methodName);
|
|
122
|
-
assertIdentifier(importName, `Host service ${serviceName}.${methodName} import`);
|
|
123
|
-
if (seenImports.has(importName)) {
|
|
124
|
-
throw new Error(`Duplicate host-service import name "${importName}".`);
|
|
125
|
-
}
|
|
126
|
-
seenImports.add(importName);
|
|
127
|
-
const args = [...definition.args];
|
|
128
|
-
args.forEach((type, index) => validateServiceType(type, `Host service ${serviceName}.${methodName} arg ${String(index)}`));
|
|
129
|
-
validateServiceType(definition.returns, `Host service ${serviceName}.${methodName} return`);
|
|
130
|
-
methods.push({
|
|
131
|
-
serviceName,
|
|
132
|
-
methodName,
|
|
133
|
-
importName,
|
|
134
|
-
args,
|
|
135
|
-
returns: definition.returns,
|
|
136
|
-
implementation: definition.implementation as (...args: readonly unknown[]) => unknown,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
methods.sort((left, right) => left.importName.localeCompare(right.importName));
|
|
141
|
-
return methods;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function getHostServiceImportNames(services: HostServicesDefinition | undefined): ReadonlySet<string> {
|
|
145
|
-
return new Set(listHostServiceMethods(services).map((method) => method.importName));
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function expectNumber(value: unknown, context: string): number {
|
|
149
|
-
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
150
|
-
throw new Error(`${context} must be a number.`);
|
|
151
|
-
}
|
|
152
|
-
return value;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function expectBoolean(value: unknown, context: string): boolean {
|
|
156
|
-
if (typeof value !== "boolean") {
|
|
157
|
-
throw new Error(`${context} must be a boolean.`);
|
|
158
|
-
}
|
|
159
|
-
return value;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function expectString(value: unknown, context: string): string {
|
|
163
|
-
if (typeof value !== "string") {
|
|
164
|
-
throw new Error(`${context} must be a string.`);
|
|
165
|
-
}
|
|
166
|
-
return value;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function expectBytes(value: unknown, context: string): Uint8Array {
|
|
170
|
-
if (!(value instanceof Uint8Array)) {
|
|
171
|
-
throw new Error(`${context} must be a Uint8Array.`);
|
|
172
|
-
}
|
|
173
|
-
return value;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function expectInt32Array(value: unknown, context: string): Int32Array {
|
|
177
|
-
if (!(value instanceof Int32Array)) {
|
|
178
|
-
throw new Error(`${context} must be an Int32Array.`);
|
|
179
|
-
}
|
|
180
|
-
return value;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function expectFloat64Array(value: unknown, context: string): Float64Array {
|
|
184
|
-
if (!(value instanceof Float64Array)) {
|
|
185
|
-
throw new Error(`${context} must be a Float64Array.`);
|
|
186
|
-
}
|
|
187
|
-
return value;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function expectBigInt64Array(value: unknown, context: string): BigInt64Array {
|
|
191
|
-
if (!(value instanceof BigInt64Array)) {
|
|
192
|
-
throw new Error(`${context} must be a BigInt64Array.`);
|
|
193
|
-
}
|
|
194
|
-
return value;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function expectBigUint64Array(value: unknown, context: string): BigUint64Array {
|
|
198
|
-
if (!(value instanceof BigUint64Array)) {
|
|
199
|
-
throw new Error(`${context} must be a BigUint64Array.`);
|
|
200
|
-
}
|
|
201
|
-
return value;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function expectUint32Array(value: unknown, context: string): Uint32Array {
|
|
205
|
-
if (!(value instanceof Uint32Array)) {
|
|
206
|
-
throw new Error(`${context} must be a Uint32Array.`);
|
|
207
|
-
}
|
|
208
|
-
return value;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function expectI32(value: unknown, context: string): number {
|
|
212
|
-
const numberValue = expectNumber(value, context);
|
|
213
|
-
if (!Number.isInteger(numberValue) || numberValue < -2147483648 || numberValue > 2147483647) {
|
|
214
|
-
throw new Error(`${context} must be a signed 32-bit integer.`);
|
|
215
|
-
}
|
|
216
|
-
return numberValue;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function expectU32(value: unknown, context: string): number {
|
|
220
|
-
const numberValue = expectNumber(value, context);
|
|
221
|
-
if (!Number.isInteger(numberValue) || numberValue < 0 || numberValue > 4294967295) {
|
|
222
|
-
throw new Error(`${context} must be an unsigned 32-bit integer.`);
|
|
223
|
-
}
|
|
224
|
-
return numberValue;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function expectI64(value: unknown, context: string): bigint {
|
|
228
|
-
if (typeof value !== "bigint") {
|
|
229
|
-
throw new Error(`${context} must be a bigint.`);
|
|
230
|
-
}
|
|
231
|
-
if (value < -9223372036854775808n || value > 9223372036854775807n) {
|
|
232
|
-
throw new Error(`${context} must be a signed 64-bit integer.`);
|
|
233
|
-
}
|
|
234
|
-
return value;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function expectU64(value: unknown, context: string): bigint {
|
|
238
|
-
if (typeof value !== "bigint") {
|
|
239
|
-
throw new Error(`${context} must be a bigint.`);
|
|
240
|
-
}
|
|
241
|
-
if (value < 0n || value > 18446744073709551615n) {
|
|
242
|
-
throw new Error(`${context} must be an unsigned 64-bit integer.`);
|
|
243
|
-
}
|
|
244
|
-
return value;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function expectLength(value: unknown, context: string): number {
|
|
248
|
-
const length = expectNumber(value, context);
|
|
249
|
-
if (!Number.isInteger(length) || length < 0) {
|
|
250
|
-
throw new Error(`${context} must be a non-negative integer.`);
|
|
251
|
-
}
|
|
252
|
-
return length;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function bytesToI32Array(bytes: Uint8Array, context: string): Int32Array {
|
|
256
|
-
if ((bytes.byteLength & 3) !== 0) {
|
|
257
|
-
throw new Error(`${context} payload length must be divisible by 4.`);
|
|
258
|
-
}
|
|
259
|
-
const values = new Int32Array(bytes.byteLength >>> 2);
|
|
260
|
-
new Uint8Array(values.buffer).set(bytes);
|
|
261
|
-
return values;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function bytesToU32Array(bytes: Uint8Array, context: string): Uint32Array {
|
|
265
|
-
if ((bytes.byteLength & 3) !== 0) {
|
|
266
|
-
throw new Error(`${context} payload length must be divisible by 4.`);
|
|
267
|
-
}
|
|
268
|
-
const values = new Uint32Array(bytes.byteLength >>> 2);
|
|
269
|
-
new Uint8Array(values.buffer).set(bytes);
|
|
270
|
-
return values;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function bytesToF64Array(bytes: Uint8Array, context: string): Float64Array {
|
|
274
|
-
if ((bytes.byteLength & 7) !== 0) {
|
|
275
|
-
throw new Error(`${context} payload length must be divisible by 8.`);
|
|
276
|
-
}
|
|
277
|
-
const values = new Float64Array(bytes.byteLength >>> 3);
|
|
278
|
-
new Uint8Array(values.buffer).set(bytes);
|
|
279
|
-
return values;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function bytesToI64Array(bytes: Uint8Array, context: string): BigInt64Array {
|
|
283
|
-
if ((bytes.byteLength & 7) !== 0) {
|
|
284
|
-
throw new Error(`${context} payload length must be divisible by 8.`);
|
|
285
|
-
}
|
|
286
|
-
const values = new BigInt64Array(bytes.byteLength >>> 3);
|
|
287
|
-
new Uint8Array(values.buffer).set(bytes);
|
|
288
|
-
return values;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function bytesToU64Array(bytes: Uint8Array, context: string): BigUint64Array {
|
|
292
|
-
if ((bytes.byteLength & 7) !== 0) {
|
|
293
|
-
throw new Error(`${context} payload length must be divisible by 8.`);
|
|
294
|
-
}
|
|
295
|
-
const values = new BigUint64Array(bytes.byteLength >>> 3);
|
|
296
|
-
new Uint8Array(values.buffer).set(bytes);
|
|
297
|
-
return values;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function typedArrayBytes(
|
|
301
|
-
value: Uint8Array | Int32Array | Uint32Array | BigInt64Array | BigUint64Array | Float64Array,
|
|
302
|
-
): Uint8Array {
|
|
303
|
-
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function consumedRawArgCount(method: NormalizedHostServiceMethod): number {
|
|
307
|
-
let count = 0;
|
|
308
|
-
method.args.forEach((type) => {
|
|
309
|
-
count += type === "string" ||
|
|
310
|
-
type === "bytes" ||
|
|
311
|
-
type === "i32_array" ||
|
|
312
|
-
type === "u32_array" ||
|
|
313
|
-
type === "i64_array" ||
|
|
314
|
-
type === "u64_array" ||
|
|
315
|
-
type === "f64_array"
|
|
316
|
-
? 2
|
|
317
|
-
: 1;
|
|
318
|
-
});
|
|
319
|
-
return count;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function decodeHostServiceArgs(
|
|
323
|
-
method: NormalizedHostServiceMethod,
|
|
324
|
-
rawArgs: readonly unknown[],
|
|
325
|
-
io: HostServiceImportIo,
|
|
326
|
-
): ReadonlyArray<unknown> {
|
|
327
|
-
const decodedArgs: Array<unknown> = [];
|
|
328
|
-
let index = 0;
|
|
329
|
-
method.args.forEach((type, argIndex) => {
|
|
330
|
-
const context = `Host service ${method.serviceName}.${method.methodName} arg ${String(argIndex)}`;
|
|
331
|
-
if (type === "string") {
|
|
332
|
-
const ptr = expectNumber(rawArgs[index], `${context} ptr`);
|
|
333
|
-
const len = expectNumber(rawArgs[index + 1], `${context} len`);
|
|
334
|
-
decodedArgs.push(len <= 0 ? "" : io.readString(ptr, len));
|
|
335
|
-
index += 2;
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
if (type === "bytes") {
|
|
339
|
-
const ptr = expectNumber(rawArgs[index], `${context} ptr`);
|
|
340
|
-
const len = expectLength(rawArgs[index + 1], `${context} len`);
|
|
341
|
-
decodedArgs.push(len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len));
|
|
342
|
-
index += 2;
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
if (type === "i32_array") {
|
|
346
|
-
const ptr = expectNumber(rawArgs[index], `${context} ptr`);
|
|
347
|
-
const len = expectLength(rawArgs[index + 1], `${context} len`);
|
|
348
|
-
const payload = len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len << 2);
|
|
349
|
-
decodedArgs.push(bytesToI32Array(payload, context));
|
|
350
|
-
index += 2;
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
if (type === "u32_array") {
|
|
354
|
-
const ptr = expectNumber(rawArgs[index], `${context} ptr`);
|
|
355
|
-
const len = expectLength(rawArgs[index + 1], `${context} len`);
|
|
356
|
-
const payload = len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len << 2);
|
|
357
|
-
decodedArgs.push(bytesToU32Array(payload, context));
|
|
358
|
-
index += 2;
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
if (type === "f64_array") {
|
|
362
|
-
const ptr = expectNumber(rawArgs[index], `${context} ptr`);
|
|
363
|
-
const len = expectLength(rawArgs[index + 1], `${context} len`);
|
|
364
|
-
const payload = len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len << 3);
|
|
365
|
-
decodedArgs.push(bytesToF64Array(payload, context));
|
|
366
|
-
index += 2;
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
if (type === "i64_array") {
|
|
370
|
-
const ptr = expectNumber(rawArgs[index], `${context} ptr`);
|
|
371
|
-
const len = expectLength(rawArgs[index + 1], `${context} len`);
|
|
372
|
-
const payload = len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len << 3);
|
|
373
|
-
decodedArgs.push(bytesToI64Array(payload, context));
|
|
374
|
-
index += 2;
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
if (type === "u64_array") {
|
|
378
|
-
const ptr = expectNumber(rawArgs[index], `${context} ptr`);
|
|
379
|
-
const len = expectLength(rawArgs[index + 1], `${context} len`);
|
|
380
|
-
const payload = len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len << 3);
|
|
381
|
-
decodedArgs.push(bytesToU64Array(payload, context));
|
|
382
|
-
index += 2;
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
const rawValue = rawArgs[index];
|
|
386
|
-
if (type === "bool") {
|
|
387
|
-
decodedArgs.push(expectNumber(rawValue, context) !== 0);
|
|
388
|
-
} else if (type === "i32") {
|
|
389
|
-
decodedArgs.push(expectI32(rawValue, context));
|
|
390
|
-
} else if (type === "u32") {
|
|
391
|
-
decodedArgs.push(expectU32(rawValue, context));
|
|
392
|
-
} else if (type === "i64") {
|
|
393
|
-
decodedArgs.push(expectI64(rawValue, context));
|
|
394
|
-
} else if (type === "u64") {
|
|
395
|
-
decodedArgs.push(expectU64(rawValue, context));
|
|
396
|
-
} else if (type === "f64") {
|
|
397
|
-
decodedArgs.push(expectNumber(rawValue, context));
|
|
398
|
-
} else {
|
|
399
|
-
throw new Error(`${context} uses unsupported type ${type}.`);
|
|
400
|
-
}
|
|
401
|
-
index += 1;
|
|
402
|
-
});
|
|
403
|
-
return decodedArgs;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
export function createHostServiceImportModule(
|
|
407
|
-
services: HostServicesDefinition | undefined,
|
|
408
|
-
io: HostServiceImportIo,
|
|
409
|
-
): Record<string, (...rawArgs: Array<unknown>) => number | bigint | void> {
|
|
410
|
-
const module: Record<string, (...rawArgs: Array<unknown>) => number | bigint | void> = {};
|
|
411
|
-
for (const method of listHostServiceMethods(services)) {
|
|
412
|
-
module[method.importName] = (...rawArgs: Array<unknown>): number | bigint | void => {
|
|
413
|
-
const decodedArgs = decodeHostServiceArgs(method, rawArgs, io);
|
|
414
|
-
const result = method.implementation(...decodedArgs);
|
|
415
|
-
const resultContext = `Host service ${method.serviceName}.${method.methodName} result`;
|
|
416
|
-
if (method.returns === "void") {
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
if (method.returns === "string") {
|
|
420
|
-
const outputIndex = consumedRawArgCount(method);
|
|
421
|
-
const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
|
|
422
|
-
const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
|
|
423
|
-
return io.writeString(ptr, capacity, expectString(result, resultContext), resultContext);
|
|
424
|
-
}
|
|
425
|
-
if (method.returns === "bytes") {
|
|
426
|
-
const outputIndex = consumedRawArgCount(method);
|
|
427
|
-
const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
|
|
428
|
-
const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
|
|
429
|
-
return io.writeBytes(ptr, capacity, expectBytes(result, resultContext), resultContext);
|
|
430
|
-
}
|
|
431
|
-
if (method.returns === "i32_array") {
|
|
432
|
-
const outputIndex = consumedRawArgCount(method);
|
|
433
|
-
const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
|
|
434
|
-
const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
|
|
435
|
-
return io.writeBytes(ptr, capacity, typedArrayBytes(expectInt32Array(result, resultContext)), resultContext);
|
|
436
|
-
}
|
|
437
|
-
if (method.returns === "u32_array") {
|
|
438
|
-
const outputIndex = consumedRawArgCount(method);
|
|
439
|
-
const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
|
|
440
|
-
const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
|
|
441
|
-
return io.writeBytes(ptr, capacity, typedArrayBytes(expectUint32Array(result, resultContext)), resultContext);
|
|
442
|
-
}
|
|
443
|
-
if (method.returns === "f64_array") {
|
|
444
|
-
const outputIndex = consumedRawArgCount(method);
|
|
445
|
-
const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
|
|
446
|
-
const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
|
|
447
|
-
return io.writeBytes(ptr, capacity, typedArrayBytes(expectFloat64Array(result, resultContext)), resultContext);
|
|
448
|
-
}
|
|
449
|
-
if (method.returns === "i64_array") {
|
|
450
|
-
const outputIndex = consumedRawArgCount(method);
|
|
451
|
-
const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
|
|
452
|
-
const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
|
|
453
|
-
return io.writeBytes(ptr, capacity, typedArrayBytes(expectBigInt64Array(result, resultContext)), resultContext);
|
|
454
|
-
}
|
|
455
|
-
if (method.returns === "u64_array") {
|
|
456
|
-
const outputIndex = consumedRawArgCount(method);
|
|
457
|
-
const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
|
|
458
|
-
const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
|
|
459
|
-
return io.writeBytes(ptr, capacity, typedArrayBytes(expectBigUint64Array(result, resultContext)), resultContext);
|
|
460
|
-
}
|
|
461
|
-
if (method.returns === "bool") {
|
|
462
|
-
return expectBoolean(result, resultContext) ? 1 : 0;
|
|
463
|
-
}
|
|
464
|
-
if (method.returns === "i32") {
|
|
465
|
-
return expectI32(result, resultContext);
|
|
466
|
-
}
|
|
467
|
-
if (method.returns === "u32") {
|
|
468
|
-
return expectU32(result, resultContext);
|
|
469
|
-
}
|
|
470
|
-
if (method.returns === "i64") {
|
|
471
|
-
return expectI64(result, resultContext);
|
|
472
|
-
}
|
|
473
|
-
if (method.returns === "u64") {
|
|
474
|
-
return expectU64(result, resultContext);
|
|
475
|
-
}
|
|
476
|
-
if (method.returns === "f64") {
|
|
477
|
-
return expectNumber(result, resultContext);
|
|
478
|
-
}
|
|
479
|
-
throw new Error(`${resultContext} uses unsupported type ${method.returns}.`);
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
return module;
|
|
483
|
-
}
|