@agoric/swingset-liveslots 0.10.3-mainnet1B-dev-b0c1f78.0 → 0.10.3-orchestration-dev-096c4e8.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 (58) hide show
  1. package/README.md +2 -0
  2. package/package.json +25 -17
  3. package/src/cache.js +5 -3
  4. package/src/collectionManager.js +147 -69
  5. package/src/index.js +2 -1
  6. package/src/liveslots.js +52 -79
  7. package/src/message.js +4 -4
  8. package/src/types.js +8 -2
  9. package/src/vatDataTypes.d.ts +234 -0
  10. package/src/vatDataTypes.js +2 -0
  11. package/src/vatstore-iterators.js +2 -0
  12. package/src/virtualObjectManager.js +107 -63
  13. package/src/virtualReferences.js +51 -0
  14. package/src/watchedPromises.js +50 -15
  15. package/test/gc-and-finalize.js +30 -1
  16. package/test/gc-helpers.js +2 -1
  17. package/test/liveslots-helpers.js +6 -6
  18. package/test/mock-gc.js +1 -0
  19. package/test/storeGC/test-lifecycle.js +2 -2
  20. package/test/storeGC/test-refcount-management.js +1 -2
  21. package/test/storeGC/test-scalar-store-kind.js +0 -1
  22. package/test/storeGC/test-weak-key.js +1 -2
  23. package/test/test-baggage.js +1 -2
  24. package/test/test-cache.js +0 -1
  25. package/test/test-collection-schema-refcount.js +1 -2
  26. package/test/test-collection-upgrade.js +1 -3
  27. package/test/test-collections.js +117 -14
  28. package/test/test-dropped-collection-weakrefs.js +1 -2
  29. package/test/test-durabilityChecks.js +3 -3
  30. package/test/test-facetiousness.js +1 -2
  31. package/test/test-gc-sensitivity.js +2 -2
  32. package/test/test-handled-promises.js +5 -7
  33. package/test/test-initial-vrefs.js +2 -3
  34. package/test/test-liveslots-mock-gc.js +2 -2
  35. package/test/test-liveslots-real-gc.js +44 -35
  36. package/test/test-liveslots.js +13 -14
  37. package/test/test-vo-test-harness.js +0 -1
  38. package/test/test-vpid-liveslots.js +4 -5
  39. package/test/util.js +2 -2
  40. package/test/vat-util.js +1 -1
  41. package/test/virtual-objects/test-cease-recognition.js +2 -2
  42. package/test/virtual-objects/test-cross-facet.js +1 -2
  43. package/test/virtual-objects/test-empty-data.js +1 -2
  44. package/test/virtual-objects/test-facets.js +1 -2
  45. package/test/virtual-objects/test-kind-changes.js +2 -2
  46. package/test/virtual-objects/test-reachable-vrefs.js +2 -2
  47. package/test/virtual-objects/test-rep-tostring.js +2 -3
  48. package/test/virtual-objects/test-retain-remotable.js +25 -24
  49. package/test/virtual-objects/test-state-shape.js +2 -2
  50. package/test/virtual-objects/test-virtualObjectGC.js +2 -2
  51. package/test/virtual-objects/test-virtualObjectManager.js +126 -8
  52. package/test/virtual-objects/test-vo-real-gc.js +8 -8
  53. package/test/virtual-objects/test-weakcollections-vref-handling.js +1 -2
  54. package/tools/fakeVirtualSupport.js +48 -21
  55. package/tools/prepare-test-env.js +13 -0
  56. package/tools/setup-vat-data.js +62 -0
  57. package/CHANGELOG.md +0 -85
  58. package/test/kmarshal.js +0 -79
@@ -1,10 +1,9 @@
1
1
  import test from 'ava';
2
- import '@endo/init/debug.js';
3
2
 
4
3
  import { Far } from '@endo/marshal';
4
+ import { kunser } from '@agoric/kmarshal';
5
5
  import { setupTestLiveslots } from './liveslots-helpers.js';
6
6
  import { vstr } from './util.js';
7
- import { kunser } from './kmarshal.js';
8
7
  import { parseVatSlot } from '../src/parseVatSlots.js';
9
8
 
10
9
  function buildRootObject(vatPowers, vatParameters, baggage) {
@@ -1,5 +1,4 @@
1
1
  import test from 'ava';
2
- import '@endo/init/debug.js';
3
2
 
4
3
  import { makeCache } from '../src/cache.js';
5
4
 
@@ -1,10 +1,9 @@
1
1
  import test from 'ava';
2
- import '@endo/init/debug.js';
3
2
 
4
3
  import { Far } from '@endo/marshal';
4
+ import { kser } from '@agoric/kmarshal';
5
5
  import { makeLiveSlots } from '../src/liveslots.js';
6
6
  import { parseVatSlot } from '../src/parseVatSlots.js';
7
- import { kser } from './kmarshal.js';
8
7
  import { buildSyscall } from './liveslots-helpers.js';
9
8
  import { makeStartVat, makeBringOutYourDead } from './util.js';
10
9
  import { makeMockGC } from './mock-gc.js';
@@ -1,12 +1,10 @@
1
- /* eslint-disable no-await-in-loop, @jessie.js/no-nested-await, no-shadow */
2
1
  import test from 'ava';
3
- import '@endo/init/debug.js';
4
2
 
5
3
  import { Far } from '@endo/marshal';
4
+ import { kser } from '@agoric/kmarshal';
6
5
  import { M } from '@agoric/store';
7
6
  import { makeLiveSlots } from '../src/liveslots.js';
8
7
  import { parseVatSlot } from '../src/parseVatSlots.js';
9
- import { kser } from './kmarshal.js';
10
8
  import { buildSyscall } from './liveslots-helpers.js';
11
9
  import { makeStartVat } from './util.js';
12
10
  import { makeMockGC } from './mock-gc.js';
@@ -1,8 +1,9 @@
1
+ // @ts-nocheck
1
2
  import test from 'ava';
2
- import '@endo/init/debug.js';
3
3
 
4
4
  import { Far } from '@endo/marshal';
5
5
  import { M } from '@agoric/store';
6
+ import { makeCopyMap, makeCopySet } from '@endo/patterns';
6
7
  import { makeFakeCollectionManager } from '../tools/fakeVirtualSupport.js';
7
8
 
8
9
  const {
@@ -100,6 +101,12 @@ function exerciseMapOperations(t, collectionName, testStore) {
100
101
  () => testStore.set(86, 'not work'),
101
102
  m(`key 86 not found in collection "${collectionName}"`),
102
103
  );
104
+ t.throws(
105
+ () => testStore.set(somethingMissing, 'not work'),
106
+ m(
107
+ `key "[Alleged: something missing]" not found in collection "${collectionName}"`,
108
+ ),
109
+ );
103
110
  t.throws(
104
111
  () => testStore.init(47, 'already there'),
105
112
  m(`key 47 already registered in collection "${collectionName}"`),
@@ -117,11 +124,17 @@ function exerciseMapOperations(t, collectionName, testStore) {
117
124
  t.is(testStore.get(somethingElse), something);
118
125
 
119
126
  testStore.delete(47);
127
+ testStore.delete(something);
120
128
  t.falsy(testStore.has(47));
129
+ t.falsy(testStore.has(something));
121
130
  t.throws(
122
131
  () => testStore.get(47),
123
132
  m(`key 47 not found in collection "${collectionName}"`),
124
133
  );
134
+ t.throws(
135
+ () => testStore.get(something),
136
+ m(`key "[Alleged: something]" not found in collection "${collectionName}"`),
137
+ );
125
138
  t.throws(
126
139
  () => testStore.delete(22),
127
140
  m(`key 22 not found in collection "${collectionName}"`),
@@ -146,7 +159,9 @@ function exerciseSetOperations(t, collectionName, testStore) {
146
159
  t.notThrows(() => testStore.add(47));
147
160
 
148
161
  testStore.delete(47);
162
+ testStore.delete(something);
149
163
  t.falsy(testStore.has(47));
164
+ t.falsy(testStore.has(something));
150
165
  t.throws(
151
166
  () => testStore.delete(22),
152
167
  m(`key 22 not found in collection "${collectionName}"`),
@@ -191,6 +206,89 @@ test('basic weak set operations', t => {
191
206
  );
192
207
  });
193
208
 
209
+ function exerciseSetAddAll(t, weak, testStore) {
210
+ const allThatStuff = stuff.map(entry => entry[0]);
211
+
212
+ testStore.addAll(allThatStuff);
213
+ for (const elem of allThatStuff) {
214
+ t.truthy(testStore.has(elem));
215
+ testStore.delete(elem);
216
+ }
217
+ if (!weak) {
218
+ t.is(testStore.getSize(), 0);
219
+ }
220
+
221
+ testStore.addAll(makeCopySet(allThatStuff));
222
+ for (const elem of allThatStuff) {
223
+ t.truthy(testStore.has(elem));
224
+ testStore.delete(elem);
225
+ }
226
+ if (!weak) {
227
+ t.is(testStore.getSize(), 0);
228
+ }
229
+
230
+ t.throws(
231
+ () => testStore.addAll({ bogus: 47 }),
232
+ m(/provided data source is not iterable/),
233
+ );
234
+ }
235
+
236
+ test('set addAll', t => {
237
+ exerciseSetAddAll(t, false, makeScalarBigSetStore('test set'));
238
+ });
239
+
240
+ test('weak set addAll', t => {
241
+ exerciseSetAddAll(t, true, makeScalarBigWeakSetStore('test weak set'));
242
+ });
243
+
244
+ test('set snapshot', t => {
245
+ const testStore = makeScalarBigSetStore('test set');
246
+ const allThatStuff = stuff.map(entry => entry[0]);
247
+ testStore.addAll(allThatStuff);
248
+ t.deepEqual(testStore.snapshot(), makeCopySet(allThatStuff));
249
+ });
250
+
251
+ function exerciseMapAddAll(t, weak, testStore) {
252
+ testStore.addAll(stuff);
253
+ for (const [k, v] of stuff) {
254
+ t.truthy(testStore.has(k));
255
+ t.is(testStore.get(k), v);
256
+ testStore.delete(k);
257
+ }
258
+ if (!weak) {
259
+ t.is(testStore.getSize(), 0);
260
+ }
261
+
262
+ testStore.addAll(makeCopyMap(stuff));
263
+ for (const [k, v] of stuff) {
264
+ t.truthy(testStore.has(k));
265
+ t.is(testStore.get(k), v);
266
+ testStore.delete(k);
267
+ }
268
+ if (!weak) {
269
+ t.is(testStore.getSize(), 0);
270
+ }
271
+
272
+ t.throws(
273
+ () => testStore.addAll({ bogus: 47 }),
274
+ m(/provided data source is not iterable/),
275
+ );
276
+ }
277
+
278
+ test('map addAll', t => {
279
+ exerciseMapAddAll(t, false, makeScalarBigMapStore('test map'));
280
+ });
281
+
282
+ test('weak map addAll', t => {
283
+ exerciseMapAddAll(t, true, makeScalarBigWeakMapStore('test weak map'));
284
+ });
285
+
286
+ test('map snapshot', t => {
287
+ const testStore = makeScalarBigMapStore('test map');
288
+ testStore.addAll(stuff);
289
+ t.deepEqual(testStore.snapshot(), makeCopyMap(stuff));
290
+ });
291
+
194
292
  test('constrain map key shape', t => {
195
293
  const stringsOnly = makeScalarBigMapStore('map key strings only', {
196
294
  keyShape: M.string(),
@@ -405,7 +503,9 @@ test('map fail on concurrent modification', t => {
405
503
  const primeMap = makeScalarBigMapStore('fmap', {
406
504
  keyShape: M.number(),
407
505
  });
408
- primes.forEach((v, i) => primeMap.init(v, `${v} is prime #${i + 1}`));
506
+ for (const [i, v] of primes.entries()) {
507
+ primeMap.init(v, `${v} is prime #${i + 1}`);
508
+ }
409
509
 
410
510
  let iter = primeMap.keys()[Symbol.iterator]();
411
511
  t.deepEqual(iter.next(), { done: false, value: 2 });
@@ -433,7 +533,9 @@ test('set fail on concurrent modification', t => {
433
533
  const primeSet = makeScalarBigSetStore('fset', {
434
534
  keyShape: M.number(),
435
535
  });
436
- primes.forEach(v => primeSet.add(v));
536
+ for (const v of primes) {
537
+ primeSet.add(v);
538
+ }
437
539
 
438
540
  let iter = primeSet.keys()[Symbol.iterator]();
439
541
  t.deepEqual(iter.next(), { done: false, value: 2 });
@@ -461,7 +563,9 @@ test('map ok with concurrent deletion', t => {
461
563
  const primeMap = makeScalarBigMapStore('fmap', {
462
564
  keyShape: M.number(),
463
565
  });
464
- primes.forEach((v, i) => primeMap.init(v, `${v} is prime #${i + 1}`));
566
+ for (const [i, v] of primes.entries()) {
567
+ primeMap.init(v, `${v} is prime #${i + 1}`);
568
+ }
465
569
  const iter = primeMap.keys()[Symbol.iterator]();
466
570
  t.deepEqual(iter.next(), { done: false, value: 2 });
467
571
  primeMap.delete(3);
@@ -476,7 +580,9 @@ test('set ok with concurrent deletion', t => {
476
580
  const primeSet = makeScalarBigSetStore('fset', {
477
581
  keyShape: M.number(),
478
582
  });
479
- primes.forEach(v => primeSet.add(v));
583
+ for (const v of primes) {
584
+ primeSet.add(v);
585
+ }
480
586
 
481
587
  const iter = primeSet.keys()[Symbol.iterator]();
482
588
  t.deepEqual(iter.next(), { done: false, value: 2 });
@@ -766,13 +872,6 @@ test('set queries', t => {
766
872
  symbolKrusty,
767
873
  undefined,
768
874
  ]);
769
-
770
- // @ts-expect-error our BigSetStore has .entries, but not the SetStore type
771
- t.deepEqual(Array.from(testStore.entries(M.number())), [
772
- [-29, -29],
773
- [3, 3],
774
- [47, 47],
775
- ]);
776
875
  });
777
876
 
778
877
  test('remotable sort order', t => {
@@ -798,7 +897,9 @@ test('complex map queries', t => {
798
897
  const primeStore = makeScalarBigMapStore('prime map', {
799
898
  keyShape: M.number(),
800
899
  });
801
- primes.forEach((v, i) => primeStore.init(v, `${v} is prime #${i + 1}`));
900
+ for (const [i, v] of primes.entries()) {
901
+ primeStore.init(v, `${v} is prime #${i + 1}`);
902
+ }
802
903
 
803
904
  t.deepEqual(Array.from(primeStore.values()), [
804
905
  '2 is prime #1',
@@ -973,7 +1074,9 @@ test('complex set queries', t => {
973
1074
  const primeStore = makeScalarBigSetStore('prime set', {
974
1075
  keyShape: M.number(),
975
1076
  });
976
- primes.forEach(v => primeStore.add(v));
1077
+ for (const v of primes) {
1078
+ primeStore.add(v);
1079
+ }
977
1080
 
978
1081
  t.deepEqual(
979
1082
  Array.from(primeStore.values()),
@@ -1,8 +1,7 @@
1
1
  import test from 'ava';
2
- import '@endo/init/debug.js';
3
2
  import { Far } from '@endo/marshal';
3
+ import { kser } from '@agoric/kmarshal';
4
4
  import { makeLiveSlots } from '../src/liveslots.js';
5
- import { kser } from './kmarshal.js';
6
5
  import { buildSyscall } from './liveslots-helpers.js';
7
6
  import { makeStartVat } from './util.js';
8
7
  import { makeMockGC } from './mock-gc.js';
@@ -1,5 +1,4 @@
1
1
  import test from 'ava';
2
- import '@endo/init/debug.js';
3
2
 
4
3
  import { Far } from '@endo/marshal';
5
4
  import { makeFakeVirtualStuff } from '../tools/fakeVirtualSupport.js';
@@ -16,6 +15,7 @@ async function runDurabilityCheckTest(t, relaxDurabilityRules) {
16
15
 
17
16
  const durableHolderKind = makeKindHandle('holder');
18
17
 
18
+ /** @param {any} held */
19
19
  const initHolder = (held = null) => ({ held });
20
20
  const holderBehavior = {
21
21
  hold: ({ state }, value) => {
@@ -269,5 +269,5 @@ async function runDurabilityCheckTest(t, relaxDurabilityRules) {
269
269
  }
270
270
  }
271
271
 
272
- test('durability checks (strict)', t => runDurabilityCheckTest(t, false));
273
- test('durability checks (relaxed)', t => runDurabilityCheckTest(t, true));
272
+ test('durability checks (strict)', runDurabilityCheckTest, false);
273
+ test('durability checks (relaxed)', runDurabilityCheckTest, true);
@@ -1,4 +1,3 @@
1
- import '@endo/init/debug.js';
2
1
  import test from 'ava';
3
2
  import {
4
3
  assessFacetiousness,
@@ -126,7 +125,7 @@ test('checkAndUpdateFacetiousness', t => {
126
125
  t.deepEqual(cauf({}, barfoo), barfoo);
127
126
 
128
127
  // a single Kind can only be redefined as another single
129
- t.deepEqual(cauf(desc(), undefined), undefined);
128
+ t.is(cauf(desc(), undefined), undefined);
130
129
  t.throws(() => cauf(desc(), foo), {
131
130
  message: 'defineDurableKindMulti called for unfaceted KindHandle "tag"',
132
131
  });
@@ -1,9 +1,9 @@
1
+ // @ts-nocheck
1
2
  import test from 'ava';
2
- import '@endo/init/debug.js';
3
3
  import { Far } from '@endo/marshal';
4
+ import { kser } from '@agoric/kmarshal';
4
5
  import { buildSyscall } from './liveslots-helpers.js';
5
6
  import { makeLiveSlots } from '../src/liveslots.js';
6
- import { kser } from './kmarshal.js';
7
7
  import { makeMockGC } from './mock-gc.js';
8
8
  import { makeMessage, makeStartVat } from './util.js';
9
9
 
@@ -1,6 +1,4 @@
1
- /* eslint-disable no-await-in-loop, @jessie.js/no-nested-await, no-shadow */
2
1
  import test from 'ava';
3
- import '@endo/init/debug.js';
4
2
 
5
3
  import { Far } from '@endo/marshal';
6
4
  import { Fail } from '@agoric/assert';
@@ -9,7 +7,7 @@ import { makePromiseKit } from '@endo/promise-kit';
9
7
  // Disabled to avoid circular dependencies.
10
8
  // import { makeStoreUtils } from '@agoric/vat-data/src/vat-data-bindings.js';
11
9
  // import { makeExoUtils } from '@agoric/vat-data/src/exo-utils.js';
12
- import { kslot, kser } from './kmarshal.js';
10
+ import { kslot, kser } from '@agoric/kmarshal';
13
11
  import { setupTestLiveslots } from './liveslots-helpers.js';
14
12
  import { makeResolve, makeReject } from './util.js';
15
13
 
@@ -233,12 +231,12 @@ test('past-incarnation watched promises', async t => {
233
231
  t.deepEqual(getDispatchLogs(), [
234
232
  fulfillmentMessage(`p-${nextPImport()}`, 'created local promise: rejected'),
235
233
  ]);
236
- t.deepEqual(
234
+ t.is(
237
235
  lastPImport - firstPImport + 1,
238
236
  4,
239
237
  'imported 4 promises (1 per dispatch)',
240
238
  );
241
- t.deepEqual(lastPExport - firstPExport + 1, 1, 'exported 1 promise: first');
239
+ t.is(lastPExport - firstPExport + 1, 1, 'exported 1 promise: first');
242
240
 
243
241
  await dispatchMessage('watchLocalPromise', 'orphaned');
244
242
  t.deepEqual(getDispatchLogs(), [
@@ -260,12 +258,12 @@ test('past-incarnation watched promises', async t => {
260
258
  fulfillmentMessage(`p-${nextPImport()}`, 'watched local promise: rejected'),
261
259
  rejectionMessage(`p+${lastPExport}`, S),
262
260
  ]);
263
- t.deepEqual(
261
+ t.is(
264
262
  lastPImport - firstPImport + 1,
265
263
  7,
266
264
  'imported 7 promises (1 per dispatch)',
267
265
  );
268
- t.deepEqual(
266
+ t.is(
269
267
  lastPExport - firstPExport + 1,
270
268
  4,
271
269
  'exported 4 promises: first, orphaned, fulfilled, rejected',
@@ -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,10 +1,10 @@
1
+ // @ts-nocheck
1
2
  import test from 'ava';
2
- import '@endo/init/debug.js';
3
3
 
4
4
  import { Far } from '@endo/marshal';
5
+ import { kslot, kser } from '@agoric/kmarshal';
5
6
  import { makeLiveSlots } from '../src/liveslots.js';
6
7
  import { parseVatSlot } from '../src/parseVatSlots.js';
7
- import { kslot, kser } from './kmarshal.js';
8
8
  import { buildSyscall } from './liveslots-helpers.js';
9
9
  import {
10
10
  makeMessage,
@@ -1,12 +1,12 @@
1
- /* global WeakRef */
1
+ // @ts-nocheck
2
+ /* global process */
2
3
  import test from 'ava';
3
- import '@endo/init/debug.js';
4
4
 
5
5
  import { Far } from '@endo/marshal';
6
6
  import { makePromiseKit } from '@endo/promise-kit';
7
+ import { kslot, kser } from '@agoric/kmarshal';
7
8
  import engineGC from './engine-gc.js';
8
- import { makeGcAndFinalize } from './gc-and-finalize.js';
9
- import { kslot, kser } from './kmarshal.js';
9
+ import { watchCollected, makeGcAndFinalize } from './gc-and-finalize.js';
10
10
  import { buildSyscall, makeDispatch } from './liveslots-helpers.js';
11
11
  import {
12
12
  makeMessage,
@@ -28,13 +28,13 @@ const gcAndFinalize = makeGcAndFinalize(engineGC);
28
28
 
29
29
  test.serial('liveslots retains pending exported promise', async t => {
30
30
  const { log, syscall } = buildSyscall();
31
- let watch;
31
+ let collected;
32
32
  const success = [];
33
33
  function build(_vatPowers) {
34
34
  const root = Far('root', {
35
35
  make() {
36
36
  const pk = makePromiseKit();
37
- watch = new WeakRef(pk.promise);
37
+ collected = watchCollected(pk.promise);
38
38
  // we export the Promise, but do not retain resolve/reject
39
39
  return [pk.promise];
40
40
  },
@@ -55,7 +55,7 @@ test.serial('liveslots retains pending exported promise', async t => {
55
55
  const resultP = 'p-1';
56
56
  await dispatch(makeMessage(rootA, 'make', [], resultP));
57
57
  await gcAndFinalize();
58
- t.truthy(watch.deref(), 'Promise not retained');
58
+ t.false(collected.result, 'Promise retained');
59
59
  t.is(log[0].type, 'resolve');
60
60
  const res0 = log[0].resolutions[0];
61
61
  t.is(res0[0], resultP);
@@ -66,13 +66,13 @@ test.serial('liveslots retains pending exported promise', async t => {
66
66
 
67
67
  test.serial('liveslots retains device nodes', async t => {
68
68
  const { syscall } = buildSyscall();
69
- let watch;
69
+ let collected;
70
70
  const recognize = new WeakSet(); // real WeakSet
71
71
  const success = [];
72
72
  function build(_vatPowers) {
73
73
  const root = Far('root', {
74
74
  first(dn) {
75
- watch = new WeakRef(dn);
75
+ collected = watchCollected(dn);
76
76
  recognize.add(dn);
77
77
  },
78
78
  second(dn) {
@@ -87,25 +87,24 @@ test.serial('liveslots retains device nodes', async t => {
87
87
  const device = 'd-1';
88
88
  await dispatch(makeMessage(rootA, 'first', [kslot(device)]));
89
89
  await gcAndFinalize();
90
- t.truthy(watch.deref(), 'Device node not retained');
90
+ t.false(collected.result, 'Device node retained');
91
91
  await dispatch(makeMessage(rootA, 'second', [kslot(device)]));
92
92
  t.deepEqual(success, [true]);
93
93
  });
94
94
 
95
95
  test.serial('GC syscall.dropImports', async t => {
96
96
  const { log, syscall } = buildSyscall();
97
- let wr;
97
+ let collected;
98
98
  function build(_vatPowers) {
99
- // eslint-disable-next-line no-unused-vars
100
- let presence1;
99
+ const holder = new Set();
101
100
  const root = Far('root', {
102
101
  one(arg) {
103
- presence1 = arg;
104
- wr = new WeakRef(arg);
102
+ holder.add(arg);
103
+ collected = watchCollected(arg);
105
104
  },
106
105
  two() {},
107
106
  three() {
108
- presence1 = undefined; // drops the import
107
+ holder.clear(); // drops the import
109
108
  },
110
109
  });
111
110
  return root;
@@ -121,19 +120,29 @@ test.serial('GC syscall.dropImports', async t => {
121
120
  // rp1 = root~.one(arg)
122
121
  await dispatch(makeMessage(rootA, 'one', [kslot(arg)]));
123
122
  await dispatch(makeBringOutYourDead());
124
- t.truthy(wr.deref());
123
+ t.false(collected.result);
125
124
 
126
125
  // an intermediate message will trigger GC, but the presence is still held
127
126
  await dispatch(makeMessage(rootA, 'two', []));
128
127
  await dispatch(makeBringOutYourDead());
129
- t.truthy(wr.deref());
128
+ t.false(collected.result);
130
129
 
131
130
  // now tell the vat to drop the 'arg' presence we gave them earlier
132
131
  await dispatch(makeMessage(rootA, 'three', []));
133
132
  await dispatch(makeBringOutYourDead());
134
133
 
134
+ const isV8 =
135
+ typeof process !== 'undefined' && 'v8' in (process.versions || {});
136
+
135
137
  // the presence itself should be gone
136
- t.falsy(wr.deref());
138
+ if (!collected.result) {
139
+ if (isV8) {
140
+ // Flake in v8/node: https://github.com/Agoric/agoric-sdk/issues/8883
141
+ t.log('skipping flake in v8');
142
+ return;
143
+ }
144
+ t.fail('import not collected');
145
+ }
137
146
 
138
147
  // first it will check that there are no VO's holding onto it
139
148
  const l2 = log.shift();
@@ -246,12 +255,12 @@ test.serial('GC dispatch.retireExports', async t => {
246
255
 
247
256
  test.serial('GC dispatch.dropExports', async t => {
248
257
  const { log, syscall } = buildSyscall();
249
- let wr;
258
+ let collected;
250
259
  function build(_vatPowers) {
251
260
  const root = Far('root', {
252
261
  one() {
253
262
  const ex1 = Far('export', {});
254
- wr = new WeakRef(ex1);
263
+ collected = watchCollected(ex1);
255
264
  return ex1;
256
265
  // ex1 goes out of scope, dropping last userspace strongref
257
266
  },
@@ -285,19 +294,19 @@ test.serial('GC dispatch.dropExports', async t => {
285
294
 
286
295
  // the exported Remotable should be held in place by exportedRemotables
287
296
  // until we tell the vat we don't need it any more
288
- t.truthy(wr.deref());
297
+ t.false(collected.result);
289
298
 
290
299
  // an intermediate message will trigger GC, but the presence is still held
291
300
  await dispatch(makeMessage(rootA, 'two', []));
292
301
  await dispatch(makeBringOutYourDead());
293
- t.truthy(wr.deref());
302
+ t.false(collected.result);
294
303
 
295
304
  // now tell the vat we don't need a strong reference to that export.
296
305
  await dispatch(makeDropExports(ex1));
297
306
  await dispatch(makeBringOutYourDead());
298
307
 
299
308
  // that should allow ex1 to be collected
300
- t.falsy(wr.deref());
309
+ t.true(collected.result);
301
310
 
302
311
  // and once it's collected, the vat should emit `syscall.retireExport`
303
312
  // because nobody else will be able to recognize it again
@@ -313,19 +322,19 @@ test.serial(
313
322
  'GC dispatch.retireExports inhibits syscall.retireExports',
314
323
  async t => {
315
324
  const { log, syscall } = buildSyscall();
316
- let wr;
325
+ let collected;
317
326
  function build(_vatPowers) {
318
- let ex1;
327
+ const holder = new Set();
319
328
  const root = Far('root', {
320
329
  hold() {
321
- ex1 = Far('export', {});
322
- wr = new WeakRef(ex1);
330
+ const ex1 = Far('export', {});
331
+ holder.add(ex1);
332
+ collected = watchCollected(ex1);
323
333
  return ex1;
324
334
  },
325
335
  two() {},
326
336
  drop() {
327
- // eslint-disable-next-line no-unused-vars
328
- ex1 = undefined; // drop the last userspace strongref
337
+ holder.clear(); // drop the last userspace strongref
329
338
  },
330
339
  });
331
340
  return root;
@@ -356,19 +365,19 @@ test.serial(
356
365
 
357
366
  // the exported Remotable should be held in place by exportedRemotables
358
367
  // until we tell the vat we don't need it any more
359
- t.truthy(wr.deref());
368
+ t.false(collected.result);
360
369
 
361
370
  // an intermediate message will trigger GC, but the presence is still held
362
371
  await dispatch(makeMessage(rootA, 'two', []));
363
372
  await dispatch(makeBringOutYourDead());
364
- t.truthy(wr.deref());
373
+ t.false(collected.result);
365
374
 
366
375
  // now tell the vat we don't need a strong reference to that export.
367
376
  await dispatch(makeDropExports(ex1));
368
377
  await dispatch(makeBringOutYourDead());
369
378
 
370
379
  // that removes the liveslots strongref, but the vat's remains in place
371
- t.truthy(wr.deref());
380
+ t.false(collected.result);
372
381
 
373
382
  // now the kernel tells the vat we can't even recognize the export
374
383
  await dispatch(makeRetireExports(ex1));
@@ -376,14 +385,14 @@ test.serial(
376
385
 
377
386
  // that ought to delete the table entry, but doesn't affect the vat
378
387
  // strongref
379
- t.truthy(wr.deref());
388
+ t.false(collected.result);
380
389
 
381
390
  // now tell the vat to drop its strongref
382
391
  await dispatch(makeMessage(rootA, 'drop', []));
383
392
  await dispatch(makeBringOutYourDead());
384
393
 
385
394
  // which should let the export be collected
386
- t.falsy(wr.deref());
395
+ t.true(collected.result);
387
396
 
388
397
  // the vat should *not* emit `syscall.retireExport`, because it already
389
398
  // received a dispatch.retireExport