@polyengine/runtime 0.1.0-pre.g633468a
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/LICENSE +202 -0
- package/README.md +23 -0
- package/esm/cabi/async_values.js +162 -0
- package/esm/cabi/bulk_lists.js +198 -0
- package/esm/cabi/context.js +42 -0
- package/esm/cabi/flatten.js +145 -0
- package/esm/cabi/float.js +67 -0
- package/esm/cabi/handles.js +253 -0
- package/esm/cabi/layout.js +149 -0
- package/esm/cabi/lift.js +196 -0
- package/esm/cabi/load.js +146 -0
- package/esm/cabi/lower.js +141 -0
- package/esm/cabi/memory.js +182 -0
- package/esm/cabi/mod.js +22 -0
- package/esm/cabi/store.js +186 -0
- package/esm/cabi/strings.js +336 -0
- package/esm/cabi/trap.js +38 -0
- package/esm/cabi/types.js +264 -0
- package/esm/cabi/values.js +64 -0
- package/esm/cache/core.js +156 -0
- package/esm/cache/dir.js +170 -0
- package/esm/cache/mod.js +4 -0
- package/esm/cache/web.js +136 -0
- package/esm/digest/digest.js +332 -0
- package/esm/digest/mod.js +3 -0
- package/esm/digest/verify.js +129 -0
- package/esm/embedder/casing.js +56 -0
- package/esm/embedder/copy.js +42 -0
- package/esm/embedder/errors.js +26 -0
- package/esm/embedder/imports.js +63 -0
- package/esm/embedder/instantiate.js +978 -0
- package/esm/embedder/mod.js +40 -0
- package/esm/embedder/resources.js +406 -0
- package/esm/embedder/streams.js +770 -0
- package/esm/embedder/values.js +450 -0
- package/esm/embedder/version.js +273 -0
- package/esm/exec/boundary.js +1902 -0
- package/esm/exec/executor.js +1004 -0
- package/esm/exec/host_streams.js +818 -0
- package/esm/exec/mod.js +4 -0
- package/esm/intrinsics/async_builtins.js +510 -0
- package/esm/intrinsics/context.js +90 -0
- package/esm/intrinsics/errors.js +13 -0
- package/esm/intrinsics/fact_calls.js +865 -0
- package/esm/intrinsics/mod.js +564 -0
- package/esm/intrinsics/stream_builtins.js +578 -0
- package/esm/intrinsics/transcode.js +443 -0
- package/esm/jspi/bridge.js +579 -0
- package/esm/jspi/mechanics.js +89 -0
- package/esm/jspi/mod.js +5 -0
- package/esm/jspi/suspending.js +15 -0
- package/esm/jspi/types.js +29 -0
- package/esm/package.json +3 -0
- package/esm/plan/format.js +5 -0
- package/esm/plan/loader.js +657 -0
- package/esm/plan/mod.js +3 -0
- package/esm/shim/mod.js +2 -0
- package/esm/shim/translator.js +94 -0
- package/esm/task/mod.js +483 -0
- package/esm/task/scheduler.js +1028 -0
- package/esm/task/streams.js +786 -0
- package/esm/task/subtask.js +135 -0
- package/esm/task/thread.js +255 -0
- package/esm/task/waitable.js +144 -0
- package/package.json +91 -0
- package/types/cabi/async_values.d.ts +35 -0
- package/types/cabi/bulk_lists.d.ts +18 -0
- package/types/cabi/context.d.ts +59 -0
- package/types/cabi/flatten.d.ts +14 -0
- package/types/cabi/float.d.ts +14 -0
- package/types/cabi/handles.d.ts +70 -0
- package/types/cabi/layout.d.ts +13 -0
- package/types/cabi/lift.d.ts +25 -0
- package/types/cabi/load.d.ts +14 -0
- package/types/cabi/lower.d.ts +10 -0
- package/types/cabi/memory.d.ts +58 -0
- package/types/cabi/mod.d.ts +15 -0
- package/types/cabi/store.d.ts +12 -0
- package/types/cabi/strings.d.ts +23 -0
- package/types/cabi/trap.d.ts +11 -0
- package/types/cabi/types.d.ts +206 -0
- package/types/cabi/values.d.ts +5 -0
- package/types/cache/core.d.ts +97 -0
- package/types/cache/dir.d.ts +6 -0
- package/types/cache/mod.d.ts +3 -0
- package/types/cache/web.d.ts +10 -0
- package/types/digest/digest.d.ts +17 -0
- package/types/digest/mod.d.ts +2 -0
- package/types/digest/verify.d.ts +48 -0
- package/types/embedder/casing.d.ts +40 -0
- package/types/embedder/copy.d.ts +24 -0
- package/types/embedder/errors.d.ts +11 -0
- package/types/embedder/imports.d.ts +47 -0
- package/types/embedder/instantiate.d.ts +88 -0
- package/types/embedder/mod.d.ts +11 -0
- package/types/embedder/resources.d.ts +158 -0
- package/types/embedder/streams.d.ts +202 -0
- package/types/embedder/values.d.ts +70 -0
- package/types/embedder/version.d.ts +85 -0
- package/types/exec/boundary.d.ts +360 -0
- package/types/exec/executor.d.ts +125 -0
- package/types/exec/host_streams.d.ts +165 -0
- package/types/exec/mod.d.ts +3 -0
- package/types/intrinsics/async_builtins.d.ts +69 -0
- package/types/intrinsics/context.d.ts +28 -0
- package/types/intrinsics/errors.d.ts +5 -0
- package/types/intrinsics/fact_calls.d.ts +120 -0
- package/types/intrinsics/mod.d.ts +187 -0
- package/types/intrinsics/stream_builtins.d.ts +113 -0
- package/types/intrinsics/transcode.d.ts +21 -0
- package/types/jspi/bridge.d.ts +227 -0
- package/types/jspi/mechanics.d.ts +50 -0
- package/types/jspi/mod.d.ts +3 -0
- package/types/jspi/suspending.d.ts +1 -0
- package/types/jspi/types.d.ts +26 -0
- package/types/plan/format.d.ts +369 -0
- package/types/plan/loader.d.ts +113 -0
- package/types/plan/mod.d.ts +2 -0
- package/types/shim/mod.d.ts +1 -0
- package/types/shim/translator.d.ts +55 -0
- package/types/task/mod.d.ts +257 -0
- package/types/task/scheduler.d.ts +421 -0
- package/types/task/streams.d.ts +370 -0
- package/types/task/subtask.d.ts +96 -0
- package/types/task/thread.d.ts +73 -0
- package/types/task/waitable.d.ts +67 -0
package/esm/cabi/lift.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// Flat lifting (definitions.py `## Flat Lifting`): core values -> component
|
|
2
|
+
// values.
|
|
3
|
+
import { assert_, trapIf } from "./trap.js";
|
|
4
|
+
import { canonicalizeNan32, canonicalizeNan64, decodeI32AsFloat, decodeI64AsFloat, } from "./float.js";
|
|
5
|
+
import { convertI32ToChar, loadStringFromRange } from "./strings.js";
|
|
6
|
+
import { convertIntToBool, loadListFromRange, unpackFlagsFromInt, } from "./load.js";
|
|
7
|
+
import { requireMemory } from "./context.js";
|
|
8
|
+
import { flattenVariant } from "./flatten.js";
|
|
9
|
+
import { liftBorrow, liftOwn } from "./handles.js";
|
|
10
|
+
import { despecialize, } from "./types.js";
|
|
11
|
+
import { liftErrorContext, liftFuture, liftStream, } from "./async_values.js";
|
|
12
|
+
export class CoreValueIter {
|
|
13
|
+
values;
|
|
14
|
+
i = 0;
|
|
15
|
+
constructor(values) {
|
|
16
|
+
this.values = values;
|
|
17
|
+
}
|
|
18
|
+
next(t) {
|
|
19
|
+
const v = this.values[this.i];
|
|
20
|
+
this.i += 1;
|
|
21
|
+
switch (t) {
|
|
22
|
+
case "i32":
|
|
23
|
+
assert_(typeof v === "number" && Number.isInteger(v) && 0 <= v &&
|
|
24
|
+
v < 2 ** 32, "expected canonical i32 lane value");
|
|
25
|
+
break;
|
|
26
|
+
case "i64":
|
|
27
|
+
assert_(typeof v === "bigint" && 0n <= v && v < 1n << 64n, "expected canonical i64 lane value");
|
|
28
|
+
break;
|
|
29
|
+
case "f32":
|
|
30
|
+
case "f64":
|
|
31
|
+
assert_(typeof v === "number", "expected float lane value");
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
return v;
|
|
35
|
+
}
|
|
36
|
+
done() {
|
|
37
|
+
return this.i === this.values.length;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function liftFlat(cx, vi, t) {
|
|
41
|
+
const d = despecialize(t);
|
|
42
|
+
switch (d.kind) {
|
|
43
|
+
case "bool":
|
|
44
|
+
return convertIntToBool(vi.next("i32"));
|
|
45
|
+
case "u8":
|
|
46
|
+
return liftFlatUnsigned32(vi, 8);
|
|
47
|
+
case "u16":
|
|
48
|
+
return liftFlatUnsigned32(vi, 16);
|
|
49
|
+
case "u32":
|
|
50
|
+
return liftFlatUnsigned32(vi, 32);
|
|
51
|
+
case "u64":
|
|
52
|
+
return liftFlatUnsigned64(vi);
|
|
53
|
+
case "s8":
|
|
54
|
+
return liftFlatSigned32(vi, 8);
|
|
55
|
+
case "s16":
|
|
56
|
+
return liftFlatSigned32(vi, 16);
|
|
57
|
+
case "s32":
|
|
58
|
+
return liftFlatSigned32(vi, 32);
|
|
59
|
+
case "s64":
|
|
60
|
+
return liftFlatSigned64(vi);
|
|
61
|
+
case "f32":
|
|
62
|
+
return canonicalizeNan32(vi.next("f32"));
|
|
63
|
+
case "f64":
|
|
64
|
+
return canonicalizeNan64(vi.next("f64"));
|
|
65
|
+
case "char":
|
|
66
|
+
return convertI32ToChar(vi.next("i32"));
|
|
67
|
+
case "string":
|
|
68
|
+
return liftFlatString(cx, vi);
|
|
69
|
+
case "error-context":
|
|
70
|
+
return liftErrorContext(cx, vi.next("i32"));
|
|
71
|
+
case "list":
|
|
72
|
+
return liftFlatList(cx, vi, d.element, d.length ?? null);
|
|
73
|
+
case "record":
|
|
74
|
+
return liftFlatRecord(cx, vi, d.fields);
|
|
75
|
+
case "variant":
|
|
76
|
+
return liftFlatVariant(cx, vi, d.cases);
|
|
77
|
+
case "flags":
|
|
78
|
+
return liftFlatFlags(vi, d.labels);
|
|
79
|
+
case "own":
|
|
80
|
+
return liftOwn(cx, vi.next("i32"), d);
|
|
81
|
+
case "borrow":
|
|
82
|
+
return liftBorrow(cx, vi.next("i32"), d);
|
|
83
|
+
case "stream":
|
|
84
|
+
return liftStream(cx, vi.next("i32"), d);
|
|
85
|
+
case "future":
|
|
86
|
+
return liftFuture(cx, vi.next("i32"), d);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// definitions.py lift_flat_unsigned/lift_flat_signed, split by core width
|
|
90
|
+
// because 32-bit lanes are numbers and 64-bit lanes are bigints.
|
|
91
|
+
export function liftFlatUnsigned32(vi, tWidth) {
|
|
92
|
+
const i = vi.next("i32");
|
|
93
|
+
assert_(0 <= i && i < 2 ** 32);
|
|
94
|
+
return i % 2 ** tWidth;
|
|
95
|
+
}
|
|
96
|
+
export function liftFlatUnsigned64(vi) {
|
|
97
|
+
const i = vi.next("i64");
|
|
98
|
+
assert_(0n <= i && i < 1n << 64n);
|
|
99
|
+
return i; // i % 2**64 == i
|
|
100
|
+
}
|
|
101
|
+
export function liftFlatSigned32(vi, tWidth) {
|
|
102
|
+
const i0 = vi.next("i32");
|
|
103
|
+
assert_(0 <= i0 && i0 < 2 ** 32);
|
|
104
|
+
const i = i0 % 2 ** tWidth;
|
|
105
|
+
if (i >= 2 ** (tWidth - 1)) {
|
|
106
|
+
return i - 2 ** tWidth;
|
|
107
|
+
}
|
|
108
|
+
return i;
|
|
109
|
+
}
|
|
110
|
+
export function liftFlatSigned64(vi) {
|
|
111
|
+
const i = vi.next("i64");
|
|
112
|
+
assert_(0n <= i && i < 1n << 64n);
|
|
113
|
+
if (i >= 1n << 63n) {
|
|
114
|
+
return i - (1n << 64n);
|
|
115
|
+
}
|
|
116
|
+
return i;
|
|
117
|
+
}
|
|
118
|
+
export function liftFlatString(cx, vi) {
|
|
119
|
+
const ptrType = requireMemory(cx.opts).ptrType();
|
|
120
|
+
const ptr = vi.next(ptrType);
|
|
121
|
+
const packedLength = vi.next(ptrType);
|
|
122
|
+
return loadStringFromRange(cx, ptr, packedLength);
|
|
123
|
+
}
|
|
124
|
+
export function liftFlatList(cx, vi, elemType, maybeLength) {
|
|
125
|
+
if (maybeLength !== null) {
|
|
126
|
+
const a = [];
|
|
127
|
+
for (let i = 0; i < maybeLength; i++) {
|
|
128
|
+
a.push(liftFlat(cx, vi, elemType));
|
|
129
|
+
}
|
|
130
|
+
// docs/architecture.md §7: list<u8> is a Uint8Array.
|
|
131
|
+
if (despecialize(elemType).kind === "u8") {
|
|
132
|
+
return Uint8Array.from(a);
|
|
133
|
+
}
|
|
134
|
+
return a;
|
|
135
|
+
}
|
|
136
|
+
const ptrType = requireMemory(cx.opts).ptrType();
|
|
137
|
+
const ptr = vi.next(ptrType);
|
|
138
|
+
const length = vi.next(ptrType);
|
|
139
|
+
return loadListFromRange(cx, ptr, length, elemType);
|
|
140
|
+
}
|
|
141
|
+
export function liftFlatRecord(cx, vi, fields) {
|
|
142
|
+
const record = {};
|
|
143
|
+
for (const f of fields) {
|
|
144
|
+
record[f.label] = liftFlat(cx, vi, f.type);
|
|
145
|
+
}
|
|
146
|
+
return record;
|
|
147
|
+
}
|
|
148
|
+
export function liftFlatVariant(cx, vi, cases) {
|
|
149
|
+
const flatTypes = flattenVariant(cases, cx.opts);
|
|
150
|
+
assert_(flatTypes.shift() === "i32");
|
|
151
|
+
const caseIndex = vi.next("i32");
|
|
152
|
+
trapIf(caseIndex >= cases.length, "invalid variant discriminant");
|
|
153
|
+
const coerceIter = {
|
|
154
|
+
next(want) {
|
|
155
|
+
const have = flatTypes.shift();
|
|
156
|
+
const x = vi.next(have);
|
|
157
|
+
if (have === "i32" && want === "f32") {
|
|
158
|
+
return decodeI32AsFloat(x);
|
|
159
|
+
}
|
|
160
|
+
else if (have === "i64" && want === "i32") {
|
|
161
|
+
return wrapI64ToI32(x);
|
|
162
|
+
}
|
|
163
|
+
else if (have === "i64" && want === "f32") {
|
|
164
|
+
return decodeI32AsFloat(wrapI64ToI32(x));
|
|
165
|
+
}
|
|
166
|
+
else if (have === "i64" && want === "f64") {
|
|
167
|
+
return decodeI64AsFloat(x);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
assert_(have === want, `lane mismatch ${have} -> ${want}`);
|
|
171
|
+
return x;
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
const c = cases[caseIndex];
|
|
176
|
+
let v;
|
|
177
|
+
if (c.type === null) {
|
|
178
|
+
v = null;
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
v = liftFlat(cx, coerceIter, c.type);
|
|
182
|
+
}
|
|
183
|
+
for (const have of flatTypes) {
|
|
184
|
+
vi.next(have);
|
|
185
|
+
}
|
|
186
|
+
return { [c.label]: v };
|
|
187
|
+
}
|
|
188
|
+
export function wrapI64ToI32(i) {
|
|
189
|
+
assert_(0n <= i && i < 1n << 64n);
|
|
190
|
+
return Number(i & 0xffffffffn);
|
|
191
|
+
}
|
|
192
|
+
export function liftFlatFlags(vi, labels) {
|
|
193
|
+
assert_(0 < labels.length && labels.length <= 32);
|
|
194
|
+
const i = vi.next("i32");
|
|
195
|
+
return unpackFlagsFromInt(i, labels);
|
|
196
|
+
}
|
package/esm/cabi/load.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Loading component values from linear memory (definitions.py `## Loading`).
|
|
2
|
+
import { assert_, trapIf } from "./trap.js";
|
|
3
|
+
import { bytesOf, loadIntS, loadIntU, loadPtr } from "./memory.js";
|
|
4
|
+
import { decodeI32AsFloat, decodeI64AsFloat } from "./float.js";
|
|
5
|
+
import { alignment, alignTo, elemSize, elemSizeFlags, maxCaseAlignment, } from "./layout.js";
|
|
6
|
+
import { convertI32ToChar, loadString } from "./strings.js";
|
|
7
|
+
import { requireMemory } from "./context.js";
|
|
8
|
+
import { tryLoadNumericList } from "./bulk_lists.js";
|
|
9
|
+
import { liftBorrow, liftOwn } from "./handles.js";
|
|
10
|
+
import { despecialize, discriminantType, } from "./types.js";
|
|
11
|
+
import { liftErrorContext, liftFuture, liftStream, } from "./async_values.js";
|
|
12
|
+
export const MAX_LIST_BYTE_LENGTH = (1 << 28) - 1;
|
|
13
|
+
export function load(cx, ptr, t) {
|
|
14
|
+
const mem = requireMemory(cx.opts);
|
|
15
|
+
assert_(ptr === alignTo(ptr, alignment(t, mem.ptrType())), "load misaligned");
|
|
16
|
+
assert_(ptr + elemSize(t, mem.ptrType()) <= mem.length, "load OOB");
|
|
17
|
+
const d = despecialize(t);
|
|
18
|
+
switch (d.kind) {
|
|
19
|
+
case "bool":
|
|
20
|
+
return convertIntToBool(loadIntU(mem, ptr, 1));
|
|
21
|
+
case "u8":
|
|
22
|
+
return loadIntU(mem, ptr, 1);
|
|
23
|
+
case "u16":
|
|
24
|
+
return loadIntU(mem, ptr, 2);
|
|
25
|
+
case "u32":
|
|
26
|
+
return loadIntU(mem, ptr, 4);
|
|
27
|
+
case "u64":
|
|
28
|
+
return loadIntU(mem, ptr, 8);
|
|
29
|
+
case "s8":
|
|
30
|
+
return loadIntS(mem, ptr, 1);
|
|
31
|
+
case "s16":
|
|
32
|
+
return loadIntS(mem, ptr, 2);
|
|
33
|
+
case "s32":
|
|
34
|
+
return loadIntS(mem, ptr, 4);
|
|
35
|
+
case "s64":
|
|
36
|
+
return loadIntS(mem, ptr, 8);
|
|
37
|
+
case "f32":
|
|
38
|
+
return decodeI32AsFloat(loadIntU(mem, ptr, 4));
|
|
39
|
+
case "f64":
|
|
40
|
+
return decodeI64AsFloat(loadIntU(mem, ptr, 8));
|
|
41
|
+
case "char":
|
|
42
|
+
return convertI32ToChar(loadIntU(mem, ptr, 4));
|
|
43
|
+
case "string":
|
|
44
|
+
return loadString(cx, ptr);
|
|
45
|
+
case "error-context":
|
|
46
|
+
return liftErrorContext(cx, loadIntU(cx.opts.memory, ptr, 4));
|
|
47
|
+
case "list":
|
|
48
|
+
return loadList(cx, ptr, d.element, d.length ?? null);
|
|
49
|
+
case "record":
|
|
50
|
+
return loadRecord(cx, ptr, d.fields);
|
|
51
|
+
case "variant":
|
|
52
|
+
return loadVariant(cx, ptr, d.cases);
|
|
53
|
+
case "flags":
|
|
54
|
+
return loadFlags(cx, ptr, d.labels);
|
|
55
|
+
case "own":
|
|
56
|
+
return liftOwn(cx, loadIntU(mem, ptr, 4), d);
|
|
57
|
+
case "borrow":
|
|
58
|
+
return liftBorrow(cx, loadIntU(mem, ptr, 4), d);
|
|
59
|
+
case "stream":
|
|
60
|
+
return liftStream(cx, loadIntU(cx.opts.memory, ptr, 4), d);
|
|
61
|
+
case "future":
|
|
62
|
+
return liftFuture(cx, loadIntU(cx.opts.memory, ptr, 4), d);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function convertIntToBool(i) {
|
|
66
|
+
assert_(i >= 0);
|
|
67
|
+
return Boolean(i);
|
|
68
|
+
}
|
|
69
|
+
export function loadList(cx, ptr, elemType, maybeLength) {
|
|
70
|
+
if (maybeLength !== null) {
|
|
71
|
+
return loadListFromValidRange(cx, ptr, maybeLength, elemType);
|
|
72
|
+
}
|
|
73
|
+
const mem = requireMemory(cx.opts);
|
|
74
|
+
const begin = loadPtr(mem, ptr);
|
|
75
|
+
const length = loadPtr(mem, ptr + mem.ptrSize());
|
|
76
|
+
return loadListFromRange(cx, begin, length, elemType);
|
|
77
|
+
}
|
|
78
|
+
export function loadListFromRange(cx, ptr, length, elemType) {
|
|
79
|
+
const mem = requireMemory(cx.opts);
|
|
80
|
+
const size = elemSize(elemType, mem.ptrType());
|
|
81
|
+
const align = alignment(elemType, mem.ptrType());
|
|
82
|
+
const byteLengthBig = BigInt(length) * BigInt(size);
|
|
83
|
+
trapIf(byteLengthBig > BigInt(MAX_LIST_BYTE_LENGTH), "list too long");
|
|
84
|
+
const ptrBig = BigInt(ptr);
|
|
85
|
+
trapIf(ptrBig % BigInt(align) !== 0n, "misaligned list pointer");
|
|
86
|
+
trapIf(ptrBig + byteLengthBig > BigInt(mem.length), "list out of bounds");
|
|
87
|
+
return loadListFromValidRange(cx, Number(ptrBig), Number(length), elemType);
|
|
88
|
+
}
|
|
89
|
+
export function loadListFromValidRange(cx, ptr, length, elemType) {
|
|
90
|
+
const mem = requireMemory(cx.opts);
|
|
91
|
+
const kind = despecialize(elemType).kind;
|
|
92
|
+
// docs/architecture.md §7: list<u8> lifts to a Uint8Array copy.
|
|
93
|
+
if (kind === "u8") {
|
|
94
|
+
return bytesOf(mem, ptr, length).slice();
|
|
95
|
+
}
|
|
96
|
+
// Other flat element types lift bulk too (issue #67) — same host shapes
|
|
97
|
+
// (number[]/bigint[]/boolean[]), same NaN canonicalization; falls through
|
|
98
|
+
// to the per-element loop for compound types, char (per-element USV
|
|
99
|
+
// validation is the point), and non-little-endian platforms.
|
|
100
|
+
const bulk = tryLoadNumericList(mem, ptr, length, kind);
|
|
101
|
+
if (bulk !== null)
|
|
102
|
+
return bulk;
|
|
103
|
+
const size = elemSize(elemType, mem.ptrType());
|
|
104
|
+
const a = [];
|
|
105
|
+
for (let i = 0; i < length; i++) {
|
|
106
|
+
a.push(load(cx, ptr + i * size, elemType));
|
|
107
|
+
}
|
|
108
|
+
return a;
|
|
109
|
+
}
|
|
110
|
+
export function loadRecord(cx, ptr, fields) {
|
|
111
|
+
const mem = requireMemory(cx.opts);
|
|
112
|
+
const record = {};
|
|
113
|
+
let p = ptr;
|
|
114
|
+
for (const field of fields) {
|
|
115
|
+
p = alignTo(p, alignment(field.type, mem.ptrType()));
|
|
116
|
+
record[field.label] = load(cx, p, field.type);
|
|
117
|
+
p += elemSize(field.type, mem.ptrType());
|
|
118
|
+
}
|
|
119
|
+
return record;
|
|
120
|
+
}
|
|
121
|
+
export function loadVariant(cx, ptr, cases) {
|
|
122
|
+
const mem = requireMemory(cx.opts);
|
|
123
|
+
const discSize = elemSize(discriminantType(cases), mem.ptrType());
|
|
124
|
+
const caseIndex = loadIntU(mem, ptr, discSize);
|
|
125
|
+
let p = ptr + discSize;
|
|
126
|
+
trapIf(caseIndex >= cases.length, "invalid variant discriminant");
|
|
127
|
+
const c = cases[caseIndex];
|
|
128
|
+
p = alignTo(p, maxCaseAlignment(cases, mem.ptrType()));
|
|
129
|
+
if (c.type === null)
|
|
130
|
+
return { [c.label]: null };
|
|
131
|
+
return { [c.label]: load(cx, p, c.type) };
|
|
132
|
+
}
|
|
133
|
+
export function loadFlags(cx, ptr, labels) {
|
|
134
|
+
const mem = requireMemory(cx.opts);
|
|
135
|
+
const i = loadIntU(mem, ptr, elemSizeFlags(labels));
|
|
136
|
+
return unpackFlagsFromInt(i, labels);
|
|
137
|
+
}
|
|
138
|
+
export function unpackFlagsFromInt(i, labels) {
|
|
139
|
+
const record = {};
|
|
140
|
+
let v = i >>> 0;
|
|
141
|
+
for (const l of labels) {
|
|
142
|
+
record[l] = Boolean(v & 1);
|
|
143
|
+
v = v >>> 1;
|
|
144
|
+
}
|
|
145
|
+
return record;
|
|
146
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Flat lowering (definitions.py `## Flat Lowering`): component values ->
|
|
2
|
+
// core values.
|
|
3
|
+
import { assert_ } from "./trap.js";
|
|
4
|
+
import { encodeFloatAsI32, encodeFloatAsI64, maybeScrambleNan32, maybeScrambleNan64, } from "./float.js";
|
|
5
|
+
import { charToI32, storeStringIntoRange } from "./strings.js";
|
|
6
|
+
import { matchCase, packFlagsIntoInt, storeListIntoRange } from "./store.js";
|
|
7
|
+
import { requireMemory } from "./context.js";
|
|
8
|
+
import { flattenType, flattenVariant } from "./flatten.js";
|
|
9
|
+
import { lowerBorrow, lowerOwn } from "./handles.js";
|
|
10
|
+
import { despecialize, } from "./types.js";
|
|
11
|
+
import { lowerErrorContext, lowerFuture, lowerStream, } from "./async_values.js";
|
|
12
|
+
export function lowerFlat(cx, v, t) {
|
|
13
|
+
const d = despecialize(t);
|
|
14
|
+
switch (d.kind) {
|
|
15
|
+
case "bool":
|
|
16
|
+
return [Number(v)];
|
|
17
|
+
case "u8":
|
|
18
|
+
case "u16":
|
|
19
|
+
case "u32":
|
|
20
|
+
return [v];
|
|
21
|
+
case "u64":
|
|
22
|
+
return [v];
|
|
23
|
+
case "s8":
|
|
24
|
+
case "s16":
|
|
25
|
+
case "s32":
|
|
26
|
+
return lowerFlatSigned32(v);
|
|
27
|
+
case "s64":
|
|
28
|
+
return lowerFlatSigned64(v);
|
|
29
|
+
case "f32":
|
|
30
|
+
return [maybeScrambleNan32(v)];
|
|
31
|
+
case "f64":
|
|
32
|
+
return [maybeScrambleNan64(v)];
|
|
33
|
+
case "char":
|
|
34
|
+
return [charToI32(v)];
|
|
35
|
+
case "string":
|
|
36
|
+
return lowerFlatString(cx, v);
|
|
37
|
+
case "error-context":
|
|
38
|
+
return [lowerErrorContext(cx, v)];
|
|
39
|
+
case "list":
|
|
40
|
+
return lowerFlatList(cx, v, d.element, d.length ?? null);
|
|
41
|
+
case "record":
|
|
42
|
+
return lowerFlatRecord(cx, v, d.fields);
|
|
43
|
+
case "variant":
|
|
44
|
+
return lowerFlatVariant(cx, v, d.cases);
|
|
45
|
+
case "flags":
|
|
46
|
+
return lowerFlatFlags(v, d.labels);
|
|
47
|
+
case "own":
|
|
48
|
+
return [lowerOwn(cx, v, d)];
|
|
49
|
+
case "borrow":
|
|
50
|
+
return [lowerBorrow(cx, v, d)];
|
|
51
|
+
case "stream":
|
|
52
|
+
return [lowerStream(cx, v, d)];
|
|
53
|
+
case "future":
|
|
54
|
+
return [lowerFuture(cx, v, d)];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// definitions.py lower_flat_signed, split by core width (number vs bigint).
|
|
58
|
+
export function lowerFlatSigned32(i) {
|
|
59
|
+
if (i < 0) {
|
|
60
|
+
return [i + 2 ** 32];
|
|
61
|
+
}
|
|
62
|
+
return [i];
|
|
63
|
+
}
|
|
64
|
+
export function lowerFlatSigned64(i) {
|
|
65
|
+
if (i < 0n) {
|
|
66
|
+
return [i + (1n << 64n)];
|
|
67
|
+
}
|
|
68
|
+
return [i];
|
|
69
|
+
}
|
|
70
|
+
export function lowerFlatString(cx, v) {
|
|
71
|
+
const [ptr, packedLength] = storeStringIntoRange(cx, v);
|
|
72
|
+
if (requireMemory(cx.opts).ptrType() === "i32") {
|
|
73
|
+
return [ptr, Number(packedLength)];
|
|
74
|
+
}
|
|
75
|
+
return [BigInt(ptr), packedLength];
|
|
76
|
+
}
|
|
77
|
+
export function lowerFlatList(cx, v, elemType, maybeLength) {
|
|
78
|
+
if (maybeLength !== null) {
|
|
79
|
+
assert_(maybeLength === v.length, "fixed-length list length mismatch");
|
|
80
|
+
const flat = [];
|
|
81
|
+
for (let i = 0; i < v.length; i++) {
|
|
82
|
+
flat.push(...lowerFlat(cx, v[i], elemType));
|
|
83
|
+
}
|
|
84
|
+
return flat;
|
|
85
|
+
}
|
|
86
|
+
const [ptr, length] = storeListIntoRange(cx, v, elemType);
|
|
87
|
+
if (requireMemory(cx.opts).ptrType() === "i32") {
|
|
88
|
+
return [ptr, length];
|
|
89
|
+
}
|
|
90
|
+
return [BigInt(ptr), BigInt(length)];
|
|
91
|
+
}
|
|
92
|
+
export function lowerFlatRecord(cx, v, fields) {
|
|
93
|
+
const flat = [];
|
|
94
|
+
for (const f of fields) {
|
|
95
|
+
flat.push(...lowerFlat(cx, v[f.label], f.type));
|
|
96
|
+
}
|
|
97
|
+
return flat;
|
|
98
|
+
}
|
|
99
|
+
export function lowerFlatVariant(cx, v, cases) {
|
|
100
|
+
const [caseIndex, caseValue] = matchCase(v, cases);
|
|
101
|
+
const flatTypes = flattenVariant(cases, cx.opts);
|
|
102
|
+
assert_(flatTypes.shift() === "i32");
|
|
103
|
+
const c = cases[caseIndex];
|
|
104
|
+
let payload;
|
|
105
|
+
if (c.type === null) {
|
|
106
|
+
payload = [];
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
payload = lowerFlat(cx, caseValue, c.type);
|
|
110
|
+
const haveTypes = flattenType(c.type, cx.opts);
|
|
111
|
+
for (let i = 0; i < payload.length; i++) {
|
|
112
|
+
const fv = payload[i];
|
|
113
|
+
const have = haveTypes[i];
|
|
114
|
+
const want = flatTypes.shift();
|
|
115
|
+
if (have === "f32" && want === "i32") {
|
|
116
|
+
payload[i] = encodeFloatAsI32(fv);
|
|
117
|
+
}
|
|
118
|
+
else if (have === "i32" && want === "i64") {
|
|
119
|
+
payload[i] = BigInt(fv);
|
|
120
|
+
}
|
|
121
|
+
else if (have === "f32" && want === "i64") {
|
|
122
|
+
payload[i] = BigInt(encodeFloatAsI32(fv));
|
|
123
|
+
}
|
|
124
|
+
else if (have === "f64" && want === "i64") {
|
|
125
|
+
payload[i] = encodeFloatAsI64(fv);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
assert_(have === want, `lane mismatch ${have} -> ${want}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Pad the remaining lanes of wider cases with zeros of the lane type.
|
|
133
|
+
for (const want of flatTypes) {
|
|
134
|
+
payload.push(want === "i64" ? 0n : 0);
|
|
135
|
+
}
|
|
136
|
+
return [caseIndex, ...payload];
|
|
137
|
+
}
|
|
138
|
+
export function lowerFlatFlags(v, labels) {
|
|
139
|
+
assert_(0 < labels.length && labels.length <= 32);
|
|
140
|
+
return [packFlagsIntoInt(v, labels)];
|
|
141
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Linear-memory access (definitions.py `MemInst`, `load_int`, `store_int`,
|
|
2
|
+
// `ptr_size`).
|
|
3
|
+
//
|
|
4
|
+
// A `MemInst` wraps a byte buffer with an address type (wasm32/wasm64). All
|
|
5
|
+
// multi-byte accesses are little-endian via DataView. Following docs/architecture.md §7,
|
|
6
|
+
// integer lanes/values of width <= 32 are JS numbers and 64-bit values are
|
|
7
|
+
// BigInt.
|
|
8
|
+
//
|
|
9
|
+
// Addresses and sizes are JS numbers throughout: real guest memories are far
|
|
10
|
+
// below Number.MAX_SAFE_INTEGER. Values arriving from i64 lanes (memory64)
|
|
11
|
+
// are bounds-checked as BigInt before conversion — see `asIndex`.
|
|
12
|
+
import { assert_, trapIf } from "./trap.js";
|
|
13
|
+
export function ptrSize(ptrType) {
|
|
14
|
+
return ptrType === "i32" ? 4 : 8;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* `MemInst` caches its `Uint8Array`/`DataView` at construction time and never
|
|
18
|
+
* re-derives them. That is only sound for a memory that cannot grow within
|
|
19
|
+
* this instance's lifetime: `memory.grow` (guest-triggered or host-triggered)
|
|
20
|
+
* detaches the backing `ArrayBuffer`, and a cached view over a detached
|
|
21
|
+
* buffer reads/writes garbage rather than trapping.
|
|
22
|
+
*
|
|
23
|
+
* Production call/lift/lower paths do NOT construct `MemInst` directly against
|
|
24
|
+
* a live, growable `WebAssembly.Memory` for this reason: the executor
|
|
25
|
+
* re-derives a fresh view per access via `LiveMemory`
|
|
26
|
+
* (`exec/boundary.ts:97-160`), which is grow-safe. `MemInst` is for contexts
|
|
27
|
+
* where the buffer is known fixed for the duration (tests, or a snapshot
|
|
28
|
+
* already taken).
|
|
29
|
+
*/
|
|
30
|
+
export class MemInst {
|
|
31
|
+
bytes;
|
|
32
|
+
view;
|
|
33
|
+
addrType;
|
|
34
|
+
constructor(bytes, addrType) {
|
|
35
|
+
this.bytes = bytes;
|
|
36
|
+
this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
37
|
+
this.addrType = addrType;
|
|
38
|
+
}
|
|
39
|
+
get length() {
|
|
40
|
+
return this.bytes.length;
|
|
41
|
+
}
|
|
42
|
+
ptrType() {
|
|
43
|
+
return this.addrType;
|
|
44
|
+
}
|
|
45
|
+
ptrSize() {
|
|
46
|
+
return ptrSize(this.addrType);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Convert a (possibly BigInt) address/length to a JS number index, after the
|
|
51
|
+
* caller has already trapped on any out-of-bounds condition using exact
|
|
52
|
+
* (BigInt) arithmetic. Asserts rather than traps: by this point the value is
|
|
53
|
+
* known small.
|
|
54
|
+
*/
|
|
55
|
+
export function asIndex(v) {
|
|
56
|
+
if (typeof v === "bigint") {
|
|
57
|
+
assert_(v >= 0n && v <= BigInt(Number.MAX_SAFE_INTEGER), "index overflow");
|
|
58
|
+
return Number(v);
|
|
59
|
+
}
|
|
60
|
+
assert_(Number.isSafeInteger(v) && v >= 0, "index not a safe integer");
|
|
61
|
+
return v;
|
|
62
|
+
}
|
|
63
|
+
export function loadIntU(mem, ptr, nbytes) {
|
|
64
|
+
assert_(ptr + nbytes <= mem.length, "load out of bounds");
|
|
65
|
+
switch (nbytes) {
|
|
66
|
+
case 1:
|
|
67
|
+
return mem.view.getUint8(ptr);
|
|
68
|
+
case 2:
|
|
69
|
+
return mem.view.getUint16(ptr, true);
|
|
70
|
+
case 4:
|
|
71
|
+
return mem.view.getUint32(ptr, true);
|
|
72
|
+
case 8:
|
|
73
|
+
return mem.view.getBigUint64(ptr, true);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function loadIntS(mem, ptr, nbytes) {
|
|
77
|
+
assert_(ptr + nbytes <= mem.length, "load out of bounds");
|
|
78
|
+
switch (nbytes) {
|
|
79
|
+
case 1:
|
|
80
|
+
return mem.view.getInt8(ptr);
|
|
81
|
+
case 2:
|
|
82
|
+
return mem.view.getInt16(ptr, true);
|
|
83
|
+
case 4:
|
|
84
|
+
return mem.view.getInt32(ptr, true);
|
|
85
|
+
case 8:
|
|
86
|
+
return mem.view.getBigInt64(ptr, true);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Load a pointer-sized unsigned integer (returns bigint for i64 memories). */
|
|
90
|
+
export function loadPtr(mem, ptr) {
|
|
91
|
+
return mem.ptrSize() === 4 ? loadIntU(mem, ptr, 4) : loadIntU(mem, ptr, 8);
|
|
92
|
+
}
|
|
93
|
+
// definitions.py store_int(cx, v, ptr, nbytes, signed): `int.to_bytes` raises
|
|
94
|
+
// `OverflowError` when `v` does not fit in `nbytes` (signed/unsigned per the
|
|
95
|
+
// flag) — a host-precondition violation, not a guest-visible trap (the value
|
|
96
|
+
// never came from validated guest bytes; it is a JS number/bigint an
|
|
97
|
+
// embedder handed the lowering path). Ported as `assert_`/`AssertionError`
|
|
98
|
+
// (see cabi/trap.ts's Trap-vs-AssertionError taxonomy), not `Trap`.
|
|
99
|
+
// definitions.py:1568-1569 (`store_int`).
|
|
100
|
+
//
|
|
101
|
+
// This scalar path is where the check lives; the bulk (TypedArray) path in
|
|
102
|
+
// bulk_lists.ts intentionally wraps instead — see that file's header for why.
|
|
103
|
+
function bigintFitsIn64(v, signed) {
|
|
104
|
+
return signed
|
|
105
|
+
? v >= -(2n ** 63n) && v < 2n ** 63n
|
|
106
|
+
: v >= 0n && v < 2n ** 64n;
|
|
107
|
+
}
|
|
108
|
+
function numberFitsInWidth(v, nbytes, signed) {
|
|
109
|
+
const bits = nbytes * 8;
|
|
110
|
+
if (signed) {
|
|
111
|
+
const min = -(2 ** (bits - 1));
|
|
112
|
+
const max = 2 ** (bits - 1) - 1;
|
|
113
|
+
return v >= min && v <= max;
|
|
114
|
+
}
|
|
115
|
+
return v >= 0 && v <= 2 ** bits - 1;
|
|
116
|
+
}
|
|
117
|
+
export function storeInt(mem, v, ptr, nbytes, signed = false) {
|
|
118
|
+
assert_(ptr + nbytes <= mem.length, "store out of bounds");
|
|
119
|
+
if (nbytes === 8) {
|
|
120
|
+
assert_(typeof v === "bigint", "64-bit store requires bigint");
|
|
121
|
+
assert_(bigintFitsIn64(v, signed), "int store: value out of range");
|
|
122
|
+
if (signed)
|
|
123
|
+
mem.view.setBigInt64(ptr, v, true);
|
|
124
|
+
else
|
|
125
|
+
mem.view.setBigUint64(ptr, v, true);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
assert_(typeof v === "number" && Number.isInteger(v), "int store");
|
|
129
|
+
assert_(numberFitsInWidth(v, nbytes, signed), "int store: value out of range");
|
|
130
|
+
switch (nbytes) {
|
|
131
|
+
case 1:
|
|
132
|
+
if (signed)
|
|
133
|
+
mem.view.setInt8(ptr, v);
|
|
134
|
+
else
|
|
135
|
+
mem.view.setUint8(ptr, v);
|
|
136
|
+
break;
|
|
137
|
+
case 2:
|
|
138
|
+
if (signed)
|
|
139
|
+
mem.view.setInt16(ptr, v, true);
|
|
140
|
+
else
|
|
141
|
+
mem.view.setUint16(ptr, v, true);
|
|
142
|
+
break;
|
|
143
|
+
case 4:
|
|
144
|
+
if (signed)
|
|
145
|
+
mem.view.setInt32(ptr, v, true);
|
|
146
|
+
else
|
|
147
|
+
mem.view.setUint32(ptr, v, true);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** Store a pointer-sized unsigned integer, taking a JS number. */
|
|
152
|
+
export function storePtr(mem, v, ptr) {
|
|
153
|
+
if (mem.ptrSize() === 4)
|
|
154
|
+
storeInt(mem, v, ptr, 4);
|
|
155
|
+
else
|
|
156
|
+
storeInt(mem, BigInt(v), ptr, 8);
|
|
157
|
+
}
|
|
158
|
+
/** Bounds-checked byte-range read (callers trap on OOB before calling). */
|
|
159
|
+
export function bytesOf(mem, ptr, len) {
|
|
160
|
+
assert_(ptr >= 0 && len >= 0 && ptr + len <= mem.length, "range OOB");
|
|
161
|
+
return mem.bytes.subarray(ptr, ptr + len);
|
|
162
|
+
}
|
|
163
|
+
/** Copy bytes into memory; used by string/list stores after trap checks. */
|
|
164
|
+
export function writeBytes(mem, ptr, src) {
|
|
165
|
+
assert_(ptr >= 0 && ptr + src.length <= mem.length, "write OOB");
|
|
166
|
+
mem.bytes.set(src, ptr);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Exact bounds check usable with i64-lane values: traps when
|
|
170
|
+
* `ptr + byteLength > len(memory)`, computed without precision loss.
|
|
171
|
+
*
|
|
172
|
+
* `what` selects the trap wording only. Callers that are checking a pointer
|
|
173
|
+
* *returned by realloc* pass wasmtime's phrasing for that case
|
|
174
|
+
* ("realloc return: beyond end of memory",
|
|
175
|
+
* `wasmtime/src/runtime/component/func/options.rs:185`) and the string-lift
|
|
176
|
+
* path passes its own ("string pointer/length out of bounds of memory",
|
|
177
|
+
* `.../func/typed.rs:1528`), because the official suite matches those texts.
|
|
178
|
+
* The default is unchanged.
|
|
179
|
+
*/
|
|
180
|
+
export function trapIfRangeExceedsMemory(mem, ptr, byteLength, what = "out of bounds of linear memory") {
|
|
181
|
+
trapIf(BigInt(ptr) + BigInt(byteLength) > BigInt(mem.length), what);
|
|
182
|
+
}
|
package/esm/cabi/mod.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Canonical ABI v1 reference interpreter — public surface.
|
|
2
|
+
//
|
|
3
|
+
// A direct TypeScript port of the value lift/lower machinery of
|
|
4
|
+
// third_party/component-model/design/mvp/canonical-abi/definitions.py
|
|
5
|
+
// (readable over fast; docs/architecture.md §8 "v1: a generic interpreter").
|
|
6
|
+
// Task/thread/waitable machinery is deliberately absent — see
|
|
7
|
+
// runtime/README.md for the port/defer inventory.
|
|
8
|
+
export * from "./trap.js";
|
|
9
|
+
export * from "./types.js";
|
|
10
|
+
export * from "./memory.js";
|
|
11
|
+
export * from "./layout.js";
|
|
12
|
+
export * from "./float.js";
|
|
13
|
+
export * from "./context.js";
|
|
14
|
+
export * from "./handles.js";
|
|
15
|
+
export * from "./strings.js";
|
|
16
|
+
export * from "./bulk_lists.js";
|
|
17
|
+
export * from "./load.js";
|
|
18
|
+
export * from "./store.js";
|
|
19
|
+
export * from "./flatten.js";
|
|
20
|
+
export * from "./lift.js";
|
|
21
|
+
export * from "./lower.js";
|
|
22
|
+
export * from "./values.js";
|