@hew-lang/sandbox-vm 0.6.0-rc7 → 0.6.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/README.md +6 -0
- package/cli/test-runner.mjs +84 -0
- package/dist/interpreter/parity-runner.js +9 -0
- package/dist/interpreter/run-program.d.ts +3 -2
- package/dist/interpreter/run-program.js +5 -4
- package/dist/interpreter/trace.d.ts +1 -0
- package/dist/interpreter/trace.js +4 -0
- package/dist/interpreter/types.d.ts +3 -0
- package/dist/interpreter/v1/codecs.d.ts +9 -0
- package/dist/interpreter/v1/codecs.js +266 -0
- package/dist/interpreter/v1/exec.js +238 -34
- package/dist/interpreter/v1/package.d.ts +38 -10
- package/dist/interpreter/v1/shims.js +273 -9
- package/dist/interpreter/v1/validate.js +33 -4
- package/dist/interpreter/v1/value-operations.js +103 -13
- package/dist/interpreter/worker.js +22 -4
- package/package.json +8 -4
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
/// where the package says `copy_value`, releases where it says `destroy_value`,
|
|
6
6
|
/// follows the `unwind` edge the package names, and takes the failure edge a
|
|
7
7
|
/// `checked.binary` names rather than trapping on its own.
|
|
8
|
+
import { executeCodec } from "./codecs.js";
|
|
8
9
|
import { TraceBuilder, runtimeFailure } from "../trace.js";
|
|
9
10
|
import { UNIT, cloneValue, toJsonValue } from "../values.js";
|
|
10
11
|
import { runtimeTag } from "./package.js";
|
|
@@ -69,7 +70,7 @@ export function runPackageV1(pkg, options = {}) {
|
|
|
69
70
|
trace.reject(rejection);
|
|
70
71
|
return trace.finish();
|
|
71
72
|
}
|
|
72
|
-
const vm = new ExecutorV1(pkg, trace, StdinReader.fromReplay(replay.inputs), options.schedulerPolicy ?? "round_robin");
|
|
73
|
+
const vm = new ExecutorV1(pkg, trace, StdinReader.fromReplay(replay.inputs), options.schedulerPolicy ?? "round_robin", options);
|
|
73
74
|
try {
|
|
74
75
|
vm.run();
|
|
75
76
|
}
|
|
@@ -85,6 +86,7 @@ class ExecutorV1 {
|
|
|
85
86
|
pkg;
|
|
86
87
|
trace;
|
|
87
88
|
stdin;
|
|
89
|
+
options;
|
|
88
90
|
current;
|
|
89
91
|
host;
|
|
90
92
|
scheduler;
|
|
@@ -103,10 +105,11 @@ class ExecutorV1 {
|
|
|
103
105
|
running = false;
|
|
104
106
|
rootComplete = false;
|
|
105
107
|
rootFault = null;
|
|
106
|
-
constructor(pkg, trace, stdin, policy) {
|
|
108
|
+
constructor(pkg, trace, stdin, policy, options) {
|
|
107
109
|
this.pkg = pkg;
|
|
108
110
|
this.trace = trace;
|
|
109
111
|
this.stdin = stdin;
|
|
112
|
+
this.options = options;
|
|
110
113
|
this.scheduler = new FrameScheduler(trace, policy);
|
|
111
114
|
this.ids = new DeterministicIds(trace.replay.seed);
|
|
112
115
|
this.host = {
|
|
@@ -171,7 +174,8 @@ class ExecutorV1 {
|
|
|
171
174
|
const act = this.current;
|
|
172
175
|
while (act.opIndex < act.block.ops.length) {
|
|
173
176
|
const op = act.block.ops[act.opIndex++];
|
|
174
|
-
|
|
177
|
+
if (op.op !== "coverage.hit")
|
|
178
|
+
this.commitStep();
|
|
175
179
|
try {
|
|
176
180
|
this.execute(act, op);
|
|
177
181
|
}
|
|
@@ -350,6 +354,11 @@ class ExecutorV1 {
|
|
|
350
354
|
// ── instructions ─────────────────────────────────────────────────────────
|
|
351
355
|
execute(act, op) {
|
|
352
356
|
switch (op.op) {
|
|
357
|
+
case "coverage.hit": {
|
|
358
|
+
const key = String(op.counter);
|
|
359
|
+
this.trace.coverage[key] = (this.trace.coverage[key] ?? 0) + 1;
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
353
362
|
case "const.int":
|
|
354
363
|
this.define(act, op.dst, {
|
|
355
364
|
kind: "i64",
|
|
@@ -404,9 +413,19 @@ class ExecutorV1 {
|
|
|
404
413
|
act.places.set(op.place, { value: UNIT, valid: false });
|
|
405
414
|
return;
|
|
406
415
|
case "store.init":
|
|
407
|
-
case "store.assign":
|
|
408
416
|
writeRef(this.placeRef(act, op.place), this.read(act, op.value));
|
|
409
417
|
return;
|
|
418
|
+
case "store.assign": {
|
|
419
|
+
const ref = this.placeRef(act, op.place);
|
|
420
|
+
const replacement = this.read(act, op.value);
|
|
421
|
+
const previous = liveRef(ref) ? readRef(ref) : UNIT;
|
|
422
|
+
// Retire the old owner before invoking user close code. Publish the
|
|
423
|
+
// replacement after cleanup, including when close records a fault;
|
|
424
|
+
// the following cleanup.dispatch then drains the new owner once.
|
|
425
|
+
invalidateRef(ref);
|
|
426
|
+
this.releaseOperation(act, previous, () => writeRef(ref, replacement));
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
410
429
|
case "load.copy":
|
|
411
430
|
this.define(act, op.dst, cloneValue(readRef(this.placeRef(act, op.place))));
|
|
412
431
|
return;
|
|
@@ -634,6 +653,106 @@ class ExecutorV1 {
|
|
|
634
653
|
this.define(act, op.dst, { kind: "generator", id });
|
|
635
654
|
return;
|
|
636
655
|
}
|
|
656
|
+
case "task.race": {
|
|
657
|
+
const scope = act.scopes.get(op.scope);
|
|
658
|
+
if (!scope)
|
|
659
|
+
throw new Error("race has no task scope");
|
|
660
|
+
const members = op.members.map((operand) => {
|
|
661
|
+
const member = this.taskFor(this.read(act, operand));
|
|
662
|
+
this.invalidate(act, operand);
|
|
663
|
+
if (member.grouped)
|
|
664
|
+
throw new Error("race reuses a task handle");
|
|
665
|
+
member.grouped = true;
|
|
666
|
+
return member;
|
|
667
|
+
});
|
|
668
|
+
if (!members.length)
|
|
669
|
+
throw new Error("race requires members");
|
|
670
|
+
const task = this.newTask();
|
|
671
|
+
scope.tasks.push(task);
|
|
672
|
+
let selected = false;
|
|
673
|
+
let winner = null;
|
|
674
|
+
let value = UNIT;
|
|
675
|
+
let failure = null;
|
|
676
|
+
let cancelled = false;
|
|
677
|
+
const losersCancelled = new Set();
|
|
678
|
+
const ready = () => {
|
|
679
|
+
if (task.done || task.finishing)
|
|
680
|
+
return;
|
|
681
|
+
if (!selected) {
|
|
682
|
+
const first = members
|
|
683
|
+
.filter((member) => member.done)
|
|
684
|
+
.sort((a, b) => a.completedAt - b.completedAt)[0];
|
|
685
|
+
if (!first)
|
|
686
|
+
return;
|
|
687
|
+
selected = true;
|
|
688
|
+
if (!first.fault) {
|
|
689
|
+
winner = first;
|
|
690
|
+
value = first.value;
|
|
691
|
+
first.value = UNIT;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
if (!cancelled) {
|
|
695
|
+
cancelled = true;
|
|
696
|
+
for (const member of members)
|
|
697
|
+
if (!member.done) {
|
|
698
|
+
losersCancelled.add(member);
|
|
699
|
+
member.cancel();
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if (members.some((member) => !member.done))
|
|
703
|
+
return;
|
|
704
|
+
task.finishing = true;
|
|
705
|
+
for (const member of members) {
|
|
706
|
+
const index = member.changed.indexOf(ready);
|
|
707
|
+
if (index >= 0)
|
|
708
|
+
member.changed.splice(index, 1);
|
|
709
|
+
}
|
|
710
|
+
for (const member of [...members].sort((a, b) => a.completedAt - b.completedAt)) {
|
|
711
|
+
if (member.fault &&
|
|
712
|
+
!(member.fault.kind === "panic" &&
|
|
713
|
+
member.fault.cancelled &&
|
|
714
|
+
losersCancelled.has(member)))
|
|
715
|
+
failure = combineFaults(failure, member.fault);
|
|
716
|
+
member.fault = null;
|
|
717
|
+
}
|
|
718
|
+
const losers = members
|
|
719
|
+
.filter((member) => member !== winner)
|
|
720
|
+
.map((member) => {
|
|
721
|
+
const result = member.value;
|
|
722
|
+
member.value = UNIT;
|
|
723
|
+
return result;
|
|
724
|
+
});
|
|
725
|
+
this.closeValueAsync({ kind: "vector", elementType: "race result", items: losers }, this.faultText(failure), (closeFault) => {
|
|
726
|
+
failure = combineFaults(failure, closeFault);
|
|
727
|
+
const finish = (releaseFault = null) => {
|
|
728
|
+
failure = combineFaults(failure, releaseFault);
|
|
729
|
+
this.settleTask(task, failure ? UNIT : value, failure);
|
|
730
|
+
for (const changed of [...scope.changed])
|
|
731
|
+
changed();
|
|
732
|
+
};
|
|
733
|
+
if (failure && winner)
|
|
734
|
+
this.closeValueAsync(value, this.faultText(failure), finish, act.context.actor);
|
|
735
|
+
else
|
|
736
|
+
finish();
|
|
737
|
+
}, act.context.actor);
|
|
738
|
+
};
|
|
739
|
+
task.cancel = () => {
|
|
740
|
+
if (task.done || task.finishing)
|
|
741
|
+
return;
|
|
742
|
+
failure ??= {
|
|
743
|
+
kind: "panic",
|
|
744
|
+
message: "task cancelled",
|
|
745
|
+
cancelled: true,
|
|
746
|
+
};
|
|
747
|
+
selected = true;
|
|
748
|
+
ready();
|
|
749
|
+
};
|
|
750
|
+
for (const member of members)
|
|
751
|
+
member.changed.push(ready);
|
|
752
|
+
this.define(act, op.dst, { kind: "task", id: task.id });
|
|
753
|
+
ready();
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
637
756
|
case "task.spawn": {
|
|
638
757
|
const group = act.scopes.get(op.scope);
|
|
639
758
|
if (!group)
|
|
@@ -656,7 +775,9 @@ class ExecutorV1 {
|
|
|
656
775
|
if (!fault || (fault.kind === "panic" && fault.cancelled))
|
|
657
776
|
fault = closeFault ?? fault;
|
|
658
777
|
this.settleTask(task, value, fault);
|
|
659
|
-
if (
|
|
778
|
+
if (!task.grouped &&
|
|
779
|
+
fault &&
|
|
780
|
+
!(fault.kind === "panic" && fault.cancelled)) {
|
|
660
781
|
for (const sibling of group.tasks)
|
|
661
782
|
if (!sibling.done)
|
|
662
783
|
sibling.cancel();
|
|
@@ -783,6 +904,8 @@ class ExecutorV1 {
|
|
|
783
904
|
...term.args.map((operand) => this.boundary(act, operand)),
|
|
784
905
|
];
|
|
785
906
|
this.current = this.activate(this.functionAt(entry.callee), args, act, term.result, term.normal, term.unwind);
|
|
907
|
+
if (entry.receiver_handback)
|
|
908
|
+
this.current.erasedHandback = receiver;
|
|
786
909
|
return;
|
|
787
910
|
}
|
|
788
911
|
case "call": {
|
|
@@ -866,6 +989,11 @@ class ExecutorV1 {
|
|
|
866
989
|
this.callShim(act, term, shim);
|
|
867
990
|
return;
|
|
868
991
|
}
|
|
992
|
+
case "wire.codec": {
|
|
993
|
+
const args = term.args.map((operand) => this.boundary(act, operand));
|
|
994
|
+
this.runValueProgram(act, term, args, executeCodec(this.pkg, term, args[0], this.options.codecBridge, this.options.propertyInput ?? []));
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
869
997
|
case "value.call": {
|
|
870
998
|
const args = term.args.map((operand) => this.boundary(act, operand));
|
|
871
999
|
this.runValueProgram(act, term, args, selectedValue(this.pkg, term.plan, args));
|
|
@@ -960,8 +1088,10 @@ class ExecutorV1 {
|
|
|
960
1088
|
return;
|
|
961
1089
|
case "unreachable":
|
|
962
1090
|
throw new Error(`${act.fn.name}: reached an unreachable endpoint`);
|
|
963
|
-
default:
|
|
964
|
-
|
|
1091
|
+
default: {
|
|
1092
|
+
const exhaustive = term;
|
|
1093
|
+
throw new Error(`unhandled terminator: ${String(exhaustive)}`);
|
|
1094
|
+
}
|
|
965
1095
|
}
|
|
966
1096
|
}
|
|
967
1097
|
takeEdge(act, edge) {
|
|
@@ -992,6 +1122,7 @@ class ExecutorV1 {
|
|
|
992
1122
|
}
|
|
993
1123
|
catch (error) {
|
|
994
1124
|
if (error instanceof ShimFault) {
|
|
1125
|
+
this.keepInputs(act, term, args);
|
|
995
1126
|
this.raiseFault(act, { kind: "trap", trap: error.trap, message: error.message }, term.unwind);
|
|
996
1127
|
return;
|
|
997
1128
|
}
|
|
@@ -999,6 +1130,20 @@ class ExecutorV1 {
|
|
|
999
1130
|
}
|
|
1000
1131
|
this.completeShim(act, term, value);
|
|
1001
1132
|
}
|
|
1133
|
+
/// A runtime call whose contract keeps its inputs on failure fails before
|
|
1134
|
+
/// taking them, so each moved argument stays with the caller, whose unwind
|
|
1135
|
+
/// edge stores it back.
|
|
1136
|
+
keepsInputs(term) {
|
|
1137
|
+
return term.op === "runtime.call" && term.keeps_inputs_on_failure === true;
|
|
1138
|
+
}
|
|
1139
|
+
keepInputs(act, term, args) {
|
|
1140
|
+
if (!this.keepsInputs(term))
|
|
1141
|
+
return;
|
|
1142
|
+
term.args.forEach((operand, index) => {
|
|
1143
|
+
if (operand.decision === "move")
|
|
1144
|
+
this.define(act, operand.value, args[index]);
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1002
1147
|
runValueProgram(act, term, args, iterator) {
|
|
1003
1148
|
this.running = false;
|
|
1004
1149
|
let initial = true;
|
|
@@ -1010,10 +1155,16 @@ class ExecutorV1 {
|
|
|
1010
1155
|
this.scheduler.enqueue(act.context.id, () => this.runFrame(act));
|
|
1011
1156
|
};
|
|
1012
1157
|
const fail = (fault, extra = UNIT) => {
|
|
1013
|
-
const
|
|
1158
|
+
const kept = this.keepsInputs(term) ? ["move"] : [];
|
|
1159
|
+
const owned = args.filter((_arg, index) => {
|
|
1160
|
+
const decision = term.args[index].decision;
|
|
1161
|
+
return (["move", "copy", "snapshot"].includes(decision) &&
|
|
1162
|
+
!kept.includes(decision));
|
|
1163
|
+
});
|
|
1014
1164
|
this.closeValueAsync({ kind: "vector", elementType: "", items: [...owned, extra] }, this.faultText(fault), (closeFault) => {
|
|
1015
1165
|
if (fault.kind === "panic" && fault.cancelled && closeFault)
|
|
1016
1166
|
fault = closeFault;
|
|
1167
|
+
this.keepInputs(act, term, args);
|
|
1017
1168
|
this.raiseFault(act, fault, term.unwind);
|
|
1018
1169
|
resume();
|
|
1019
1170
|
});
|
|
@@ -1116,6 +1267,13 @@ class ExecutorV1 {
|
|
|
1116
1267
|
if (!act.normal) {
|
|
1117
1268
|
throw new Error(`${act.fn.name}: returned from a call with a "never" result`);
|
|
1118
1269
|
}
|
|
1270
|
+
if (act.erasedHandback) {
|
|
1271
|
+
if (value.kind !== "record" || value.fields.length !== 2) {
|
|
1272
|
+
throw new Error(`${act.fn.name}: invalid erased receiver dual return`);
|
|
1273
|
+
}
|
|
1274
|
+
act.erasedHandback.value = value.fields[1];
|
|
1275
|
+
value = value.fields[0];
|
|
1276
|
+
}
|
|
1119
1277
|
// The result is defined on the normal edge, like a block parameter, so it
|
|
1120
1278
|
// is in scope for that edge's arguments.
|
|
1121
1279
|
if (act.result && act.result !== "never") {
|
|
@@ -1166,12 +1324,15 @@ class ExecutorV1 {
|
|
|
1166
1324
|
}
|
|
1167
1325
|
// A failing `var self` method hands its receiver back, defined on the
|
|
1168
1326
|
// unwind edge like a result on the normal edge.
|
|
1169
|
-
if ((handback === null) !== !act.handback) {
|
|
1327
|
+
if ((handback === null) !== !(act.handback || act.erasedHandback)) {
|
|
1170
1328
|
throw new Error(`${act.fn.name}: receiver handback disagrees with its call`);
|
|
1171
1329
|
}
|
|
1172
1330
|
if (handback !== null && act.handback) {
|
|
1173
1331
|
this.define(caller, act.handback.value, handback);
|
|
1174
1332
|
}
|
|
1333
|
+
if (handback !== null && act.erasedHandback) {
|
|
1334
|
+
act.erasedHandback.value = handback;
|
|
1335
|
+
}
|
|
1175
1336
|
caller.fault = fault;
|
|
1176
1337
|
this.current = caller;
|
|
1177
1338
|
this.takeEdge(caller, act.unwind);
|
|
@@ -1305,11 +1466,10 @@ class ExecutorV1 {
|
|
|
1305
1466
|
return;
|
|
1306
1467
|
}
|
|
1307
1468
|
const failure = group.deadline ??
|
|
1308
|
-
group.tasks.find((task) => task.
|
|
1469
|
+
group.tasks.find((task) => !task.grouped &&
|
|
1470
|
+
task.fault &&
|
|
1309
1471
|
!(task.fault.kind === "panic" && task.fault.cancelled))?.fault;
|
|
1310
|
-
wake(UNIT, mode === "propagate_fault"
|
|
1311
|
-
? null
|
|
1312
|
-
: (failure ?? null));
|
|
1472
|
+
wake(UNIT, mode === "propagate_fault" ? null : (failure ?? null));
|
|
1313
1473
|
};
|
|
1314
1474
|
// Cancellation requests descendant cleanup; it cannot bypass the
|
|
1315
1475
|
// join barrier and let the parent retire their captured resources.
|
|
@@ -1380,7 +1540,12 @@ class ExecutorV1 {
|
|
|
1380
1540
|
return;
|
|
1381
1541
|
}
|
|
1382
1542
|
case "NativeIo": {
|
|
1383
|
-
|
|
1543
|
+
if (term.detail?.operation === "SinkFinish") {
|
|
1544
|
+
this.host.pipes.close(this.boundary(act, term.inputs[0]));
|
|
1545
|
+
this.park(act, term)(UNIT);
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
// Admission passed `StdinReadLine`. The record is the raw line,
|
|
1384
1549
|
// so the records concatenate to the stdin the program consumed.
|
|
1385
1550
|
const line = this.stdin.readLine() ?? new Uint8Array();
|
|
1386
1551
|
this.trace.recordReplayInput({ kind: "stdin", data: new TextDecoder().decode(line) }, false);
|
|
@@ -1608,6 +1773,7 @@ class ExecutorV1 {
|
|
|
1608
1773
|
newTask() {
|
|
1609
1774
|
const task = {
|
|
1610
1775
|
id: this.ids.task(),
|
|
1776
|
+
grouped: false,
|
|
1611
1777
|
done: false,
|
|
1612
1778
|
finishing: false,
|
|
1613
1779
|
value: UNIT,
|
|
@@ -1715,6 +1881,7 @@ class ExecutorV1 {
|
|
|
1715
1881
|
fields: layout.state_fields.map((field) => field.deferred ? UNIT : (args[supplied++] ?? UNIT)),
|
|
1716
1882
|
},
|
|
1717
1883
|
mailbox: [],
|
|
1884
|
+
disposals: [],
|
|
1718
1885
|
admission: [],
|
|
1719
1886
|
busy: true,
|
|
1720
1887
|
alive: true,
|
|
@@ -1792,6 +1959,7 @@ class ExecutorV1 {
|
|
|
1792
1959
|
handler,
|
|
1793
1960
|
payload: fields,
|
|
1794
1961
|
reply: false,
|
|
1962
|
+
obligated: true,
|
|
1795
1963
|
complete: (_value, error, drained) => {
|
|
1796
1964
|
if (error)
|
|
1797
1965
|
this.closeValue(payload, error);
|
|
@@ -1871,13 +2039,13 @@ class ExecutorV1 {
|
|
|
1871
2039
|
}
|
|
1872
2040
|
const full = actor.layout.mailbox_capacity !== undefined &&
|
|
1873
2041
|
actor.mailbox.length >= actor.layout.mailbox_capacity;
|
|
1874
|
-
//
|
|
1875
|
-
// sender's
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
2042
|
+
// Declared loss transfers cleanup to the receiver. Sender-selected
|
|
2043
|
+
// loss still closes the sender's unadmitted request in its own frame.
|
|
2044
|
+
const discardAtReceiver = (value, report) => {
|
|
2045
|
+
actor.disposals.push(value);
|
|
2046
|
+
this.dispatchActor(actor);
|
|
2047
|
+
finish(report);
|
|
2048
|
+
};
|
|
1881
2049
|
const discard = (value, report) => {
|
|
1882
2050
|
let settled = false;
|
|
1883
2051
|
let waiting = false;
|
|
@@ -1900,7 +2068,7 @@ class ExecutorV1 {
|
|
|
1900
2068
|
if (declared === "coalesce" && coalesce) {
|
|
1901
2069
|
const key = coalesce.keys.find((key) => key.message === handler.message_id);
|
|
1902
2070
|
const queued = key
|
|
1903
|
-
? actor.mailbox.findIndex((message) => !message.
|
|
2071
|
+
? actor.mailbox.findIndex((message) => !message.obligated &&
|
|
1904
2072
|
message.handler.message_id === handler.message_id &&
|
|
1905
2073
|
equals(message.payload[key.param], payload.fields[key.param]))
|
|
1906
2074
|
: -1;
|
|
@@ -1910,19 +2078,19 @@ class ExecutorV1 {
|
|
|
1910
2078
|
...superseded,
|
|
1911
2079
|
payload: payload.fields,
|
|
1912
2080
|
};
|
|
1913
|
-
|
|
2081
|
+
discardAtReceiver({ kind: "record", typeId: "", fields: superseded.payload }, accepted(false));
|
|
1914
2082
|
return;
|
|
1915
2083
|
}
|
|
1916
2084
|
declared = coalesce.fallback;
|
|
1917
2085
|
}
|
|
1918
2086
|
if (declared === "drop_new") {
|
|
1919
|
-
|
|
2087
|
+
discardAtReceiver(payload, accepted(true));
|
|
1920
2088
|
return;
|
|
1921
2089
|
}
|
|
1922
2090
|
// A queued completion call owes its caller a reply, so `drop_old`
|
|
1923
2091
|
// evicts only a one-way message and refuses when there is none.
|
|
1924
2092
|
const oldest = declared === "drop_old"
|
|
1925
|
-
? actor.mailbox.findIndex((message) => !message.
|
|
2093
|
+
? actor.mailbox.findIndex((message) => !message.obligated)
|
|
1926
2094
|
: -1;
|
|
1927
2095
|
if (oldest >= 0) {
|
|
1928
2096
|
const [evicted] = actor.mailbox.splice(oldest, 1);
|
|
@@ -1930,10 +2098,11 @@ class ExecutorV1 {
|
|
|
1930
2098
|
handler,
|
|
1931
2099
|
payload: payload.fields,
|
|
1932
2100
|
reply: false,
|
|
2101
|
+
obligated: false,
|
|
1933
2102
|
complete: (_value, _error, drained) => drained?.(null),
|
|
1934
2103
|
});
|
|
1935
2104
|
this.dispatchActor(actor);
|
|
1936
|
-
|
|
2105
|
+
discardAtReceiver({ kind: "record", typeId: "", fields: evicted.payload }, accepted(false));
|
|
1937
2106
|
return;
|
|
1938
2107
|
}
|
|
1939
2108
|
if (full) {
|
|
@@ -1958,6 +2127,7 @@ class ExecutorV1 {
|
|
|
1958
2127
|
handler,
|
|
1959
2128
|
payload: payload.fields,
|
|
1960
2129
|
reply: false,
|
|
2130
|
+
obligated: false,
|
|
1961
2131
|
complete: (_value, _error, drained) => drained?.(null),
|
|
1962
2132
|
});
|
|
1963
2133
|
this.trace.snapshot("actor.send", {
|
|
@@ -2252,7 +2422,13 @@ class ExecutorV1 {
|
|
|
2252
2422
|
}
|
|
2253
2423
|
admitted = true;
|
|
2254
2424
|
receiver = actor;
|
|
2255
|
-
actor.mailbox.push({
|
|
2425
|
+
actor.mailbox.push({
|
|
2426
|
+
handler,
|
|
2427
|
+
payload,
|
|
2428
|
+
complete: finish,
|
|
2429
|
+
reply: true,
|
|
2430
|
+
obligated: true,
|
|
2431
|
+
});
|
|
2256
2432
|
this.trace.snapshot("actor.ask", {
|
|
2257
2433
|
actor_id: actor.id,
|
|
2258
2434
|
handler: handler.name,
|
|
@@ -2272,6 +2448,18 @@ class ExecutorV1 {
|
|
|
2272
2448
|
dispatchActor(actor) {
|
|
2273
2449
|
if (actor.busy || !actor.alive || actor.drainingMailbox)
|
|
2274
2450
|
return;
|
|
2451
|
+
if (actor.disposals.length > 0) {
|
|
2452
|
+
actor.busy = true;
|
|
2453
|
+
const values = actor.disposals.splice(0);
|
|
2454
|
+
this.closeValueAsync({ kind: "vector", elementType: "message", items: values }, this.faultText(actor.crashing), (fault) => {
|
|
2455
|
+
actor.busy = false;
|
|
2456
|
+
if (fault)
|
|
2457
|
+
this.crashActor(actor, fault);
|
|
2458
|
+
else
|
|
2459
|
+
this.dispatchActor(actor);
|
|
2460
|
+
}, actor);
|
|
2461
|
+
return;
|
|
2462
|
+
}
|
|
2275
2463
|
if (actor.terminating && actor.mailbox.length > 0) {
|
|
2276
2464
|
this.drainMailbox(actor, this.faultText(actor.closingFault), (fault) => {
|
|
2277
2465
|
actor.closingFault = combineFaults(actor.closingFault, fault);
|
|
@@ -2649,11 +2837,14 @@ class ExecutorV1 {
|
|
|
2649
2837
|
this.closeValueAsync({
|
|
2650
2838
|
kind: "vector",
|
|
2651
2839
|
elementType: "message",
|
|
2652
|
-
items:
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2840
|
+
items: [
|
|
2841
|
+
...actor.disposals.splice(0),
|
|
2842
|
+
...messages.map((message) => ({
|
|
2843
|
+
kind: "record",
|
|
2844
|
+
typeId: "",
|
|
2845
|
+
fields: message.payload,
|
|
2846
|
+
})),
|
|
2847
|
+
],
|
|
2657
2848
|
}, fault, (closeFault) => {
|
|
2658
2849
|
actor.drainingMailbox = false;
|
|
2659
2850
|
for (const message of messages)
|
|
@@ -2906,8 +3097,6 @@ function sendErrorRole(reason) {
|
|
|
2906
3097
|
return "SendErrorCancelled";
|
|
2907
3098
|
case "VersionMismatch":
|
|
2908
3099
|
return "SendErrorVersionMismatch";
|
|
2909
|
-
case "Unauthorized":
|
|
2910
|
-
return "SendErrorUnauthorized";
|
|
2911
3100
|
case "Backpressure":
|
|
2912
3101
|
return "SendErrorBackpressure";
|
|
2913
3102
|
case "Dead":
|
|
@@ -3104,6 +3293,21 @@ function applyBinary(op, lhs, rhs, ty) {
|
|
|
3104
3293
|
return { kind: "bool", value: truth(lhs) && truth(rhs) };
|
|
3105
3294
|
case "Or":
|
|
3106
3295
|
return { kind: "bool", value: truth(lhs) || truth(rhs) };
|
|
3296
|
+
case "BitAnd":
|
|
3297
|
+
case "BitOr":
|
|
3298
|
+
case "BitXor":
|
|
3299
|
+
// Both operands were already evaluated; bool operands stay bool.
|
|
3300
|
+
if (lhs.kind === "bool" && rhs.kind === "bool") {
|
|
3301
|
+
return {
|
|
3302
|
+
kind: "bool",
|
|
3303
|
+
value: op === "BitAnd"
|
|
3304
|
+
? lhs.value && rhs.value
|
|
3305
|
+
: op === "BitOr"
|
|
3306
|
+
? lhs.value || rhs.value
|
|
3307
|
+
: lhs.value !== rhs.value,
|
|
3308
|
+
};
|
|
3309
|
+
}
|
|
3310
|
+
break;
|
|
3107
3311
|
default:
|
|
3108
3312
|
break;
|
|
3109
3313
|
}
|
|
@@ -34,7 +34,7 @@ export interface VariantShape {
|
|
|
34
34
|
cases: VariantCase[];
|
|
35
35
|
runtime_tags?: Partial<Record<RuntimeVariantRole, number>>;
|
|
36
36
|
}
|
|
37
|
-
export type RuntimeVariantRole = "OptionSome" | "OptionNone" | "ResultOk" | "ResultErr" | "ActorErrorRejected" | "ActorErrorFailed" | "ActorErrorTrapped" | "ActorErrorDead" | "ActorErrorTimedOut" | "ActorErrorNodeNotRunning" | "ActorErrorRoutingFailed" | "ActorErrorEncodeFailed" | "ActorErrorConnectionDropped" | "ActorErrorPartition" | "SendErrorFull" | "SendErrorClosed" | "SendErrorPartition" | "SendErrorStaleRef" | "SendErrorLocalShutdown" | "SendErrorCancelled" | "SendErrorVersionMismatch" | "
|
|
37
|
+
export type RuntimeVariantRole = "OptionSome" | "OptionNone" | "ResultOk" | "ResultErr" | "ActorErrorRejected" | "ActorErrorFailed" | "ActorErrorTrapped" | "ActorErrorDead" | "ActorErrorTimedOut" | "ActorErrorNodeNotRunning" | "ActorErrorRoutingFailed" | "ActorErrorEncodeFailed" | "ActorErrorConnectionDropped" | "ActorErrorPartition" | "SendErrorFull" | "SendErrorClosed" | "SendErrorPartition" | "SendErrorStaleRef" | "SendErrorLocalShutdown" | "SendErrorCancelled" | "SendErrorVersionMismatch" | "SendErrorBackpressure" | "SendErrorDead" | "SendErrorWriteTimedOut" | "DeliveryAccepted" | "DeliveryDiscarded" | "LinkErrorNoContext";
|
|
38
38
|
export declare function runtimeTag(shape: VariantShape, role: RuntimeVariantRole): number;
|
|
39
39
|
export interface RuntimeFamilyEntry {
|
|
40
40
|
id: number;
|
|
@@ -44,6 +44,7 @@ export interface RuntimeFamilyEntry {
|
|
|
44
44
|
export interface ExternEntry {
|
|
45
45
|
id: number;
|
|
46
46
|
symbol: string;
|
|
47
|
+
native_capability?: string;
|
|
47
48
|
}
|
|
48
49
|
export type PlaceDecl = {
|
|
49
50
|
id: number;
|
|
@@ -75,11 +76,20 @@ export type PlaceDecl = {
|
|
|
75
76
|
};
|
|
76
77
|
export interface ValueCapability {
|
|
77
78
|
id: number;
|
|
78
|
-
capability: "Eq" | "Hash";
|
|
79
|
+
capability: "Eq" | "Hash" | "Less" | "LessEqual" | "Greater" | "GreaterEqual";
|
|
79
80
|
ty: string;
|
|
80
81
|
callable?: number;
|
|
81
|
-
components?:
|
|
82
|
-
variants?:
|
|
82
|
+
components?: ValueComponentCapabilities[];
|
|
83
|
+
variants?: ValueComponentCapabilities[][];
|
|
84
|
+
}
|
|
85
|
+
export interface ValueComponentCapabilities {
|
|
86
|
+
selected: number;
|
|
87
|
+
ordering?: {
|
|
88
|
+
less: number;
|
|
89
|
+
less_equal: number;
|
|
90
|
+
greater: number;
|
|
91
|
+
greater_equal: number;
|
|
92
|
+
};
|
|
83
93
|
}
|
|
84
94
|
export interface ClosureShape {
|
|
85
95
|
id: number;
|
|
@@ -93,6 +103,7 @@ export interface VtableShape {
|
|
|
93
103
|
method: string;
|
|
94
104
|
callee: number;
|
|
95
105
|
receiver: BoundaryDecision;
|
|
106
|
+
receiver_handback?: boolean;
|
|
96
107
|
}>;
|
|
97
108
|
}
|
|
98
109
|
export interface FunctionV1 {
|
|
@@ -115,6 +126,9 @@ interface OpBase {
|
|
|
115
126
|
span?: SpanV1 | null;
|
|
116
127
|
}
|
|
117
128
|
export type OpV1 = (OpBase & {
|
|
129
|
+
op: "coverage.hit";
|
|
130
|
+
counter: number;
|
|
131
|
+
}) | (OpBase & {
|
|
118
132
|
op: "const.int";
|
|
119
133
|
dst: number;
|
|
120
134
|
value: string;
|
|
@@ -341,6 +355,11 @@ export type OpV1 = (OpBase & {
|
|
|
341
355
|
}) | (OpBase & {
|
|
342
356
|
op: "task_scope.close";
|
|
343
357
|
scope: number;
|
|
358
|
+
}) | (OpBase & {
|
|
359
|
+
op: "task.race";
|
|
360
|
+
dst: number;
|
|
361
|
+
scope: number;
|
|
362
|
+
members: Operand[];
|
|
344
363
|
}) | (OpBase & {
|
|
345
364
|
op: "task.spawn";
|
|
346
365
|
dst: number;
|
|
@@ -414,12 +433,25 @@ export type TermV1 = (TermBase & {
|
|
|
414
433
|
}) | (TermBase & CallShape & {
|
|
415
434
|
op: "value.call";
|
|
416
435
|
plan: number;
|
|
436
|
+
}) | (TermBase & CallShape & {
|
|
437
|
+
op: "wire.codec";
|
|
438
|
+
codec: number;
|
|
439
|
+
format: number;
|
|
440
|
+
direction: "encode" | "decode" | "test_input";
|
|
441
|
+
decoded: {
|
|
442
|
+
shape: number;
|
|
443
|
+
ok: number;
|
|
444
|
+
error: number;
|
|
445
|
+
error_root: number;
|
|
446
|
+
} | null;
|
|
447
|
+
callbacks: Record<string, number[]>;
|
|
417
448
|
}) | (TermBase & CallShape & {
|
|
418
449
|
op: "runtime.call";
|
|
419
450
|
family: number;
|
|
420
451
|
structural?: number | null;
|
|
421
452
|
callbacks?: number[];
|
|
422
453
|
releases_contents?: boolean;
|
|
454
|
+
keeps_inputs_on_failure?: boolean;
|
|
423
455
|
result_member_shapes?: Array<number | null>;
|
|
424
456
|
result_shape: number | null;
|
|
425
457
|
}) | (TermBase & CallShape & {
|
|
@@ -436,10 +468,6 @@ export type TermV1 = (TermBase & {
|
|
|
436
468
|
};
|
|
437
469
|
result_shape: number | null;
|
|
438
470
|
error_shape: number | null;
|
|
439
|
-
}) | (TermBase & CallShape & {
|
|
440
|
-
op: "wire.codec";
|
|
441
|
-
direction: string;
|
|
442
|
-
plan: number;
|
|
443
471
|
}) | (TermBase & {
|
|
444
472
|
op: "panic";
|
|
445
473
|
message: BoundaryOperand;
|
|
@@ -498,7 +526,7 @@ export interface EntryPlan {
|
|
|
498
526
|
export interface ActorProtocol {
|
|
499
527
|
actor: number;
|
|
500
528
|
message: number;
|
|
501
|
-
policy: "reject" | "wait" | "drop_newest"
|
|
529
|
+
policy: "reject" | "wait" | "drop_newest";
|
|
502
530
|
sealed: boolean;
|
|
503
531
|
}
|
|
504
532
|
export type ActorOperation = {
|
|
@@ -558,7 +586,6 @@ export interface ActorShape {
|
|
|
558
586
|
keys: Array<{
|
|
559
587
|
message: number;
|
|
560
588
|
param: number;
|
|
561
|
-
kind: string;
|
|
562
589
|
}>;
|
|
563
590
|
};
|
|
564
591
|
max_heap_bytes?: number;
|
|
@@ -582,6 +609,7 @@ export interface SupervisorShape {
|
|
|
582
609
|
}>;
|
|
583
610
|
}
|
|
584
611
|
export interface PackageV1 {
|
|
612
|
+
codecs?: string[];
|
|
585
613
|
resources?: Array<{
|
|
586
614
|
kind: string;
|
|
587
615
|
ty?: string;
|