@hew-lang/sandbox-vm 0.10.0-rc1 → 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.
@@ -54,8 +54,10 @@ var SandboxExitCode;
54
54
  SandboxExitCode[SandboxExitCode["HeapExceeded"] = 200] = "HeapExceeded";
55
55
  SandboxExitCode[SandboxExitCode["IntegerOverflow"] = 201] = "IntegerOverflow";
56
56
  SandboxExitCode[SandboxExitCode["DivideByZero"] = 202] = "DivideByZero";
57
+ SandboxExitCode[SandboxExitCode["SignedMinDivNegOne"] = 203] = "SignedMinDivNegOne";
57
58
  SandboxExitCode[SandboxExitCode["ShiftOutOfRange"] = 204] = "ShiftOutOfRange";
58
59
  SandboxExitCode[SandboxExitCode["IndexOutOfBounds"] = 205] = "IndexOutOfBounds";
60
+ SandboxExitCode[SandboxExitCode["StackOverflow"] = 215] = "StackOverflow";
59
61
  SandboxExitCode[SandboxExitCode["ActorSendFailed"] = 206] = "ActorSendFailed";
60
62
  SandboxExitCode[SandboxExitCode["MachineDispatchUnreachable"] = 207] = "MachineDispatchUnreachable";
61
63
  SandboxExitCode[SandboxExitCode["ExhaustivenessFallthrough"] = 208] = "ExhaustivenessFallthrough";
@@ -66,6 +68,8 @@ const TRAP_EXIT_CODES = {
66
68
  integer_overflow: SandboxExitCode.IntegerOverflow,
67
69
  shift_out_of_range: SandboxExitCode.ShiftOutOfRange,
68
70
  divide_by_zero: SandboxExitCode.DivideByZero,
71
+ signed_min_div_neg_one: SandboxExitCode.SignedMinDivNegOne,
72
+ stack_overflow: SandboxExitCode.StackOverflow,
69
73
  invalid_local: SandboxExitCode.InternalError,
70
74
  invalid_block: SandboxExitCode.InternalError,
71
75
  invalid_call: SandboxExitCode.InternalError,
@@ -2,7 +2,7 @@ export type JsonValue = null | boolean | number | string | JsonValue[] | {
2
2
  [key: string]: JsonValue;
3
3
  };
4
4
  export type SandboxBytecodePackage = import("./v1/package.js").PackageV1;
5
- export type TrapKind = "heap_exceeded" | "actor_refused" | "integer_overflow" | "shift_out_of_range" | "divide_by_zero" | "invalid_local" | "invalid_block" | "invalid_call" | "invalid_enum_tag" | "invalid_record_field" | "vector_bounds" | "string_bounds" | "regex_compile" | "capability_missing" | "budget_exhausted" | "panic" | "unsupported_instruction" | "internal_error";
5
+ export type TrapKind = import("./v1/package.js").TrapName | "invalid_local" | "invalid_block" | "invalid_call" | "invalid_enum_tag" | "invalid_record_field" | "string_bounds" | "regex_compile" | "capability_missing" | "budget_exhausted" | "panic" | "unsupported_instruction" | "internal_error";
6
6
  export type RuntimeStatus = "ok" | "compile_error" | "sandbox_rejected" | "runtime_failure" | "budget_exhausted" | "panic" | "trap";
7
7
  export interface RuntimeFailure {
8
8
  kind: "panic" | "trap" | "budget_exhausted" | "unsupported" | "internal_error";
@@ -1,4 +1,4 @@
1
- import { UNIT } from "../values.js";
1
+ import { vectorItems, UNIT } from "../values.js";
2
2
  export const DIRECTORY_OPERATIONS = {
3
3
  hew_dir_open_read: {
4
4
  release: "hew_dir_read_close",
@@ -250,7 +250,7 @@ export class BrowserDirectories {
250
250
  const data = args[1];
251
251
  if (data?.kind !== "vector")
252
252
  throw new Error("file write requires bytes");
253
- const bytes = Uint8Array.from(data.items, (value) => {
253
+ const bytes = Uint8Array.from(vectorItems(data), (value) => {
254
254
  if (value.kind !== "i64")
255
255
  throw new Error("file write requires byte elements");
256
256
  return Number(value.value);
@@ -1,4 +1,4 @@
1
- import { UNIT, stringValue } from "../values.js";
1
+ import { vectorItems, UNIT, stringValue } from "../values.js";
2
2
  import { runtimeTag } from "./package.js";
3
3
  import { collectionOperation } from "./value-operations.js";
4
4
  class DecodeFailure extends Error {
@@ -19,7 +19,7 @@ function fields(value) {
19
19
  if (value.kind === "record")
20
20
  return value.fields;
21
21
  if (value.kind === "vector")
22
- return value.items;
22
+ return vectorItems(value);
23
23
  throw new Error(`codec aggregate received ${value.kind}`);
24
24
  }
25
25
  function toBridge(pkg, shape, id, value) {
@@ -11,7 +11,7 @@ import { executeCodec } from "./codecs.js";
11
11
  import { actorIdentical } from "./actor-identity.js";
12
12
  import { ActorWaits } from "./actor-waits.js";
13
13
  import { TraceBuilder, runtimeFailure } from "../trace.js";
14
- import { UNIT, cloneValue, releaseVector, byteVectorValue, vectorStorage, stringValue, toJsonValue, } from "../values.js";
14
+ import { UNIT, cloneValue, releaseVector, byteVectorValue, vectorStorage, vectorItems, vectorLength, vectorElement, stringValue, toJsonValue, } from "../values.js";
15
15
  import { runtimeTag, staticOperations } from "./package.js";
16
16
  import { StructuralRenderer } from "./structural.js";
17
17
  import { timestampBinary } from "./timestamp.js";
@@ -26,6 +26,11 @@ import { AdmittedPackage } from "./validate.js";
26
26
  import { HeapExceeded, ManagedBudget, ManagedLayouts, } from "./managed-budget.js";
27
27
  const DEFAULT_STEP_BUDGET = 1_000_000;
28
28
  const DEFAULT_ACTOR_STOP_NS = 5_000_000_000;
29
+ /// Deepest activation a task may reach below its root before a call fails
30
+ /// with `StackOverflow`. HEW-SPEC-2026 §2.2 guarantees 10,000 frames. Twice
31
+ /// that matches what a native stack admits for small frames and leaves an
32
+ /// unbounded recursion failing well inside the default step budget.
33
+ const MAX_FRAME_DEPTH = 20_000;
29
34
  /// Ends the run. The status is already recorded on the trace.
30
35
  class Halt extends Error {
31
36
  status;
@@ -404,6 +409,7 @@ class ExecutorV1 {
404
409
  fault: null,
405
410
  parked: [],
406
411
  caller,
412
+ depth: caller ? caller.depth + 1 : 0,
407
413
  result,
408
414
  normal: normal ?? undefined,
409
415
  unwind,
@@ -523,8 +529,8 @@ class ExecutorV1 {
523
529
  (row.kind === "vector" ||
524
530
  row.kind === "array" ||
525
531
  row.kind === "slice")
526
- ? value.items.map((item) => this.managed.copy(item, row.element))
527
- : value.items;
532
+ ? vectorItems(value).map((item) => this.managed.copy(item, row.element))
533
+ : vectorItems(value);
528
534
  if (appended?.length) {
529
535
  if (row.kind !== "vector" &&
530
536
  row.kind !== "array" &&
@@ -538,6 +544,8 @@ class ExecutorV1 {
538
544
  budget.checkMutation(value, {
539
545
  ...value,
540
546
  items,
547
+ offset: 0,
548
+ length: undefined,
541
549
  storage: { ...storage, capacity, items, owners: 1 },
542
550
  }, detached);
543
551
  }
@@ -1695,7 +1703,13 @@ class ExecutorV1 {
1695
1703
  case "checked.binary": {
1696
1704
  const failure = checkedFailure(term.binary_op, this.read(act, term.lhs), this.read(act, term.rhs), term.ty);
1697
1705
  if (failure) {
1698
- const edge = term.failures.find((candidate) => candidate.trap === failure);
1706
+ // Earlier v1 packages fold the signed minimum by -1 into their
1707
+ // overflow edge, so they keep the IntegerOverflow trap their
1708
+ // compiler emitted.
1709
+ const edge = term.failures.find((candidate) => candidate.trap === failure) ??
1710
+ (failure === "signed_min_div_neg_one"
1711
+ ? term.failures.find((candidate) => candidate.trap === "integer_overflow")
1712
+ : undefined);
1699
1713
  if (!edge) {
1700
1714
  throw new Error(`checked.binary names no ${failure} edge`);
1701
1715
  }
@@ -1988,6 +2002,26 @@ class ExecutorV1 {
1988
2002
  tag: deadline ? term.deadline_variant : term.fault_variant,
1989
2003
  payload: [stringValue(faultRecoveryMessage(fault))],
1990
2004
  };
2005
+ // Scope recovery must preserve the original fault even when the
2006
+ // current actor cannot retain its diagnostic text. Preflight the same
2007
+ // managed graph that completion will retain, then use the canonical
2008
+ // empty string on refusal without publishing an uncharged owner.
2009
+ const result = term.result?.value;
2010
+ const type = result == null ? undefined : act.managedTypes.get(result);
2011
+ const budget = result != null && act.owned.has(result)
2012
+ ? act.context.actor?.budget
2013
+ : undefined;
2014
+ if (budget && type != null) {
2015
+ try {
2016
+ this.managed.stampGraph({ value, type });
2017
+ budget.check([{ value, type }]);
2018
+ }
2019
+ catch (error) {
2020
+ if (!(error instanceof HeapExceeded))
2021
+ throw error;
2022
+ value.payload[0] = stringValue("");
2023
+ }
2024
+ }
1991
2025
  act.fault = null;
1992
2026
  if (act.context.actor)
1993
2027
  act.context.actor.crashing = null;
@@ -2359,13 +2393,13 @@ class ExecutorV1 {
2359
2393
  if (operation === "Len") {
2360
2394
  this.completeShim(act, term, {
2361
2395
  kind: "i64",
2362
- value: BigInt(view.items.length),
2396
+ value: BigInt(vectorLength(view)),
2363
2397
  });
2364
2398
  return;
2365
2399
  }
2366
2400
  if (index?.kind !== "i64")
2367
2401
  throw new Error("pool operation has no index");
2368
- const value = view.items[Number(index.value)];
2402
+ const value = vectorElement(view, Number(index.value));
2369
2403
  if (operation === "Get") {
2370
2404
  this.completeShim(act, term, this.variant(term.result_shape, value ? "OptionSome" : "OptionNone", value ? [value] : []));
2371
2405
  return;
@@ -2402,7 +2436,7 @@ class ExecutorV1 {
2402
2436
  }, (fault) => {
2403
2437
  this.raiseFault(act, fault, term.unwind);
2404
2438
  this.scheduler.enqueue(act.context.id, () => this.runFrame(act));
2405
- });
2439
+ }, act.context.actor?.rcOwner ?? null, false, undefined, act);
2406
2440
  act.context.cancel = (fault) => callback.cancel?.(fault);
2407
2441
  };
2408
2442
  continueRendering();
@@ -3015,6 +3049,16 @@ class ExecutorV1 {
3015
3049
  return;
3016
3050
  }
3017
3051
  case "Safepoint": {
3052
+ // A function's entry safepoint is its stack check (HEW-SPEC-2026
3053
+ // §2.2): past the frame limit the call fails with `StackOverflow`
3054
+ // and its parameters follow the cancellation cleanup.
3055
+ if (term.cancel &&
3056
+ act.depth > MAX_FRAME_DEPTH &&
3057
+ act.block.id === act.fn.entry) {
3058
+ act.fault = { kind: "trap", trap: "stack_overflow" };
3059
+ this.takeEdge(act, term.cancel);
3060
+ return;
3061
+ }
3018
3062
  if (act.context.pollSuppression) {
3019
3063
  this.takeEdge(act, term.resumes[0]);
3020
3064
  return;
@@ -3816,7 +3860,11 @@ class ExecutorV1 {
3816
3860
  reply: { value: envelope, type: provider.reply },
3817
3861
  };
3818
3862
  }
3819
- invokeFrame(actor, callable, args, returned, failed, rcOwner = actor?.rcOwner ?? null, inline = false, retainedBy) {
3863
+ invokeFrame(actor, callable, args, returned, failed, rcOwner = actor?.rcOwner ?? null, inline = false, retainedBy,
3864
+ // The activation this frame runs on behalf of. Its depth carries on, so a
3865
+ // recursion through runtime callbacks reaches the frame limit as a direct
3866
+ // one does.
3867
+ invoker = retainedBy) {
3820
3868
  const context = {
3821
3869
  id: actor?.id ?? "actor:root",
3822
3870
  actor,
@@ -3834,6 +3882,8 @@ class ExecutorV1 {
3834
3882
  },
3835
3883
  };
3836
3884
  const frame = this.activate(this.functionAt(callable), args, null, null, undefined, null, context);
3885
+ if (invoker)
3886
+ frame.depth = invoker.depth + 1;
3837
3887
  retainedBy?.managedChildren.add(frame);
3838
3888
  let cancelled = null;
3839
3889
  context.cancel = (fault = { kind: "panic", message: "task cancelled", cancelled: true }) => {
@@ -4374,27 +4424,32 @@ class ExecutorV1 {
4374
4424
  if (isPool && count?.kind !== "i64")
4375
4425
  throw new Error("pool construction has no count");
4376
4426
  const configured = isPool ? args.slice(0, -1) : args;
4377
- const initial = layout.backoff
4378
- ? configured[configured.length - 2]
4379
- : undefined;
4380
- const maximum = layout.backoff
4381
- ? configured[configured.length - 1]
4382
- : undefined;
4383
- const backoff = initial?.kind === "i64" && maximum?.kind === "i64"
4384
- ? { initial: initial.value, maximum: maximum.value }
4385
- : null;
4386
- const validBackoff = !layout.backoff ||
4387
- (backoff !== null &&
4388
- backoff.initial > 0n &&
4389
- backoff.maximum >= backoff.initial);
4390
- const config = layout.backoff ? configured.slice(0, -2) : configured;
4427
+ // Config, then each backoff child's `(initial, maximum)` pair in
4428
+ // declaration order.
4429
+ const pairs = layout.children.filter((child) => child.backoff).length;
4430
+ const config = configured.slice(0, configured.length - 2 * pairs);
4431
+ let pair = config.length;
4432
+ const declared = layout.children.map((child) => {
4433
+ if (!child.backoff)
4434
+ return null;
4435
+ const initial = configured[pair];
4436
+ const maximum = configured[pair + 1];
4437
+ pair += 2;
4438
+ return initial?.kind === "i64" && maximum?.kind === "i64"
4439
+ ? { initial: initial.value, maximum: maximum.value }
4440
+ : undefined;
4441
+ });
4442
+ const validBackoff = declared.every((backoff) => backoff === null ||
4443
+ (backoff !== undefined &&
4444
+ ((backoff.initial === 0n && backoff.maximum === 0n) ||
4445
+ (backoff.initial > 0n && backoff.maximum >= backoff.initial))));
4391
4446
  if (!validBackoff ||
4392
4447
  (count?.kind === "i64" &&
4393
4448
  (count.value <= 0n || count.value > 2147483647n))) {
4394
4449
  const fault = {
4395
4450
  kind: "panic",
4396
4451
  message: !validBackoff
4397
- ? "supervisor backoff requires positive initial and maximum >= initial"
4452
+ ? "supervisor backoff requires positive initial and maximum >= initial, or both zero"
4398
4453
  : "supervisor pool count must be between 1 and 2147483647",
4399
4454
  };
4400
4455
  this.running = false;
@@ -4423,7 +4478,7 @@ class ExecutorV1 {
4423
4478
  teardownChild: null,
4424
4479
  teardownCancelTimer: null,
4425
4480
  restartTimes: [],
4426
- backoff,
4481
+ backoffs: declared.flatMap((backoff) => Array.from({ length: members }, () => backoff ?? null)),
4427
4482
  restartDelays: new Map(),
4428
4483
  restartTimers: new Map(),
4429
4484
  closed: [],
@@ -5238,7 +5293,9 @@ class ExecutorV1 {
5238
5293
  ? "CrashKindTimerOverdue"
5239
5294
  : code === 213
5240
5295
  ? "CrashKindHandlerFailed"
5241
- : "CrashKindFault"),
5296
+ : code === 215
5297
+ ? "CrashKindStackOverflow"
5298
+ : "CrashKindFault"),
5242
5299
  payload: [],
5243
5300
  },
5244
5301
  stringValue(String(message)),
@@ -5420,24 +5477,38 @@ class ExecutorV1 {
5420
5477
  return;
5421
5478
  }
5422
5479
  if (owner.restartBusy) {
5423
- owner.pendingRestarts.push({ child, debts });
5480
+ owner.pendingRestarts.push({
5481
+ child,
5482
+ debts,
5483
+ incarnation: owner.children[child] ?? null,
5484
+ });
5424
5485
  return;
5425
5486
  }
5426
5487
  const now = this.trace.virtualTimeMs;
5427
- if (owner.backoff && debts.length > 0 && !delayElapsed) {
5488
+ const backoff = owner.backoffs[child];
5489
+ if (backoff && backoff.initial > 0n && debts.length > 0 && !delayElapsed) {
5428
5490
  const previous = owner.restartDelays.get(child);
5429
5491
  const healthy = previous?.publishedAt != null &&
5430
5492
  now - previous.publishedAt >= owner.layout.window_secs * 1000;
5431
- const doubled = previous ? previous.delay * 2n : owner.backoff.initial;
5493
+ const doubled = previous ? previous.delay * 2n : backoff.initial;
5432
5494
  const delay = !previous || healthy
5433
- ? owner.backoff.initial
5434
- : doubled < owner.backoff.maximum
5495
+ ? backoff.initial
5496
+ : doubled < backoff.maximum
5435
5497
  ? doubled
5436
- : owner.backoff.maximum;
5498
+ : backoff.maximum;
5437
5499
  owner.restartDelays.set(child, { delay, publishedAt: null });
5438
5500
  owner.recovering.add(child);
5439
5501
  owner.restartTimers.set(child, this.scheduler.after(delay, () => {
5440
5502
  owner.restartTimers.delete(child);
5503
+ // A group restart that already replaced this role recovered its
5504
+ // fault; restarting again would tear down a healthy group and
5505
+ // charge the budget twice.
5506
+ if (owner.restartDelays.get(child)?.publishedAt != null) {
5507
+ for (const debt of debts)
5508
+ this.faultDebts.delete(debt);
5509
+ this.wakeRoles();
5510
+ return;
5511
+ }
5441
5512
  this.restartChild(owner, child, debts, true);
5442
5513
  }));
5443
5514
  return;
@@ -5484,11 +5555,16 @@ class ExecutorV1 {
5484
5555
  if (index === undefined) {
5485
5556
  for (const debt of debts)
5486
5557
  this.faultDebts.delete(debt);
5487
- for (const pending of owner.pendingRestarts.filter((pending) => affected.includes(pending.child))) {
5558
+ // This group replaced a queued crash's incarnation, which recovers it.
5559
+ // A replacement that crashed after its own publication still needs
5560
+ // its restart.
5561
+ const replaced = (pending) => affected.includes(pending.child) &&
5562
+ owner.children[pending.child] !== pending.incarnation;
5563
+ for (const pending of owner.pendingRestarts.filter(replaced)) {
5488
5564
  for (const debt of pending.debts)
5489
5565
  this.faultDebts.delete(debt);
5490
5566
  }
5491
- owner.pendingRestarts = owner.pendingRestarts.filter((pending) => !affected.includes(pending.child));
5567
+ owner.pendingRestarts = owner.pendingRestarts.filter((pending) => !replaced(pending));
5492
5568
  owner.restartBusy = false;
5493
5569
  this.wakeRoles();
5494
5570
  const pending = owner.pendingRestarts.shift();
@@ -5500,6 +5576,12 @@ class ExecutorV1 {
5500
5576
  const delay = owner.restartDelays.get(index);
5501
5577
  if (delay)
5502
5578
  delay.publishedAt = this.trace.virtualTimeMs;
5579
+ // The crashed role's replacement is the recovery that settles its
5580
+ // fault, as natively; a later sibling's cancelled start does not
5581
+ // reopen it.
5582
+ if (index === child)
5583
+ for (const debt of debts)
5584
+ this.faultDebts.delete(debt);
5503
5585
  start();
5504
5586
  }, () => {
5505
5587
  owner.restartBusy = false;
@@ -5828,15 +5910,19 @@ function faultInfo(fault) {
5828
5910
  return [-2, "Deadline"];
5829
5911
  return [212, "UserPanic"];
5830
5912
  }
5831
- return {
5832
- integer_overflow: [201, "IntegerOverflow"],
5833
- divide_by_zero: [202, "DivideByZero"],
5834
- shift_out_of_range: [204, "ShiftOutOfRange"],
5835
- vector_bounds: [205, "IndexOutOfBounds"],
5836
- actor_refused: [-4, "Refused"],
5837
- heap_exceeded: [200, "HeapExceeded"],
5838
- }[fault.trap];
5913
+ return TRAP_FAULTS[fault.trap];
5839
5914
  }
5915
+ /// The native exit reason each package trap reports, as code and name.
5916
+ const TRAP_FAULTS = {
5917
+ integer_overflow: [201, "IntegerOverflow"],
5918
+ divide_by_zero: [202, "DivideByZero"],
5919
+ signed_min_div_neg_one: [203, "SignedMinDivNegOne"],
5920
+ shift_out_of_range: [204, "ShiftOutOfRange"],
5921
+ vector_bounds: [205, "IndexOutOfBounds"],
5922
+ actor_refused: [-4, "Refused"],
5923
+ heap_exceeded: [200, "HeapExceeded"],
5924
+ stack_overflow: [215, "StackOverflow"],
5925
+ };
5840
5926
  function faultApplicationMessage(fault) {
5841
5927
  return fault.kind === "panic" &&
5842
5928
  (fault.cancelled || fault.deadline || fault.timerOverdue)
@@ -6034,7 +6120,7 @@ function toComparable(value) {
6034
6120
  case "enum":
6035
6121
  return [value.tag, value.payload.map(toComparable)];
6036
6122
  case "vector":
6037
- return value.items.map(toComparable);
6123
+ return vectorItems(value).map(toComparable);
6038
6124
  case "rc":
6039
6125
  return "Rc";
6040
6126
  case "weak":
@@ -6074,6 +6160,8 @@ function trapMessage(trap) {
6074
6160
  return "integer overflow";
6075
6161
  case "divide_by_zero":
6076
6162
  return "divide by zero";
6163
+ case "signed_min_div_neg_one":
6164
+ return "signed minimum divided by -1";
6077
6165
  case "shift_out_of_range":
6078
6166
  return "shift amount out of range";
6079
6167
  case "vector_bounds":
@@ -6082,6 +6170,8 @@ function trapMessage(trap) {
6082
6170
  return "delivery refused by interceptor";
6083
6171
  case "heap_exceeded":
6084
6172
  return "actor heap limit exceeded";
6173
+ case "stack_overflow":
6174
+ return "stack overflow";
6085
6175
  }
6086
6176
  }
6087
6177
  /// The operand type the package records. `isize` and `usize` are 64-bit in the
@@ -6131,8 +6221,16 @@ function checkedFailure(op, lhs, rhs, ty) {
6131
6221
  return null;
6132
6222
  }
6133
6223
  const scalar = intTy(ty);
6134
- if ((op === "Divide" || op === "Modulo") && rhs.value === 0n) {
6135
- return "divide_by_zero";
6224
+ if (op === "Divide" || op === "Modulo") {
6225
+ if (rhs.value === 0n)
6226
+ return "divide_by_zero";
6227
+ // Both the quotient and the remainder of the signed minimum by -1 trap,
6228
+ // although the remainder alone would be representable.
6229
+ if (scalar.signed &&
6230
+ rhs.value === -1n &&
6231
+ lhs.value === -(1n << (scalar.bits - 1n))) {
6232
+ return "signed_min_div_neg_one";
6233
+ }
6136
6234
  }
6137
6235
  if ((op === "Shl" || op === "Shr") &&
6138
6236
  (rhs.value < 0n || rhs.value >= scalar.bits)) {
@@ -6316,8 +6414,8 @@ function equals(lhs, rhs) {
6316
6414
  lhs.payload.every((field, at) => equals(field, rhs.payload[at])));
6317
6415
  }
6318
6416
  if (lhs.kind === "vector" && rhs.kind === "vector") {
6319
- return (lhs.items.length === rhs.items.length &&
6320
- lhs.items.every((item, at) => equals(item, rhs.items[at])));
6417
+ return (vectorLength(lhs) === vectorLength(rhs) &&
6418
+ vectorItems(lhs).every((item, at) => equals(item, vectorElement(rhs, at))));
6321
6419
  }
6322
6420
  return canonical(lhs) === canonical(rhs);
6323
6421
  }
@@ -776,7 +776,7 @@ export type RuntimeCallFamily = {
776
776
  } | "VecRemoveAtBool" | "VecRemoveAtLayout" | "VecRemoveAtOwned" | "VecSetBool" | "VecSetLayout" | "VecSetOwned" | "VecSetOwnedMove" | {
777
777
  VecSliceRange: VecSliceElem;
778
778
  } | "VtableDispatchPanicOnOob";
779
- export type RuntimeVariantRole = "CrashKindFault" | "CrashKindHeapExceeded" | "CrashKindPartitionDetected" | "CrashKindHandlerFailed" | "CrashKindTimerOverdue" | "OptionSome" | "OptionNone" | "ResultOk" | "ResultErr" | "ActorErrorRejected" | "ActorErrorFailed" | "ActorErrorTrapped" | "ActorErrorRefused" | "CallerActor" | "CallerTask" | "CallerTimer" | "CallerNode" | "ActorErrorDead" | "ActorErrorTimedOut" | "ActorErrorNodeNotRunning" | "ActorErrorRoutingFailed" | "ActorErrorEncodeFailed" | "ActorErrorMessageTooLarge" | "ActorErrorConnectionDropped" | "ActorErrorPartition" | "SendErrorFull" | "SendErrorClosed" | "SendErrorPartition" | "SendErrorStaleRef" | "SendErrorLocalShutdown" | "SendErrorCancelled" | "SendErrorVersionMismatch" | "SendErrorBackpressure" | "SendErrorDead" | "SendErrorUnauthorized" | "SendErrorInvalidMessage" | "SendErrorMessageTooLarge" | "SendErrorTimedOut" | "SendErrorWriteTimedOut" | "DeliveryAccepted" | "DeliveryDiscarded" | "LinkErrorNoContext";
779
+ export type RuntimeVariantRole = "CrashKindFault" | "CrashKindHeapExceeded" | "CrashKindPartitionDetected" | "CrashKindHandlerFailed" | "CrashKindTimerOverdue" | "CrashKindStackOverflow" | "OptionSome" | "OptionNone" | "ResultOk" | "ResultErr" | "ActorErrorRejected" | "ActorErrorFailed" | "ActorErrorTrapped" | "ActorErrorRefused" | "CallerActor" | "CallerTask" | "CallerTimer" | "CallerNode" | "ActorErrorDead" | "ActorErrorTimedOut" | "ActorErrorNodeNotRunning" | "ActorErrorRoutingFailed" | "ActorErrorEncodeFailed" | "ActorErrorMessageTooLarge" | "ActorErrorConnectionDropped" | "ActorErrorPartition" | "SendErrorFull" | "SendErrorClosed" | "SendErrorPartition" | "SendErrorStaleRef" | "SendErrorLocalShutdown" | "SendErrorCancelled" | "SendErrorVersionMismatch" | "SendErrorBackpressure" | "SendErrorDead" | "SendErrorUnauthorized" | "SendErrorInvalidMessage" | "SendErrorMessageTooLarge" | "SendErrorTimedOut" | "SendErrorWriteTimedOut" | "DeliveryAccepted" | "DeliveryDiscarded" | "LinkErrorNoContext";
780
780
  export type SafepointDetail = {
781
781
  budget: number;
782
782
  };
@@ -865,6 +865,11 @@ export type SupervisorChild = {
865
865
  stop_ns?: number | null;
866
866
  stop_config?: number | null;
867
867
  is_pool?: boolean;
868
+ /**
869
+ * Construction transfers this child's effective restart delay as an
870
+ * `(initial, maximum)` argument pair after the config, in declaration order.
871
+ */
872
+ backoff?: boolean;
868
873
  spawn: number;
869
874
  };
870
875
  export type SupervisorPoolOp = "Len" | "Member" | "Get";
@@ -873,7 +878,6 @@ export type SupervisorShape = {
873
878
  strategy: RestartStrategy;
874
879
  max_restarts: number;
875
880
  window_secs: number;
876
- backoff?: boolean;
877
881
  children: Array<SupervisorChild>;
878
882
  startup_order?: Array<number>;
879
883
  };
@@ -1141,7 +1145,7 @@ export type TermV1 = {
1141
1145
  })));
1142
1146
  export type TimerSendPolicy = "reject" | "drop_newest";
1143
1147
  export type TimestampBinaryOp = "Add" | "Subtract";
1144
- export type TrapName = "heap_exceeded" | "integer_overflow" | "divide_by_zero" | "shift_out_of_range" | "vector_bounds" | "actor_refused";
1148
+ export type TrapName = "heap_exceeded" | "integer_overflow" | "divide_by_zero" | "signed_min_div_neg_one" | "shift_out_of_range" | "vector_bounds" | "actor_refused" | "stack_overflow";
1145
1149
  export type ValueCapability = {
1146
1150
  id: number;
1147
1151
  capability: Capability;
@@ -1,4 +1,4 @@
1
- import { vectorStorage } from "../values.js";
1
+ import { vectorElement, vectorItems, vectorLength, vectorStorage, } from "../values.js";
2
2
  import { cloneEncoding } from "./encoding-storage.js";
3
3
  const U64_MAX = 0xffffffffffffffffn;
4
4
  export class HeapExceeded extends Error {
@@ -128,12 +128,14 @@ export class ManagedLayouts {
128
128
  if (value.kind !== "vector")
129
129
  throw new Error("managed copy has no sequence");
130
130
  const storage = vectorStorage(value);
131
- if (row.share_on_copy && value.items.length)
131
+ if (row.share_on_copy && vectorLength(value))
132
132
  return value;
133
- const items = value.items.map((item) => this.copy(item, row.element));
133
+ const items = vectorItems(value).map((item) => this.copy(item, row.element));
134
134
  return {
135
135
  ...value,
136
136
  items,
137
+ offset: 0,
138
+ length: undefined,
137
139
  storage: { ...storage, items, capacity: items.length, owners: 1 },
138
140
  };
139
141
  }
@@ -274,7 +276,10 @@ export class ManagedLayouts {
274
276
  throw new Error("managed sequence has no backing");
275
277
  const storage = value.storage ?? vectorStorage(value);
276
278
  if (storage.capacity)
277
- allocation(storage, this.width(row.element, storage.capacity), () => storage.items.forEach((item) => visit(item, row.element)));
279
+ allocation(storage, this.width(row.element, storage.capacity), () => {
280
+ for (let index = 0; index < vectorLength(value); index++)
281
+ visit(vectorElement(value, index), row.element);
282
+ });
278
283
  return;
279
284
  }
280
285
  case "aggregate":