@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
@@ -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}"`),
@@ -132,6 +145,14 @@ function exerciseMapOperations(t, collectionName, testStore) {
132
145
  `key "[Alleged: something missing]" not found in collection "${collectionName}"`,
133
146
  ),
134
147
  );
148
+ if (collectionName === 'map') {
149
+ // strong map, so we can .clear
150
+ testStore.clear();
151
+ for (const [key, _value] of stuff) {
152
+ t.false(testStore.has(key));
153
+ }
154
+ fillBasicMapStore(testStore);
155
+ }
135
156
  }
136
157
 
137
158
  function exerciseSetOperations(t, collectionName, testStore) {
@@ -146,7 +167,9 @@ function exerciseSetOperations(t, collectionName, testStore) {
146
167
  t.notThrows(() => testStore.add(47));
147
168
 
148
169
  testStore.delete(47);
170
+ testStore.delete(something);
149
171
  t.falsy(testStore.has(47));
172
+ t.falsy(testStore.has(something));
150
173
  t.throws(
151
174
  () => testStore.delete(22),
152
175
  m(`key 22 not found in collection "${collectionName}"`),
@@ -157,6 +180,14 @@ function exerciseSetOperations(t, collectionName, testStore) {
157
180
  `key "[Alleged: something missing]" not found in collection "${collectionName}"`,
158
181
  ),
159
182
  );
183
+ if (collectionName === 'set') {
184
+ // strong set, so we can .clear
185
+ testStore.clear();
186
+ for (const [key, _value] of stuff) {
187
+ t.false(testStore.has(key));
188
+ }
189
+ fillBasicSetStore(testStore);
190
+ }
160
191
  }
161
192
 
162
193
  test('basic map operations', t => {
@@ -191,6 +222,89 @@ test('basic weak set operations', t => {
191
222
  );
192
223
  });
193
224
 
225
+ function exerciseSetAddAll(t, weak, testStore) {
226
+ const allThatStuff = stuff.map(entry => entry[0]);
227
+
228
+ testStore.addAll(allThatStuff);
229
+ for (const elem of allThatStuff) {
230
+ t.truthy(testStore.has(elem));
231
+ testStore.delete(elem);
232
+ }
233
+ if (!weak) {
234
+ t.is(testStore.getSize(), 0);
235
+ }
236
+
237
+ testStore.addAll(makeCopySet(allThatStuff));
238
+ for (const elem of allThatStuff) {
239
+ t.truthy(testStore.has(elem));
240
+ testStore.delete(elem);
241
+ }
242
+ if (!weak) {
243
+ t.is(testStore.getSize(), 0);
244
+ }
245
+
246
+ t.throws(
247
+ () => testStore.addAll({ bogus: 47 }),
248
+ m(/provided data source is not iterable/),
249
+ );
250
+ }
251
+
252
+ test('set addAll', t => {
253
+ exerciseSetAddAll(t, false, makeScalarBigSetStore('test set'));
254
+ });
255
+
256
+ test('weak set addAll', t => {
257
+ exerciseSetAddAll(t, true, makeScalarBigWeakSetStore('test weak set'));
258
+ });
259
+
260
+ test('set snapshot', t => {
261
+ const testStore = makeScalarBigSetStore('test set');
262
+ const allThatStuff = stuff.map(entry => entry[0]);
263
+ testStore.addAll(allThatStuff);
264
+ t.deepEqual(testStore.snapshot(), makeCopySet(allThatStuff));
265
+ });
266
+
267
+ function exerciseMapAddAll(t, weak, testStore) {
268
+ testStore.addAll(stuff);
269
+ for (const [k, v] of stuff) {
270
+ t.truthy(testStore.has(k));
271
+ t.is(testStore.get(k), v);
272
+ testStore.delete(k);
273
+ }
274
+ if (!weak) {
275
+ t.is(testStore.getSize(), 0);
276
+ }
277
+
278
+ testStore.addAll(makeCopyMap(stuff));
279
+ for (const [k, v] of stuff) {
280
+ t.truthy(testStore.has(k));
281
+ t.is(testStore.get(k), v);
282
+ testStore.delete(k);
283
+ }
284
+ if (!weak) {
285
+ t.is(testStore.getSize(), 0);
286
+ }
287
+
288
+ t.throws(
289
+ () => testStore.addAll({ bogus: 47 }),
290
+ m(/provided data source is not iterable/),
291
+ );
292
+ }
293
+
294
+ test('map addAll', t => {
295
+ exerciseMapAddAll(t, false, makeScalarBigMapStore('test map'));
296
+ });
297
+
298
+ test('weak map addAll', t => {
299
+ exerciseMapAddAll(t, true, makeScalarBigWeakMapStore('test weak map'));
300
+ });
301
+
302
+ test('map snapshot', t => {
303
+ const testStore = makeScalarBigMapStore('test map');
304
+ testStore.addAll(stuff);
305
+ t.deepEqual(testStore.snapshot(), makeCopyMap(stuff));
306
+ });
307
+
194
308
  test('constrain map key shape', t => {
195
309
  const stringsOnly = makeScalarBigMapStore('map key strings only', {
196
310
  keyShape: M.string(),
@@ -389,6 +503,19 @@ test('map clear', t => {
389
503
  t.is(testStore.getSize(), 0);
390
504
  });
391
505
 
506
+ test('map clear with pattern', t => {
507
+ const testStore = makeScalarBigMapStore('cmap', { keyShape: M.any() });
508
+ testStore.init('a', 'ax');
509
+ testStore.init('b', 'bx');
510
+ testStore.init('c', 'cx');
511
+ console.log(`M is`, M);
512
+ testStore.clear(M.eq('c'));
513
+ t.true(testStore.has('a'));
514
+ t.true(testStore.has('b'));
515
+ t.false(testStore.has('c'));
516
+ t.is(testStore.getSize(), 2);
517
+ });
518
+
392
519
  test('set clear', t => {
393
520
  const testStore = makeScalarBigSetStore('cset', { keyShape: M.any() });
394
521
  testStore.add('a');
@@ -401,11 +528,25 @@ test('set clear', t => {
401
528
  t.is(testStore.getSize(), 0);
402
529
  });
403
530
 
531
+ test('set clear with pattern', t => {
532
+ const testStore = makeScalarBigSetStore('cset', { keyShape: M.any() });
533
+ testStore.add('a');
534
+ testStore.add('b');
535
+ testStore.add('c');
536
+ testStore.clear(M.eq('c'));
537
+ t.true(testStore.has('a'));
538
+ t.true(testStore.has('b'));
539
+ t.false(testStore.has('c'));
540
+ t.is(testStore.getSize(), 2);
541
+ });
542
+
404
543
  test('map fail on concurrent modification', t => {
405
544
  const primeMap = makeScalarBigMapStore('fmap', {
406
545
  keyShape: M.number(),
407
546
  });
408
- primes.forEach((v, i) => primeMap.init(v, `${v} is prime #${i + 1}`));
547
+ for (const [i, v] of primes.entries()) {
548
+ primeMap.init(v, `${v} is prime #${i + 1}`);
549
+ }
409
550
 
410
551
  let iter = primeMap.keys()[Symbol.iterator]();
411
552
  t.deepEqual(iter.next(), { done: false, value: 2 });
@@ -433,7 +574,9 @@ test('set fail on concurrent modification', t => {
433
574
  const primeSet = makeScalarBigSetStore('fset', {
434
575
  keyShape: M.number(),
435
576
  });
436
- primes.forEach(v => primeSet.add(v));
577
+ for (const v of primes) {
578
+ primeSet.add(v);
579
+ }
437
580
 
438
581
  let iter = primeSet.keys()[Symbol.iterator]();
439
582
  t.deepEqual(iter.next(), { done: false, value: 2 });
@@ -461,7 +604,9 @@ test('map ok with concurrent deletion', t => {
461
604
  const primeMap = makeScalarBigMapStore('fmap', {
462
605
  keyShape: M.number(),
463
606
  });
464
- primes.forEach((v, i) => primeMap.init(v, `${v} is prime #${i + 1}`));
607
+ for (const [i, v] of primes.entries()) {
608
+ primeMap.init(v, `${v} is prime #${i + 1}`);
609
+ }
465
610
  const iter = primeMap.keys()[Symbol.iterator]();
466
611
  t.deepEqual(iter.next(), { done: false, value: 2 });
467
612
  primeMap.delete(3);
@@ -476,7 +621,9 @@ test('set ok with concurrent deletion', t => {
476
621
  const primeSet = makeScalarBigSetStore('fset', {
477
622
  keyShape: M.number(),
478
623
  });
479
- primes.forEach(v => primeSet.add(v));
624
+ for (const v of primes) {
625
+ primeSet.add(v);
626
+ }
480
627
 
481
628
  const iter = primeSet.keys()[Symbol.iterator]();
482
629
  t.deepEqual(iter.next(), { done: false, value: 2 });
@@ -766,13 +913,6 @@ test('set queries', t => {
766
913
  symbolKrusty,
767
914
  undefined,
768
915
  ]);
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
916
  });
777
917
 
778
918
  test('remotable sort order', t => {
@@ -798,7 +938,9 @@ test('complex map queries', t => {
798
938
  const primeStore = makeScalarBigMapStore('prime map', {
799
939
  keyShape: M.number(),
800
940
  });
801
- primes.forEach((v, i) => primeStore.init(v, `${v} is prime #${i + 1}`));
941
+ for (const [i, v] of primes.entries()) {
942
+ primeStore.init(v, `${v} is prime #${i + 1}`);
943
+ }
802
944
 
803
945
  t.deepEqual(Array.from(primeStore.values()), [
804
946
  '2 is prime #1',
@@ -973,7 +1115,9 @@ test('complex set queries', t => {
973
1115
  const primeStore = makeScalarBigSetStore('prime set', {
974
1116
  keyShape: M.number(),
975
1117
  });
976
- primes.forEach(v => primeStore.add(v));
1118
+ for (const v of primes) {
1119
+ primeStore.add(v);
1120
+ }
977
1121
 
978
1122
  t.deepEqual(
979
1123
  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';
@@ -0,0 +1,80 @@
1
+ import test from 'ava';
2
+ import { Far } from '@endo/marshal';
3
+ import { kser, kslot } from '@agoric/kmarshal';
4
+ import { makeLiveSlots } from '../src/liveslots.js';
5
+ import { buildSyscall } from './liveslots-helpers.js';
6
+ import { makeStartVat, makeMessage, makeBringOutYourDead } from './util.js';
7
+ import { makeMockGC } from './mock-gc.js';
8
+
9
+ // Test for https://github.com/Agoric/agoric-sdk/issues/9939
10
+
11
+ test('weakset deletion vs retire', async t => {
12
+ const { syscall, log } = buildSyscall();
13
+ const gcTools = makeMockGC();
14
+
15
+ // #9939 was a bug in liveslots that caused a vat to emit
16
+ // syscall.retireImports despite not having done dropImports
17
+ // first. The setup is:
18
+ //
19
+ // * import a Presence (raising the RAM pillar)
20
+ // * store it in a virtual object (raising the vdata pillar)
21
+ // * use it as a key of a voAwareWeakMap or voAwareWeakSet
22
+ // * drop the Presence (dropping the RAM pillar)
23
+ // * do a BOYD
24
+ // * delete the voAwareWeakSet
25
+ // * do a BOYD
26
+ //
27
+ // When the voAwareWeakSet is collected, a finalizer callback named
28
+ // finalizeDroppedCollection is called, which clears the entries,
29
+ // and adds all its vref keys to possiblyRetiredSet. Later, during
30
+ // BOYD, a loop examines possiblyRetiredSet and adds qualified vrefs
31
+ // to importsToRetire, for the syscall.retireImports at the end.
32
+ //
33
+ // That qualification check was sufficient to prevent the retirement
34
+ // of vrefs that still have a RAM pillar, and also vrefs that were
35
+ // being dropped in this particular BOYD, but it was not sufficient
36
+ // to protect vrefs that still have a vdata pillar.
37
+
38
+ let myVOAwareWeakSet;
39
+ let myPresence;
40
+ function buildRootObject(vatPowers, _vatParameters, baggage) {
41
+ const { WeakSet } = vatPowers;
42
+ myVOAwareWeakSet = new WeakSet();
43
+ return Far('root', {
44
+ store: p => {
45
+ myPresence = p;
46
+ myVOAwareWeakSet.add(p);
47
+ baggage.init('presence', p);
48
+ },
49
+ });
50
+ }
51
+
52
+ const makeNS = () => ({ buildRootObject });
53
+ const ls = makeLiveSlots(syscall, 'vatA', {}, {}, gcTools, undefined, makeNS);
54
+ const { dispatch } = ls;
55
+ await dispatch(makeStartVat(kser()));
56
+ t.truthy(myVOAwareWeakSet);
57
+
58
+ await dispatch(makeMessage('o+0', 'store', [kslot('o-1')]));
59
+ t.truthy(myPresence);
60
+
61
+ log.length = 0;
62
+ gcTools.kill(myPresence);
63
+ gcTools.flushAllFRs();
64
+ await dispatch(makeBringOutYourDead());
65
+
66
+ log.length = 0;
67
+ gcTools.kill(myVOAwareWeakSet);
68
+ gcTools.flushAllFRs();
69
+ await dispatch(makeBringOutYourDead());
70
+
71
+ // The imported vref is still reachable by the 'baggage' durable
72
+ // store, so it must not be dropped or retired yet. The bug caused
73
+ // the vref to be retired without first doing a drop, which is a
74
+ // vat-fatal syscall error
75
+ const gcCalls = log.filter(
76
+ l => l.type === 'dropImports' || l.type === 'retireImports',
77
+ );
78
+ t.deepEqual(gcCalls, []);
79
+ log.length = 0;
80
+ });
@@ -1,4 +1,4 @@
1
- import { assert } from '@agoric/assert';
1
+ import { assert } from '@endo/errors';
2
2
 
3
3
  export function makeDummyMeterControl() {
4
4
  let meteringDisabled = 0;
@@ -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) => {
@@ -262,12 +262,12 @@ async function runDurabilityCheckTest(t, relaxDurabilityRules) {
262
262
  passVal(() => virtualMap.init('non-scalar key', aNonScalarKey));
263
263
  passVal(() => virtualMap.init('non-scalar non-key', aNonScalarNonKey));
264
264
 
265
- condVal(() => durableMap.init('promise', aPassablePromise));
265
+ failVal(() => durableMap.init('promise', aPassablePromise));
266
266
  passVal(() => durableMap.init('error', aPassableError));
267
267
  passVal(() => durableMap.init('non-scalar key', aNonScalarKey));
268
268
  passVal(() => durableMap.init('non-scalar non-key', aNonScalarNonKey));
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);
@@ -0,0 +1,70 @@
1
+ import { provideLazy as provide } from '@agoric/store';
2
+
3
+ // Partially duplicates @agoric/vat-data to avoid circular dependencies.
4
+ export const makeExoUtils = VatData => {
5
+ const { defineDurableKind, makeKindHandle, watchPromise } = VatData;
6
+
7
+ const provideKindHandle = (baggage, kindName) =>
8
+ provide(baggage, `${kindName}_kindHandle`, () => makeKindHandle(kindName));
9
+
10
+ const emptyRecord = harden({});
11
+ const initEmpty = () => emptyRecord;
12
+
13
+ const defineDurableExoClass = (
14
+ kindHandle,
15
+ interfaceGuard,
16
+ init,
17
+ methods,
18
+ options,
19
+ ) =>
20
+ defineDurableKind(kindHandle, init, methods, {
21
+ ...options,
22
+ thisfulMethods: true,
23
+ interfaceGuard,
24
+ });
25
+
26
+ const prepareExoClass = (
27
+ baggage,
28
+ kindName,
29
+ interfaceGuard,
30
+ init,
31
+ methods,
32
+ options = undefined,
33
+ ) =>
34
+ defineDurableExoClass(
35
+ provideKindHandle(baggage, kindName),
36
+ interfaceGuard,
37
+ init,
38
+ methods,
39
+ options,
40
+ );
41
+
42
+ const prepareExo = (
43
+ baggage,
44
+ kindName,
45
+ interfaceGuard,
46
+ methods,
47
+ options = undefined,
48
+ ) => {
49
+ const makeSingleton = prepareExoClass(
50
+ baggage,
51
+ kindName,
52
+ interfaceGuard,
53
+ initEmpty,
54
+ methods,
55
+ options,
56
+ );
57
+ return provide(baggage, `the_${kindName}`, () => makeSingleton());
58
+ };
59
+
60
+ return {
61
+ defineDurableKind,
62
+ makeKindHandle,
63
+ watchPromise,
64
+
65
+ provideKindHandle,
66
+ defineDurableExoClass,
67
+ prepareExoClass,
68
+ prepareExo,
69
+ };
70
+ };
@@ -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,4 +1,4 @@
1
- /* global setImmediate */
1
+ /* global setImmediate, FinalizationRegistry */
2
2
 
3
3
  /* A note on our GC terminology:
4
4
  *
@@ -89,3 +89,32 @@ export function makeGcAndFinalize(gcPower) {
89
89
  await new Promise(setImmediate);
90
90
  };
91
91
  }
92
+
93
+ const fr = new FinalizationRegistry(({ promise, resolve }) => {
94
+ promise.result = true;
95
+ resolve(true);
96
+ });
97
+
98
+ const makeCollectedResultKit = () => {
99
+ /** @type {(val: true) => void} */
100
+ let resolve;
101
+
102
+ const promise = /** @type {Promise<true> & {result: boolean}} */ (
103
+ new Promise(r => {
104
+ resolve = r;
105
+ })
106
+ );
107
+ promise.result = false;
108
+ return {
109
+ promise,
110
+ // @ts-expect-error
111
+ resolve,
112
+ };
113
+ };
114
+
115
+ export function watchCollected(target) {
116
+ const kit = makeCollectedResultKit();
117
+ fr.register(target, kit);
118
+
119
+ return kit.promise;
120
+ }