@agoric/swingset-liveslots 0.10.3-other-dev-8f8782b.0 → 0.10.3-other-dev-3eb1a1d.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.
Files changed (77) hide show
  1. package/README.md +2 -0
  2. package/package.json +27 -19
  3. package/src/boyd-gc.js +598 -0
  4. package/src/cache.js +3 -2
  5. package/src/capdata.js +1 -2
  6. package/src/collectionManager.js +219 -103
  7. package/src/facetiousness.js +1 -1
  8. package/src/index.js +4 -2
  9. package/src/liveslots.js +131 -301
  10. package/src/message.js +5 -5
  11. package/src/parseVatSlots.js +1 -1
  12. package/src/types-index.d.ts +4 -0
  13. package/src/types-index.js +2 -0
  14. package/src/types.js +8 -2
  15. package/src/vatstore-iterators.js +2 -0
  16. package/src/virtualObjectManager.js +185 -71
  17. package/src/virtualReferences.js +135 -26
  18. package/src/watchedPromises.js +67 -17
  19. package/test/{test-baggage.js → baggage.test.js} +1 -2
  20. package/test/{test-cache.js → cache.test.js} +0 -1
  21. package/test/clear-collection.test.js +586 -0
  22. package/test/{test-collection-schema-refcount.js → collection-schema-refcount.test.js} +1 -2
  23. package/test/{test-collection-upgrade.js → collection-upgrade.test.js} +1 -3
  24. package/test/{test-collections.js → collections.test.js} +158 -14
  25. package/test/{test-dropped-collection-weakrefs.js → dropped-collection-weakrefs.test.js} +1 -2
  26. package/test/dropped-weakset-9939.test.js +80 -0
  27. package/test/dummyMeterControl.js +1 -1
  28. package/test/{test-durabilityChecks.js → durabilityChecks.test.js} +4 -4
  29. package/test/exo-utils.js +70 -0
  30. package/test/{test-facetiousness.js → facetiousness.test.js} +1 -2
  31. package/test/gc-and-finalize.js +30 -1
  32. package/test/gc-before-finalizer.test.js +230 -0
  33. package/test/gc-helpers.js +2 -3
  34. package/test/{test-gc-sensitivity.js → gc-sensitivity.test.js} +2 -2
  35. package/test/handled-promises.test.js +506 -0
  36. package/test/{test-initial-vrefs.js → initial-vrefs.test.js} +2 -3
  37. package/test/liveslots-helpers.js +12 -7
  38. package/test/{test-liveslots-mock-gc.js → liveslots-mock-gc.test.js} +101 -2
  39. package/test/{test-liveslots-real-gc.js → liveslots-real-gc.test.js} +62 -37
  40. package/test/{test-liveslots.js → liveslots.test.js} +14 -15
  41. package/test/mock-gc.js +1 -0
  42. package/test/storeGC/{test-lifecycle.js → lifecycle.test.js} +2 -2
  43. package/test/storeGC/{test-refcount-management.js → refcount-management.test.js} +1 -2
  44. package/test/storeGC/{test-scalar-store-kind.js → scalar-store-kind.test.js} +0 -1
  45. package/test/storeGC/{test-weak-key.js → weak-key.test.js} +1 -2
  46. package/test/strict-test-env-upgrade.test.js +94 -0
  47. package/test/util.js +2 -2
  48. package/test/vat-environment.test.js +65 -0
  49. package/test/vat-util.js +2 -2
  50. package/test/virtual-objects/{test-cease-recognition.js → cease-recognition.test.js} +2 -2
  51. package/test/virtual-objects/{test-cross-facet.js → cross-facet.test.js} +5 -4
  52. package/test/virtual-objects/{test-empty-data.js → empty-data.test.js} +1 -2
  53. package/test/virtual-objects/{test-facets.js → facets.test.js} +1 -2
  54. package/test/virtual-objects/{test-kind-changes.js → kind-changes.test.js} +2 -2
  55. package/test/virtual-objects/{test-reachable-vrefs.js → reachable-vrefs.test.js} +2 -2
  56. package/test/virtual-objects/{test-rep-tostring.js → rep-tostring.test.js} +3 -5
  57. package/test/virtual-objects/{test-retain-remotable.js → retain-remotable.test.js} +25 -24
  58. package/test/virtual-objects/set-debug-label-instances.js +1 -1
  59. package/test/virtual-objects/{test-state-shape.js → state-shape.test.js} +2 -2
  60. package/test/virtual-objects/{test-virtualObjectGC.js → virtualObjectGC.test.js} +2 -2
  61. package/test/virtual-objects/{test-virtualObjectManager.js → virtualObjectManager.test.js} +126 -8
  62. package/test/virtual-objects/{test-vo-real-gc.js → vo-real-gc.test.js} +8 -8
  63. package/test/virtual-objects/{test-weakcollections-vref-handling.js → weakcollections-vref-handling.test.js} +1 -2
  64. package/test/{test-vo-test-harness.js → vo-test-harness.test.js} +0 -1
  65. package/test/{test-vpid-liveslots.js → vpid-liveslots.test.js} +105 -5
  66. package/test/waitUntilQuiescent.js +2 -1
  67. package/test/weakset-dropped-remotable.test.js +50 -0
  68. package/tools/fakeCollectionManager.js +44 -0
  69. package/tools/fakeVirtualObjectManager.js +62 -0
  70. package/tools/fakeVirtualSupport.js +389 -0
  71. package/tools/prepare-strict-test-env.js +124 -0
  72. package/tools/prepare-test-env.js +13 -0
  73. package/tools/setup-vat-data.js +96 -0
  74. package/tools/vo-test-harness.js +143 -0
  75. package/CHANGELOG.md +0 -61
  76. package/test/kmarshal.js +0 -79
  77. package/test/test-handled-promises.js +0 -360
@@ -0,0 +1,506 @@
1
+ import test from 'ava';
2
+
3
+ import { Fail } from '@endo/errors';
4
+ import { Far } from '@endo/marshal';
5
+ import { M } from '@agoric/store';
6
+ import { makePromiseKit } from '@endo/promise-kit';
7
+ // Disabled to avoid circular dependencies.
8
+ // import { makeStoreUtils } from '@agoric/vat-data/src/vat-data-bindings.js';
9
+ // import { makeExoUtils } from '@agoric/vat-data/src/exo-utils.js';
10
+ import { kslot, kser } from '@agoric/kmarshal';
11
+ import { setupTestLiveslots } from './liveslots-helpers.js';
12
+ import { makeResolve, makeReject } from './util.js';
13
+ import { makeExoUtils } from './exo-utils.js';
14
+
15
+ // eslint-disable-next-line no-unused-vars
16
+ const compareEntriesByKey = ([ka], [kb]) => (ka < kb ? -1 : 1);
17
+
18
+ // cf. packages/SwingSet/test/vat-durable-promise-watcher.js
19
+ const buildPromiseWatcherRootObject = (vatPowers, vatParameters, baggage) => {
20
+ const { VatData } = vatPowers;
21
+ const { watchPromise } = VatData;
22
+ const { prepareExo } = makeExoUtils(VatData);
23
+ // const { makeScalarBigMapStore } = makeStoreUtils(VatData);
24
+ const PromiseWatcherI = M.interface('ExtraArgPromiseWatcher', {
25
+ onFulfilled: M.call(M.any(), M.string()).returns(),
26
+ onRejected: M.call(M.any(), M.string()).returns(),
27
+ });
28
+ const watchResolutions = new Map();
29
+ const watcher = prepareExo(
30
+ baggage,
31
+ // No longer ignoring, but the name is set in stone in `kvStoreDataV1`
32
+ 'DurablePromiseIgnorer',
33
+ PromiseWatcherI,
34
+ {
35
+ onFulfilled(value, name) {
36
+ watchResolutions.set(name, { status: 'fulfilled', value });
37
+ },
38
+ onRejected(reason, name) {
39
+ watchResolutions.set(name, { status: 'rejected', reason });
40
+ },
41
+ },
42
+ );
43
+
44
+ const knownPromises = new Map();
45
+
46
+ const root = Far('root', {
47
+ getPromise: name => {
48
+ const promise = knownPromises.get(name);
49
+ promise || Fail`promise doesn't exists: ${name}`;
50
+ return { promise };
51
+ },
52
+ importPromise: (name, promise) => {
53
+ !knownPromises.has(name) || Fail`promise already exists: ${name}`;
54
+ knownPromises.set(name, promise);
55
+ return `imported promise: ${name}`;
56
+ },
57
+ createLocalPromise: (name, fulfillment, rejection) => {
58
+ !knownPromises.has(name) || Fail`promise already exists: ${name}`;
59
+ const { promise, resolve, reject } = makePromiseKit();
60
+ if (fulfillment !== undefined) {
61
+ resolve(fulfillment);
62
+ } else if (rejection !== undefined) {
63
+ reject(rejection);
64
+ }
65
+ knownPromises.set(name, promise);
66
+ return `created local promise: ${name}`;
67
+ },
68
+ watchPromise: name => {
69
+ knownPromises.has(name) || Fail`promise not found: ${name}`;
70
+ watchPromise(knownPromises.get(name), watcher, name);
71
+ return `watched promise: ${name}`;
72
+ },
73
+ getWatchResolution: name => {
74
+ return watchResolutions.get(name);
75
+ },
76
+ });
77
+
78
+ const startOperations = vatParameters?.startOperations || [];
79
+ for (const [method, ...args] of startOperations) {
80
+ root[method](...args);
81
+ }
82
+
83
+ return root;
84
+ };
85
+ const kvStoreDataV1 = Object.entries({
86
+ baggageID: 'o+d6/1',
87
+ idCounters: '{"exportID":11,"collectionID":5,"promiseID":9}',
88
+ kindIDID: '1',
89
+ storeKindIDTable:
90
+ '{"scalarMapStore":2,"scalarWeakMapStore":3,"scalarSetStore":4,"scalarWeakSetStore":5,"scalarDurableMapStore":6,"scalarDurableWeakMapStore":7,"scalarDurableSetStore":8,"scalarDurableWeakSetStore":9}',
91
+ 'vc.1.sDurablePromiseIgnorer_kindHandle':
92
+ '{"body":"#\\"$0.Alleged: kind\\"","slots":["o+d1/10"]}',
93
+ 'vc.1.sthe_DurablePromiseIgnorer':
94
+ '{"body":"#\\"$0.Alleged: DurablePromiseIgnorer\\"","slots":["o+d10/1"]}',
95
+ 'vc.1.|entryCount': '2',
96
+ 'vc.1.|nextOrdinal': '1',
97
+ 'vc.1.|schemata':
98
+ '{"label":"baggage","body":"#{\\"keyShape\\":{\\"#tag\\":\\"match:string\\",\\"payload\\":[]}}","slots":[]}',
99
+ // non-durable
100
+ // 'vc.2.sp+6': '{"body":"#\\"&0\\"","slots":["p+6"]}',
101
+ // 'vc.2.|entryCount': '1',
102
+ // 'vc.2.|nextOrdinal': '1',
103
+ // 'vc.2.|schemata': '{"label":"promiseRegistrations","body":"#{\\"keyShape\\":{\\"#tag\\":\\"match:scalar\\",\\"payload\\":\\"#undefined\\"}}","slots":[]}',
104
+ 'vc.3.|entryCount': '0',
105
+ 'vc.3.|nextOrdinal': '1',
106
+ 'vc.3.|schemata':
107
+ '{"label":"promiseWatcherByKind","body":"#{\\"keyShape\\":{\\"#tag\\":\\"match:scalar\\",\\"payload\\":\\"#undefined\\"}}","slots":[]}',
108
+ 'vc.4.sp+6':
109
+ '{"body":"#[[\\"$0.Alleged: DurablePromiseIgnorer\\",\\"orphaned\\"]]","slots":["o+d10/1"]}',
110
+ 'vc.4.sp-8':
111
+ '{"body":"#[[\\"$0.Alleged: DurablePromiseIgnorer\\",\\"unresolved\\"]]","slots":["o+d10/1"]}',
112
+ 'vc.4.sp-9':
113
+ '{"body":"#[[\\"$0.Alleged: DurablePromiseIgnorer\\",\\"late-rejected\\"]]","slots":["o+d10/1"]}',
114
+ 'vc.4.|entryCount': '3',
115
+ 'vc.4.|nextOrdinal': '1',
116
+ 'vc.4.|schemata':
117
+ '{"label":"watchedPromises","body":"#{\\"keyShape\\":{\\"#tag\\":\\"match:and\\",\\"payload\\":[{\\"#tag\\":\\"match:scalar\\",\\"payload\\":\\"#undefined\\"},{\\"#tag\\":\\"match:string\\",\\"payload\\":[]}]}}","slots":[]}',
118
+ 'vom.dkind.10.descriptor':
119
+ '{"kindID":"10","tag":"DurablePromiseIgnorer","unfaceted":true}',
120
+ 'vom.dkind.10.nextID': '2',
121
+ 'vom.o+d10/1': '{}',
122
+ 'vom.rc.o+d1/10': '1',
123
+ 'vom.rc.o+d10/1': '3',
124
+ 'vom.rc.o+d6/1': '1',
125
+ 'vom.rc.o+d6/3': '1',
126
+ 'vom.rc.o+d6/4': '1',
127
+ watchedPromiseTableID: 'o+d6/4',
128
+ watcherTableID: 'o+d6/3',
129
+ });
130
+ const kvStoreDataV1VpidsToReject = ['p+6', 'p-9'];
131
+ const kvStoreDataV1KeysToDelete = ['vc.4.sp+6', 'vc.4.sp-9'];
132
+ const kvStoreDataV1VpidsToKeep = ['p-8'];
133
+ const kvStoreDataV1KeysToKeep = ['vc.4.sp-8'];
134
+
135
+ test('past-incarnation watched promises', async t => {
136
+ const S = 'settlement';
137
+ // Anchor promise counters upon which the other assertions depend.
138
+ const firstPImport = 9;
139
+ // cf. src/liveslots.js:initialIDCounters
140
+ const firstPExport = 5;
141
+
142
+ const startImportedP = firstPImport - 2;
143
+
144
+ const v1StartOperations = [
145
+ ['createLocalPromise', 'start orphaned'],
146
+ ['watchPromise', 'start orphaned'],
147
+ ['createLocalPromise', 'start fulfilled', S],
148
+ ['watchPromise', 'start fulfilled'],
149
+ ['importPromise', 'start imported', kslot(`p-${startImportedP}`)],
150
+ ['watchPromise', 'start imported'],
151
+ ];
152
+ const kvStore = new Map();
153
+ let {
154
+ v,
155
+ dispatch,
156
+ dispatchMessage: rawDispatch,
157
+ } = await setupTestLiveslots(
158
+ t,
159
+ buildPromiseWatcherRootObject,
160
+ 'durable-promise-watcher',
161
+ {
162
+ kvStore,
163
+ nextPromiseImportNumber: firstPImport,
164
+ vatParameters: { startOperations: v1StartOperations },
165
+ },
166
+ );
167
+ let vatLogs = v.log;
168
+
169
+ let lastPImport = firstPImport - 1;
170
+ let lastPExport = firstPExport - 1;
171
+ let dispatches = 0;
172
+ const exportedPromises = new Set();
173
+ const v1OrphanedPExports = [];
174
+ const nextPImport = () => (lastPImport += 1);
175
+ const nextPExport = () => (lastPExport += 1);
176
+ /** @type {typeof rawDispatch} */
177
+ const dispatchMessage = (...args) => {
178
+ dispatches += 1;
179
+ return rawDispatch(...args);
180
+ };
181
+ const recordExportedPromise = name => {
182
+ exportedPromises.add(name);
183
+ return name;
184
+ };
185
+ // Ignore vatstore syscalls.
186
+ const getDispatchLogs = () =>
187
+ vatLogs.splice(0).filter(m => !m.type.startsWith('vatstore'));
188
+ const settlementMessage = (vpid, rejected, value) => ({
189
+ type: 'resolve',
190
+ resolutions: [[vpid, rejected, kser(value)]],
191
+ });
192
+ const fulfillmentMessage = (vpid, value) =>
193
+ settlementMessage(vpid, false, value);
194
+ const rejectionMessage = (vpid, value) =>
195
+ settlementMessage(vpid, true, value);
196
+ const subscribeMessage = vpid => ({
197
+ type: 'subscribe',
198
+ target: vpid,
199
+ });
200
+
201
+ // startVat logs
202
+ v1OrphanedPExports.push(nextPExport());
203
+ recordExportedPromise('start orphaned');
204
+ v1OrphanedPExports.push(nextPExport());
205
+ recordExportedPromise('start fulfilled');
206
+ t.deepEqual(getDispatchLogs(), [
207
+ subscribeMessage(`p-${startImportedP}`),
208
+ subscribeMessage(`p+${lastPExport - 1}`),
209
+ subscribeMessage(`p+${lastPExport}`),
210
+ fulfillmentMessage(`p+${lastPExport}`, S),
211
+ ]);
212
+ await dispatchMessage('createLocalPromise', 'exported', S);
213
+ t.deepEqual(getDispatchLogs(), [
214
+ fulfillmentMessage(`p-${nextPImport()}`, 'created local promise: exported'),
215
+ ]);
216
+ await dispatchMessage('getPromise', recordExportedPromise('exported'));
217
+ t.deepEqual(getDispatchLogs(), [
218
+ fulfillmentMessage(`p-${nextPImport()}`, {
219
+ promise: kslot(`p+${nextPExport()}`),
220
+ }),
221
+ fulfillmentMessage(`p+${lastPExport}`, S),
222
+ ]);
223
+ const importedP = firstPImport - 1;
224
+ await dispatchMessage('importPromise', 'imported', kslot(`p-${importedP}`));
225
+ t.deepEqual(getDispatchLogs(), [
226
+ subscribeMessage(`p-${importedP}`),
227
+ fulfillmentMessage(`p-${nextPImport()}`, 'imported promise: imported'),
228
+ ]);
229
+ await dispatchMessage('createLocalPromise', 'orphaned');
230
+ t.deepEqual(getDispatchLogs(), [
231
+ fulfillmentMessage(`p-${nextPImport()}`, 'created local promise: orphaned'),
232
+ ]);
233
+ await dispatchMessage('createLocalPromise', 'orphaned exported');
234
+ await dispatchMessage(
235
+ 'getPromise',
236
+ recordExportedPromise('orphaned exported'),
237
+ );
238
+ const orphanedExportedP = nextPExport();
239
+ v1OrphanedPExports.push(orphanedExportedP);
240
+ t.deepEqual(getDispatchLogs(), [
241
+ fulfillmentMessage(
242
+ `p-${nextPImport()}`,
243
+ 'created local promise: orphaned exported',
244
+ ),
245
+ fulfillmentMessage(`p-${nextPImport()}`, {
246
+ promise: kslot(`p+${orphanedExportedP}`),
247
+ }),
248
+ ]);
249
+ await dispatchMessage('createLocalPromise', 'fulfilled', S);
250
+ t.deepEqual(getDispatchLogs(), [
251
+ fulfillmentMessage(
252
+ `p-${nextPImport()}`,
253
+ 'created local promise: fulfilled',
254
+ ),
255
+ ]);
256
+ await dispatchMessage('createLocalPromise', 'rejected', undefined, S);
257
+ t.deepEqual(getDispatchLogs(), [
258
+ fulfillmentMessage(`p-${nextPImport()}`, 'created local promise: rejected'),
259
+ ]);
260
+ t.is(
261
+ lastPImport - firstPImport + 1,
262
+ dispatches,
263
+ `imported ${dispatches} promises (1 per dispatch)`,
264
+ );
265
+ t.is(
266
+ lastPExport - firstPExport + 1,
267
+ exportedPromises.size,
268
+ `exported ${exportedPromises.size} promises: ${[...exportedPromises].join(', ')}`,
269
+ );
270
+
271
+ await dispatchMessage('watchPromise', recordExportedPromise('orphaned'));
272
+ v1OrphanedPExports.push(nextPExport());
273
+ t.deepEqual(getDispatchLogs(), [
274
+ subscribeMessage(`p+${lastPExport}`),
275
+ fulfillmentMessage(`p-${nextPImport()}`, 'watched promise: orphaned'),
276
+ ]);
277
+ await dispatchMessage('watchPromise', recordExportedPromise('fulfilled'));
278
+ t.deepEqual(getDispatchLogs(), [
279
+ subscribeMessage(`p+${nextPExport()}`),
280
+ fulfillmentMessage(`p-${nextPImport()}`, 'watched promise: fulfilled'),
281
+ fulfillmentMessage(`p+${lastPExport}`, S),
282
+ ]);
283
+ await dispatchMessage('watchPromise', recordExportedPromise('rejected'));
284
+ t.deepEqual(getDispatchLogs(), [
285
+ subscribeMessage(`p+${nextPExport()}`),
286
+ fulfillmentMessage(`p-${nextPImport()}`, 'watched promise: rejected'),
287
+ rejectionMessage(`p+${lastPExport}`, S),
288
+ ]);
289
+ await dispatchMessage('watchPromise', 'imported');
290
+ t.deepEqual(getDispatchLogs(), [
291
+ // no subscribe, we already did at import
292
+ fulfillmentMessage(`p-${nextPImport()}`, 'watched promise: imported'),
293
+ ]);
294
+ await dispatchMessage('getWatchResolution', 'fulfilled');
295
+ t.deepEqual(getDispatchLogs(), [
296
+ fulfillmentMessage(`p-${nextPImport()}`, {
297
+ status: 'fulfilled',
298
+ value: S,
299
+ }),
300
+ ]);
301
+ await dispatchMessage('getWatchResolution', 'rejected');
302
+ t.deepEqual(getDispatchLogs(), [
303
+ fulfillmentMessage(`p-${nextPImport()}`, {
304
+ status: 'rejected',
305
+ reason: S,
306
+ }),
307
+ ]);
308
+ await dispatchMessage('getWatchResolution', 'start fulfilled');
309
+ t.deepEqual(getDispatchLogs(), [
310
+ fulfillmentMessage(`p-${nextPImport()}`, {
311
+ status: 'fulfilled',
312
+ value: S,
313
+ }),
314
+ ]);
315
+
316
+ t.is(
317
+ lastPImport - firstPImport + 1,
318
+ dispatches,
319
+ `imported ${dispatches} promises (1 per dispatch)`,
320
+ );
321
+ t.is(
322
+ lastPExport - firstPExport + 1,
323
+ exportedPromises.size,
324
+ `exported ${exportedPromises.size} promises: ${[...exportedPromises].join(', ')}`,
325
+ );
326
+
327
+ // Simulate upgrade by starting from the non-empty kvStore.
328
+ // t.log(Object.fromEntries([...kvStore.entries()].sort(compareEntriesByKey)));
329
+ const clonedStore = new Map(kvStore);
330
+ const startImported2P = firstPImport - 3;
331
+ const v2StartOperations = [
332
+ ['importPromise', 'start imported 2', kslot(`p-${startImported2P}`)], // import of new promise
333
+ ['importPromise', 'imported', kslot(`p-${importedP}`)], // import previously imported and watched promise
334
+ ['importPromise', 'orphaned exported', kslot(`p+${orphanedExportedP}`)], // import previously exported but unwatched promise
335
+ ['watchPromise', 'orphaned exported'],
336
+ ];
337
+ ({
338
+ v,
339
+ dispatch,
340
+ dispatchMessage: rawDispatch,
341
+ } = await setupTestLiveslots(
342
+ t,
343
+ buildPromiseWatcherRootObject,
344
+ 'durable-promise-watcher-v2',
345
+ {
346
+ kvStore: clonedStore,
347
+ nextPromiseImportNumber: lastPImport + 1,
348
+ vatParameters: { startOperations: v2StartOperations },
349
+ },
350
+ ));
351
+ vatLogs = v.log;
352
+
353
+ // startVat logs
354
+ t.deepEqual(getDispatchLogs(), [
355
+ subscribeMessage(`p-${startImported2P}`),
356
+ subscribeMessage(`p-${importedP}`),
357
+ subscribeMessage(`p+${orphanedExportedP}`),
358
+ ]);
359
+ // Simulate kernel rejection of promises orphaned by termination/upgrade of their decider vat.
360
+ const expectedDeletions = [...clonedStore.entries()].filter(entry =>
361
+ entry[1].includes('orphaned'),
362
+ );
363
+ t.true(expectedDeletions.length >= 1);
364
+ for (const orphanedPExport of v1OrphanedPExports) {
365
+ await dispatch(
366
+ makeReject(`p+${orphanedPExport}`, kser('tomorrow never came')),
367
+ );
368
+ }
369
+ await dispatchMessage('getWatchResolution', 'orphaned');
370
+ t.deepEqual(getDispatchLogs(), [
371
+ fulfillmentMessage(`p-${nextPImport()}`, {
372
+ status: 'rejected',
373
+ reason: 'tomorrow never came',
374
+ }),
375
+ ]);
376
+ await dispatchMessage('getWatchResolution', 'start orphaned');
377
+ t.deepEqual(getDispatchLogs(), [
378
+ fulfillmentMessage(`p-${nextPImport()}`, {
379
+ status: 'rejected',
380
+ reason: 'tomorrow never came',
381
+ }),
382
+ ]);
383
+ await dispatchMessage('getWatchResolution', 'orphaned exported');
384
+ t.deepEqual(getDispatchLogs(), [
385
+ fulfillmentMessage(`p-${nextPImport()}`, {
386
+ status: 'rejected',
387
+ reason: 'tomorrow never came',
388
+ }),
389
+ ]);
390
+ for (const [key, value] of expectedDeletions) {
391
+ t.false(clonedStore.has(key), `entry should be removed: ${key}: ${value}`);
392
+ }
393
+ // Simulate resolution of imported promises watched in previous incarnation
394
+ await dispatch(makeResolve(`p-${importedP}`, kser(undefined)));
395
+ await dispatchMessage('getWatchResolution', 'imported');
396
+ t.deepEqual(getDispatchLogs(), [
397
+ fulfillmentMessage(`p-${nextPImport()}`, {
398
+ status: 'fulfilled',
399
+ value: undefined,
400
+ }),
401
+ ]);
402
+ await dispatch(makeResolve(`p-${startImportedP}`, kser(undefined)));
403
+ await dispatchMessage('getWatchResolution', 'start imported');
404
+ t.deepEqual(getDispatchLogs(), [
405
+ fulfillmentMessage(`p-${nextPImport()}`, {
406
+ status: 'fulfilled',
407
+ value: undefined,
408
+ }),
409
+ ]);
410
+ await dispatch(makeResolve(`p-${startImported2P}`, kser(undefined)));
411
+ await dispatchMessage('getWatchResolution', 'start imported 2');
412
+ t.deepEqual(getDispatchLogs(), [
413
+ fulfillmentMessage(`p-${nextPImport()}`, undefined),
414
+ ]);
415
+ // simulate resolution of imported promise watched after resolution
416
+ await dispatchMessage('watchPromise', 'start imported 2');
417
+ t.deepEqual(getDispatchLogs(), [
418
+ // Promise was previously resolved, so it is re-exported
419
+ subscribeMessage(`p+${nextPExport()}`),
420
+ fulfillmentMessage(
421
+ `p-${nextPImport()}`,
422
+ 'watched promise: start imported 2',
423
+ ),
424
+ fulfillmentMessage(`p+${lastPExport}`, undefined),
425
+ ]);
426
+ await dispatchMessage('getWatchResolution', 'start imported 2');
427
+ t.deepEqual(getDispatchLogs(), [
428
+ fulfillmentMessage(`p-${nextPImport()}`, {
429
+ status: 'fulfilled',
430
+ value: undefined,
431
+ }),
432
+ ]);
433
+
434
+ // Verify that the data is still in loadable condition.
435
+ const finalClonedStore = new Map(clonedStore);
436
+ ({
437
+ v,
438
+ dispatch,
439
+ dispatchMessage: rawDispatch,
440
+ } = await setupTestLiveslots(
441
+ t,
442
+ buildPromiseWatcherRootObject,
443
+ 'durable-promise-watcher-final',
444
+ { kvStore: finalClonedStore, nextPromiseImportNumber: lastPImport + 1 },
445
+ ));
446
+ vatLogs = v.log;
447
+ vatLogs.length = 0;
448
+ await dispatchMessage('createLocalPromise', 'final', S);
449
+ await dispatchMessage('watchPromise', 'final');
450
+ await dispatchMessage('getWatchResolution', 'final');
451
+ t.deepEqual(getDispatchLogs(), [
452
+ fulfillmentMessage(`p-${nextPImport()}`, 'created local promise: final'),
453
+ subscribeMessage(`p+${nextPExport()}`),
454
+ fulfillmentMessage(`p-${nextPImport()}`, 'watched promise: final'),
455
+ fulfillmentMessage(`p+${lastPExport}`, S),
456
+ fulfillmentMessage(`p-${nextPImport()}`, {
457
+ status: 'fulfilled',
458
+ value: S,
459
+ }),
460
+ ]);
461
+ });
462
+
463
+ test('past-incarnation watched promises from original-format kvStore', async t => {
464
+ const kvStore = new Map(kvStoreDataV1);
465
+ for (const key of [
466
+ ...kvStoreDataV1KeysToDelete,
467
+ ...kvStoreDataV1KeysToKeep,
468
+ ]) {
469
+ t.true(kvStore.has(key), `key must be initially present: ${key}`);
470
+ }
471
+
472
+ let { v, dispatch, dispatchMessage } = await setupTestLiveslots(
473
+ t,
474
+ buildPromiseWatcherRootObject,
475
+ 'durable-promise-watcher',
476
+ { kvStore, nextPromiseImportNumber: 100 },
477
+ );
478
+ let vatLogs = v.log;
479
+ for (const vpid of kvStoreDataV1VpidsToReject) {
480
+ await dispatch(makeReject(vpid, kser('tomorrow never came')));
481
+ }
482
+ for (const key of kvStoreDataV1KeysToDelete) {
483
+ t.false(kvStore.has(key), `key should be removed: ${key}`);
484
+ }
485
+ for (const key of kvStoreDataV1KeysToKeep) {
486
+ t.true(kvStore.has(key), `key should remain: ${key}`);
487
+ }
488
+
489
+ // Verify that the data is still in loadable condition.
490
+ const finalClonedStore = new Map(kvStore);
491
+ // eslint-disable-next-line no-unused-vars
492
+ ({ v, dispatch, dispatchMessage } = await setupTestLiveslots(
493
+ t,
494
+ buildPromiseWatcherRootObject,
495
+ 'durable-promise-watcher-final',
496
+ { kvStore: finalClonedStore, nextPromiseImportNumber: 200 },
497
+ ));
498
+ vatLogs = v.log;
499
+ vatLogs.length = 0;
500
+ for (const vpid of kvStoreDataV1VpidsToKeep) {
501
+ await dispatch(makeResolve(vpid, kser('finally')));
502
+ }
503
+ for (const key of kvStoreDataV1KeysToKeep) {
504
+ t.false(finalClonedStore.has(key), `key should be removed: ${key}`);
505
+ }
506
+ });
@@ -1,10 +1,9 @@
1
1
  import test from 'ava';
2
- import '@endo/init/debug.js';
3
2
 
4
3
  import { Far } from '@endo/far';
4
+ import { kunser } from '@agoric/kmarshal';
5
5
  import { M } from '@agoric/store';
6
6
  import { setupTestLiveslots } from './liveslots-helpers.js';
7
- import { kunser } from './kmarshal.js';
8
7
 
9
8
  function buildRootObject(vatPowers, vatParameters, baggage) {
10
9
  const vd = vatPowers.VatData;
@@ -148,5 +147,5 @@ test('vrefs', async t => {
148
147
  const expectedStore1Vref = `o+v${initialKindIDs.scalarMapStore}/5`;
149
148
  const store1Vref = (await run('getStore1')).slots[0];
150
149
  t.is(store1Vref, expectedStore1Vref);
151
- t.deepEqual(kunser(JSON.parse(fakestore.get(`vc.5.s${'key'}`))), 'value');
150
+ t.is(kunser(JSON.parse(fakestore.get(`vc.5.s${'key'}`))), 'value');
152
151
  });
@@ -1,6 +1,7 @@
1
1
  /* global WeakRef, FinalizationRegistry */
2
- import engineGC from './engine-gc.js';
2
+ import { kser } from '@agoric/kmarshal';
3
3
 
4
+ import engineGC from './engine-gc.js';
4
5
  import { waitUntilQuiescent } from './waitUntilQuiescent.js';
5
6
  import { makeGcAndFinalize } from './gc-and-finalize.js';
6
7
  import { makeDummyMeterControl } from './dummyMeterControl.js';
@@ -12,12 +13,11 @@ import {
12
13
  makeRetireExports,
13
14
  makeBringOutYourDead,
14
15
  } from './util.js';
15
- import { kser } from './kmarshal.js';
16
16
 
17
17
  /**
18
18
  * @param {object} [options]
19
- * @param {boolean} [options.skipLogging = false]
20
- * @param {Map<string, string>} [options.kvStore = new Map()]
19
+ * @param {boolean} [options.skipLogging]
20
+ * @param {Map<string, string>} [options.kvStore]
21
21
  */
22
22
  export function buildSyscall(options = {}) {
23
23
  const { skipLogging = false, kvStore: fakestore = new Map() } = options;
@@ -131,6 +131,7 @@ export async function makeDispatch(
131
131
  build,
132
132
  vatID = 'vatA',
133
133
  liveSlotsOptions = {},
134
+ vatParameters = undefined,
134
135
  ) {
135
136
  const gcTools = harden({
136
137
  WeakRef,
@@ -150,7 +151,7 @@ export async function makeDispatch(
150
151
  return { buildRootObject: build };
151
152
  },
152
153
  );
153
- await dispatch(['startVat', kser()]);
154
+ await dispatch(['startVat', kser(vatParameters)]);
154
155
  return { dispatch, testHooks };
155
156
  }
156
157
 
@@ -168,9 +169,10 @@ function makeRPMaker(nextNumber = 1) {
168
169
  * @param {string} vatName
169
170
  * @param {object} [options]
170
171
  * @param {boolean} [options.forceGC]
171
- * @param {Map<string, string>} [options.kvStore = new Map()]
172
+ * @param {Map<string, string>} [options.kvStore]
172
173
  * @param {number} [options.nextPromiseImportNumber]
173
- * @param {boolean} [options.skipLogging = false]
174
+ * @param {boolean} [options.skipLogging]
175
+ * @param {any} [options.vatParameters]
174
176
  */
175
177
  export async function setupTestLiveslots(
176
178
  t,
@@ -183,6 +185,7 @@ export async function setupTestLiveslots(
183
185
  kvStore = new Map(),
184
186
  nextPromiseImportNumber,
185
187
  skipLogging = false,
188
+ vatParameters,
186
189
  } = options;
187
190
  const { log, syscall, fakestore } = buildSyscall({ skipLogging, kvStore });
188
191
  const nextRP = makeRPMaker(nextPromiseImportNumber);
@@ -190,6 +193,8 @@ export async function setupTestLiveslots(
190
193
  syscall,
191
194
  buildRootObject,
192
195
  vatName,
196
+ {},
197
+ vatParameters,
193
198
  );
194
199
 
195
200
  async function dispatchMessage(message, ...args) {