@agoric/swingset-vat 0.33.0-u17.1 → 0.33.0-u18.1

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.
@@ -7,6 +7,7 @@ import { isObject } from '@endo/marshal';
7
7
  import { parseKernelSlot } from '../parseKernelSlots.js';
8
8
  import { makeVatSlot, parseVatSlot } from '../../lib/parseVatSlots.js';
9
9
  import { insistVatID } from '../../lib/id.js';
10
+ import { insistCapData } from '../../lib/capdata.js';
10
11
  import { kdebug } from '../../lib/kdebug.js';
11
12
  import {
12
13
  parseReachableAndVatSlot,
@@ -83,49 +84,53 @@ export function initializeVatState(
83
84
  }
84
85
 
85
86
  /**
86
- * Produce a vat keeper for a vat.
87
+ * @typedef {object} VatKeeperPowers
88
+ * @property {TranscriptStore} transcriptStore Accompanying transcript store, for the transcripts
89
+ * @property {*} kernelSlog
90
+ * @property {*} addKernelObject Kernel function to add a new object to the kernel's mapping tables.
91
+ * @property {*} addKernelPromiseForVat Kernel function to add a new promise to the kernel's mapping tables.
92
+ * @property {(kernelSlot: string) => boolean} kernelObjectExists
93
+ * @property {*} incrementRefCount
94
+ * @property {*} decrementRefCount
95
+ * @property {(kernelSlot: string) => {reachable: number, recognizable: number}} getObjectRefCount
96
+ * @property {(kernelSlot: string, o: { reachable: number, recognizable: number }) => void} setObjectRefCount
97
+ * @property {(vatID: string, kernelSlot: string) => {isReachable: boolean, vatSlot: string}} getReachableAndVatSlot
98
+ * @property {(kernelSlot: string) => void} addMaybeFreeKref
99
+ * @property {*} incStat
100
+ * @property {*} decStat
101
+ * @property {*} getCrankNumber
102
+ * @property {*} scheduleReap
103
+ * @property {SnapStore} snapStore
104
+ */
105
+
106
+ /**
107
+ * Produce a "vat keeper" for the kernel state of a vat.
87
108
  *
88
- * @param {KVStore} kvStore The keyValue store in which the persistent state will be kept
89
- * @param {TranscriptStore} transcriptStore Accompanying transcript store, for the transcripts
90
- * @param {*} kernelSlog
91
109
  * @param {string} vatID The vat ID string of the vat in question
92
- * @param {*} addKernelObject Kernel function to add a new object to the kernel's
93
- * mapping tables.
94
- * @param {*} addKernelPromiseForVat Kernel function to add a new promise to the
95
- * kernel's mapping tables.
96
- * @param {(kernelSlot: string) => boolean} kernelObjectExists
97
- * @param {*} incrementRefCount
98
- * @param {*} decrementRefCount
99
- * @param {(kernelSlot: string) => {reachable: number, recognizable: number}} getObjectRefCount
100
- * @param {(kernelSlot: string, o: { reachable: number, recognizable: number }) => void} setObjectRefCount
101
- * @param {(vatID: string, kernelSlot: string) => {isReachable: boolean, vatSlot: string}} getReachableAndVatSlot
102
- * @param {(kernelSlot: string) => void} addMaybeFreeKref
103
- * @param {*} incStat
104
- * @param {*} decStat
105
- * @param {*} getCrankNumber
106
- * @param {*} scheduleReap
107
- * @param {SnapStore} [snapStore]
108
- * returns an object to hold and access the kernel's state for the given vat
110
+ * @param {KVStore} kvStore The keyValue store in which the persistent state will be kept
111
+ * @param {VatKeeperPowers} powers
109
112
  */
110
113
  export function makeVatKeeper(
111
- kvStore,
112
- transcriptStore,
113
- kernelSlog,
114
114
  vatID,
115
- addKernelObject,
116
- addKernelPromiseForVat,
117
- kernelObjectExists,
118
- incrementRefCount,
119
- decrementRefCount,
120
- getObjectRefCount,
121
- setObjectRefCount,
122
- getReachableAndVatSlot,
123
- addMaybeFreeKref,
124
- incStat,
125
- decStat,
126
- getCrankNumber,
127
- scheduleReap,
128
- snapStore = undefined,
115
+ kvStore,
116
+ {
117
+ transcriptStore,
118
+ kernelSlog,
119
+ addKernelObject,
120
+ addKernelPromiseForVat,
121
+ kernelObjectExists,
122
+ incrementRefCount,
123
+ decrementRefCount,
124
+ getObjectRefCount,
125
+ setObjectRefCount,
126
+ getReachableAndVatSlot,
127
+ addMaybeFreeKref,
128
+ incStat,
129
+ decStat,
130
+ getCrankNumber,
131
+ scheduleReap,
132
+ snapStore,
133
+ },
129
134
  ) {
130
135
  insistVatID(vatID);
131
136
 
@@ -173,6 +178,35 @@ export function makeVatKeeper(
173
178
  return harden(options);
174
179
  }
175
180
 
181
+ /**
182
+ * @param {SwingSetCapData} newVPCD
183
+ */
184
+ function setVatParameters(newVPCD) {
185
+ insistCapData(newVPCD);
186
+ const key = `${vatID}.vatParameters`;
187
+ // increment-before-decrement to minimize spurious rc=0 checks
188
+ for (const kref of newVPCD.slots) {
189
+ incrementRefCount(kref, `${vatID}.vatParameters`);
190
+ }
191
+ const old = kvStore.get(key) || '{"slots":[]}';
192
+ for (const kref of JSON.parse(old).slots) {
193
+ decrementRefCount(kref, `${vatID}.vatParameters`);
194
+ }
195
+ kvStore.set(key, JSON.stringify(newVPCD));
196
+ }
197
+
198
+ /**
199
+ * @returns {SwingSetCapData | undefined} vpcd
200
+ */
201
+ function getVatParameters() {
202
+ const key = `${vatID}.vatParameters`;
203
+ const old = kvStore.get(key);
204
+ if (old) {
205
+ return JSON.parse(old);
206
+ }
207
+ return undefined;
208
+ }
209
+
176
210
  // This is named "addDirt" because it should increment all dirt
177
211
  // counters (both for reap/BOYD and for heap snapshotting). We don't
178
212
  // have `heapSnapshotDirt` yet, but when we do, it should get
@@ -768,6 +802,8 @@ export function makeVatKeeper(
768
802
  setSourceAndOptions,
769
803
  getSourceAndOptions,
770
804
  getOptions,
805
+ setVatParameters,
806
+ getVatParameters,
771
807
  addDirt,
772
808
  getReapDirt,
773
809
  clearReapDirt,
@@ -97,6 +97,7 @@ export function makeSyscallSimulator(
97
97
  deliveryNum,
98
98
  transcriptEntry,
99
99
  ) {
100
+ const context = `anachrophobia in ${vatID} delivery d${deliveryNum}`;
100
101
  const syscallsExpected = [...transcriptEntry.sc]; // copy
101
102
  const syscallsMade = [];
102
103
  // syscallStatus's length will be max(syscallsExpected,
@@ -107,31 +108,36 @@ export function makeSyscallSimulator(
107
108
  let replayError; // sticky
108
109
 
109
110
  const explain = () => {
110
- console.log(`anachrophobia strikes ${vatID} on delivery ${deliveryNum}`);
111
+ console.log(
112
+ `anachrophobia strikes ${vatID} delivery d${deliveryNum} syscalls`,
113
+ );
111
114
  for (const [idx, status] of syscallStatus.entries()) {
112
115
  const expected = syscallsExpected[idx];
113
116
  const got = syscallsMade[idx];
114
117
  switch (status) {
115
118
  case 'ok': {
116
- console.log(`sc[${idx}]: ok: ${djson.stringify(got)}`);
119
+ console.log(`sc${idx}: ok: ${djson.stringify(got)}`);
117
120
  break;
118
121
  }
119
122
  case 'wrong': {
120
- console.log(`sc[${idx}]: wrong`);
121
- console.log(` expected: ${djson.stringify(expected.s)}`);
122
- console.log(` got : ${djson.stringify(got)}`);
123
+ console.log(
124
+ `
125
+ sc${idx}: WRONG
126
+ expected: ${djson.stringify(expected.s)}
127
+ got : ${djson.stringify(got)}`.trimStart(),
128
+ );
123
129
  break;
124
130
  }
125
131
  case 'extra': {
126
- console.log(`sc[${idx}]: extra: ${djson.stringify(got)}`);
132
+ console.log(`sc${idx}: EXTRA: ${djson.stringify(got)}`);
127
133
  break;
128
134
  }
129
135
  case 'missing': {
130
- console.log(`sc[${idx}]: missing: ${djson.stringify(expected.s)}`);
136
+ console.log(`sc${idx}: MISSING: ${djson.stringify(expected.s)}`);
131
137
  break;
132
138
  }
133
139
  default:
134
- Fail`bad ${status}`;
140
+ Fail`sc${idx}: bad status ${status}`;
135
141
  }
136
142
  }
137
143
  };
@@ -140,16 +146,16 @@ export function makeSyscallSimulator(
140
146
  // slog entries have no kernel-translated kso/ksr
141
147
  const finish = kernelSlog.syscall(vatID, undefined, vso);
142
148
  const expected = syscallsExpected[syscallsMade.length];
143
- syscallsMade.push(vso);
149
+ const idx = syscallsMade.push(vso) - 1;
144
150
  if (!expected) {
145
151
  syscallStatus.push('extra');
146
- const error = Error(`anachrophobia in ${vatID}: extra syscall`);
152
+ const error = Error(`${context}: extra syscall at index sc${idx}`);
147
153
  replayError ||= error;
148
154
  throw error;
149
155
  }
150
156
  if (!syscallsAreIdentical(expected.s, vso)) {
151
157
  syscallStatus.push('wrong');
152
- const error = Error(`anachrophobia in ${vatID}: wrong syscall`);
158
+ const error = Error(`${context}: wrong syscall at index sc${idx}`);
153
159
  replayError ||= error;
154
160
  throw error;
155
161
  }
@@ -159,12 +165,14 @@ export function makeSyscallSimulator(
159
165
  };
160
166
 
161
167
  const finishSimulation = () => {
162
- if (syscallsMade.length < syscallsExpected.length) {
163
- const missing = syscallsExpected.length - syscallsMade.length;
168
+ const missing = syscallsExpected.length - syscallsMade.length;
169
+ if (missing > 0) {
164
170
  for (let i = 0; i < missing; i += 1) {
165
171
  syscallStatus.push('missing');
166
172
  }
167
- const error = Error(`anachrophobia in ${vatID}: missing syscalls`);
173
+ const error = Error(
174
+ `${context}: missing ${missing} syscall(s) at index sc${syscallsMade.length}`,
175
+ );
168
176
  replayError ||= error;
169
177
  }
170
178
 
@@ -389,7 +397,6 @@ export function makeVatWarehouse({
389
397
  // entriesReplayed, // retval of replayTranscript() above
390
398
  // );
391
399
  ephemeral.vats.set(vatID, result);
392
- // eslint-disable-next-line no-use-before-define
393
400
  await applyAvailabilityPolicy(vatID);
394
401
  return result;
395
402
  }
@@ -596,7 +603,6 @@ export function makeVatWarehouse({
596
603
  //
597
604
  /** @type { KernelDeliveryObject } */
598
605
  const kd = harden(['bringOutYourDead']);
599
- // eslint-disable-next-line no-use-before-define
600
606
  const vd = kernelDeliveryToVatDelivery(vatID, kd);
601
607
  const vs = kernelSlog.provideVatSlogger(vatID).vatSlog;
602
608
  await deliverToVat(vatID, kd, vd, vs);
@@ -50,6 +50,7 @@ function makeTranslateKernelDeliveryToVatDelivery(vatID, kernelKeeper) {
50
50
  parseVatSlot(targetSlot).allocatedByVat || Fail`deliver() to wrong vat`;
51
51
  } else if (type === 'promise') {
52
52
  const p = kernelKeeper.getKernelPromise(target);
53
+ assert(p.state === 'unresolved');
53
54
  p.decider === vatID || Fail`wrong decider`;
54
55
  }
55
56
  const inputSlots = msg.methargs.slots.map(slot =>
@@ -59,7 +60,9 @@ function makeTranslateKernelDeliveryToVatDelivery(vatID, kernelKeeper) {
59
60
  if (msg.result) {
60
61
  insistKernelType('promise', msg.result);
61
62
  const p = kernelKeeper.getKernelPromise(msg.result);
62
- p.state === 'unresolved' || Fail`result ${msg.result} already resolved`;
63
+ if (p.state !== 'unresolved') {
64
+ throw Fail`result ${msg.result} already resolved`;
65
+ }
63
66
  !p.decider || Fail`result ${msg.result} already has decider ${p.decider}`;
64
67
  resultSlot = vatKeeper.mapKernelSlotToVatSlot(msg.result);
65
68
  insistVatType('promise', resultSlot);
@@ -318,8 +321,9 @@ function makeTranslateVatSyscallToKernelSyscall(vatID, kernelKeeper) {
318
321
  // In the case of non-pipelining vats these checks are redundant since
319
322
  // we're guaranteed to have a promise newly allocated by the vat.
320
323
  const p = kernelKeeper.getKernelPromise(result);
321
- p.state === 'unresolved' ||
322
- Fail`send() result ${result} is already resolved`;
324
+ if (p.state !== 'unresolved') {
325
+ throw Fail`send() result ${result} is already resolved`;
326
+ }
323
327
  p.decider === vatID ||
324
328
  Fail`send() result ${result} is decided by ${p.decider} not ${vatID}`;
325
329
  kernelKeeper.clearDecider(result);
@@ -36,6 +36,7 @@ workerLog(`supervisor started`);
36
36
 
37
37
  function makeNetstringReader({ fd, encoding }) {
38
38
  const input = Buffer.alloc(32 * 1024);
39
+ /** @type {Buffer<ArrayBufferLike>} */
39
40
  let buffered = Buffer.alloc(0);
40
41
  let decoded = [];
41
42
 
package/src/typeGuards.js CHANGED
@@ -10,10 +10,11 @@ export const ManagerType = M.or(
10
10
 
11
11
  const Bundle = M.splitRecord({ moduleType: M.string() });
12
12
 
13
- const SwingsetConfigOptions = harden({
13
+ const SwingsetConfigOptions = {
14
14
  creationOptions: M.splitRecord({}, { critical: M.boolean() }),
15
15
  parameters: M.recordOf(M.string(), M.any()),
16
- });
16
+ };
17
+ harden(SwingsetConfigOptions);
17
18
 
18
19
  const SwingSetConfigProperties = M.or(
19
20
  M.splitRecord({ sourceSpec: M.string() }, SwingsetConfigOptions),
@@ -124,7 +124,7 @@ export {};
124
124
  *
125
125
  * @typedef { { transcriptCount: number } } VatStats
126
126
  * @typedef { ReturnType<typeof import('./kernel/state/vatKeeper.js').makeVatKeeper> } VatKeeper
127
- * @typedef { ReturnType<typeof import('./kernel/state/kernelKeeper.js').default> } KernelKeeper
127
+ * @typedef { import('./kernel/state/kernelKeeper.js').KernelKeeper } KernelKeeper
128
128
  * @typedef { Awaited<ReturnType<typeof import('@agoric/xsnap').xsnap>> } XSnap
129
129
  * @typedef { (dr: VatDeliveryResult) => void } SlogFinishDelivery
130
130
  * @typedef { (ksr: KernelSyscallResult, vsr: VatSyscallResult) => void } SlogFinishSyscall
@@ -227,6 +227,7 @@ export {};
227
227
  *
228
228
  * @typedef { { exports: number,
229
229
  * imports: number,
230
+ * promises: number,
230
231
  * kv: number,
231
232
  * snapshots: number,
232
233
  * transcripts: number,
@@ -283,6 +284,13 @@ export {};
283
284
  * @property { boolean } [restartWorkerOnSnapshot] Reload worker immediately upon snapshot creation
284
285
  */
285
286
 
287
+ /**
288
+ * @typedef { import('./devices/mailbox/mailbox.js').Mailbox } Mailbox
289
+ */
290
+ /**
291
+ * @typedef { import('./devices/mailbox/mailbox.js').MailboxExport } MailboxExport
292
+ */
293
+
286
294
  /**
287
295
  * Vat Creation and Management
288
296
  *
@@ -92,6 +92,17 @@ export {};
92
92
  * @property {number} [computrons]
93
93
  */
94
94
 
95
+ /**
96
+ * @typedef {{ state: 'unresolved', refCount: number,
97
+ * decider: string | undefined, policy: string,
98
+ * subscribers: string[], queue: string[],
99
+ * }} UnresolvedPromiseRecord
100
+ * @typedef {{ state: 'fulfilled' | 'rejected', refCount: number,
101
+ * data: SwingSetCapData,
102
+ * }} SettledPromiseRecord
103
+ * @typedef {UnresolvedPromiseRecord | SettledPromiseRecord} PromiseRecord
104
+ */
105
+
95
106
  /**
96
107
  * @typedef {{
97
108
  * enablePipelining: boolean,
@@ -1,5 +1,3 @@
1
- /* eslint-disable no-use-before-define */
2
-
3
1
  import { assert, Fail } from '@endo/errors';
4
2
  import { kser } from '@agoric/kmarshal';
5
3
  import { parseLocalSlot, insistLocalType } from './parseLocalSlots.js';
@@ -150,7 +150,6 @@ export function makeState(syscall) {
150
150
  store.set('r.nextID', '1');
151
151
  store.set('initialized', 'true');
152
152
  if (controller) {
153
- // eslint-disable-next-line no-use-before-define
154
153
  addMetaObject(controller);
155
154
  cdebug(`comms controller is ${controller}`);
156
155
  }
@@ -393,7 +392,6 @@ export function makeState(syscall) {
393
392
  // the object is unreachable
394
393
 
395
394
  const { owner, isReachable, isRecognizable } =
396
- // eslint-disable-next-line no-use-before-define
397
395
  getOwnerAndStatus(lref);
398
396
  if (isReachable) {
399
397
  // but the exporter doesn't realize it yet, so schedule a
@@ -558,7 +556,6 @@ export function makeState(syscall) {
558
556
  isReachable = isReachableByKernel(lref);
559
557
  isRecognizable = !!mapToKernel(lref);
560
558
  } else {
561
- // eslint-disable-next-line no-use-before-define
562
559
  const remote = getRemote(owner);
563
560
  isReachable = remote.isReachable(lref);
564
561
  isRecognizable = !!remote.mapToRemote(lref);
@@ -794,7 +791,6 @@ export function makeState(syscall) {
794
791
  insistPromiseIsUnresolved,
795
792
  markPromiseAsResolved,
796
793
 
797
- // eslint-disable-next-line no-use-before-define
798
794
  getRemote,
799
795
  addRemote,
800
796
  getRemoteIDForName,
@@ -1,5 +1,3 @@
1
- /* eslint-disable no-use-before-define */
2
-
3
1
  import { assert } from '@endo/errors';
4
2
  import { Far, E, passStyleOf } from '@endo/far';
5
3
  import { makePromiseKit } from '@endo/promise-kit';
@@ -100,7 +100,6 @@ export function buildRootObject(vatPowers, _vatParameters, baggage) {
100
100
  // getNotifier: ({ state }) => state.notifier, // XXX RESTORE
101
101
  getNotifier: ({ _self }) => Fail`not implemented, see #7234`, // XXX TEMP
102
102
  },
103
- // eslint-disable-next-line no-use-before-define
104
103
  { finish: finishMeter },
105
104
  );
106
105
 
@@ -120,18 +119,15 @@ export function buildRootObject(vatPowers, _vatParameters, baggage) {
120
119
  // getNotifier: ({ state }) => state.notifier, // will never fire // XXX RESTORE
121
120
  getNotifier: ({ _self }) => Fail`not implemented, see #7234`, // XXX TEMP
122
121
  },
123
- // eslint-disable-next-line no-use-before-define
124
122
  { finish: finishMeter },
125
123
  );
126
124
 
127
125
  function finishMeter({ state, self }) {
128
- // eslint-disable-next-line no-use-before-define
129
126
  meterByID.init(
130
127
  state.meterID,
131
128
  // harden({ meter: self, updater: state.updater }), // XXX RESTORE
132
129
  harden({ meter: self }), // XXX TEMP
133
130
  );
134
- // eslint-disable-next-line no-use-before-define
135
131
  meterIDByMeter.set(self, state.meterID);
136
132
  }
137
133
 
@@ -1,8 +1,6 @@
1
1
  import { makeMarshal } from '@endo/marshal';
2
2
  import { Far } from '@endo/far';
3
3
 
4
- /* eslint-disable no-use-before-define */
5
-
6
4
  function fakeSTV(slot, iface = 'Remotable') {
7
5
  return Far(iface, {
8
6
  getSlot: () => slot,
@@ -11,7 +11,6 @@ export function buildRootObject() {
11
11
  async function runTests(phase) {
12
12
  testLog = [];
13
13
  doneP = makePromiseKit();
14
- // eslint-disable-next-line no-use-before-define
15
14
  await E(testVatRoot).runTests(self, phase);
16
15
  await doneP.promise;
17
16
  return testLog;
@@ -1,3 +1,12 @@
1
+ /**
2
+ * @file Source code for a bootstrap vat that runs blockchain behaviors (such as
3
+ * bridge vat integration) and exposes reflective methods for use in testing.
4
+ *
5
+ * TODO: Build from ./vat-puppet.js makeReflectionMethods
6
+ * and share code with packages/vats/tools/vat-reflective-chain-bootstrap.js
7
+ * (which basically extends this for better [mock] blockchain integration).
8
+ */
9
+
1
10
  import { Fail, q } from '@endo/errors';
2
11
  import { objectMap } from '@agoric/internal';
3
12
  import { Far, E } from '@endo/far';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Like prepare-strict-test-env but also sets up ses-ava and provides
3
+ * the ses-ava `test` function to be used as if it is the ava
4
+ * `test` function.
5
+ */
6
+
7
+ import '@agoric/swingset-liveslots/tools/prepare-strict-test-env.js';
8
+
9
+ import { wrapTest } from '@endo/ses-ava';
10
+ import rawTest from 'ava';
11
+
12
+ export * from '@agoric/swingset-liveslots/tools/prepare-strict-test-env.js';
13
+
14
+ export const test = wrapTest(rawTest);
15
+
16
+ // Does not import from a module because we're testing the global env
17
+ /* global globalThis */
18
+ export const VatData = globalThis.VatData;
19
+ assert(VatData);
@@ -2,12 +2,18 @@ import { Fail, q } from '@endo/errors';
2
2
  import { kunser } from '@agoric/kmarshal';
3
3
  import { makeQueue } from '@endo/stream';
4
4
 
5
- /** @import { ERef } from '@endo/far' */
5
+ /**
6
+ * @import { ERef } from '@endo/far'
7
+ * @import { RunPolicy } from '../src/types-external.js'
8
+ */
9
+
10
+ /** @typedef {{ provideRunPolicy: () => RunPolicy | undefined }} RunHarness */
6
11
 
7
12
  /**
8
13
  * @param {import('../src/controller/controller.js').SwingsetController} controller
14
+ * @param {RunHarness} [harness]
9
15
  */
10
- export const makeRunUtils = controller => {
16
+ export const makeRunUtils = (controller, harness) => {
11
17
  const mutex = makeQueue();
12
18
  const logRunFailure = reason =>
13
19
  console.log('controller.run() failure', reason);
@@ -17,7 +23,7 @@ export const makeRunUtils = controller => {
17
23
  * Wait for exclusive access to the controller, then before relinquishing that access,
18
24
  * enqueue and process a delivery and return the result.
19
25
  *
20
- * @param {() => ERef<void | ReturnType<controller['queueToVatObject']>>} deliveryThunk
26
+ * @param {() => ERef<void | ReturnType<typeof controller['queueToVatObject']>>} deliveryThunk
21
27
  * function for enqueueing a delivery and returning the result kpid (if any)
22
28
  * @param {boolean} [voidResult] whether to ignore the result
23
29
  * @returns {Promise<any>}
@@ -25,7 +31,8 @@ export const makeRunUtils = controller => {
25
31
  const queueAndRun = async (deliveryThunk, voidResult = false) => {
26
32
  await mutex.get();
27
33
  const kpid = await deliveryThunk();
28
- const runResultP = controller.run();
34
+ const runPolicy = harness && harness.provideRunPolicy();
35
+ const runResultP = controller.run(runPolicy);
29
36
  mutex.put(runResultP.catch(logRunFailure));
30
37
  await runResultP;
31
38
 
@@ -0,0 +1,111 @@
1
+ /**
2
+ * @file Source code for a vat that exposes reflective methods for use in
3
+ * testing.
4
+ */
5
+
6
+ import { Fail, q } from '@endo/errors';
7
+ import { Far, E } from '@endo/far';
8
+ import { makePromiseKit } from '@endo/promise-kit';
9
+ import { objectMap } from '@agoric/internal';
10
+
11
+ /**
12
+ * @callback Die
13
+ * @param {unknown} completion
14
+ * @param {[target: unknown, method: string, ...args: unknown[]]} [finalSend]
15
+ */
16
+
17
+ /**
18
+ * @typedef {Array<[name: string, ...args: unknown[]]>} CallLog
19
+ */
20
+
21
+ /**
22
+ * @param {import('@agoric/swingset-vat').VatPowers} vatPowers
23
+ * @param {import('@agoric/vat-data').Baggage} baggage
24
+ */
25
+ export const makeReflectionMethods = (vatPowers, baggage) => {
26
+ let baggageHoldCount = 0;
27
+ /** @type {Map<object, CallLog>} */
28
+ const callLogsByRemotable = new Map();
29
+ const heldInHeap = [];
30
+ const send = (target, method, ...args) => E(target)[method](...args);
31
+ const makeSpy = (value, name, callLog) => {
32
+ const spyName = `get ${name}`;
33
+ const spy = {
34
+ [spyName](...args) {
35
+ callLog.push([name, ...args]);
36
+ return value;
37
+ },
38
+ }[spyName];
39
+ return spy;
40
+ };
41
+
42
+ return {
43
+ /** @type {Die} */
44
+ dieHappy: (completion, finalSend) => {
45
+ vatPowers.exitVat(completion);
46
+ if (finalSend) send(...finalSend);
47
+ },
48
+
49
+ /** @type {Die} */
50
+ dieSad: (reason, finalSend) => {
51
+ vatPowers.exitVatWithFailure(/** @type {Error} */ (reason));
52
+ if (finalSend) send(...finalSend);
53
+ },
54
+
55
+ holdInBaggage: (...values) => {
56
+ for (const value of values) {
57
+ baggage.init(`held-${baggageHoldCount}`, value);
58
+ baggageHoldCount += 1;
59
+ }
60
+ return baggageHoldCount;
61
+ },
62
+
63
+ holdInHeap: (...values) => heldInHeap.push(...values),
64
+
65
+ makePromiseKit: () => {
66
+ const { promise, ...resolverMethods } = makePromiseKit();
67
+ void promise.catch(() => {});
68
+ const resolver = Far('resolver', resolverMethods);
69
+ return harden({ promise, resolver });
70
+ },
71
+
72
+ makeUnsettledPromise() {
73
+ const { promise } = makePromiseKit();
74
+ void promise.catch(() => {});
75
+ return promise;
76
+ },
77
+
78
+ /**
79
+ * Returns a remotable with methods that return provided values. Invocations
80
+ * of those methods and their arguments are captured for later retrieval by
81
+ * `getCallLogForRemotable`.
82
+ *
83
+ * @param {string} [label]
84
+ * @param {Record<string, any>} [fields]
85
+ */
86
+ makeRemotable: (label = 'Remotable', fields = {}) => {
87
+ /** @type {CallLog} */
88
+ const callLog = [];
89
+ const methods = objectMap(fields, (value, name) =>
90
+ makeSpy(value, name, callLog),
91
+ );
92
+ const remotable = Far(label, { ...methods });
93
+ callLogsByRemotable.set(remotable, callLog);
94
+ return remotable;
95
+ },
96
+
97
+ /**
98
+ * @param {object} remotable
99
+ * @returns {CallLog}
100
+ */
101
+ getCallLogForRemotable: remotable =>
102
+ callLogsByRemotable.get(remotable) ||
103
+ Fail`unknown remotable ${q(remotable)}`,
104
+ };
105
+ };
106
+ harden(makeReflectionMethods);
107
+
108
+ export function buildRootObject(vatPowers, _vatParameters, baggage) {
109
+ const methods = makeReflectionMethods(vatPowers, baggage);
110
+ return Far('root', methods);
111
+ }