@hew-lang/sandbox-vm 0.6.1 → 0.6.3
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/README.md +48 -4
- package/cli/test-runner.mjs +4 -1
- package/dist/interpreter/interpreter.d.ts +1 -1
- package/dist/interpreter/interpreter.js +1 -1
- package/dist/interpreter/parity-runner.js +1 -1
- package/dist/interpreter/run-program.d.ts +3 -2
- package/dist/interpreter/run-program.js +5 -3
- package/dist/interpreter/schema-validator.d.ts +9 -4
- package/dist/interpreter/schema-validator.js +15 -49
- package/dist/interpreter/strings.d.ts +21 -0
- package/dist/interpreter/strings.js +191 -0
- package/dist/interpreter/types.d.ts +6 -0
- package/dist/interpreter/unicode-data.d.ts +4 -0
- package/dist/interpreter/unicode-data.js +1673 -0
- package/dist/interpreter/unicode.d.ts +3 -0
- package/dist/interpreter/unicode.js +35 -0
- package/dist/interpreter/v1/actor-identity.d.ts +3 -0
- package/dist/interpreter/v1/actor-identity.js +7 -0
- package/dist/interpreter/v1/actor-waits.d.ts +6 -0
- package/dist/interpreter/v1/actor-waits.js +33 -0
- package/dist/interpreter/v1/browser-directory.d.ts +107 -0
- package/dist/interpreter/v1/browser-directory.js +288 -0
- package/dist/interpreter/v1/calendar.d.ts +4 -0
- package/dist/interpreter/v1/calendar.js +64 -0
- package/dist/interpreter/v1/codecs.js +8 -11
- package/dist/interpreter/v1/exec.d.ts +2 -2
- package/dist/interpreter/v1/exec.js +1761 -489
- package/dist/interpreter/v1/instructions.generated.d.ts +1135 -0
- package/dist/interpreter/v1/instructions.generated.js +106 -0
- package/dist/interpreter/v1/package-graph.d.ts +2 -0
- package/dist/interpreter/v1/package-graph.js +729 -0
- package/dist/interpreter/v1/package-validator.generated.d.ts +3 -0
- package/dist/interpreter/v1/package-validator.generated.js +75902 -0
- package/dist/interpreter/v1/package.d.ts +5 -645
- package/dist/interpreter/v1/package.js +0 -12
- package/dist/interpreter/v1/pipes.d.ts +1 -0
- package/dist/interpreter/v1/pipes.js +9 -6
- package/dist/interpreter/v1/scheduler.d.ts +8 -1
- package/dist/interpreter/v1/scheduler.js +50 -11
- package/dist/interpreter/v1/shims.d.ts +260 -7
- package/dist/interpreter/v1/shims.js +647 -175
- package/dist/interpreter/v1/structural.d.ts +2 -28
- package/dist/interpreter/v1/timestamp.d.ts +7 -0
- package/dist/interpreter/v1/timestamp.js +54 -0
- package/dist/interpreter/v1/validate.d.ts +8 -2
- package/dist/interpreter/v1/validate.js +284 -200
- package/dist/interpreter/v1/value-operations.d.ts +11 -3
- package/dist/interpreter/v1/value-operations.js +70 -18
- package/dist/interpreter/values.d.ts +30 -8
- package/dist/interpreter/values.js +31 -5
- package/dist/interpreter/worker.js +4 -3
- package/package.json +4 -4
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
///
|
|
5
|
-
///
|
|
6
|
-
|
|
7
|
-
/// absence, and a `NativeIo` suspension by an operation missing from
|
|
8
|
-
/// [`SUPPORTED_NATIVE_IO`].
|
|
9
|
-
import { UNIT, cloneValue, compareUtf8, renderStdout, } from "../values.js";
|
|
1
|
+
import { DIRECTORY_OPERATIONS, } from "./browser-directory.js";
|
|
2
|
+
import { executeCalendar } from "./calendar.js";
|
|
3
|
+
import { charProperty, charCase } from "../unicode.js";
|
|
4
|
+
/// Runtime implementations and their host execution registration. Admission and
|
|
5
|
+
/// execution consume the same ordinary, resumable or refused disposition.
|
|
6
|
+
import { UNIT, cloneValue, compareUtf8, stringValue, renderStdout, } from "../values.js";
|
|
10
7
|
import { seededMt } from "./mt19937.js";
|
|
11
8
|
/// A language-visible fault raised inside a shim. The executor routes it to the
|
|
12
9
|
/// call's `unwind` edge, or ends the program when the call names none.
|
|
@@ -18,125 +15,252 @@ export class ShimFault extends Error {
|
|
|
18
15
|
this.name = "ShimFault";
|
|
19
16
|
}
|
|
20
17
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
function ordinary(shim) {
|
|
19
|
+
return { kind: "ordinary", shim };
|
|
20
|
+
}
|
|
21
|
+
function refused(capability, native = false) {
|
|
22
|
+
return {
|
|
23
|
+
kind: "refused",
|
|
24
|
+
category: native ? "native_only" : "not_implemented",
|
|
25
|
+
capability,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function resumable(continuation) {
|
|
29
|
+
return { kind: "resumable", ...continuation };
|
|
30
|
+
}
|
|
31
|
+
/// A registered algorithm may still require a host-supplied Wasm bridge.
|
|
32
|
+
export function registrationRefusal(registration, bridges) {
|
|
33
|
+
if (registration.kind === "refused")
|
|
34
|
+
return registration;
|
|
35
|
+
if (registration.kind === "directory" &&
|
|
36
|
+
registration.operation.action === "root" &&
|
|
37
|
+
!bridges.directoryRoots?.[registration.operation.access])
|
|
38
|
+
return {
|
|
39
|
+
...refused(`directoryRoots.${registration.operation.access}`),
|
|
40
|
+
message: `This operation requires a host-supplied OPFS ${registration.operation.access} directory handle.`,
|
|
41
|
+
};
|
|
42
|
+
return "bridge" in registration &&
|
|
43
|
+
registration.bridge &&
|
|
44
|
+
typeof bridges[registration.bridge] !== "function"
|
|
45
|
+
? refused(registration.bridge)
|
|
46
|
+
: null;
|
|
47
|
+
}
|
|
48
|
+
export const CODEC_REGISTRATION = {
|
|
49
|
+
kind: "resumable",
|
|
50
|
+
bridge: "codecBridge",
|
|
51
|
+
};
|
|
52
|
+
/// Each typed runtime identity has exactly one host disposition. An added Rust
|
|
53
|
+
/// family or nested operation must acquire a disposition before TypeScript builds.
|
|
54
|
+
export function resolveRuntime(entry) {
|
|
25
55
|
switch (entry.family) {
|
|
26
56
|
case "SupervisorPool":
|
|
27
|
-
return
|
|
28
|
-
? () => {
|
|
29
|
-
throw new Error("SupervisorPool requires its resumable executor");
|
|
30
|
-
}
|
|
31
|
-
: undefined;
|
|
32
|
-
case "ActorCaller":
|
|
33
|
-
case "ActorRefusalLog":
|
|
34
|
-
case "StructuralFormat":
|
|
35
|
-
return () => {
|
|
36
|
-
throw new Error("StructuralFormat requires its resumable executor");
|
|
37
|
-
};
|
|
38
|
-
case "FileRead":
|
|
39
|
-
return entry.detail === "LastError"
|
|
40
|
-
? () => ({ kind: "string", value: "" })
|
|
41
|
-
: undefined;
|
|
57
|
+
return resumable({ handler: "supervisor_pool", operation: entry.detail });
|
|
42
58
|
case "Print":
|
|
43
|
-
return printShim(entry.detail);
|
|
44
|
-
case "
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
throw new Error("shared operation requires its resumable executor");
|
|
53
|
-
};
|
|
54
|
-
case "Vector":
|
|
55
|
-
return ["Contains", "Set", "Clear"].includes(String(entry.detail))
|
|
56
|
-
? () => {
|
|
57
|
-
throw new Error("vector Contains requires its selected Eq executor");
|
|
59
|
+
return ordinary(printShim(entry.detail));
|
|
60
|
+
case "FileRead":
|
|
61
|
+
switch (entry.detail) {
|
|
62
|
+
case "HasError":
|
|
63
|
+
case "LastErrorKind":
|
|
64
|
+
case "LastErrno":
|
|
65
|
+
case "LastError": {
|
|
66
|
+
const operation = entry.detail;
|
|
67
|
+
return ordinary((host) => host.fileError(operation));
|
|
58
68
|
}
|
|
59
|
-
:
|
|
69
|
+
default:
|
|
70
|
+
return refused(`FileRead::${entry.detail}`, true);
|
|
71
|
+
}
|
|
72
|
+
case "AsyncIo":
|
|
73
|
+
case "Tcp":
|
|
74
|
+
return refused(`${entry.family}::${entry.detail}`, true);
|
|
75
|
+
case "Semaphore":
|
|
76
|
+
case "Service":
|
|
77
|
+
case "VecContainsScalar":
|
|
78
|
+
case "VecGet":
|
|
79
|
+
case "VecSliceRange":
|
|
80
|
+
return refused(`${entry.family}::${entry.detail}`);
|
|
81
|
+
case "Encoding":
|
|
82
|
+
case "VecScalar":
|
|
83
|
+
return refused(entry.family);
|
|
84
|
+
case "Vector":
|
|
85
|
+
switch (entry.detail) {
|
|
86
|
+
case "Contains":
|
|
87
|
+
case "Set":
|
|
88
|
+
case "Clear":
|
|
89
|
+
return resumable({
|
|
90
|
+
handler: "collection",
|
|
91
|
+
family: "Vector",
|
|
92
|
+
operation: entry.detail,
|
|
93
|
+
});
|
|
94
|
+
case "Pop":
|
|
95
|
+
case "GetBorrow":
|
|
96
|
+
case "TakeFirst":
|
|
97
|
+
case "TakeAll":
|
|
98
|
+
case "Append":
|
|
99
|
+
return refused(`Vector::${entry.detail}`);
|
|
100
|
+
default:
|
|
101
|
+
return ordinary(VECTOR_SHIMS[entry.detail]);
|
|
102
|
+
}
|
|
60
103
|
case "Array":
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
104
|
+
switch (entry.detail) {
|
|
105
|
+
case "Set":
|
|
106
|
+
return resumable({
|
|
107
|
+
handler: "collection",
|
|
108
|
+
family: "Vector",
|
|
109
|
+
operation: entry.detail,
|
|
110
|
+
});
|
|
111
|
+
case "Len":
|
|
112
|
+
case "Index":
|
|
113
|
+
case "IndexBorrow":
|
|
114
|
+
return ordinary(VECTOR_SHIMS[entry.detail]);
|
|
115
|
+
default: {
|
|
116
|
+
const exhaustive = entry;
|
|
117
|
+
throw new Error(`unknown array operation ${String(exhaustive)}`);
|
|
64
118
|
}
|
|
65
|
-
|
|
66
|
-
? (_host, args) => sequenceElement(args)
|
|
67
|
-
: ["Len", "Index"].includes(String(entry.detail))
|
|
68
|
-
? VECTOR_SHIMS[detailName(entry.detail)]
|
|
69
|
-
: undefined;
|
|
119
|
+
}
|
|
70
120
|
case "Map":
|
|
121
|
+
return resumable({
|
|
122
|
+
handler: "collection",
|
|
123
|
+
family: "Map",
|
|
124
|
+
operation: entry.detail,
|
|
125
|
+
});
|
|
71
126
|
case "Set":
|
|
72
|
-
return
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"GetBorrow",
|
|
78
|
-
"ContainsKey",
|
|
79
|
-
"Contains",
|
|
80
|
-
"Insert",
|
|
81
|
-
"Remove",
|
|
82
|
-
"Clear",
|
|
83
|
-
"Keys",
|
|
84
|
-
"Values",
|
|
85
|
-
"Entries",
|
|
86
|
-
"Elements",
|
|
87
|
-
].includes(detailName(entry.detail))
|
|
88
|
-
? () => {
|
|
89
|
-
throw new Error("collection operation requires its resumable executor");
|
|
90
|
-
}
|
|
91
|
-
: undefined;
|
|
127
|
+
return resumable({
|
|
128
|
+
handler: "collection",
|
|
129
|
+
family: "Set",
|
|
130
|
+
operation: entry.detail,
|
|
131
|
+
});
|
|
92
132
|
case "MathIntrinsic":
|
|
93
|
-
return MATH_SHIMS[
|
|
133
|
+
return ordinary(MATH_SHIMS[entry.detail]);
|
|
94
134
|
case "FloatMethod":
|
|
95
|
-
return FLOAT_METHOD_SHIMS[
|
|
135
|
+
return ordinary(FLOAT_METHOD_SHIMS[entry.detail]);
|
|
96
136
|
case "IntMethod":
|
|
97
|
-
return intMethodShim(entry.detail);
|
|
137
|
+
return ordinary(intMethodShim(entry.detail));
|
|
98
138
|
case "IntArith":
|
|
99
|
-
return intArithShim(entry.detail);
|
|
100
|
-
default:
|
|
101
|
-
|
|
139
|
+
return ordinary(intArithShim(entry.detail));
|
|
140
|
+
default: {
|
|
141
|
+
const registration = UNIT_FAMILIES[entry.family];
|
|
142
|
+
return typeof registration === "function"
|
|
143
|
+
? ordinary(registration)
|
|
144
|
+
: registration;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
export function resolveExtern(entry) {
|
|
149
|
+
if (Object.hasOwn(DIRECTORY_OPERATIONS, entry.symbol))
|
|
150
|
+
return {
|
|
151
|
+
kind: "directory",
|
|
152
|
+
operation: DIRECTORY_OPERATIONS[entry.symbol],
|
|
153
|
+
};
|
|
154
|
+
const registration = Object.hasOwn(EXTERN_SHIMS, entry.symbol)
|
|
155
|
+
? EXTERN_SHIMS[entry.symbol]
|
|
156
|
+
: undefined;
|
|
157
|
+
if (typeof registration === "function")
|
|
158
|
+
return ordinary(registration);
|
|
159
|
+
return (registration ?? {
|
|
160
|
+
...refused(entry.symbol, entry.native_capability != null),
|
|
161
|
+
description: entry.native_capability ?? undefined,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
const SUSPENSION_ROUTES = {
|
|
165
|
+
GeneratorNext: "GeneratorNext",
|
|
166
|
+
Yield: "Yield",
|
|
167
|
+
Sleep: "Sleep",
|
|
168
|
+
Safepoint: "Safepoint",
|
|
169
|
+
Ask: "Ask",
|
|
170
|
+
Await: "Await",
|
|
171
|
+
Join: "Join",
|
|
172
|
+
Select: "Select",
|
|
173
|
+
StreamSend: "StreamSend",
|
|
174
|
+
StreamNext: "StreamNext",
|
|
175
|
+
NativeIo: "NativeIo",
|
|
176
|
+
Read: refused("Read", true),
|
|
177
|
+
Accept: refused("Accept", true),
|
|
178
|
+
SleepUntil: refused("SleepUntil"),
|
|
179
|
+
ServiceAcquire: refused("ServiceAcquire"),
|
|
180
|
+
SemaphoreAcquire: refused("SemaphoreAcquire"),
|
|
181
|
+
ActorSend: refused("ActorSend"),
|
|
182
|
+
RestartWait: refused("RestartWait"),
|
|
183
|
+
CallClosure: refused("CallClosure"),
|
|
184
|
+
Timeout: refused("Timeout"),
|
|
185
|
+
ScopeDeadline: refused("ScopeDeadline"),
|
|
186
|
+
Offload: refused("Offload"),
|
|
187
|
+
};
|
|
188
|
+
export function suspensionRefusal(kind) {
|
|
189
|
+
const route = SUSPENSION_ROUTES[kind];
|
|
190
|
+
return typeof route === "string" ? null : route;
|
|
191
|
+
}
|
|
192
|
+
function executableSuspension(term) {
|
|
193
|
+
return suspensionRefusal(term.kind) === null;
|
|
194
|
+
}
|
|
195
|
+
const NATIVE_IO_ROUTES = {
|
|
196
|
+
StdinReadLine: "StdinReadLine",
|
|
197
|
+
SinkFinish: "SinkFinish",
|
|
198
|
+
TcpRead: refused("NativeIo::TcpRead", true),
|
|
199
|
+
TcpWrite: refused("NativeIo::TcpWrite", true),
|
|
200
|
+
TcpAccept: refused("NativeIo::TcpAccept", true),
|
|
201
|
+
TcpConnect: refused("NativeIo::TcpConnect", true),
|
|
202
|
+
TcpConnectTimeout: refused("NativeIo::TcpConnectTimeout", true),
|
|
203
|
+
TlsConnect: refused("NativeIo::TlsConnect", true),
|
|
204
|
+
TlsRead: refused("NativeIo::TlsRead", true),
|
|
205
|
+
TlsWrite: refused("NativeIo::TlsWrite", true),
|
|
206
|
+
FileRead: "FileRead",
|
|
207
|
+
FileWrite: "FileWrite",
|
|
208
|
+
};
|
|
209
|
+
function nativeIoRefusal(operation) {
|
|
210
|
+
const route = NATIVE_IO_ROUTES[operation];
|
|
211
|
+
return typeof route === "string" ? null : route;
|
|
212
|
+
}
|
|
213
|
+
function executableNativeIo(term) {
|
|
214
|
+
return nativeIoRefusal(term.detail.operation) === null;
|
|
215
|
+
}
|
|
216
|
+
/// The executor handles exactly the resumable identities registered above.
|
|
217
|
+
export function resolveSuspension(term) {
|
|
218
|
+
if (!executableSuspension(term))
|
|
219
|
+
return suspensionRefusal(term.kind);
|
|
220
|
+
if (term.kind === "NativeIo") {
|
|
221
|
+
if (!executableNativeIo(term))
|
|
222
|
+
return nativeIoRefusal(term.detail.operation);
|
|
223
|
+
return { kind: "resumable", term };
|
|
102
224
|
}
|
|
225
|
+
return { kind: "resumable", term };
|
|
103
226
|
}
|
|
104
|
-
export function resolveExternShim(symbol) {
|
|
105
|
-
return EXTERN_SHIMS[symbol];
|
|
106
|
-
}
|
|
107
|
-
/// The suspend kinds a sequential package may carry. `NativeIo` is admitted
|
|
108
|
-
/// per operation through [`SUPPORTED_NATIVE_IO`] instead.
|
|
109
|
-
export const SUPPORTED_SUSPEND_KINDS = new Set([
|
|
110
|
-
"GeneratorNext",
|
|
111
|
-
"Yield",
|
|
112
|
-
"Sleep",
|
|
113
|
-
"Ask",
|
|
114
|
-
"Await",
|
|
115
|
-
"Join",
|
|
116
|
-
"Select",
|
|
117
|
-
"StreamSend",
|
|
118
|
-
"StreamNext",
|
|
119
|
-
]);
|
|
120
|
-
/// The VM serves replay standard input and in-memory sink closure.
|
|
121
|
-
export const SUPPORTED_NATIVE_IO = new Set([
|
|
122
|
-
"StdinReadLine",
|
|
123
|
-
"SinkFinish",
|
|
124
|
-
]);
|
|
125
227
|
// ── families ────────────────────────────────────────────────────────────────
|
|
126
228
|
/// `Print { kind, newline }`. The rendering follows the value's own
|
|
127
229
|
/// representation: casts normalize an integer into its target range, so the
|
|
128
230
|
/// declared `kind` never changes how a value prints.
|
|
129
231
|
function printShim(detail) {
|
|
130
|
-
|
|
131
|
-
return undefined;
|
|
132
|
-
}
|
|
133
|
-
const newline = detail.newline === true;
|
|
232
|
+
const newline = detail.newline;
|
|
134
233
|
return (host, args) => {
|
|
135
234
|
host.writeStdout(newline ? `${renderStdout(arg(args, 0))}\n` : renderStdout(arg(args, 0)));
|
|
136
235
|
return UNIT;
|
|
137
236
|
};
|
|
138
237
|
}
|
|
139
|
-
const
|
|
238
|
+
const UNIT_FAMILIES = {
|
|
239
|
+
TimestampNow: (host, _args, _variant, aggregate) => {
|
|
240
|
+
const millis = host.unixTimeMillis();
|
|
241
|
+
if (!Number.isFinite(millis))
|
|
242
|
+
throw new ShimFault("integer_overflow", "host wall clock is not finite");
|
|
243
|
+
const seconds = Math.floor(millis / 1000);
|
|
244
|
+
let nanos = Math.round((millis - seconds * 1000) * 1_000_000);
|
|
245
|
+
let whole = BigInt(seconds);
|
|
246
|
+
if (nanos === 1_000_000_000) {
|
|
247
|
+
whole += 1n;
|
|
248
|
+
nanos = 0;
|
|
249
|
+
}
|
|
250
|
+
if (whole < -(1n << 63n) ||
|
|
251
|
+
whole >= 1n << 63n ||
|
|
252
|
+
nanos < 0 ||
|
|
253
|
+
nanos >= 1_000_000_000)
|
|
254
|
+
throw new ShimFault("integer_overflow", "host wall clock exceeds timestamp range");
|
|
255
|
+
if (!aggregate || aggregate.builtin !== "Timestamp")
|
|
256
|
+
throw new TypeError("timestamp clock lacks its aggregate descriptor");
|
|
257
|
+
return {
|
|
258
|
+
kind: "record",
|
|
259
|
+
typeId: aggregate.name,
|
|
260
|
+
shape: aggregate.id,
|
|
261
|
+
fields: [int(whole), int(BigInt(nanos))],
|
|
262
|
+
};
|
|
263
|
+
},
|
|
140
264
|
StderrWrite: (host, args) => {
|
|
141
265
|
host.writeStderr(text(args, 0));
|
|
142
266
|
return UNIT;
|
|
@@ -144,51 +268,104 @@ const UNIT_FAMILY_SHIMS = {
|
|
|
144
268
|
StringToBytes: (_host, args) => ({
|
|
145
269
|
kind: "vector",
|
|
146
270
|
elementType: "u8",
|
|
147
|
-
items: [...
|
|
148
|
-
}),
|
|
149
|
-
StringToUppercase: (_host, args) => ({
|
|
150
|
-
kind: "string",
|
|
151
|
-
value: text(args, 0).toUpperCase(),
|
|
152
|
-
}),
|
|
153
|
-
StringToLowercase: (_host, args) => ({
|
|
154
|
-
kind: "string",
|
|
155
|
-
value: text(args, 0).toLowerCase(),
|
|
271
|
+
items: [...indexed(args, 0).encoded()].map((byte) => int(BigInt(byte))),
|
|
156
272
|
}),
|
|
273
|
+
StringToUppercase: (_host, args) => str(text(args, 0).toUpperCase()),
|
|
274
|
+
StringToLowercase: (_host, args) => str(text(args, 0).toLowerCase()),
|
|
157
275
|
StreamPairSink: (host, args) => host.pipes.extract(arg(args, 0), "sink"),
|
|
158
276
|
StreamPairStream: (host, args) => host.pipes.extract(arg(args, 0), "stream"),
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
},
|
|
164
|
-
StreamClose: (host, args) => {
|
|
165
|
-
host.closePipe(arg(args, 0));
|
|
166
|
-
return UNIT;
|
|
167
|
-
},
|
|
277
|
+
StreamFromGenerator: (host, args) => host.generatorStream(arg(args, 0)),
|
|
278
|
+
SinkFromEncoder: (host, args) => host.encoderSink(arg(args, 0), arg(args, 1)),
|
|
279
|
+
SinkClone: (host, args) => host.cloneSink(arg(args, 0)),
|
|
280
|
+
SinkClose: resumable({ handler: "close" }),
|
|
281
|
+
StreamClose: resumable({ handler: "close" }),
|
|
168
282
|
StringConcat: (_host, args) => str(`${text(args, 0)}${text(args, 1)}`),
|
|
169
|
-
StringClone: (_host, args) =>
|
|
283
|
+
StringClone: (_host, args) => cloneValue(arg(args, 0)),
|
|
170
284
|
StringEquals: (_host, args) => bool(text(args, 0) === text(args, 1)),
|
|
171
285
|
StringCompare: (_host, args) => int(BigInt(compareUtf8(text(args, 0), text(args, 1)))),
|
|
172
|
-
|
|
286
|
+
StringStartsWith: (_host, args) => bool(text(args, 0).startsWith(text(args, 1))),
|
|
287
|
+
StringEndsWith: (_host, args) => bool(text(args, 0).endsWith(text(args, 1))),
|
|
288
|
+
StringContains: (_host, args) => bool(indexed(args, 0).find(indexed(args, 1)) !== undefined),
|
|
173
289
|
StringTrim: (_host, args) => str(text(args, 0).replace(/^\p{White_Space}+|\p{White_Space}+$/gu, "")),
|
|
174
|
-
|
|
175
|
-
|
|
290
|
+
StringLen: (_host, args) => int(BigInt(indexed(args, 0).scalarLength)),
|
|
291
|
+
StringByteLen: (_host, args) => int(BigInt(indexed(args, 0).byteLength)),
|
|
176
292
|
StringIndex: (_host, args) => {
|
|
177
|
-
const
|
|
293
|
+
const value = indexed(args, 0);
|
|
178
294
|
const at = index(args, 1);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return str(
|
|
295
|
+
const scalar = value.at(at);
|
|
296
|
+
if (scalar === undefined)
|
|
297
|
+
throw new ShimFault("vector_bounds", `string index ${at} out of bounds for length ${value.scalarLength}`);
|
|
298
|
+
return str(scalar);
|
|
299
|
+
},
|
|
300
|
+
StringGet: (host, args, shape) => {
|
|
301
|
+
if (!shape)
|
|
302
|
+
throw new TypeError("string get requires its Option shape");
|
|
303
|
+
const scalar = indexed(args, 0).at(index(args, 1));
|
|
304
|
+
return scalar === undefined
|
|
305
|
+
? host.enumValue(shape, "OptionNone", [])
|
|
306
|
+
: host.enumValue(shape, "OptionSome", [str(scalar)]);
|
|
307
|
+
},
|
|
308
|
+
StringFind: (host, args, shape) => {
|
|
309
|
+
if (!shape)
|
|
310
|
+
throw new TypeError("string find requires its Option shape");
|
|
311
|
+
const found = indexed(args, 0).find(indexed(args, 1));
|
|
312
|
+
return found === undefined
|
|
313
|
+
? host.enumValue(shape, "OptionNone", [])
|
|
314
|
+
: host.enumValue(shape, "OptionSome", [int(BigInt(found))]);
|
|
315
|
+
},
|
|
316
|
+
StringFindFrom: (host, args, shape) => {
|
|
317
|
+
if (!shape)
|
|
318
|
+
throw new TypeError("string find_from requires its Option shape");
|
|
319
|
+
const found = indexed(args, 0).find(indexed(args, 1), index(args, 2));
|
|
320
|
+
return found === undefined
|
|
321
|
+
? host.enumValue(shape, "OptionNone", [])
|
|
322
|
+
: host.enumValue(shape, "OptionSome", [int(BigInt(found))]);
|
|
323
|
+
},
|
|
324
|
+
StringAppend: (_host, args) => indexed(args, 0).append(indexed(args, 1)),
|
|
325
|
+
StringPush: (_host, args) => indexed(args, 0).append(indexed(args, 1)),
|
|
326
|
+
StringClear: (_host, args) => {
|
|
327
|
+
indexed(args, 0).release();
|
|
328
|
+
return str("");
|
|
329
|
+
},
|
|
330
|
+
StringIsEmpty: (_host, args) => bool(indexed(args, 0).scalarLength === 0),
|
|
331
|
+
StringSplit: (_host, args) => {
|
|
332
|
+
const value = indexed(args, 0).value;
|
|
333
|
+
const separator = indexed(args, 1).value;
|
|
334
|
+
const parts = separator === "" ? Array.from(value) : value.split(separator);
|
|
335
|
+
return { kind: "vector", elementType: "string", items: parts.map(str) };
|
|
183
336
|
},
|
|
184
|
-
StringIsEmpty: (_host, args) => bool(text(args, 0).length === 0),
|
|
185
337
|
StringReplace: (_host, args) => str(text(args, 0).split(text(args, 1)).join(text(args, 2))),
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
return
|
|
338
|
+
StringRepeat: (_host, args) => str(text(args, 0).repeat(Math.max(0, index(args, 1)))),
|
|
339
|
+
StringGetRange: (host, args, shape) => {
|
|
340
|
+
if (!shape)
|
|
341
|
+
throw new TypeError("string range get requires its Option shape");
|
|
342
|
+
const value = stringRange(args);
|
|
343
|
+
return value === undefined
|
|
344
|
+
? host.enumValue(shape, "OptionNone", [])
|
|
345
|
+
: host.enumValue(shape, "OptionSome", [value]);
|
|
346
|
+
},
|
|
347
|
+
StringSliceRange: (_host, args) => {
|
|
348
|
+
const value = stringRange(args);
|
|
349
|
+
if (value === undefined)
|
|
350
|
+
throw new ShimFault("vector_bounds", "string range out of bounds");
|
|
351
|
+
return value;
|
|
352
|
+
},
|
|
353
|
+
StringSliceCodepoints: (_host, args) => {
|
|
354
|
+
const value = indexed(args, 0);
|
|
355
|
+
const start = index(args, 1);
|
|
356
|
+
const end = index(args, 2);
|
|
357
|
+
const sliced = value.slice(start, end);
|
|
358
|
+
if (!sliced)
|
|
359
|
+
throw new ShimFault("vector_bounds", `string slice ${start}..${end} out of bounds for length ${value.scalarLength}`);
|
|
360
|
+
return sliced;
|
|
361
|
+
},
|
|
362
|
+
StringSliceCodepointsFrom: (_host, args) => {
|
|
363
|
+
const value = indexed(args, 0);
|
|
364
|
+
const start = index(args, 1);
|
|
365
|
+
const sliced = value.slice(start, value.scalarLength);
|
|
366
|
+
if (!sliced)
|
|
367
|
+
throw new ShimFault("vector_bounds", `string slice ${start}.. out of bounds for length ${value.scalarLength}`);
|
|
368
|
+
return sliced;
|
|
192
369
|
},
|
|
193
370
|
I64ToString: (_host, args) => str(renderStdout(arg(args, 0))),
|
|
194
371
|
U64ToString: (_host, args) => str(renderStdout(arg(args, 0))),
|
|
@@ -197,8 +374,39 @@ const UNIT_FAMILY_SHIMS = {
|
|
|
197
374
|
U8ToString: (_host, args) => str(renderStdout(arg(args, 0))),
|
|
198
375
|
BoolToString: (_host, args) => str(renderStdout(arg(args, 0))),
|
|
199
376
|
F64ToString: (_host, args) => str(renderStdout(arg(args, 0))),
|
|
377
|
+
CharIsDigit: (_host, args) => bool(charProperty(text(args, 0), 0)),
|
|
378
|
+
CharIsLetter: (_host, args) => bool(charProperty(text(args, 0), 1)),
|
|
379
|
+
CharIsAlphanumeric: (_host, args) => bool(charProperty(text(args, 0), 2)),
|
|
380
|
+
CharIsWhitespace: (_host, args) => bool(charProperty(text(args, 0), 3)),
|
|
381
|
+
CharIsUpper: (_host, args) => bool(charProperty(text(args, 0), 4)),
|
|
382
|
+
CharIsLower: (_host, args) => bool(charProperty(text(args, 0), 5)),
|
|
383
|
+
CharIsPunctuation: (_host, args) => bool(charProperty(text(args, 0), 6)),
|
|
384
|
+
CharIsControl: (_host, args) => bool(charProperty(text(args, 0), 7)),
|
|
385
|
+
CharIsAscii: (_host, args) => bool(charProperty(text(args, 0), 8)),
|
|
386
|
+
CharLenUtf8: (_host, args) => int(BigInt(indexed(args, 0).byteLength)),
|
|
387
|
+
CharToUpper: (_host, args) => str(charCase(text(args, 0), "upper")),
|
|
388
|
+
CharToLower: (_host, args) => str(charCase(text(args, 0), "lower")),
|
|
389
|
+
CharToTitle: (_host, args) => str(charCase(text(args, 0), "title")),
|
|
390
|
+
CharToDigit: (host, args, shape) => {
|
|
391
|
+
if (!shape)
|
|
392
|
+
throw new TypeError("char to_digit requires its Option shape");
|
|
393
|
+
const radix = index(args, 1);
|
|
394
|
+
const scalar = text(args, 0).codePointAt(0);
|
|
395
|
+
const digit = scalar >= 48 && scalar <= 57
|
|
396
|
+
? scalar - 48
|
|
397
|
+
: scalar >= 65 && scalar <= 90
|
|
398
|
+
? scalar - 65 + 10
|
|
399
|
+
: scalar >= 97 && scalar <= 122
|
|
400
|
+
? scalar - 97 + 10
|
|
401
|
+
: -1;
|
|
402
|
+
return radix < 2 || radix > 36 || digit < 0 || digit >= radix
|
|
403
|
+
? host.enumValue(shape, "OptionNone", [])
|
|
404
|
+
: host.enumValue(shape, "OptionSome", [int(BigInt(digit))]);
|
|
405
|
+
},
|
|
406
|
+
CharFromI64: checkedChar,
|
|
407
|
+
CharFromU64: checkedChar,
|
|
200
408
|
CharToString: (_host, args) => str(renderStdout(arg(args, 0))),
|
|
201
|
-
BytesDecodeUtf8: (
|
|
409
|
+
BytesDecodeUtf8: resumable({ handler: "utf8_decode" }),
|
|
202
410
|
// TextDecoder replaces each maximal invalid subsequence with U+FFFD, as
|
|
203
411
|
// native lossy decoding does.
|
|
204
412
|
BytesDecodeUtf8Lossy: (_host, args) => str(decodeUtf8(arg(args, 0))),
|
|
@@ -293,6 +501,165 @@ const UNIT_FAMILY_SHIMS = {
|
|
|
293
501
|
],
|
|
294
502
|
};
|
|
295
503
|
},
|
|
504
|
+
ActorDemonitor: refused("ActorDemonitor"),
|
|
505
|
+
ActorLink: refused("ActorLink"),
|
|
506
|
+
ActorMonitor: refused("ActorMonitor"),
|
|
507
|
+
ActorCaller: resumable({ handler: "actor_caller" }),
|
|
508
|
+
ActorRefusalLog: resumable({ handler: "actor_refusal_log" }),
|
|
509
|
+
ActorUnlink: refused("ActorUnlink"),
|
|
510
|
+
AutoMutexAlloc: refused("AutoMutexAlloc"),
|
|
511
|
+
AutoMutexFree: refused("AutoMutexFree"),
|
|
512
|
+
AutoMutexLock: refused("AutoMutexLock"),
|
|
513
|
+
AutoMutexUnlock: refused("AutoMutexUnlock"),
|
|
514
|
+
DequePopFront: refused("DequePopFront"),
|
|
515
|
+
DequePopBack: refused("DequePopBack"),
|
|
516
|
+
CancelTokenIsRequested: refused("CancelTokenIsRequested"),
|
|
517
|
+
CancelTokenRelease: refused("CancelTokenRelease"),
|
|
518
|
+
CancelTokenRetain: refused("CancelTokenRetain"),
|
|
519
|
+
ActorRequestRelease: refused("ActorRequestRelease"),
|
|
520
|
+
ActorCallFree: refused("ActorCallFree"),
|
|
521
|
+
ActorRequestTake: refused("ActorRequestTake"),
|
|
522
|
+
DurationAbs: refused("DurationAbs"),
|
|
523
|
+
DurationHours: refused("DurationHours"),
|
|
524
|
+
DurationIsZero: refused("DurationIsZero"),
|
|
525
|
+
DurationMicros: refused("DurationMicros"),
|
|
526
|
+
DurationMillis: refused("DurationMillis"),
|
|
527
|
+
DurationMins: refused("DurationMins"),
|
|
528
|
+
DurationNanos: refused("DurationNanos"),
|
|
529
|
+
DurationSecs: refused("DurationSecs"),
|
|
530
|
+
DynBoxAlloc: refused("DynBoxAlloc"),
|
|
531
|
+
DynBoxFree: refused("DynBoxFree"),
|
|
532
|
+
HashMapContainsKeyLayout: refused("HashMapContainsKeyLayout"),
|
|
533
|
+
HashMapClearLayout: refused("HashMapClearLayout"),
|
|
534
|
+
HashMapCloneLayout: refused("HashMapCloneLayout"),
|
|
535
|
+
HashMapEntriesLayout: refused("HashMapEntriesLayout"),
|
|
536
|
+
HashMapFreeLayout: refused("HashMapFreeLayout"),
|
|
537
|
+
HashMapGetLayout: refused("HashMapGetLayout"),
|
|
538
|
+
HashMapInsertLayout: refused("HashMapInsertLayout"),
|
|
539
|
+
HashMapKeysLayout: refused("HashMapKeysLayout"),
|
|
540
|
+
HashMapLenLayout: refused("HashMapLenLayout"),
|
|
541
|
+
HashMapNew: refused("HashMapNew"),
|
|
542
|
+
HashMapNewWithLayout: refused("HashMapNewWithLayout"),
|
|
543
|
+
HashMapRemoveLayout: refused("HashMapRemoveLayout"),
|
|
544
|
+
HashMapValuesLayout: refused("HashMapValuesLayout"),
|
|
545
|
+
HashSetContainsLayout: refused("HashSetContainsLayout"),
|
|
546
|
+
HashSetClearLayout: refused("HashSetClearLayout"),
|
|
547
|
+
HashSetCloneLayout: refused("HashSetCloneLayout"),
|
|
548
|
+
HashSetFreeLayout: refused("HashSetFreeLayout"),
|
|
549
|
+
HashSetInsertLayout: refused("HashSetInsertLayout"),
|
|
550
|
+
HashSetIsEmptyLayout: refused("HashSetIsEmptyLayout"),
|
|
551
|
+
HashSetLenLayout: refused("HashSetLenLayout"),
|
|
552
|
+
HashSetNew: refused("HashSetNew"),
|
|
553
|
+
HashSetNewWithLayout: refused("HashSetNewWithLayout"),
|
|
554
|
+
HashSetRemoveLayout: refused("HashSetRemoveLayout"),
|
|
555
|
+
HashSetToVecLayout: refused("HashSetToVecLayout"),
|
|
556
|
+
InstantDurationSince: refused("InstantDurationSince"),
|
|
557
|
+
InstantElapsed: refused("InstantElapsed"),
|
|
558
|
+
InstantNow: refused("InstantNow"),
|
|
559
|
+
NodeAllowPeer: refused("NodeAllowPeer"),
|
|
560
|
+
NodeConnect: refused("NodeConnect"),
|
|
561
|
+
NodeId: refused("NodeId"),
|
|
562
|
+
NodeIdDisplay: refused("NodeIdDisplay"),
|
|
563
|
+
LocationNodeId: refused("LocationNodeId"),
|
|
564
|
+
LocationIncarnation: refused("LocationIncarnation"),
|
|
565
|
+
LocationDisplay: refused("LocationDisplay"),
|
|
566
|
+
NodeIdentityKey: refused("NodeIdentityKey"),
|
|
567
|
+
NodeLoadKeys: refused("NodeLoadKeys"),
|
|
568
|
+
NodeLookup: refused("NodeLookup"),
|
|
569
|
+
NodeRegister: refused("NodeRegister"),
|
|
570
|
+
NodeSetTransport: refused("NodeSetTransport"),
|
|
571
|
+
NodeShutdown: refused("NodeShutdown"),
|
|
572
|
+
NodeStart: refused("NodeStart"),
|
|
573
|
+
MetricCounterRegister: refused("MetricCounterRegister"),
|
|
574
|
+
MetricCounterInc: refused("MetricCounterInc"),
|
|
575
|
+
MetricCounterAdd: refused("MetricCounterAdd"),
|
|
576
|
+
MetricGaugeRegister: refused("MetricGaugeRegister"),
|
|
577
|
+
MetricGaugeSet: refused("MetricGaugeSet"),
|
|
578
|
+
MetricGaugeInc: refused("MetricGaugeInc"),
|
|
579
|
+
MetricGaugeDec: refused("MetricGaugeDec"),
|
|
580
|
+
MetricGaugeAdd: refused("MetricGaugeAdd"),
|
|
581
|
+
MetricHistogramRegister: refused("MetricHistogramRegister"),
|
|
582
|
+
MetricHistogramRegisterSimple: refused("MetricHistogramRegisterSimple"),
|
|
583
|
+
MetricHistogramRecord: refused("MetricHistogramRecord"),
|
|
584
|
+
MetricVecRegister: refused("MetricVecRegister"),
|
|
585
|
+
MetricVecWith: refused("MetricVecWith"),
|
|
586
|
+
ObserveReadU64: refused("ObserveReadU64"),
|
|
587
|
+
ObserveScrape: refused("ObserveScrape"),
|
|
588
|
+
ObserveSeries: refused("ObserveSeries"),
|
|
589
|
+
ObserveBarrier: refused("ObserveBarrier"),
|
|
590
|
+
RcClone: resumable({ handler: "shared", operation: "RcClone" }),
|
|
591
|
+
RcDowngrade: resumable({ handler: "shared", operation: "RcDowngrade" }),
|
|
592
|
+
RcDrop: refused("RcDrop"),
|
|
593
|
+
RcGet: resumable({ handler: "shared", operation: "RcGet" }),
|
|
594
|
+
RcIsUnique: resumable({ handler: "shared", operation: "RcIsUnique" }),
|
|
595
|
+
RcNew: resumable({ handler: "shared", operation: "RcNew" }),
|
|
596
|
+
RcSet: resumable({ handler: "shared", operation: "RcSet" }),
|
|
597
|
+
RcStrongCount: resumable({ handler: "shared", operation: "RcStrongCount" }),
|
|
598
|
+
RcWeakCount: resumable({ handler: "shared", operation: "RcWeakCount" }),
|
|
599
|
+
WeakCloneRc: resumable({ handler: "shared", operation: "WeakCloneRc" }),
|
|
600
|
+
WeakDropRc: refused("WeakDropRc"),
|
|
601
|
+
WeakUpgradeRc: resumable({ handler: "shared", operation: "WeakUpgradeRc" }),
|
|
602
|
+
RegexCapture: refused("RegexCapture"),
|
|
603
|
+
RegexCompile: refused("RegexCompile"),
|
|
604
|
+
RegexFreeCapture: refused("RegexFreeCapture"),
|
|
605
|
+
RegexHandle: refused("RegexHandle"),
|
|
606
|
+
RegexMatch: refused("RegexMatch"),
|
|
607
|
+
ReplyChannelCancel: refused("ReplyChannelCancel"),
|
|
608
|
+
ReplyChannelFree: refused("ReplyChannelFree"),
|
|
609
|
+
ReplyChannelNew: refused("ReplyChannelNew"),
|
|
610
|
+
ReplyPayloadFree: refused("ReplyPayloadFree"),
|
|
611
|
+
ReplyWait: refused("ReplyWait"),
|
|
612
|
+
SelectFirst: refused("SelectFirst"),
|
|
613
|
+
StreamChunks: refused("StreamChunks"),
|
|
614
|
+
StreamForward: refused("StreamForward"),
|
|
615
|
+
StreamLines: refused("StreamLines"),
|
|
616
|
+
StreamNextLayout: refused("StreamNextLayout"),
|
|
617
|
+
StreamSendLayout: refused("StreamSendLayout"),
|
|
618
|
+
StreamTake: refused("StreamTake"),
|
|
619
|
+
StreamTryNextLayout: refused("StreamTryNextLayout"),
|
|
620
|
+
StreamTrySendLayout: refused("StreamTrySendLayout"),
|
|
621
|
+
StructuralFormat: resumable({ handler: "structural_format" }),
|
|
622
|
+
StringLines: refused("StringLines"),
|
|
623
|
+
ProcessExit: refused("ProcessExit"),
|
|
624
|
+
SupervisorDirectId: refused("SupervisorDirectId"),
|
|
625
|
+
SupervisorChildGet: refused("SupervisorChildGet"),
|
|
626
|
+
LocalPidSupervisorChildGet: refused("LocalPidSupervisorChildGet"),
|
|
627
|
+
SupervisorNestedGet: refused("SupervisorNestedGet"),
|
|
628
|
+
SupervisorPoolChildGet: refused("SupervisorPoolChildGet"),
|
|
629
|
+
LocalPidSupervisorPoolChildRefGet: refused("LocalPidSupervisorPoolChildRefGet"),
|
|
630
|
+
SupervisorPoolLen: refused("SupervisorPoolLen"),
|
|
631
|
+
SupervisorStop: refused("SupervisorStop"),
|
|
632
|
+
TlsAttachLocal: refused("TlsAttachLocal"),
|
|
633
|
+
WebSocketAttachLocal: refused("WebSocketAttachLocal"),
|
|
634
|
+
TaskFree: refused("TaskFree"),
|
|
635
|
+
GeneratorFree: refused("GeneratorFree"),
|
|
636
|
+
VecAppend: refused("VecAppend"),
|
|
637
|
+
VecClear: refused("VecClear"),
|
|
638
|
+
VecClone: refused("VecClone"),
|
|
639
|
+
VecCloneLayout: refused("VecCloneLayout"),
|
|
640
|
+
VecCloneOwned: refused("VecCloneOwned"),
|
|
641
|
+
VecContainsLayout: refused("VecContainsLayout"),
|
|
642
|
+
VecTakeAll: refused("VecTakeAll"),
|
|
643
|
+
VecContainsOwned: refused("VecContainsOwned"),
|
|
644
|
+
VecIsEmpty: refused("VecIsEmpty"),
|
|
645
|
+
VecJoinStr: refused("VecJoinStr"),
|
|
646
|
+
VecLen: refused("VecLen"),
|
|
647
|
+
VecNew: refused("VecNew"),
|
|
648
|
+
VecPopBool: refused("VecPopBool"),
|
|
649
|
+
VecPopLayout: refused("VecPopLayout"),
|
|
650
|
+
VecPopOwned: refused("VecPopOwned"),
|
|
651
|
+
VecPushBool: refused("VecPushBool"),
|
|
652
|
+
VecPushLayout: refused("VecPushLayout"),
|
|
653
|
+
VecPushOwned: refused("VecPushOwned"),
|
|
654
|
+
VecPushOwnedMove: refused("VecPushOwnedMove"),
|
|
655
|
+
VecRemoveAtBool: refused("VecRemoveAtBool"),
|
|
656
|
+
VecRemoveAtLayout: refused("VecRemoveAtLayout"),
|
|
657
|
+
VecRemoveAtOwned: refused("VecRemoveAtOwned"),
|
|
658
|
+
VecSetBool: refused("VecSetBool"),
|
|
659
|
+
VecSetLayout: refused("VecSetLayout"),
|
|
660
|
+
VecSetOwned: refused("VecSetOwned"),
|
|
661
|
+
VecSetOwnedMove: refused("VecSetOwnedMove"),
|
|
662
|
+
VtableDispatchPanicOnOob: refused("VtableDispatchPanicOnOob"),
|
|
296
663
|
};
|
|
297
664
|
const VECTOR_SHIMS = {
|
|
298
665
|
New: () => ({ kind: "vector", elementType: "", items: [] }),
|
|
@@ -326,6 +693,12 @@ const VECTOR_SHIMS = {
|
|
|
326
693
|
Index: (_host, args) => cloneValue(sequenceElement(args)),
|
|
327
694
|
IndexBorrow: (_host, args) => sequenceElement(args),
|
|
328
695
|
Len: (_host, args) => int(BigInt(vec(args, 0).items.length)),
|
|
696
|
+
Join: (_host, args) => {
|
|
697
|
+
const items = vec(args, 0).items;
|
|
698
|
+
return str(items
|
|
699
|
+
.map((_, at) => indexed(items, at).value)
|
|
700
|
+
.join(indexed(args, 1).value));
|
|
701
|
+
},
|
|
329
702
|
Slice: (_host, args) => {
|
|
330
703
|
const items = vec(args, 0).items;
|
|
331
704
|
const start = index(args, 1);
|
|
@@ -495,8 +868,6 @@ const FLOAT_METHOD_SHIMS = {
|
|
|
495
868
|
},
|
|
496
869
|
IsSignNegative: (_host, args) => bool(toBits(real(args, 0)) >> 63n === 1n),
|
|
497
870
|
};
|
|
498
|
-
/// The receiver width an `IntMethod`/`IntArith` detail names. The VM is a
|
|
499
|
-
/// 64-bit target, so the pointer-sized widths are 64 bits wide.
|
|
500
871
|
const INT_WIDTHS = {
|
|
501
872
|
I8: { bits: 8, signed: true },
|
|
502
873
|
I16: { bits: 16, signed: true },
|
|
@@ -509,15 +880,6 @@ const INT_WIDTHS = {
|
|
|
509
880
|
U64: { bits: 64, signed: false },
|
|
510
881
|
Usize: { bits: 64, signed: false },
|
|
511
882
|
};
|
|
512
|
-
/// `[op, width]`, as `RuntimeCallFamily` serializes a two-field variant.
|
|
513
|
-
function intDetail(detail) {
|
|
514
|
-
if (!Array.isArray(detail) || detail.length !== 2) {
|
|
515
|
-
return undefined;
|
|
516
|
-
}
|
|
517
|
-
const [op, width] = detail;
|
|
518
|
-
const shape = typeof width === "string" ? INT_WIDTHS[width] : undefined;
|
|
519
|
-
return typeof op === "string" && shape ? [op, shape] : undefined;
|
|
520
|
-
}
|
|
521
883
|
function wrapTo(width, value) {
|
|
522
884
|
return width.signed
|
|
523
885
|
? BigInt.asIntN(width.bits, value)
|
|
@@ -541,11 +903,8 @@ function popcount(value) {
|
|
|
541
903
|
/// works on the receiver's unsigned bit pattern and the rotate amount is taken
|
|
542
904
|
/// modulo the width.
|
|
543
905
|
function intMethodShim(detail) {
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
return undefined;
|
|
547
|
-
}
|
|
548
|
-
const [op, width] = parsed;
|
|
906
|
+
const [op, name] = detail;
|
|
907
|
+
const width = INT_WIDTHS[name];
|
|
549
908
|
const bits = BigInt(width.bits);
|
|
550
909
|
const unsigned = (args) => BigInt.asUintN(width.bits, integer(args, 0));
|
|
551
910
|
const result = (pattern) => int(wrapTo(width, pattern));
|
|
@@ -602,30 +961,34 @@ function intMethodShim(detail) {
|
|
|
602
961
|
return (_host, args) => rotate(args, true);
|
|
603
962
|
case "RotateRight":
|
|
604
963
|
return (_host, args) => rotate(args, false);
|
|
605
|
-
default:
|
|
606
|
-
|
|
964
|
+
default: {
|
|
965
|
+
const exhaustive = op;
|
|
966
|
+
throw new Error(`unknown integer operation ${String(exhaustive)}`);
|
|
967
|
+
}
|
|
607
968
|
}
|
|
608
969
|
}
|
|
609
970
|
/// Non-trapping integer arithmetic at the receiver's width: wrapping keeps
|
|
610
971
|
/// the low bits, saturating clamps to the width's bounds and checked answers
|
|
611
972
|
/// `None` where native's overflow flag is set.
|
|
612
973
|
function intArithShim(detail) {
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
return undefined;
|
|
616
|
-
}
|
|
617
|
-
const [kind, width] = parsed;
|
|
974
|
+
const [kind, name] = detail;
|
|
975
|
+
const width = INT_WIDTHS[name];
|
|
618
976
|
const [min, max] = intBounds(width);
|
|
977
|
+
const add = (x, y) => x + y;
|
|
978
|
+
const sub = (x, y) => x - y;
|
|
979
|
+
const mul = (x, y) => x * y;
|
|
619
980
|
const operations = {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
981
|
+
WrappingAdd: ["Wrapping", add],
|
|
982
|
+
WrappingSub: ["Wrapping", sub],
|
|
983
|
+
WrappingMul: ["Wrapping", mul],
|
|
984
|
+
SaturatingAdd: ["Saturating", add],
|
|
985
|
+
SaturatingSub: ["Saturating", sub],
|
|
986
|
+
SaturatingMul: ["Saturating", mul],
|
|
987
|
+
CheckedAdd: ["Checked", add],
|
|
988
|
+
CheckedSub: ["Checked", sub],
|
|
989
|
+
CheckedMul: ["Checked", mul],
|
|
623
990
|
};
|
|
624
|
-
const mode
|
|
625
|
-
const apply = mode ? operations[kind.slice(mode.length)] : undefined;
|
|
626
|
-
if (!mode || !apply) {
|
|
627
|
-
return undefined;
|
|
628
|
-
}
|
|
991
|
+
const [mode, apply] = operations[kind];
|
|
629
992
|
const exact = (args) => apply(wrapTo(width, integer(args, 0)), wrapTo(width, integer(args, 1)));
|
|
630
993
|
switch (mode) {
|
|
631
994
|
case "Wrapping":
|
|
@@ -665,6 +1028,45 @@ function bigMax(left, right) {
|
|
|
665
1028
|
}
|
|
666
1029
|
// ── externs ─────────────────────────────────────────────────────────────────
|
|
667
1030
|
const EXTERN_SHIMS = {
|
|
1031
|
+
hew_path_separator: () => str("/"),
|
|
1032
|
+
hew_calendar_format: {
|
|
1033
|
+
kind: "ordinary",
|
|
1034
|
+
bridge: "calendarBridge",
|
|
1035
|
+
shim: (host, args, _variant, shape) => executeCalendar(host.calendarBridge, "hew_calendar_format", args, shape),
|
|
1036
|
+
},
|
|
1037
|
+
hew_calendar_parse: {
|
|
1038
|
+
kind: "ordinary",
|
|
1039
|
+
bridge: "calendarBridge",
|
|
1040
|
+
shim: (host, args, _variant, shape) => executeCalendar(host.calendarBridge, "hew_calendar_parse", args, shape),
|
|
1041
|
+
},
|
|
1042
|
+
hew_calendar_components: {
|
|
1043
|
+
kind: "ordinary",
|
|
1044
|
+
bridge: "calendarBridge",
|
|
1045
|
+
shim: (host, args, _variant, shape) => executeCalendar(host.calendarBridge, "hew_calendar_components", args, shape),
|
|
1046
|
+
},
|
|
1047
|
+
hew_calendar_from_parts: {
|
|
1048
|
+
kind: "ordinary",
|
|
1049
|
+
bridge: "calendarBridge",
|
|
1050
|
+
shim: (host, args, _variant, shape) => executeCalendar(host.calendarBridge, "hew_calendar_from_parts", args, shape),
|
|
1051
|
+
},
|
|
1052
|
+
hew_args_count: (host) => int(BigInt(host.argv.length)),
|
|
1053
|
+
// A native null managed string is canonical empty. Each result owns fresh
|
|
1054
|
+
// string storage; releasing it cannot change the host's supplied arguments.
|
|
1055
|
+
hew_args_get: (host, args) => str(host.argv[index(args, 0)] ?? ""),
|
|
1056
|
+
hew_actor_timer_cancel: (_host, args) => {
|
|
1057
|
+
const timer = arg(args, 0);
|
|
1058
|
+
if (timer.kind !== "timer" || timer.closed)
|
|
1059
|
+
throw new Error("invalid timer handle");
|
|
1060
|
+
timer.cancel();
|
|
1061
|
+
return UNIT;
|
|
1062
|
+
},
|
|
1063
|
+
hew_actor_timer_release: (_host, args) => {
|
|
1064
|
+
const timer = arg(args, 0);
|
|
1065
|
+
if (timer.kind !== "timer" || timer.closed)
|
|
1066
|
+
throw new Error("invalid timer handle");
|
|
1067
|
+
timer.closed = true;
|
|
1068
|
+
return UNIT;
|
|
1069
|
+
},
|
|
668
1070
|
hew_msg_envelope_release: (host, args) => {
|
|
669
1071
|
host.releaseValue(arg(args, 0));
|
|
670
1072
|
return UNIT;
|
|
@@ -675,7 +1077,7 @@ const EXTERN_SHIMS = {
|
|
|
675
1077
|
host.pipes.freePair(arg(args, 0));
|
|
676
1078
|
return UNIT;
|
|
677
1079
|
},
|
|
678
|
-
hew_stream_last_error: () => (
|
|
1080
|
+
hew_stream_last_error: () => str(""),
|
|
679
1081
|
hew_regex_new: (host, args) => compileRegex(host, args),
|
|
680
1082
|
hew_regex_clone: (_host, args) => cloneValue(arg(args, 0)),
|
|
681
1083
|
// Releasing a handle the VM traces by reference is nothing to do.
|
|
@@ -721,6 +1123,47 @@ const EXTERN_SHIMS = {
|
|
|
721
1123
|
return int(high <= low ? low : low + host.prng.randbelow(high - low));
|
|
722
1124
|
},
|
|
723
1125
|
};
|
|
1126
|
+
export function decodeUtf8Checked(value) {
|
|
1127
|
+
if (value.kind !== "vector")
|
|
1128
|
+
throw new TypeError("UTF-8 input must be bytes");
|
|
1129
|
+
const bytes = Uint8Array.from(value.items.map((item) => Number(integer([item], 0))));
|
|
1130
|
+
let at = 0;
|
|
1131
|
+
while (at < bytes.length) {
|
|
1132
|
+
const first = bytes[at];
|
|
1133
|
+
if (first < 128) {
|
|
1134
|
+
at++;
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
const width = first >= 194 && first <= 223
|
|
1138
|
+
? 2
|
|
1139
|
+
: first >= 224 && first <= 239
|
|
1140
|
+
? 3
|
|
1141
|
+
: first >= 240 && first <= 244
|
|
1142
|
+
? 4
|
|
1143
|
+
: 0;
|
|
1144
|
+
if (width === 0)
|
|
1145
|
+
return { validUpTo: at, errorLen: 1 };
|
|
1146
|
+
for (let offset = 1; offset < width; offset++) {
|
|
1147
|
+
if (at + offset >= bytes.length)
|
|
1148
|
+
return { validUpTo: at, errorLen: null };
|
|
1149
|
+
const next = bytes[at + offset];
|
|
1150
|
+
const lower = offset === 1 && first === 224
|
|
1151
|
+
? 160
|
|
1152
|
+
: offset === 1 && first === 240
|
|
1153
|
+
? 144
|
|
1154
|
+
: 128;
|
|
1155
|
+
const upper = offset === 1 && first === 237
|
|
1156
|
+
? 159
|
|
1157
|
+
: offset === 1 && first === 244
|
|
1158
|
+
? 143
|
|
1159
|
+
: 191;
|
|
1160
|
+
if (next < lower || next > upper)
|
|
1161
|
+
return { validUpTo: at, errorLen: offset };
|
|
1162
|
+
}
|
|
1163
|
+
at += width;
|
|
1164
|
+
}
|
|
1165
|
+
return { value: new TextDecoder("utf-8", { ignoreBOM: true }).decode(bytes) };
|
|
1166
|
+
}
|
|
724
1167
|
/// `bytes` is a vector of byte values, so decoding walks its items.
|
|
725
1168
|
function decodeUtf8(value) {
|
|
726
1169
|
if (value.kind === "string") {
|
|
@@ -744,12 +1187,15 @@ function pattern(host, value) {
|
|
|
744
1187
|
return value.kind === "string" ? value.value : renderStdout(value);
|
|
745
1188
|
}
|
|
746
1189
|
// ── operand helpers ─────────────────────────────────────────────────────────
|
|
747
|
-
function detailName(detail) {
|
|
748
|
-
return typeof detail === "string" ? detail : "";
|
|
749
|
-
}
|
|
750
1190
|
function arg(args, at) {
|
|
751
1191
|
return args[at] ?? UNIT;
|
|
752
1192
|
}
|
|
1193
|
+
function indexed(args, at) {
|
|
1194
|
+
const value = arg(args, at);
|
|
1195
|
+
if (value.kind !== "string")
|
|
1196
|
+
throw new TypeError(`runtime call expected a string operand, got ${value.kind}`);
|
|
1197
|
+
return value;
|
|
1198
|
+
}
|
|
753
1199
|
function text(args, at) {
|
|
754
1200
|
const value = arg(args, at);
|
|
755
1201
|
return value.kind === "string" ? value.value : renderStdout(value);
|
|
@@ -779,7 +1225,7 @@ function comparable(value) {
|
|
|
779
1225
|
return `${value.kind}:${renderStdout(value)}`;
|
|
780
1226
|
}
|
|
781
1227
|
function str(value) {
|
|
782
|
-
return
|
|
1228
|
+
return stringValue(value);
|
|
783
1229
|
}
|
|
784
1230
|
function bool(value) {
|
|
785
1231
|
return { kind: "bool", value };
|
|
@@ -787,3 +1233,29 @@ function bool(value) {
|
|
|
787
1233
|
function int(value) {
|
|
788
1234
|
return { kind: "i64", value };
|
|
789
1235
|
}
|
|
1236
|
+
function checkedChar(host, args, shape) {
|
|
1237
|
+
if (!shape)
|
|
1238
|
+
throw new TypeError("try_to_char requires its Option shape");
|
|
1239
|
+
const value = integer(args, 0);
|
|
1240
|
+
return value < 0n ||
|
|
1241
|
+
value > 0x10ffffn ||
|
|
1242
|
+
(value >= 0xd800n && value <= 0xdfffn)
|
|
1243
|
+
? host.enumValue(shape, "OptionNone", [])
|
|
1244
|
+
: host.enumValue(shape, "OptionSome", [
|
|
1245
|
+
str(String.fromCodePoint(Number(value))),
|
|
1246
|
+
]);
|
|
1247
|
+
}
|
|
1248
|
+
function stringRange(args) {
|
|
1249
|
+
const value = indexed(args, 0);
|
|
1250
|
+
const inclusive = arg(args, 3);
|
|
1251
|
+
const openEnd = arg(args, 4);
|
|
1252
|
+
if (inclusive.kind !== "bool" || openEnd.kind !== "bool")
|
|
1253
|
+
throw new TypeError("string range flags must be bool");
|
|
1254
|
+
const start = integer(args, 1);
|
|
1255
|
+
const end = openEnd.value
|
|
1256
|
+
? BigInt(value.scalarLength)
|
|
1257
|
+
: integer(args, 2) + (inclusive.value ? 1n : 0n);
|
|
1258
|
+
if (start < 0n || end < start || end > BigInt(value.scalarLength))
|
|
1259
|
+
return undefined;
|
|
1260
|
+
return value.slice(Number(start), Number(end));
|
|
1261
|
+
}
|