@agoric/swingset-liveslots 0.10.3-other-dev-8f8782b.0 → 0.10.3-other-dev-fbe72e7.0.fbe72e7

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 (141) hide show
  1. package/README.md +2 -0
  2. package/package.json +34 -26
  3. package/src/boyd-gc.d.ts +12 -0
  4. package/src/boyd-gc.d.ts.map +1 -0
  5. package/src/boyd-gc.js +598 -0
  6. package/src/cache.d.ts +71 -0
  7. package/src/cache.d.ts.map +1 -0
  8. package/src/cache.js +3 -2
  9. package/src/capdata.d.ts +16 -0
  10. package/src/capdata.d.ts.map +1 -0
  11. package/src/capdata.js +17 -10
  12. package/src/collectionManager.d.ts +47 -0
  13. package/src/collectionManager.d.ts.map +1 -0
  14. package/src/collectionManager.js +220 -103
  15. package/src/facetiousness.d.ts +25 -0
  16. package/src/facetiousness.d.ts.map +1 -0
  17. package/src/facetiousness.js +1 -1
  18. package/src/index.d.ts +4 -0
  19. package/src/index.d.ts.map +1 -0
  20. package/src/index.js +4 -2
  21. package/src/kdebug.d.ts +7 -0
  22. package/src/kdebug.d.ts.map +1 -0
  23. package/src/liveslots.d.ts +42 -0
  24. package/src/liveslots.d.ts.map +1 -0
  25. package/src/liveslots.js +137 -305
  26. package/src/message.d.ts +49 -0
  27. package/src/message.d.ts.map +1 -0
  28. package/src/message.js +9 -5
  29. package/src/parseVatSlots.d.ts +125 -0
  30. package/src/parseVatSlots.d.ts.map +1 -0
  31. package/src/parseVatSlots.js +1 -1
  32. package/src/types-index.d.ts +4 -0
  33. package/src/types-index.js +2 -0
  34. package/src/types.d.ts +81 -0
  35. package/src/types.d.ts.map +1 -0
  36. package/src/types.js +14 -7
  37. package/src/vatDataTypes.d.ts +170 -0
  38. package/src/vatDataTypes.d.ts.map +1 -0
  39. package/src/vatDataTypes.ts +272 -0
  40. package/src/vatstore-iterators.d.ts +4 -0
  41. package/src/vatstore-iterators.d.ts.map +1 -0
  42. package/src/vatstore-iterators.js +2 -0
  43. package/src/vatstore-usage.md +198 -0
  44. package/src/virtualObjectManager.d.ts +44 -0
  45. package/src/virtualObjectManager.d.ts.map +1 -0
  46. package/src/virtualObjectManager.js +254 -84
  47. package/src/virtualReferences.d.ts +61 -0
  48. package/src/virtualReferences.d.ts.map +1 -0
  49. package/src/virtualReferences.js +135 -26
  50. package/src/vpid-tracking.md +92 -0
  51. package/src/watchedPromises.d.ts +31 -0
  52. package/src/watchedPromises.d.ts.map +1 -0
  53. package/src/watchedPromises.js +81 -24
  54. package/test/{test-baggage.js → baggage.test.js} +1 -2
  55. package/test/{test-cache.js → cache.test.js} +0 -1
  56. package/test/clear-collection.test.js +586 -0
  57. package/test/{test-collection-schema-refcount.js → collection-schema-refcount.test.js} +1 -2
  58. package/test/{test-collection-upgrade.js → collection-upgrade.test.js} +1 -3
  59. package/test/{test-collections.js → collections.test.js} +183 -18
  60. package/test/{test-dropped-collection-weakrefs.js → dropped-collection-weakrefs.test.js} +1 -2
  61. package/test/dropped-weakset-9939.test.js +80 -0
  62. package/test/dummyMeterControl.d.ts +2 -0
  63. package/test/dummyMeterControl.d.ts.map +1 -0
  64. package/test/dummyMeterControl.js +1 -1
  65. package/test/{test-durabilityChecks.js → durabilityChecks.test.js} +4 -4
  66. package/test/engine-gc.d.ts +3 -0
  67. package/test/engine-gc.d.ts.map +1 -0
  68. package/test/exo-utils.js +70 -0
  69. package/test/{test-facetiousness.js → facetiousness.test.js} +1 -2
  70. package/test/gc-and-finalize.d.ts +5 -0
  71. package/test/gc-and-finalize.d.ts.map +1 -0
  72. package/test/gc-and-finalize.js +30 -1
  73. package/test/gc-before-finalizer.test.js +230 -0
  74. package/test/gc-helpers.js +4 -5
  75. package/test/{test-gc-sensitivity.js → gc-sensitivity.test.js} +2 -2
  76. package/test/handled-promises.test.js +872 -0
  77. package/test/{test-initial-vrefs.js → initial-vrefs.test.js} +13 -20
  78. package/test/liveslots-helpers.d.ts +64 -0
  79. package/test/liveslots-helpers.d.ts.map +1 -0
  80. package/test/liveslots-helpers.js +13 -7
  81. package/test/{test-liveslots-mock-gc.js → liveslots-mock-gc.test.js} +101 -2
  82. package/test/{test-liveslots-real-gc.js → liveslots-real-gc.test.js} +73 -46
  83. package/test/{test-liveslots.js → liveslots.test.js} +17 -18
  84. package/test/mock-gc.js +1 -0
  85. package/test/storeGC/{test-lifecycle.js → lifecycle.test.js} +15 -14
  86. package/test/storeGC/{test-refcount-management.js → refcount-management.test.js} +1 -2
  87. package/test/storeGC/{test-scalar-store-kind.js → scalar-store-kind.test.js} +0 -1
  88. package/test/storeGC/{test-weak-key.js → weak-key.test.js} +1 -2
  89. package/test/strict-test-env-upgrade.test.js +94 -0
  90. package/test/util.d.ts +25 -0
  91. package/test/util.d.ts.map +1 -0
  92. package/test/util.js +4 -4
  93. package/test/vat-environment.test.js +65 -0
  94. package/test/vat-util.d.ts +9 -0
  95. package/test/vat-util.d.ts.map +1 -0
  96. package/test/vat-util.js +2 -2
  97. package/test/virtual-objects/{test-cease-recognition.js → cease-recognition.test.js} +2 -2
  98. package/test/virtual-objects/{test-cross-facet.js → cross-facet.test.js} +5 -4
  99. package/test/virtual-objects/{test-empty-data.js → empty-data.test.js} +1 -2
  100. package/test/virtual-objects/{test-facets.js → facets.test.js} +1 -2
  101. package/test/virtual-objects/{test-kind-changes.js → kind-changes.test.js} +2 -2
  102. package/test/virtual-objects/{test-reachable-vrefs.js → reachable-vrefs.test.js} +2 -2
  103. package/test/virtual-objects/{test-rep-tostring.js → rep-tostring.test.js} +3 -5
  104. package/test/virtual-objects/{test-retain-remotable.js → retain-remotable.test.js} +25 -24
  105. package/test/virtual-objects/set-debug-label-instances.js +1 -1
  106. package/test/virtual-objects/state-shape.test.js +389 -0
  107. package/test/virtual-objects/{test-virtualObjectGC.js → virtualObjectGC.test.js} +39 -38
  108. package/test/virtual-objects/{test-virtualObjectManager.js → virtualObjectManager.test.js} +104 -8
  109. package/test/virtual-objects/{test-vo-real-gc.js → vo-real-gc.test.js} +8 -8
  110. package/test/virtual-objects/{test-weakcollections-vref-handling.js → weakcollections-vref-handling.test.js} +1 -2
  111. package/test/{test-vo-test-harness.js → vo-test-harness.test.js} +13 -10
  112. package/test/{test-vpid-liveslots.js → vpid-liveslots.test.js} +105 -5
  113. package/test/waitUntilQuiescent.d.ts +3 -0
  114. package/test/waitUntilQuiescent.d.ts.map +1 -0
  115. package/test/waitUntilQuiescent.js +2 -1
  116. package/test/weakset-dropped-remotable.test.js +50 -0
  117. package/tools/fakeCollectionManager.d.ts +14 -0
  118. package/tools/fakeCollectionManager.d.ts.map +1 -0
  119. package/tools/fakeCollectionManager.js +44 -0
  120. package/tools/fakeVirtualObjectManager.d.ts +32 -0
  121. package/tools/fakeVirtualObjectManager.d.ts.map +1 -0
  122. package/tools/fakeVirtualObjectManager.js +62 -0
  123. package/tools/fakeVirtualSupport.d.ts +278 -0
  124. package/tools/fakeVirtualSupport.d.ts.map +1 -0
  125. package/tools/fakeVirtualSupport.js +389 -0
  126. package/tools/prepare-strict-test-env.d.ts +37 -0
  127. package/tools/prepare-strict-test-env.d.ts.map +1 -0
  128. package/tools/prepare-strict-test-env.js +124 -0
  129. package/tools/prepare-test-env.d.ts +2 -0
  130. package/tools/prepare-test-env.d.ts.map +1 -0
  131. package/tools/prepare-test-env.js +13 -0
  132. package/tools/setup-vat-data.d.ts +9 -0
  133. package/tools/setup-vat-data.d.ts.map +1 -0
  134. package/tools/setup-vat-data.js +95 -0
  135. package/tools/vo-test-harness.d.ts +33 -0
  136. package/tools/vo-test-harness.d.ts.map +1 -0
  137. package/tools/vo-test-harness.js +164 -0
  138. package/CHANGELOG.md +0 -61
  139. package/test/kmarshal.js +0 -79
  140. package/test/test-handled-promises.js +0 -360
  141. package/test/virtual-objects/test-state-shape.js +0 -298
@@ -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;
@@ -71,34 +70,28 @@ test('initial vatstore contents', async t => {
71
70
  t.deepEqual(kunser(JSON.parse(get(`vc.1.|schemata`))), stringSchema);
72
71
 
73
72
  // then three tables for the promise watcher (one virtual, two durable)
74
- t.is(getLabel(`vc.3.|schemata`), 'promiseWatcherByKind'); // durable
75
- t.is(getLabel(`vc.4.|schemata`), 'watchedPromises'); // durable
76
- // the promiseRegistrations table is not durable, and only gets vc.2
77
- // on the first incarnation: it will get a new ID on subsequent
78
- // incarnations
79
- t.is(getLabel(`vc.2.|schemata`), `promiseRegistrations`); // virtual
73
+ t.is(getLabel(`vc.2.|schemata`), 'promiseWatcherByKind'); // durable
74
+ t.is(getLabel(`vc.3.|schemata`), 'watchedPromises'); // durable
80
75
 
81
76
  const watcherTableVref = get('watcherTableID');
82
77
  const watchedPromiseTableVref = get('watchedPromiseTableID');
83
- t.is(watcherTableVref, `${dmsBase}/3`);
84
- t.is(watchedPromiseTableVref, `${dmsBase}/4`);
78
+ t.is(watcherTableVref, `${dmsBase}/2`);
79
+ t.is(watchedPromiseTableVref, `${dmsBase}/3`);
85
80
 
86
81
  // baggage and the two durable promise-watcher tables are pinned
87
82
  t.is(get(`vom.rc.${baggageVref}`), '1');
88
83
  t.is(get(`vom.rc.${watcherTableVref}`), '1');
89
84
  t.is(get(`vom.rc.${watchedPromiseTableVref}`), '1');
90
85
 
91
- // promiseRegistrations and promiseWatcherByKind arbitrary scalars as keys
92
- const scalarSchema2 = { label: 'promiseRegistrations', keyShape: M.scalar() };
86
+ // promiseWatcherByKind arbitrary scalars as keys
93
87
  const scalarSchema3 = { label: 'promiseWatcherByKind', keyShape: M.scalar() };
94
- t.deepEqual(kunser(JSON.parse(get(`vc.2.|schemata`))), scalarSchema2);
95
- t.deepEqual(kunser(JSON.parse(get(`vc.3.|schemata`))), scalarSchema3);
88
+ t.deepEqual(kunser(JSON.parse(get(`vc.2.|schemata`))), scalarSchema3);
96
89
  // watchedPromises uses vref (string) keys
97
90
  const scalarStringSchema = {
98
91
  label: 'watchedPromises',
99
92
  keyShape: M.and(M.scalar(), M.string()),
100
93
  };
101
- t.deepEqual(kunser(JSON.parse(get(`vc.4.|schemata`))), scalarStringSchema);
94
+ t.deepEqual(kunser(JSON.parse(get(`vc.3.|schemata`))), scalarStringSchema);
102
95
  });
103
96
 
104
97
  test('vrefs', async t => {
@@ -142,11 +135,11 @@ test('vrefs', async t => {
142
135
  const dh2Vref = (await run('getDinstance2')).slots[0];
143
136
  t.is(dh2Vref, expectedDH2Vref);
144
137
 
145
- // the liveslots-created collections consume vc.1 through vc.4,
146
- // leaving vc.5 for the first user-created collection
147
- t.is(getLabel('vc.5.|schemata'), 'store1');
148
- const expectedStore1Vref = `o+v${initialKindIDs.scalarMapStore}/5`;
138
+ // the liveslots-created collections consume vc.1 through vc.3,
139
+ // leaving vc.4 for the first user-created collection
140
+ t.is(getLabel('vc.4.|schemata'), 'store1');
141
+ const expectedStore1Vref = `o+v${initialKindIDs.scalarMapStore}/4`;
149
142
  const store1Vref = (await run('getStore1')).slots[0];
150
143
  t.is(store1Vref, expectedStore1Vref);
151
- t.deepEqual(kunser(JSON.parse(fakestore.get(`vc.5.s${'key'}`))), 'value');
144
+ t.is(kunser(JSON.parse(fakestore.get(`vc.4.s${'key'}`))), 'value');
152
145
  });
@@ -0,0 +1,64 @@
1
+ /**
2
+ * @param {object} [options]
3
+ * @param {boolean} [options.skipLogging]
4
+ * @param {Map<string, string>} [options.kvStore]
5
+ */
6
+ export function buildSyscall(options?: {
7
+ skipLogging?: boolean | undefined;
8
+ kvStore?: Map<string, string> | undefined;
9
+ }): {
10
+ syscall: {
11
+ send(targetSlot: any, methargs: any, resultSlot: any): void;
12
+ subscribe(target: any): void;
13
+ resolve(resolutions: any): void;
14
+ dropImports(slots: any): void;
15
+ retireImports(slots: any): void;
16
+ retireExports(slots: any): void;
17
+ exit(isFailure: any, info: any): void;
18
+ vatstoreGet(key: any): any;
19
+ vatstoreGetNextKey(priorKey: any): any;
20
+ vatstoreSet(key: any, value: any): void;
21
+ vatstoreDelete(key: any): void;
22
+ };
23
+ fakestore: Map<any, any>;
24
+ log: any[];
25
+ };
26
+ export function makeDispatch(syscall: any, build: any, vatID?: string, liveSlotsOptions?: {}, vatParameters?: undefined): Promise<{
27
+ dispatch: any;
28
+ testHooks: any;
29
+ }>;
30
+ /**
31
+ * @param {import('ava').ExecutionContext} t
32
+ * @param {Function} buildRootObject
33
+ * @param {string} vatName
34
+ * @param {object} [options]
35
+ * @param {boolean} [options.forceGC]
36
+ * @param {Map<string, string>} [options.kvStore]
37
+ * @param {number} [options.nextPromiseImportNumber]
38
+ * @param {boolean} [options.skipLogging]
39
+ * @param {any} [options.vatParameters]
40
+ */
41
+ export function setupTestLiveslots(t: import("ava").ExecutionContext, buildRootObject: Function, vatName: string, options?: {
42
+ forceGC?: boolean | undefined;
43
+ kvStore?: Map<string, string> | undefined;
44
+ nextPromiseImportNumber?: number | undefined;
45
+ skipLogging?: boolean | undefined;
46
+ vatParameters?: any;
47
+ }): Promise<{
48
+ v: {
49
+ t: import("ava").ExecutionContext<unknown>;
50
+ log: any[];
51
+ fakestore: Map<any, any>;
52
+ dumpFakestore: () => void;
53
+ };
54
+ dispatch: any;
55
+ dispatchMessage: (message: any, ...args: any[]) => Promise<string>;
56
+ dispatchMessageSuccessfully: (message: any, ...args: any[]) => Promise<any>;
57
+ dispatchDropExports: (...vrefs: any[]) => Promise<void>;
58
+ dispatchRetireExports: (...vrefs: any[]) => Promise<void>;
59
+ dispatchRetireImports: (...vrefs: any[]) => Promise<void>;
60
+ nextPImport: () => string;
61
+ testHooks: any;
62
+ }>;
63
+ export function findSyscallsByType(log: any, type: any): any;
64
+ //# sourceMappingURL=liveslots-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"liveslots-helpers.d.ts","sourceRoot":"","sources":["liveslots-helpers.js"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,uCAHG;IAA0B,WAAW;IACC,OAAO;CAAC;;;;;;;;;;;;;;;;EA2GhD;AAED;;;GA2BC;AAUD;;;;;;;;;;GAUG;AACH,sCAVW,OAAO,KAAK,EAAE,gBAAgB,sCAE9B,MAAM,YAEd;IAA0B,OAAO;IACK,OAAO;IACpB,uBAAuB;IACtB,WAAW;IACf,aAAa,GAA3B,GAAG;CAAyB;;;;;;;;;;;;;;;GA6GtC;AAED,6DAEC"}
@@ -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) {
@@ -273,6 +278,7 @@ export async function setupTestLiveslots(
273
278
  dispatchDropExports,
274
279
  dispatchRetireExports,
275
280
  dispatchRetireImports,
281
+ nextPImport: nextRP,
276
282
  testHooks,
277
283
  };
278
284
  }
@@ -1,16 +1,17 @@
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,
11
11
  makeStartVat,
12
12
  makeBringOutYourDead,
13
13
  makeResolve,
14
+ makeRetireImports,
14
15
  } from './util.js';
15
16
  import { makeMockGC } from './mock-gc.js';
16
17
 
@@ -465,3 +466,101 @@ for (const firstType of ['object', 'collection']) {
465
466
  }
466
467
 
467
468
  // test('double-free', doublefreetest, { firstType: 'object', lastType: 'collection', order: 'first->last' });
469
+
470
+ test('retirement', async t => {
471
+ const { syscall, fakestore, log } = buildSyscall();
472
+ const gcTools = makeMockGC();
473
+
474
+ // A is a weak collection, with one entry, whose key is B (a
475
+ // Presence). We drop the RAM pillar for B and do a BOYD, which
476
+ // should provoke a syscall.dropImports. Then, when we delete A (by
477
+ // dropping the RAM pillar), the next BOYD should see a
478
+ // `syscall.retireImports`.
479
+
480
+ let weakmapA;
481
+ let presenceB;
482
+
483
+ function buildRootObject(vatPowers) {
484
+ const { VatData } = vatPowers;
485
+ const { makeScalarBigWeakMapStore } = VatData;
486
+
487
+ weakmapA = makeScalarBigWeakMapStore();
488
+
489
+ return Far('root', {
490
+ add: p => {
491
+ presenceB = p;
492
+ weakmapA.init(presenceB, 'value');
493
+ },
494
+ });
495
+ }
496
+
497
+ const makeNS = () => ({ buildRootObject });
498
+ const ls = makeLiveSlots(syscall, 'vatA', {}, {}, gcTools, undefined, makeNS);
499
+ const { dispatch, testHooks } = ls;
500
+ const { valToSlot } = testHooks;
501
+
502
+ await dispatch(makeStartVat(kser()));
503
+ log.length = 0;
504
+ const weakmapAvref = valToSlot.get(weakmapA);
505
+ const { subid } = parseVatSlot(weakmapAvref);
506
+ const collectionID = String(subid);
507
+
508
+ const rootA = 'o+0';
509
+ const presenceBvref = 'o-1';
510
+ await dispatch(makeMessage(rootA, 'add', [kslot(presenceBvref)]));
511
+ log.length = 0;
512
+
513
+ // the fact that weakmapA can recognize presenceA is recorded in a
514
+ // vatstore key
515
+ const recognizerKey = `vom.ir.${presenceBvref}|${collectionID}`;
516
+ t.is(fakestore.get(recognizerKey), '1');
517
+
518
+ // tell mockGC that userspace has dropped presenceB
519
+ gcTools.kill(presenceB);
520
+ gcTools.flushAllFRs();
521
+
522
+ await dispatch(makeBringOutYourDead());
523
+ const priorKey = `vom.ir.${presenceBvref}|`;
524
+
525
+ t.deepEqual(log.splice(0), [
526
+ // when a Presence is dropped, scanForDeadObjects can't drop the
527
+ // underlying vref import until it knows that virtual data isn't
528
+ // holding a reference, so we expect a refcount check
529
+ { type: 'vatstoreGet', key: `vom.rc.${presenceBvref}`, result: undefined },
530
+
531
+ // the vref is now in importsToDrop, but since this commonly means
532
+ // it can be retired too, scanForDeadObjects goes ahead and checks
533
+ // for recognizers
534
+ { type: 'vatstoreGetNextKey', priorKey, result: recognizerKey },
535
+
536
+ // it found a recognizer, so the vref cannot be retired
537
+ // yet. scanForDeadObjects finishes the BOYD by emitting the
538
+ // dropImports, but should keep watching for an opportunity to
539
+ // retire it too
540
+ { type: 'dropImports', slots: [presenceBvref] },
541
+ ]);
542
+
543
+ // now tell mockGC that we're dropping the weakmap too
544
+ gcTools.kill(weakmapA);
545
+ gcTools.flushAllFRs();
546
+
547
+ // this will provoke the deletion of the collection and all its
548
+ // data. It should *also* trigger a syscall.retireImports of the
549
+ // no-longer-recognizable key
550
+ await dispatch(makeBringOutYourDead());
551
+ const retires = log.filter(e => e.type === 'retireImports');
552
+
553
+ t.deepEqual(retires, [{ type: 'retireImports', slots: [presenceBvref] }]);
554
+
555
+ // If the bug is present, the vat won't send `syscall.retireImports`
556
+ // to the kernel. In a full system, that means the kernel can
557
+ // eventually send a `dispatch.retireImports` into the vat, if/when
558
+ // the object's hosting vat decides to drop it. Make sure that won't
559
+ // cause a crash.
560
+
561
+ if (!retires.length) {
562
+ console.log(`testing kernel's dispatch.retireImports`);
563
+ await dispatch(makeRetireImports(presenceBvref));
564
+ console.log(`dispatch.retireImports did not crash`);
565
+ }
566
+ });
@@ -1,12 +1,13 @@
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';
8
+ import { avaRetry } from '@agoric/internal/tools/avaRetry.js';
7
9
  import engineGC from './engine-gc.js';
8
- import { makeGcAndFinalize } from './gc-and-finalize.js';
9
- import { kslot, kser } from './kmarshal.js';
10
+ import { watchCollected, makeGcAndFinalize } from './gc-and-finalize.js';
10
11
  import { buildSyscall, makeDispatch } from './liveslots-helpers.js';
11
12
  import {
12
13
  makeMessage,
@@ -26,15 +27,15 @@ const gcAndFinalize = makeGcAndFinalize(engineGC);
26
27
  // inconsistent GC behavior under Node.js and AVA with tests running
27
28
  // in parallel, so we mark them all with test.serial()
28
29
 
29
- test.serial('liveslots retains pending exported promise', async t => {
30
+ avaRetry(test.serial, 'liveslots retains pending exported promise', async t => {
30
31
  const { log, syscall } = buildSyscall();
31
- let watch;
32
+ let collected;
32
33
  const success = [];
33
34
  function build(_vatPowers) {
34
35
  const root = Far('root', {
35
36
  make() {
36
37
  const pk = makePromiseKit();
37
- watch = new WeakRef(pk.promise);
38
+ collected = watchCollected(pk.promise);
38
39
  // we export the Promise, but do not retain resolve/reject
39
40
  return [pk.promise];
40
41
  },
@@ -55,7 +56,7 @@ test.serial('liveslots retains pending exported promise', async t => {
55
56
  const resultP = 'p-1';
56
57
  await dispatch(makeMessage(rootA, 'make', [], resultP));
57
58
  await gcAndFinalize();
58
- t.truthy(watch.deref(), 'Promise not retained');
59
+ t.false(collected.result, 'Promise retained');
59
60
  t.is(log[0].type, 'resolve');
60
61
  const res0 = log[0].resolutions[0];
61
62
  t.is(res0[0], resultP);
@@ -64,15 +65,15 @@ test.serial('liveslots retains pending exported promise', async t => {
64
65
  t.deepEqual(success, ['yes']);
65
66
  });
66
67
 
67
- test.serial('liveslots retains device nodes', async t => {
68
+ avaRetry(test.serial, 'liveslots retains device nodes', async t => {
68
69
  const { syscall } = buildSyscall();
69
- let watch;
70
+ let collected;
70
71
  const recognize = new WeakSet(); // real WeakSet
71
72
  const success = [];
72
73
  function build(_vatPowers) {
73
74
  const root = Far('root', {
74
75
  first(dn) {
75
- watch = new WeakRef(dn);
76
+ collected = watchCollected(dn);
76
77
  recognize.add(dn);
77
78
  },
78
79
  second(dn) {
@@ -87,25 +88,24 @@ test.serial('liveslots retains device nodes', async t => {
87
88
  const device = 'd-1';
88
89
  await dispatch(makeMessage(rootA, 'first', [kslot(device)]));
89
90
  await gcAndFinalize();
90
- t.truthy(watch.deref(), 'Device node not retained');
91
+ t.false(collected.result, 'Device node retained');
91
92
  await dispatch(makeMessage(rootA, 'second', [kslot(device)]));
92
93
  t.deepEqual(success, [true]);
93
94
  });
94
95
 
95
- test.serial('GC syscall.dropImports', async t => {
96
+ avaRetry(test.serial, 'GC syscall.dropImports', async t => {
96
97
  const { log, syscall } = buildSyscall();
97
- let wr;
98
+ let collected;
98
99
  function build(_vatPowers) {
99
- // eslint-disable-next-line no-unused-vars
100
- let presence1;
100
+ const holder = new Set();
101
101
  const root = Far('root', {
102
102
  one(arg) {
103
- presence1 = arg;
104
- wr = new WeakRef(arg);
103
+ holder.add(arg);
104
+ collected = watchCollected(arg);
105
105
  },
106
106
  two() {},
107
107
  three() {
108
- presence1 = undefined; // drops the import
108
+ holder.clear(); // drops the import
109
109
  },
110
110
  });
111
111
  return root;
@@ -121,19 +121,29 @@ test.serial('GC syscall.dropImports', async t => {
121
121
  // rp1 = root~.one(arg)
122
122
  await dispatch(makeMessage(rootA, 'one', [kslot(arg)]));
123
123
  await dispatch(makeBringOutYourDead());
124
- t.truthy(wr.deref());
124
+ t.false(collected.result);
125
125
 
126
126
  // an intermediate message will trigger GC, but the presence is still held
127
127
  await dispatch(makeMessage(rootA, 'two', []));
128
128
  await dispatch(makeBringOutYourDead());
129
- t.truthy(wr.deref());
129
+ t.false(collected.result);
130
130
 
131
131
  // now tell the vat to drop the 'arg' presence we gave them earlier
132
132
  await dispatch(makeMessage(rootA, 'three', []));
133
133
  await dispatch(makeBringOutYourDead());
134
134
 
135
+ const isV8 =
136
+ typeof process !== 'undefined' && 'v8' in (process.versions || {});
137
+
135
138
  // the presence itself should be gone
136
- t.falsy(wr.deref());
139
+ if (!collected.result) {
140
+ if (isV8) {
141
+ // Flake in v8/node: https://github.com/Agoric/agoric-sdk/issues/8883
142
+ t.log('skipping flake in v8');
143
+ return;
144
+ }
145
+ t.fail('import not collected');
146
+ }
137
147
 
138
148
  // first it will check that there are no VO's holding onto it
139
149
  const l2 = log.shift();
@@ -168,7 +178,7 @@ test.serial('GC syscall.dropImports', async t => {
168
178
  t.deepEqual(log, []);
169
179
  });
170
180
 
171
- test.serial('GC dispatch.retireImports', async t => {
181
+ avaRetry(test.serial, 'GC dispatch.retireImports', async t => {
172
182
  const { log, syscall } = buildSyscall();
173
183
  function build(_vatPowers) {
174
184
  // eslint-disable-next-line no-unused-vars
@@ -201,7 +211,7 @@ test.serial('GC dispatch.retireImports', async t => {
201
211
  // when we implement VOM.vrefIsRecognizable, this test might do more
202
212
  });
203
213
 
204
- test.serial('GC dispatch.retireExports', async t => {
214
+ avaRetry(test.serial, 'GC dispatch.retireExports', async t => {
205
215
  const { log, syscall } = buildSyscall();
206
216
  function build(_vatPowers) {
207
217
  const ex1 = Far('export', {});
@@ -244,14 +254,14 @@ test.serial('GC dispatch.retireExports', async t => {
244
254
  t.deepEqual(log, []);
245
255
  });
246
256
 
247
- test.serial('GC dispatch.dropExports', async t => {
257
+ avaRetry(test.serial, 'GC dispatch.dropExports', async t => {
248
258
  const { log, syscall } = buildSyscall();
249
- let wr;
259
+ let collected;
250
260
  function build(_vatPowers) {
251
261
  const root = Far('root', {
252
262
  one() {
253
263
  const ex1 = Far('export', {});
254
- wr = new WeakRef(ex1);
264
+ collected = watchCollected(ex1);
255
265
  return ex1;
256
266
  // ex1 goes out of scope, dropping last userspace strongref
257
267
  },
@@ -279,25 +289,33 @@ test.serial('GC dispatch.dropExports', async t => {
279
289
  t.deepEqual(log.shift(), {
280
290
  type: 'vatstoreSet',
281
291
  key: 'idCounters',
282
- value: '{"exportID":11,"collectionID":5,"promiseID":5}',
292
+ value: '{"exportID":11,"collectionID":4,"promiseID":5}',
283
293
  });
284
294
  t.deepEqual(log, []);
285
295
 
286
296
  // the exported Remotable should be held in place by exportedRemotables
287
297
  // until we tell the vat we don't need it any more
288
- t.truthy(wr.deref());
298
+ t.false(collected.result);
289
299
 
290
300
  // an intermediate message will trigger GC, but the presence is still held
291
301
  await dispatch(makeMessage(rootA, 'two', []));
292
302
  await dispatch(makeBringOutYourDead());
293
- t.truthy(wr.deref());
303
+ t.false(collected.result);
294
304
 
295
305
  // now tell the vat we don't need a strong reference to that export.
296
306
  await dispatch(makeDropExports(ex1));
297
307
  await dispatch(makeBringOutYourDead());
298
308
 
299
309
  // that should allow ex1 to be collected
300
- t.falsy(wr.deref());
310
+ t.true(collected.result);
311
+
312
+ // upon collection, the vat should scan for local recognizers (weak
313
+ // collection keys) in case any need to be dropped, and find none
314
+ t.deepEqual(log.shift(), {
315
+ type: 'vatstoreGetNextKey',
316
+ priorKey: `vom.ir.${ex1}|`,
317
+ result: 'vom.rc.o+d6/1',
318
+ });
301
319
 
302
320
  // and once it's collected, the vat should emit `syscall.retireExport`
303
321
  // because nobody else will be able to recognize it again
@@ -309,23 +327,24 @@ test.serial('GC dispatch.dropExports', async t => {
309
327
  t.deepEqual(log, []);
310
328
  });
311
329
 
312
- test.serial(
330
+ avaRetry(
331
+ test.serial,
313
332
  'GC dispatch.retireExports inhibits syscall.retireExports',
314
333
  async t => {
315
334
  const { log, syscall } = buildSyscall();
316
- let wr;
335
+ let collected;
317
336
  function build(_vatPowers) {
318
- let ex1;
337
+ const holder = new Set();
319
338
  const root = Far('root', {
320
339
  hold() {
321
- ex1 = Far('export', {});
322
- wr = new WeakRef(ex1);
340
+ const ex1 = Far('export', {});
341
+ holder.add(ex1);
342
+ collected = watchCollected(ex1);
323
343
  return ex1;
324
344
  },
325
345
  two() {},
326
346
  drop() {
327
- // eslint-disable-next-line no-unused-vars
328
- ex1 = undefined; // drop the last userspace strongref
347
+ holder.clear(); // drop the last userspace strongref
329
348
  },
330
349
  });
331
350
  return root;
@@ -350,25 +369,25 @@ test.serial(
350
369
  t.deepEqual(log.shift(), {
351
370
  type: 'vatstoreSet',
352
371
  key: 'idCounters',
353
- value: '{"exportID":11,"collectionID":5,"promiseID":5}',
372
+ value: '{"exportID":11,"collectionID":4,"promiseID":5}',
354
373
  });
355
374
  t.deepEqual(log, []);
356
375
 
357
376
  // the exported Remotable should be held in place by exportedRemotables
358
377
  // until we tell the vat we don't need it any more
359
- t.truthy(wr.deref());
378
+ t.false(collected.result);
360
379
 
361
380
  // an intermediate message will trigger GC, but the presence is still held
362
381
  await dispatch(makeMessage(rootA, 'two', []));
363
382
  await dispatch(makeBringOutYourDead());
364
- t.truthy(wr.deref());
383
+ t.false(collected.result);
365
384
 
366
385
  // now tell the vat we don't need a strong reference to that export.
367
386
  await dispatch(makeDropExports(ex1));
368
387
  await dispatch(makeBringOutYourDead());
369
388
 
370
389
  // that removes the liveslots strongref, but the vat's remains in place
371
- t.truthy(wr.deref());
390
+ t.false(collected.result);
372
391
 
373
392
  // now the kernel tells the vat we can't even recognize the export
374
393
  await dispatch(makeRetireExports(ex1));
@@ -376,17 +395,25 @@ test.serial(
376
395
 
377
396
  // that ought to delete the table entry, but doesn't affect the vat
378
397
  // strongref
379
- t.truthy(wr.deref());
398
+ t.false(collected.result);
380
399
 
381
400
  // now tell the vat to drop its strongref
382
401
  await dispatch(makeMessage(rootA, 'drop', []));
383
402
  await dispatch(makeBringOutYourDead());
384
403
 
385
404
  // which should let the export be collected
386
- t.falsy(wr.deref());
405
+ t.true(collected.result);
406
+
407
+ // the vat should scan for local recognizers (weak collection
408
+ // keys) in case any need to be dropped, and find none
409
+ t.deepEqual(log.shift(), {
410
+ type: 'vatstoreGetNextKey',
411
+ priorKey: 'vom.ir.o+10|',
412
+ result: 'vom.rc.o+d6/1',
413
+ });
387
414
 
388
- // the vat should *not* emit `syscall.retireExport`, because it already
389
- // received a dispatch.retireExport
415
+ // the vat should *not* emit `syscall.retireExport`, because it
416
+ // already received a dispatch.retireExport
390
417
  t.deepEqual(log, []);
391
418
  },
392
419
  );