@hew-lang/sandbox-vm 0.9.3 → 0.10.0
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/dist/interpreter/run-program.js +5 -0
- package/dist/interpreter/strings.d.ts +6 -0
- package/dist/interpreter/strings.js +8 -0
- package/dist/interpreter/types.d.ts +1 -1
- package/dist/interpreter/v1/browser-directory.js +2 -2
- package/dist/interpreter/v1/codecs.js +2 -2
- package/dist/interpreter/v1/encoding-storage.d.ts +33 -0
- package/dist/interpreter/v1/encoding-storage.js +113 -0
- package/dist/interpreter/v1/encoding-values.js +28 -18
- package/dist/interpreter/v1/exec.js +1506 -130
- package/dist/interpreter/v1/instructions.generated.d.ts +100 -8
- package/dist/interpreter/v1/instructions.generated.js +1 -0
- package/dist/interpreter/v1/managed-budget.d.ts +88 -0
- package/dist/interpreter/v1/managed-budget.js +561 -0
- package/dist/interpreter/v1/package-graph.js +77 -2
- package/dist/interpreter/v1/package-validator.generated.js +22796 -17531
- package/dist/interpreter/v1/package.d.ts +4 -2
- package/dist/interpreter/v1/package.js +6 -0
- package/dist/interpreter/v1/pipes.d.ts +24 -1
- package/dist/interpreter/v1/pipes.js +91 -8
- package/dist/interpreter/v1/shims.d.ts +1 -0
- package/dist/interpreter/v1/shims.js +88 -62
- package/dist/interpreter/v1/structural.js +4 -2
- package/dist/interpreter/v1/validate.js +4 -2
- package/dist/interpreter/v1/value-operations.d.ts +3 -1
- package/dist/interpreter/v1/value-operations.js +32 -18
- package/dist/interpreter/values.d.ts +45 -11
- package/dist/interpreter/values.js +176 -8
- package/package.json +4 -4
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import type { TermV1, VariantShape, RuntimeVariantRole } from "./instructions.generated.js";
|
|
2
|
-
export type { Own, BoundaryDecision, TrapName, SpanV1, BoundaryOperand, Edge, ValueDef, CallResult, OpV1, OpName, TermV1, TermName, SwitchArm, CheckedFailure, RequestShapes, ActorProtocol, ActorOperation, RuntimeFamilyEntry, AggregateShape, VariantCase, VariantShape, RuntimeVariantRole, ExternEntry, PlaceDecl, ValueCapability, ValueComponentCapabilities, ClosureShape, VtableShape, FunctionV1, BlockV1, EntryPlan, ActorShape, SupervisorShape, PackageV1, Resource, StructuralRecipe, } from "./instructions.generated.js";
|
|
1
|
+
import type { TermV1, BlockV1, OpV1, VariantShape, RuntimeVariantRole } from "./instructions.generated.js";
|
|
2
|
+
export type { Own, BoundaryDecision, TrapName, SpanV1, BoundaryOperand, Edge, ValueDef, CallResult, OpV1, OpName, TermV1, TermName, SwitchArm, CheckedFailure, RequestShapes, ActorProtocol, ActorOperation, RuntimeFamilyEntry, AggregateShape, VariantCase, VariantShape, RuntimeVariantRole, ExternEntry, PlaceDecl, ValueCapability, ValueComponentCapabilities, ClosureShape, VtableShape, FunctionV1, BlockV1, EntryPlan, ActorShape, SupervisorShape, PackageV1, Resource, StructuralRecipe, ManagedType, } from "./instructions.generated.js";
|
|
3
3
|
export type Operand = number;
|
|
4
4
|
export type CallShape = Pick<Extract<TermV1, {
|
|
5
5
|
op: "call";
|
|
6
6
|
}>, "args" | "result" | "normal" | "unwind" | "request_shapes">;
|
|
7
7
|
export declare function runtimeTag(shape: VariantShape, role: RuntimeVariantRole): number;
|
|
8
|
+
/** Static inventory includes a checked operation; execution follows its edge. */
|
|
9
|
+
export declare function staticOperations(block: BlockV1): Iterable<OpV1>;
|
|
@@ -7,3 +7,9 @@ export function runtimeTag(shape, role) {
|
|
|
7
7
|
throw new Error(`variant shape ${shape.id} has no checked ${role} tag`);
|
|
8
8
|
return tag;
|
|
9
9
|
}
|
|
10
|
+
/** Static inventory includes a checked operation; execution follows its edge. */
|
|
11
|
+
export function* staticOperations(block) {
|
|
12
|
+
yield* block.ops;
|
|
13
|
+
if (block.term.op === "materialize")
|
|
14
|
+
yield block.term.operation;
|
|
15
|
+
}
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import { type VmValue } from "../values.js";
|
|
2
2
|
interface PendingRead {
|
|
3
|
-
complete(value: VmValue | null, fault: string | null): void;
|
|
3
|
+
complete(value: VmValue | null, fault: string | null, released?: () => void): void;
|
|
4
|
+
}
|
|
5
|
+
interface Envelope {
|
|
6
|
+
value: VmValue;
|
|
7
|
+
release?: () => void;
|
|
8
|
+
}
|
|
9
|
+
interface Receiver {
|
|
10
|
+
owner: object;
|
|
11
|
+
acquire(envelope: Envelope): () => void;
|
|
12
|
+
refused(): void;
|
|
4
13
|
}
|
|
5
14
|
export declare class Pipes {
|
|
6
15
|
private readonly pairs;
|
|
7
16
|
private readonly halves;
|
|
8
17
|
private next;
|
|
18
|
+
private managedReceivers;
|
|
19
|
+
get hasManagedReceivers(): boolean;
|
|
9
20
|
create(id: string, capacity: number): VmValue;
|
|
10
21
|
freePair(pair: VmValue): void;
|
|
11
22
|
extract(pair: VmValue, kind: "sink" | "stream"): VmValue;
|
|
@@ -14,6 +25,11 @@ export declare class Pipes {
|
|
|
14
25
|
cloneSink(value: VmValue): VmValue;
|
|
15
26
|
isClosed(value: VmValue): boolean;
|
|
16
27
|
close(value: VmValue, fault?: string | null): VmValue[];
|
|
28
|
+
finishClose(value: VmValue): void;
|
|
29
|
+
/** Called only after ownership acquisition succeeds. The receiving half
|
|
30
|
+
* carries queued envelopes; producer aliases never acquire them. */
|
|
31
|
+
bind(value: VmValue, receiver?: Receiver): void;
|
|
32
|
+
private admit;
|
|
17
33
|
peerClosed(value: VmValue): boolean;
|
|
18
34
|
readable(value: VmValue): boolean;
|
|
19
35
|
observe(value: VmValue, changed: () => void): () => void;
|
|
@@ -21,5 +37,12 @@ export declare class Pipes {
|
|
|
21
37
|
receive(value: VmValue, park: boolean, complete: PendingRead["complete"]): () => void;
|
|
22
38
|
private flush;
|
|
23
39
|
private notify;
|
|
40
|
+
/** Only the unique receiving half owns admitted typed envelopes. */
|
|
41
|
+
managedRetained(value: VmValue): {
|
|
42
|
+
identity: object;
|
|
43
|
+
queued: readonly {
|
|
44
|
+
value: VmValue;
|
|
45
|
+
}[];
|
|
46
|
+
};
|
|
24
47
|
}
|
|
25
48
|
export {};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { HeapExceeded } from "./managed-budget.js";
|
|
1
2
|
/// The VM's one bounded pipe substrate. Selection observes readiness; only
|
|
2
3
|
/// the winning receive removes an item. Values transfer without a snapshot.
|
|
3
4
|
export class Pipes {
|
|
4
5
|
pairs = new Map();
|
|
5
6
|
halves = new Map();
|
|
6
7
|
next = 0;
|
|
8
|
+
managedReceivers = 0;
|
|
9
|
+
get hasManagedReceivers() {
|
|
10
|
+
return this.managedReceivers !== 0;
|
|
11
|
+
}
|
|
7
12
|
create(id, capacity) {
|
|
8
13
|
this.pairs.set(id, {
|
|
9
14
|
capacity,
|
|
@@ -65,11 +70,58 @@ export class Pipes {
|
|
|
65
70
|
}
|
|
66
71
|
else {
|
|
67
72
|
half.pipe.readerOpen = false;
|
|
68
|
-
|
|
73
|
+
if (half.pipe.receiver)
|
|
74
|
+
this.managedReceivers--;
|
|
75
|
+
half.pipe.receiver = undefined;
|
|
76
|
+
for (const item of half.pipe.queue.splice(0)) {
|
|
77
|
+
discarded.push(item.value);
|
|
78
|
+
if (item.release)
|
|
79
|
+
(half.retiring ??= []).push(item.release);
|
|
80
|
+
}
|
|
69
81
|
}
|
|
70
82
|
this.flush(half.pipe);
|
|
71
83
|
return discarded;
|
|
72
84
|
}
|
|
85
|
+
finishClose(value) {
|
|
86
|
+
if (value.kind !== "stream" && value.kind !== "sink")
|
|
87
|
+
return;
|
|
88
|
+
for (const release of this.halves
|
|
89
|
+
.get(value.channelId)
|
|
90
|
+
?.retiring?.splice(0) ?? [])
|
|
91
|
+
release();
|
|
92
|
+
}
|
|
93
|
+
/** Called only after ownership acquisition succeeds. The receiving half
|
|
94
|
+
* carries queued envelopes; producer aliases never acquire them. */
|
|
95
|
+
bind(value, receiver) {
|
|
96
|
+
const half = this.get(value);
|
|
97
|
+
if (half.kind !== "stream" || half.pipe.receiver?.owner === receiver?.owner)
|
|
98
|
+
return;
|
|
99
|
+
this.managedReceivers += Number(!!receiver) - Number(!!half.pipe.receiver);
|
|
100
|
+
half.pipe.receiver = receiver;
|
|
101
|
+
for (const envelope of half.pipe.queue) {
|
|
102
|
+
const release = receiver?.acquire(envelope);
|
|
103
|
+
envelope.release?.();
|
|
104
|
+
envelope.release = release;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
admit(pipe, value) {
|
|
108
|
+
const envelope = { value };
|
|
109
|
+
try {
|
|
110
|
+
envelope.release = pipe.receiver?.acquire(envelope);
|
|
111
|
+
return envelope;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (!(error instanceof HeapExceeded))
|
|
115
|
+
throw error;
|
|
116
|
+
// Acquisition is atomic. A refused recipient follows its existing
|
|
117
|
+
// checked failure path while the producer still owns the item.
|
|
118
|
+
pipe.readerOpen = false;
|
|
119
|
+
pipe.fault = "actor heap limit exceeded";
|
|
120
|
+
pipe.receiver?.refused();
|
|
121
|
+
this.flush(pipe);
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
73
125
|
peerClosed(value) {
|
|
74
126
|
return !this.get(value).pipe.readerOpen;
|
|
75
127
|
}
|
|
@@ -94,11 +146,21 @@ export class Pipes {
|
|
|
94
146
|
return () => { };
|
|
95
147
|
}
|
|
96
148
|
if (pipe.reading.length > 0) {
|
|
97
|
-
|
|
149
|
+
const envelope = this.admit(pipe, item);
|
|
150
|
+
if (!envelope) {
|
|
151
|
+
complete(1);
|
|
152
|
+
return () => { };
|
|
153
|
+
}
|
|
154
|
+
pipe.reading.shift().complete(item, null, envelope.release);
|
|
98
155
|
complete(0);
|
|
99
156
|
}
|
|
100
157
|
else if (pipe.queue.length < pipe.capacity) {
|
|
101
|
-
|
|
158
|
+
const envelope = this.admit(pipe, item);
|
|
159
|
+
if (!envelope) {
|
|
160
|
+
complete(1);
|
|
161
|
+
return () => { };
|
|
162
|
+
}
|
|
163
|
+
pipe.queue.push(envelope);
|
|
102
164
|
complete(0);
|
|
103
165
|
}
|
|
104
166
|
else if (!park) {
|
|
@@ -120,13 +182,21 @@ export class Pipes {
|
|
|
120
182
|
receive(value, park, complete) {
|
|
121
183
|
const pipe = this.get(value).pipe;
|
|
122
184
|
if (pipe.queue.length > 0) {
|
|
123
|
-
|
|
185
|
+
const envelope = pipe.queue.shift();
|
|
186
|
+
complete(envelope.value, null, envelope.release);
|
|
124
187
|
this.flush(pipe);
|
|
125
188
|
}
|
|
126
189
|
else if (pipe.sending.length > 0) {
|
|
127
190
|
const sender = pipe.sending.shift();
|
|
128
|
-
|
|
129
|
-
|
|
191
|
+
const envelope = this.admit(pipe, sender.value);
|
|
192
|
+
if (envelope) {
|
|
193
|
+
complete(sender.value, null, envelope.release);
|
|
194
|
+
sender.complete(0);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
complete(null, pipe.fault);
|
|
198
|
+
sender.complete(1);
|
|
199
|
+
}
|
|
130
200
|
}
|
|
131
201
|
else if (pipe.fault !== null ||
|
|
132
202
|
pipe.sinks === 0 ||
|
|
@@ -155,11 +225,16 @@ export class Pipes {
|
|
|
155
225
|
while (pipe.sending.length > 0 &&
|
|
156
226
|
(pipe.reading.length > 0 || pipe.queue.length < pipe.capacity)) {
|
|
157
227
|
const sender = pipe.sending.shift();
|
|
228
|
+
const envelope = this.admit(pipe, sender.value);
|
|
229
|
+
if (!envelope) {
|
|
230
|
+
sender.complete(1);
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
158
233
|
const reader = pipe.reading.shift();
|
|
159
234
|
if (reader)
|
|
160
|
-
reader.complete(sender.value, null);
|
|
235
|
+
reader.complete(sender.value, null, envelope.release);
|
|
161
236
|
else
|
|
162
|
-
pipe.queue.push(
|
|
237
|
+
pipe.queue.push(envelope);
|
|
163
238
|
sender.complete(0);
|
|
164
239
|
}
|
|
165
240
|
}
|
|
@@ -175,4 +250,12 @@ export class Pipes {
|
|
|
175
250
|
for (const observer of [...pipe.observers])
|
|
176
251
|
observer();
|
|
177
252
|
}
|
|
253
|
+
/** Only the unique receiving half owns admitted typed envelopes. */
|
|
254
|
+
managedRetained(value) {
|
|
255
|
+
const half = this.get(value);
|
|
256
|
+
return {
|
|
257
|
+
identity: half.kind === "stream" ? half.pipe : half,
|
|
258
|
+
queued: half.open && half.kind === "stream" ? half.pipe.queue : [],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
178
261
|
}
|
|
@@ -27,6 +27,7 @@ export interface ShimHost {
|
|
|
27
27
|
writeStdout(text: string): void;
|
|
28
28
|
writeStderr(text: string): void;
|
|
29
29
|
prng: Mt19937;
|
|
30
|
+
arenaInstanceId(): bigint;
|
|
30
31
|
regexPatterns: readonly string[];
|
|
31
32
|
enumValue(shape: VariantShape, role: RuntimeVariantRole, payload: VmValue[]): VmValue;
|
|
32
33
|
}
|
|
@@ -4,7 +4,7 @@ import { executeEncoding } from "./encoding-values.js";
|
|
|
4
4
|
import { charProperty, charCase } from "../unicode.js";
|
|
5
5
|
/// Runtime implementations and their host execution registration. Admission and
|
|
6
6
|
/// execution consume the same ordinary, resumable or refused disposition.
|
|
7
|
-
import { UNIT, cloneValue, compareUtf8, stringValue, renderStdout, } from "../values.js";
|
|
7
|
+
import { UNIT, byteVectorValue, clearByteVector, mutableVector, vectorItems, vectorLength, vectorElement, takeVectorEnd, sliceByteVector, cloneValue, compareUtf8, stringValue, renderStdout, } from "../values.js";
|
|
8
8
|
import { seededMt } from "./mt19937.js";
|
|
9
9
|
/// A language-visible fault raised inside a shim. The executor routes it to the
|
|
10
10
|
/// call's `unwind` edge, or ends the program when the call names none.
|
|
@@ -122,7 +122,6 @@ export function resolveRuntime(entry) {
|
|
|
122
122
|
});
|
|
123
123
|
case "GetBorrow":
|
|
124
124
|
case "TakeFirst":
|
|
125
|
-
case "Append":
|
|
126
125
|
return refused(`Vector::${entry.detail}`);
|
|
127
126
|
default:
|
|
128
127
|
return ordinary(VECTOR_SHIMS[entry.detail]);
|
|
@@ -295,13 +294,19 @@ const UNIT_FAMILIES = {
|
|
|
295
294
|
host.writeStderr(text(args, 0));
|
|
296
295
|
return UNIT;
|
|
297
296
|
},
|
|
298
|
-
StringToBytes: (_host, args) => (
|
|
299
|
-
kind: "vector",
|
|
300
|
-
elementType: "u8",
|
|
301
|
-
items: [...indexed(args, 0).encoded()].map((byte) => int(BigInt(byte))),
|
|
302
|
-
}),
|
|
297
|
+
StringToBytes: (_host, args) => byteVectorValue([...indexed(args, 0).encoded()].map((byte) => int(BigInt(byte)))),
|
|
303
298
|
StringToUppercase: (_host, args) => str(text(args, 0).toUpperCase()),
|
|
304
299
|
StringToLowercase: (_host, args) => str(text(args, 0).toLowerCase()),
|
|
300
|
+
StreamPipe: (host, args) => {
|
|
301
|
+
const pair = host.newPipe(Number(integer(args, 0)));
|
|
302
|
+
const fields = [
|
|
303
|
+
host.pipes.extract(pair, "sink"),
|
|
304
|
+
host.pipes.extract(pair, "stream"),
|
|
305
|
+
bool(true),
|
|
306
|
+
];
|
|
307
|
+
host.pipes.freePair(pair);
|
|
308
|
+
return { kind: "record", typeId: "", fields };
|
|
309
|
+
},
|
|
305
310
|
StreamPairSink: (host, args) => host.pipes.extract(arg(args, 0), "sink"),
|
|
306
311
|
StreamPairStream: (host, args) => host.pipes.extract(arg(args, 0), "stream"),
|
|
307
312
|
StreamFromGenerator: (host, args) => host.generatorStream(arg(args, 0)),
|
|
@@ -440,68 +445,68 @@ const UNIT_FAMILIES = {
|
|
|
440
445
|
// TextDecoder replaces each maximal invalid subsequence with U+FFFD, as
|
|
441
446
|
// native lossy decoding does.
|
|
442
447
|
BytesDecodeUtf8Lossy: (_host, args) => str(decodeUtf8(arg(args, 0))),
|
|
443
|
-
BytesNew: () => (
|
|
444
|
-
BytesIsEmpty: (_host, args) => bool(vec(args, 0)
|
|
445
|
-
BytesLen: (_host, args) => int(BigInt(vec(args, 0)
|
|
448
|
+
BytesNew: () => byteVectorValue([]),
|
|
449
|
+
BytesIsEmpty: (_host, args) => bool(vectorLength(vec(args, 0)) === 0),
|
|
450
|
+
BytesLen: (_host, args) => int(BigInt(vectorLength(vec(args, 0)))),
|
|
446
451
|
BytesContains: (_host, args) => {
|
|
447
452
|
const byte = integer(args, 1);
|
|
448
|
-
return bool(vec(args, 0).
|
|
453
|
+
return bool(vectorItems(vec(args, 0)).some((item) => integer([item], 0) === byte));
|
|
449
454
|
},
|
|
450
455
|
BytesIndex: (_host, args) => {
|
|
451
|
-
const items = vec(args, 0)
|
|
456
|
+
const items = vectorItems(vec(args, 0));
|
|
452
457
|
const at = index(args, 1);
|
|
453
458
|
if (at < 0 || at >= items.length) {
|
|
454
459
|
throw new ShimFault("vector_bounds", `bytes index ${at} out of bounds for length ${items.length}`);
|
|
455
460
|
}
|
|
456
461
|
return cloneValue(items[at]);
|
|
457
462
|
},
|
|
458
|
-
//
|
|
459
|
-
// a copy is observably the same.
|
|
463
|
+
// Nonempty views retain the exact original allocation and payload capacity.
|
|
460
464
|
BytesSlice: (_host, args) => {
|
|
461
|
-
const items = vec(args, 0)
|
|
465
|
+
const items = vectorItems(vec(args, 0));
|
|
462
466
|
const start = index(args, 1);
|
|
463
467
|
const end = index(args, 2);
|
|
464
468
|
checkSliceBounds(start, end, items.length);
|
|
465
|
-
return
|
|
469
|
+
return sliceByteVector(vec(args, 0), start, end);
|
|
466
470
|
},
|
|
467
471
|
BytesSliceFrom: (_host, args) => {
|
|
468
|
-
const items = vec(args, 0)
|
|
472
|
+
const items = vectorItems(vec(args, 0));
|
|
469
473
|
const start = index(args, 1);
|
|
470
474
|
checkSliceBounds(start, items.length, items.length);
|
|
471
|
-
return
|
|
475
|
+
return sliceByteVector(vec(args, 0), start, items.length);
|
|
472
476
|
},
|
|
473
|
-
BytesConcat: (_host, args) => byteVector([...vec(args, 0)
|
|
474
|
-
BytesStartsWith: (_host, args) => bool(runAt(vec(args, 0)
|
|
477
|
+
BytesConcat: (_host, args) => byteVector([...vectorItems(vec(args, 0)), ...vectorItems(vec(args, 1))]),
|
|
478
|
+
BytesStartsWith: (_host, args) => bool(runAt(vectorItems(vec(args, 0)), vectorItems(vec(args, 1)), 0)),
|
|
475
479
|
BytesEndsWith: (_host, args) => {
|
|
476
|
-
const items = vec(args, 0)
|
|
477
|
-
const run = vec(args, 1)
|
|
480
|
+
const items = vectorItems(vec(args, 0));
|
|
481
|
+
const run = vectorItems(vec(args, 1));
|
|
478
482
|
return bool(runAt(items, run, items.length - run.length));
|
|
479
483
|
},
|
|
480
484
|
// A mutating call receives its receiver by `move` and returns it.
|
|
481
485
|
BytesPush: (_host, args) => {
|
|
482
486
|
const target = vec(args, 0);
|
|
487
|
+
mutableVector(target, vectorLength(target) + 1);
|
|
483
488
|
target.items.push(cloneValue(arg(args, 1)));
|
|
484
489
|
return target;
|
|
485
490
|
},
|
|
486
491
|
BytesSet: (_host, args) => {
|
|
487
492
|
const target = vec(args, 0);
|
|
488
493
|
const at = index(args, 1);
|
|
489
|
-
if (at < 0 || at >= target
|
|
490
|
-
throw new ShimFault("vector_bounds", `bytes index ${at} out of bounds for length ${target
|
|
494
|
+
if (at < 0 || at >= vectorLength(target)) {
|
|
495
|
+
throw new ShimFault("vector_bounds", `bytes index ${at} out of bounds for length ${vectorLength(target)}`);
|
|
491
496
|
}
|
|
497
|
+
mutableVector(target);
|
|
492
498
|
target.items[at] = cloneValue(arg(args, 2));
|
|
493
499
|
return target;
|
|
494
500
|
},
|
|
495
501
|
BytesClear: (_host, args) => {
|
|
496
502
|
const target = vec(args, 0);
|
|
497
|
-
target
|
|
498
|
-
return target;
|
|
503
|
+
return clearByteVector(target);
|
|
499
504
|
},
|
|
500
505
|
BytesGet: (host, args, shape) => {
|
|
501
506
|
if (!shape) {
|
|
502
507
|
throw new TypeError("BytesGet names no result shape to build its Option against");
|
|
503
508
|
}
|
|
504
|
-
const items = vec(args, 0)
|
|
509
|
+
const items = vectorItems(vec(args, 0));
|
|
505
510
|
const at = index(args, 1);
|
|
506
511
|
return at >= 0 && at < items.length
|
|
507
512
|
? host.enumValue(shape, "OptionSome", [cloneValue(items[at])])
|
|
@@ -511,7 +516,9 @@ const UNIT_FAMILIES = {
|
|
|
511
516
|
// append, paired with the removed byte for a pop.
|
|
512
517
|
BytesAppend: (_host, args) => {
|
|
513
518
|
const target = vec(args, 0);
|
|
514
|
-
|
|
519
|
+
const added = vectorItems(vec(args, 1)).map(cloneValue);
|
|
520
|
+
mutableVector(target, vectorLength(target) + added.length);
|
|
521
|
+
target.items.push(...added);
|
|
515
522
|
return target;
|
|
516
523
|
},
|
|
517
524
|
BytesPop: (host, args, shape) => {
|
|
@@ -519,6 +526,8 @@ const UNIT_FAMILIES = {
|
|
|
519
526
|
throw new TypeError("BytesPop names no result shape to build its Option against");
|
|
520
527
|
}
|
|
521
528
|
const target = vec(args, 0);
|
|
529
|
+
if (vectorLength(target))
|
|
530
|
+
mutableVector(target);
|
|
522
531
|
const last = target.items.pop();
|
|
523
532
|
return {
|
|
524
533
|
kind: "record",
|
|
@@ -645,9 +654,7 @@ const UNIT_FAMILIES = {
|
|
|
645
654
|
ReplyPayloadFree: refused("ReplyPayloadFree"),
|
|
646
655
|
ReplyWait: refused("ReplyWait"),
|
|
647
656
|
SelectFirst: refused("SelectFirst"),
|
|
648
|
-
StreamChunks: refused("StreamChunks"),
|
|
649
657
|
StreamForward: refused("StreamForward"),
|
|
650
|
-
StreamLines: refused("StreamLines"),
|
|
651
658
|
StreamNextLayout: refused("StreamNextLayout"),
|
|
652
659
|
StreamSendLayout: refused("StreamSendLayout"),
|
|
653
660
|
StreamTake: refused("StreamTake"),
|
|
@@ -701,11 +708,12 @@ const VECTOR_SHIMS = {
|
|
|
701
708
|
const left = index(args, 1);
|
|
702
709
|
const right = index(args, 2);
|
|
703
710
|
if (left < 0 ||
|
|
704
|
-
left >= target
|
|
711
|
+
left >= vectorLength(target) ||
|
|
705
712
|
right < 0 ||
|
|
706
|
-
right >= target
|
|
713
|
+
right >= vectorLength(target)) {
|
|
707
714
|
throw new ShimFault("vector_bounds", "vector swap index out of bounds");
|
|
708
715
|
}
|
|
716
|
+
mutableVector(target);
|
|
709
717
|
[target.items[left], target.items[right]] = [
|
|
710
718
|
target.items[right],
|
|
711
719
|
target.items[left],
|
|
@@ -714,13 +722,13 @@ const VECTOR_SHIMS = {
|
|
|
714
722
|
},
|
|
715
723
|
Pop: (_host, args) => {
|
|
716
724
|
const target = vec(args, 0);
|
|
717
|
-
if (target
|
|
725
|
+
if (vectorLength(target) === 0) {
|
|
718
726
|
throw new ShimFault("vector_bounds", "pop from empty vector");
|
|
719
727
|
}
|
|
720
728
|
return {
|
|
721
729
|
kind: "record",
|
|
722
730
|
typeId: "",
|
|
723
|
-
fields: [target, target
|
|
731
|
+
fields: [target, takeVectorEnd(target, true)],
|
|
724
732
|
};
|
|
725
733
|
},
|
|
726
734
|
New: () => ({ kind: "vector", elementType: "", items: [] }),
|
|
@@ -729,69 +737,83 @@ const VECTOR_SHIMS = {
|
|
|
729
737
|
// ownership decision SIR proved, so it moves in as it arrives.
|
|
730
738
|
Push: (_host, args) => {
|
|
731
739
|
const target = vec(args, 0);
|
|
740
|
+
mutableVector(target, vectorLength(target) + 1);
|
|
732
741
|
target.items.push(arg(args, 1));
|
|
733
742
|
return target;
|
|
734
743
|
},
|
|
744
|
+
Append: (_host, args) => {
|
|
745
|
+
const target = vec(args, 0);
|
|
746
|
+
const added = [...vectorItems(vec(args, 1))];
|
|
747
|
+
if (added.length) {
|
|
748
|
+
mutableVector(target, vectorLength(target) + added.length, added);
|
|
749
|
+
target.items.push(...added.map(cloneValue));
|
|
750
|
+
}
|
|
751
|
+
return target;
|
|
752
|
+
},
|
|
735
753
|
Remove: (_host, args) => {
|
|
736
754
|
const target = vec(args, 0);
|
|
737
755
|
const at = index(args, 1);
|
|
738
|
-
if (at < 0 || at >= target
|
|
739
|
-
throw new ShimFault("vector_bounds", `vector index ${at} out of bounds for length ${target
|
|
756
|
+
if (at < 0 || at >= vectorLength(target)) {
|
|
757
|
+
throw new ShimFault("vector_bounds", `vector index ${at} out of bounds for length ${vectorLength(target)}`);
|
|
740
758
|
}
|
|
741
|
-
const
|
|
759
|
+
const item = at === 0
|
|
760
|
+
? takeVectorEnd(target, false)
|
|
761
|
+
: mutableVector(target).items.splice(at, 1)[0];
|
|
742
762
|
return { kind: "record", typeId: "", fields: [target, item] };
|
|
743
763
|
},
|
|
744
764
|
Get: (host, args, shape) => {
|
|
745
765
|
if (!shape) {
|
|
746
766
|
throw new TypeError("Vector::Get names no result shape to build its Option against");
|
|
747
767
|
}
|
|
748
|
-
const
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
? host.enumValue(shape, "OptionSome", [cloneValue(items[at])])
|
|
768
|
+
const item = vectorElement(vec(args, 0), index(args, 1));
|
|
769
|
+
return item
|
|
770
|
+
? host.enumValue(shape, "OptionSome", [cloneValue(item)])
|
|
752
771
|
: host.enumValue(shape, "OptionNone", []);
|
|
753
772
|
},
|
|
754
773
|
Index: (_host, args) => cloneValue(sequenceElement(args)),
|
|
755
774
|
IndexBorrow: (_host, args) => sequenceElement(args),
|
|
756
|
-
Len: (_host, args) => int(BigInt(vec(args, 0)
|
|
775
|
+
Len: (_host, args) => int(BigInt(vectorLength(vec(args, 0)))),
|
|
757
776
|
Join: (_host, args) => {
|
|
758
|
-
const items = vec(args, 0)
|
|
777
|
+
const items = vectorItems(vec(args, 0));
|
|
759
778
|
return str(items
|
|
760
779
|
.map((_, at) => indexed(items, at).value)
|
|
761
780
|
.join(indexed(args, 1).value));
|
|
762
781
|
},
|
|
763
782
|
Slice: (_host, args) => {
|
|
764
|
-
const items = vec(args, 0)
|
|
783
|
+
const items = vectorItems(vec(args, 0));
|
|
765
784
|
const start = index(args, 1);
|
|
766
785
|
const end = index(args, 2);
|
|
767
786
|
checkSliceBounds(start, end, items.length);
|
|
768
|
-
return
|
|
769
|
-
kind: "vector",
|
|
770
|
-
elementType: "",
|
|
771
|
-
items: items.slice(start, end).map(cloneValue),
|
|
772
|
-
};
|
|
787
|
+
return vectorSlice(items.slice(start, end));
|
|
773
788
|
},
|
|
774
789
|
SliceFrom: (_host, args) => {
|
|
775
|
-
const items = vec(args, 0)
|
|
790
|
+
const items = vectorItems(vec(args, 0));
|
|
776
791
|
const start = index(args, 1);
|
|
777
792
|
checkSliceBounds(start, items.length, items.length);
|
|
778
|
-
return
|
|
779
|
-
kind: "vector",
|
|
780
|
-
elementType: "",
|
|
781
|
-
items: items.slice(start).map(cloneValue),
|
|
782
|
-
};
|
|
793
|
+
return vectorSlice(items.slice(start));
|
|
783
794
|
},
|
|
784
795
|
};
|
|
796
|
+
function vectorSlice(items) {
|
|
797
|
+
const result = {
|
|
798
|
+
kind: "vector",
|
|
799
|
+
elementType: "",
|
|
800
|
+
items: [],
|
|
801
|
+
};
|
|
802
|
+
mutableVector(result, items.length);
|
|
803
|
+
result.items.push(...items.map(cloneValue));
|
|
804
|
+
return result;
|
|
805
|
+
}
|
|
785
806
|
function sequenceElement(args) {
|
|
786
|
-
const
|
|
807
|
+
const target = vec(args, 0);
|
|
787
808
|
const at = index(args, 1);
|
|
788
|
-
|
|
789
|
-
|
|
809
|
+
const item = vectorElement(target, at);
|
|
810
|
+
if (!item) {
|
|
811
|
+
throw new ShimFault("vector_bounds", `vector index ${at} out of bounds for length ${vectorLength(target)}`);
|
|
790
812
|
}
|
|
791
|
-
return
|
|
813
|
+
return item;
|
|
792
814
|
}
|
|
793
815
|
function byteVector(items) {
|
|
794
|
-
return
|
|
816
|
+
return byteVectorValue(items.map(cloneValue));
|
|
795
817
|
}
|
|
796
818
|
/// Whether `run` occurs in `items` starting at `at`.
|
|
797
819
|
function runAt(items, run, at) {
|
|
@@ -1092,6 +1114,10 @@ function saturatingI64(value) {
|
|
|
1092
1114
|
}
|
|
1093
1115
|
// ── externs ─────────────────────────────────────────────────────────────────
|
|
1094
1116
|
const EXTERN_SHIMS = {
|
|
1117
|
+
hew_arena_instance_id_new: (host) => ({
|
|
1118
|
+
kind: "i64",
|
|
1119
|
+
value: host.arenaInstanceId(),
|
|
1120
|
+
}),
|
|
1095
1121
|
hew_semaphore_new: (host, args) => host.semaphores.create(integer(args, 0)),
|
|
1096
1122
|
hew_semaphore_clone: (host, args) => host.semaphores.clone(arg(args, 0)),
|
|
1097
1123
|
hew_semaphore_try_acquire: (host, args) => bool(host.semaphores.tryAcquire(arg(args, 0))),
|
|
@@ -1204,7 +1230,7 @@ const EXTERN_SHIMS = {
|
|
|
1204
1230
|
export function decodeUtf8Checked(value) {
|
|
1205
1231
|
if (value.kind !== "vector")
|
|
1206
1232
|
throw new TypeError("UTF-8 input must be bytes");
|
|
1207
|
-
const bytes = Uint8Array.from(value.
|
|
1233
|
+
const bytes = Uint8Array.from(vectorItems(value).map((item) => Number(integer([item], 0))));
|
|
1208
1234
|
let at = 0;
|
|
1209
1235
|
while (at < bytes.length) {
|
|
1210
1236
|
const first = bytes[at];
|
|
@@ -1250,7 +1276,7 @@ function decodeUtf8(value) {
|
|
|
1250
1276
|
if (value.kind !== "vector") {
|
|
1251
1277
|
return renderStdout(value);
|
|
1252
1278
|
}
|
|
1253
|
-
return new TextDecoder().decode(Uint8Array.from(value.
|
|
1279
|
+
return new TextDecoder().decode(Uint8Array.from(vectorItems(value).map((item) => item.kind === "i64" ? Number(item.value) : 0)));
|
|
1254
1280
|
}
|
|
1255
1281
|
function compileRegex(host, args) {
|
|
1256
1282
|
const source = pattern(host, arg(args, 0));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renderStdout } from "../values.js";
|
|
1
|
+
import { vectorItems, renderStdout } from "../values.js";
|
|
2
2
|
/** A borrowed traversal. Display callbacks can suspend, so their callers retain
|
|
3
3
|
* this stack and resume it with the returned string. No later sibling is read
|
|
4
4
|
* after a callback fails. */
|
|
@@ -49,7 +49,9 @@ export class StructuralRenderer {
|
|
|
49
49
|
case "vector": {
|
|
50
50
|
if (value.kind !== "vector")
|
|
51
51
|
throw new Error("structural vector has no elements");
|
|
52
|
-
this.sequence("[", "]", value.
|
|
52
|
+
this.sequence("[", "]", vectorItems(value).map((value) => [
|
|
53
|
+
{ value, recipe: recipe.members[0] },
|
|
54
|
+
]));
|
|
53
55
|
break;
|
|
54
56
|
}
|
|
55
57
|
case "map": {
|
|
@@ -210,7 +210,8 @@ export function admitPackage(pkg, bridges = {}) {
|
|
|
210
210
|
const supervisor = pkg.supervisors?.find((layout) => layout.id === id);
|
|
211
211
|
const config = term.args.length -
|
|
212
212
|
(supervisor?.children.some((child) => child.is_pool) ? 1 : 0) -
|
|
213
|
-
|
|
213
|
+
2 *
|
|
214
|
+
(supervisor?.children.filter((child) => child.backoff).length ?? 0);
|
|
214
215
|
if (!supervisor ||
|
|
215
216
|
config < 0 ||
|
|
216
217
|
supervisor.children.some((child) => {
|
|
@@ -294,7 +295,8 @@ export function admitPackage(pkg, bridges = {}) {
|
|
|
294
295
|
};
|
|
295
296
|
if (actor.exit != null || actor.down != null)
|
|
296
297
|
return unavailable("actor.local_observation");
|
|
297
|
-
if (actor.max_heap_bytes != null
|
|
298
|
+
if (actor.max_heap_bytes != null &&
|
|
299
|
+
(actor.state_type == null || !pkg.managed_types?.length))
|
|
298
300
|
return unavailable("actor.heap_limit");
|
|
299
301
|
}
|
|
300
302
|
for (const supervisor of pkg.supervisors ?? []) {
|
|
@@ -12,6 +12,8 @@ export type ValueRequest = {
|
|
|
12
12
|
kind: "release";
|
|
13
13
|
value: VmValue;
|
|
14
14
|
onFault?: VmValue;
|
|
15
|
+
retained?: readonly VmValue[];
|
|
16
|
+
retainedType?: number;
|
|
15
17
|
};
|
|
16
18
|
export type ValueProgram = Generator<ValueRequest, VmValue, VmValue>;
|
|
17
19
|
/** Execute the exact checked plan, including every derived component selection.
|
|
@@ -25,4 +27,4 @@ export declare function sharedOperation(operation: Extract<RuntimeContinuation,
|
|
|
25
27
|
* borrow their owners until every fallible callback has completed. */
|
|
26
28
|
export declare function collectionOperation(pkg: PackageV1, request: Extract<RuntimeContinuation, {
|
|
27
29
|
handler: "collection";
|
|
28
|
-
}>, args: VmValue[], callbacks: number[], shape: number | null | undefined): ValueProgram;
|
|
30
|
+
}>, args: VmValue[], callbacks: number[], shape: number | null | undefined, elementType?: number): ValueProgram;
|