@agoric/swingset-liveslots 0.10.3-other-dev-1f26562.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
package/src/message.js CHANGED
@@ -1,9 +1,9 @@
1
- import { assert, Fail } from '@agoric/assert';
1
+ import { assert, Fail } from '@endo/errors';
2
2
  import { insistCapData } from './capdata.js';
3
3
 
4
4
  /**
5
5
  * @typedef {{
6
- * methargs: import('./types').SwingSetCapData, // of [method, args]
6
+ * methargs: import('./types.js').SwingSetCapData, // of [method, args]
7
7
  * result: string | undefined | null,
8
8
  * }} Message
9
9
  */
@@ -32,7 +32,7 @@ export function insistMessage(message) {
32
32
 
33
33
  /**
34
34
  * @param {unknown} vdo
35
- * @returns {asserts vdo is VatDeliveryObject}
35
+ * @returns {asserts vdo is import('./types').VatDeliveryObject}
36
36
  */
37
37
 
38
38
  export function insistVatDeliveryObject(vdo) {
@@ -114,7 +114,7 @@ export function insistVatDeliveryResult(vdr) {
114
114
 
115
115
  /**
116
116
  * @param {unknown} vso
117
- * @returns {asserts vso is VatSyscallObject}
117
+ * @returns {asserts vso is import('./types').VatSyscallObject}
118
118
  */
119
119
 
120
120
  export function insistVatSyscallObject(vso) {
@@ -194,7 +194,7 @@ export function insistVatSyscallObject(vso) {
194
194
 
195
195
  /**
196
196
  * @param {unknown} vsr
197
- * @returns {asserts vsr is VatSyscallResult}
197
+ * @returns {asserts vsr is import('./types').VatSyscallResult}
198
198
  */
199
199
 
200
200
  export function insistVatSyscallResult(vsr) {
@@ -1,5 +1,5 @@
1
1
  import { Nat } from '@endo/nat';
2
- import { assert, Fail } from '@agoric/assert';
2
+ import { assert, Fail } from '@endo/errors';
3
3
 
4
4
  // NOTE: confusing terminology: "slot" vs. "reference". All these things
5
5
  // called "slots" are references, but the word "slot" suggests something into
@@ -0,0 +1,4 @@
1
+ // Export all the types this package provides
2
+ // eslint-disable-next-line import/export
3
+ export type * from './types.js';
4
+ export type * from './vatDataTypes.js';
@@ -0,0 +1,2 @@
1
+ // Empty JS file to correspond with its .d.ts twin
2
+ export {};
package/src/types.js CHANGED
@@ -1,3 +1,6 @@
1
+ // Ensure this is a module.
2
+ export {};
3
+
1
4
  /**
2
5
  * @callback makeLiveSlots
3
6
  */
@@ -79,5 +82,8 @@
79
82
  *
80
83
  */
81
84
 
82
- // Ensure this is a module.
83
- export {};
85
+ /**
86
+ * @template V fulfilled value
87
+ * @template {any[]} [A=unknown[]] arguments
88
+ * @typedef { {onFulfilled?: (value: V, ...args: A) => void, onRejected?: (reason: unknown, ...args: A) => void} } PromiseWatcher
89
+ */
@@ -18,6 +18,7 @@ export function* enumerateKeysStartEnd(syscall, start, end, checkF) {
18
18
  dbKey = syscall.vatstoreGetNextKey(dbKey);
19
19
  }
20
20
  }
21
+ harden(enumerateKeysStartEnd);
21
22
 
22
23
  // return an iterator of all existing keys that start with 'prefix'
23
24
  // (excluding the prefix itself)
@@ -32,6 +33,7 @@ export function* enumerateKeysWithPrefix(syscall, prefix) {
32
33
  yield key;
33
34
  }
34
35
  }
36
+ harden(enumerateKeysWithPrefix);
35
37
 
36
38
  export function prefixedKeysExist(syscall, prefix) {
37
39
  const nextKey = syscall.vatstoreGetNextKey(prefix);
@@ -1,7 +1,8 @@
1
1
  /* global globalThis */
2
- /* eslint-disable no-use-before-define, jsdoc/require-returns-type */
2
+ /* eslint-disable jsdoc/require-returns-type */
3
3
 
4
- import { assert, Fail } from '@agoric/assert';
4
+ import { environmentOptionsListHas } from '@endo/env-options';
5
+ import { assert, Fail, q, b } from '@endo/errors';
5
6
  import { assertPattern, mustMatch } from '@agoric/store';
6
7
  import { defendPrototype, defendPrototypeKit } from '@endo/exo/tools.js';
7
8
  import { Far, passStyleOf } from '@endo/marshal';
@@ -14,17 +15,22 @@ import {
14
15
  checkAndUpdateFacetiousness,
15
16
  } from './facetiousness.js';
16
17
 
17
- /** @template T @typedef {import('@agoric/vat-data').DefineKindOptions<T>} DefineKindOptions */
18
+ /**
19
+ * @import {DurableKindHandle} from '@agoric/swingset-liveslots'
20
+ * @import {DefineKindOptions} from '@agoric/swingset-liveslots'
21
+ * @import {ClassContextProvider, KitContextProvider} from '@endo/exo'
22
+ * @import {ToCapData, FromCapData} from '@endo/marshal';
23
+ */
18
24
 
19
- const { hasOwn, defineProperty, getOwnPropertyNames } = Object;
25
+ const {
26
+ hasOwn,
27
+ defineProperty,
28
+ getOwnPropertyNames,
29
+ values,
30
+ entries,
31
+ fromEntries,
32
+ } = Object;
20
33
  const { ownKeys } = Reflect;
21
- const { quote: q } = assert;
22
-
23
- // import { kdebug } from './kdebug.js';
24
-
25
- // TODO Use environment-options.js currently in ses/src after factoring it out
26
- // to a new package.
27
- const env = (globalThis.process || {}).env || {};
28
34
 
29
35
  // Turn on to give each exo instance its own toStringTag value which exposes
30
36
  // the SwingSet vref.
@@ -33,9 +39,7 @@ const env = (globalThis.process || {}).env || {};
33
39
  // confidential object-creation activity, so this must not be something
34
40
  // that unprivileged vat code (including unprivileged contracts) can do
35
41
  // for themselves.
36
- const LABEL_INSTANCES = (env.DEBUG || '')
37
- .split(':')
38
- .includes('label-instances');
42
+ const LABEL_INSTANCES = environmentOptionsListHas('DEBUG', 'label-instances');
39
43
 
40
44
  // This file implements the "Virtual Objects" system, currently documented in
41
45
  // {@link https://github.com/Agoric/agoric-sdk/blob/master/packages/SwingSet/docs/virtual-objects.md})
@@ -132,40 +136,6 @@ const makeContextCache = (makeState, makeContext) => {
132
136
  return makeCache(readBacking, writeBacking, deleteBacking);
133
137
  };
134
138
 
135
- /**
136
- * @typedef {import('@endo/exo/src/exo-tools.js').ContextProvider } ContextProvider
137
- */
138
-
139
- /**
140
- * @param {*} contextCache
141
- * @param {*} getSlotForVal
142
- * @returns {ContextProvider}
143
- */
144
- const makeContextProvider = (contextCache, getSlotForVal) => {
145
- return harden(rep => contextCache.get(getSlotForVal(rep)));
146
- };
147
-
148
- const makeContextProviderKit = (contextCache, getSlotForVal, facetNames) => {
149
- /** @type { Record<string, any> } */
150
- const contextProviderKit = {};
151
- for (const [index, name] of facetNames.entries()) {
152
- contextProviderKit[name] = rep => {
153
- const vref = getSlotForVal(rep);
154
- const { baseRef, facet } = parseVatSlot(vref);
155
-
156
- // Without this check, an attacker (with access to both cohort1.facetA
157
- // and cohort2.facetB) could effectively forge access to cohort1.facetB
158
- // and cohort2.facetA. They could not forge the identity of those two
159
- // objects, but they could invoke all their equivalent methods, by using
160
- // e.g. cohort1.facetA.foo.apply(cohort2.facetB, [...args])
161
- Number(facet) === index || Fail`illegal cross-facet access`;
162
-
163
- return harden(contextCache.get(baseRef));
164
- };
165
- }
166
- return harden(contextProviderKit);
167
- };
168
-
169
139
  // The management of single Representatives (i.e. defineKind) is very similar
170
140
  // to that of a cohort of facets (i.e. defineKindMulti). In this description,
171
141
  // we use "self/facets" to refer to either 'self' or 'facets', as appropriate
@@ -260,15 +230,15 @@ const makeFacets = (
260
230
  };
261
231
 
262
232
  const insistDurableCapdata = (vrm, what, capdata, valueFor) => {
263
- capdata.slots.forEach((vref, idx) => {
233
+ for (const [idx, vref] of entries(capdata.slots)) {
264
234
  if (!vrm.isDurable(vref)) {
265
235
  if (valueFor) {
266
- Fail`value for ${what} is not durable: slot ${q(idx)} of ${capdata}`;
236
+ Fail`value for ${what} is not durable: slot ${b(idx)} of ${capdata}`;
267
237
  } else {
268
- Fail`${what} is not durable: slot ${q(idx)} of ${capdata}`;
238
+ Fail`${what} is not durable: slot ${b(idx)} of ${capdata}`;
269
239
  }
270
240
  }
271
- });
241
+ }
272
242
  };
273
243
 
274
244
  const insistSameCapData = (oldCD, newCD) => {
@@ -281,11 +251,11 @@ const insistSameCapData = (oldCD, newCD) => {
281
251
  if (oldCD.slots.length !== newCD.slots.length) {
282
252
  Fail`durable Kind stateShape mismatch (slots.length)`;
283
253
  }
284
- oldCD.slots.forEach((oldVref, idx) => {
254
+ for (const [idx, oldVref] of entries(oldCD.slots)) {
285
255
  if (newCD.slots[idx] !== oldVref) {
286
256
  Fail`durable Kind stateShape mismatch (slot[${idx}])`;
287
257
  }
288
- });
258
+ }
289
259
  };
290
260
 
291
261
  /**
@@ -302,11 +272,11 @@ const insistSameCapData = (oldCD, newCD) => {
302
272
  * @param {(slot: string) => object} requiredValForSlot
303
273
  * @param {*} registerValue Function to register a new slot+value in liveSlot's
304
274
  * various tables
305
- * @param {import('@endo/marshal').ToCapData<string>} serialize Serializer for this vat
306
- * @param {import('@endo/marshal').FromCapData<string>} unserialize Unserializer for this vat
275
+ * @param {ToCapData<string>} serialize Serializer for this vat
276
+ * @param {FromCapData<string>} unserialize Unserializer for this vat
307
277
  * @param {*} assertAcceptableSyscallCapdataSize Function to check for oversized
308
278
  * syscall params
309
- * @param {import('./types').LiveSlotsOptions} [liveSlotsOptions]
279
+ * @param {import('./types.js').LiveSlotsOptions} [liveSlotsOptions]
310
280
  * @param {{ WeakMap: typeof WeakMap, WeakSet: typeof WeakSet }} [powers]
311
281
  * Specifying the underlying WeakMap/WeakSet objects to wrap with
312
282
  * VirtualObjectAwareWeakMap/Set. By default, capture the ones currently
@@ -314,7 +284,7 @@ const insistSameCapData = (oldCD, newCD) => {
314
284
  * recursion if our returned WeakMap/WeakSet wrappers are subsequently installed
315
285
  * on globalThis.
316
286
  *
317
- * @returns {object} a new virtual object manager.
287
+ * @returns a new virtual object manager.
318
288
  *
319
289
  * The virtual object manager allows the creation of persistent objects that do
320
290
  * not need to occupy memory when they are not in use. It provides five
@@ -707,10 +677,18 @@ export const makeVirtualObjectManager = (
707
677
  durableKindDescriptor = undefined, // only for durables
708
678
  ) => {
709
679
  const {
710
- finish,
680
+ finish = undefined,
711
681
  stateShape = undefined,
682
+ receiveAmplifier = undefined,
683
+ receiveInstanceTester = undefined,
712
684
  thisfulMethods = false,
685
+ } = options;
686
+ let {
687
+ // These are "let" rather than "const" only to accommodate code
688
+ // below that tolerates an old version of the vat-data package.
689
+ // See there for more explanation.
713
690
  interfaceGuard = undefined,
691
+ interfaceGuardKit = undefined,
714
692
  } = options;
715
693
 
716
694
  const statePrototype = {}; // Not frozen yet
@@ -731,11 +709,35 @@ export const makeVirtualObjectManager = (
731
709
  switch (assessFacetiousness(behavior)) {
732
710
  case 'one': {
733
711
  assert(!multifaceted);
712
+ interfaceGuardKit === undefined ||
713
+ Fail`Use an interfaceGuard, not interfaceGuardKit, to protect class ${q(
714
+ tag,
715
+ )}`;
734
716
  proposedFacetNames = undefined;
735
717
  break;
736
718
  }
737
719
  case 'many': {
738
720
  assert(multifaceted);
721
+
722
+ if (interfaceGuard && interfaceGuardKit === undefined) {
723
+ // This if clause is for the purpose of tolerating versions
724
+ // of the vata-data package that precede
725
+ // https://github.com/Agoric/agoric-sdk/pull/8220 .
726
+ // Before that PR, the options name `interfaceGuard` would
727
+ // actually carry the InterfaceGuardKit.
728
+ //
729
+ // Tolerating the old vat-data with the new types.
730
+ // @ts-expect-error
731
+ interfaceGuardKit = interfaceGuard;
732
+ interfaceGuard = undefined;
733
+ // The rest of the code from here makes no further compromise
734
+ // for that old version of the vat-data package.
735
+ }
736
+
737
+ interfaceGuard === undefined ||
738
+ Fail`Use an interfaceGuardKit, not an interfaceGuard, to protect class kit ${q(
739
+ tag,
740
+ )}`;
739
741
  proposedFacetNames = ownKeys(behavior).sort();
740
742
  break;
741
743
  }
@@ -763,6 +765,11 @@ export const makeVirtualObjectManager = (
763
765
  Fail`A stateShape must be a copyRecord: ${q(stateShape)}`;
764
766
  assertPattern(stateShape);
765
767
 
768
+ if (!multifaceted) {
769
+ receiveAmplifier === undefined ||
770
+ Fail`Only facets of an exo class kit can be amplified, not ${q(tag)}`;
771
+ }
772
+
766
773
  let facetNames;
767
774
 
768
775
  if (isDurable) {
@@ -850,7 +857,9 @@ export const makeVirtualObjectManager = (
850
857
  return harden({
851
858
  get() {
852
859
  const baseRef = getBaseRef(this);
853
- const { valueMap, capdatas } = dataCache.get(baseRef);
860
+ const record = dataCache.get(baseRef);
861
+ assert(record !== undefined);
862
+ const { valueMap, capdatas } = record;
854
863
  if (!valueMap.has(prop)) {
855
864
  const value = harden(unserialize(capdatas[prop]));
856
865
  checkStatePropertyValue(value, prop);
@@ -867,6 +876,7 @@ export const makeVirtualObjectManager = (
867
876
  insistDurableCapdata(vrm, prop, capdata, true);
868
877
  }
869
878
  const record = dataCache.get(baseRef); // mutable
879
+ assert(record !== undefined);
870
880
  const oldSlots = record.capdatas[prop].slots;
871
881
  const newSlots = capdata.slots;
872
882
  vrm.updateReferenceCounts(oldSlots, newSlots);
@@ -890,7 +900,9 @@ export const makeVirtualObjectManager = (
890
900
  const makeState = baseRef => {
891
901
  const state = { __proto__: statePrototype };
892
902
  if (stateShape === undefined) {
893
- for (const prop of ownKeys(dataCache.get(baseRef).capdatas)) {
903
+ const record = dataCache.get(baseRef);
904
+ assert(record !== undefined);
905
+ for (const prop of ownKeys(record.capdatas)) {
894
906
  assert(typeof prop === 'string');
895
907
  checkStateProperty(prop);
896
908
  defineProperty(state, prop, makeFieldDescriptor(prop));
@@ -940,18 +952,57 @@ export const makeVirtualObjectManager = (
940
952
  // and into method-invocation time (which is not).
941
953
 
942
954
  let proto;
955
+ /** @type {ClassContextProvider | undefined} */
956
+ let contextProviderVar;
957
+ /** @type { Record<string, KitContextProvider> | undefined } */
958
+ let contextProviderKitVar;
959
+
943
960
  if (multifaceted) {
961
+ contextProviderKitVar = fromEntries(
962
+ facetNames.map((name, index) => [
963
+ name,
964
+ rep => {
965
+ const vref = getSlotForVal(rep);
966
+ if (vref === undefined) {
967
+ return undefined;
968
+ }
969
+ const { baseRef, facet } = parseVatSlot(vref);
970
+
971
+ // Without this check, an attacker (with access to both
972
+ // cohort1.facetA and cohort2.facetB)
973
+ // could effectively forge access to
974
+ // cohort1.facetB and cohort2.facetA.
975
+ // They could not forge the identity of those two
976
+ // objects, but they could invoke all their equivalent methods,
977
+ // by using e.g.
978
+ // cohort1.facetA.foo.apply(cohort2.facetB, [...args])
979
+ if (Number(facet) !== index) {
980
+ return undefined;
981
+ }
982
+
983
+ return harden(contextCache.get(baseRef));
984
+ },
985
+ ]),
986
+ );
987
+
944
988
  proto = defendPrototypeKit(
945
989
  tag,
946
- makeContextProviderKit(contextCache, getSlotForVal, facetNames),
990
+ harden(contextProviderKitVar),
947
991
  behavior,
948
992
  thisfulMethods,
949
- interfaceGuard,
993
+ interfaceGuardKit,
950
994
  );
951
995
  } else {
996
+ contextProviderVar = rep => {
997
+ const slot = getSlotForVal(rep);
998
+ if (slot === undefined) {
999
+ return undefined;
1000
+ }
1001
+ return harden(contextCache.get(slot));
1002
+ };
952
1003
  proto = defendPrototype(
953
1004
  tag,
954
- makeContextProvider(contextCache, getSlotForVal),
1005
+ harden(contextProviderVar),
955
1006
  behavior,
956
1007
  thisfulMethods,
957
1008
  interfaceGuard,
@@ -959,6 +1010,10 @@ export const makeVirtualObjectManager = (
959
1010
  }
960
1011
  harden(proto);
961
1012
 
1013
+ // All this to let typescript know that it won't vary during a closure
1014
+ const contextProvider = contextProviderVar;
1015
+ const contextProviderKit = contextProviderKitVar;
1016
+
962
1017
  // this builds new Representatives, both when creating a new instance and
963
1018
  // for reanimating an existing one when the old rep gets GCed
964
1019
 
@@ -973,10 +1028,11 @@ export const makeVirtualObjectManager = (
973
1028
  const deleteStoredVO = baseRef => {
974
1029
  let doMoreGC = false;
975
1030
  const record = dataCache.get(baseRef);
1031
+ assert(record !== undefined);
976
1032
  for (const valueCD of Object.values(record.capdatas)) {
977
- valueCD.slots.forEach(vref => {
1033
+ for (const vref of valueCD.slots) {
978
1034
  doMoreGC = vrm.removeReachableVref(vref) || doMoreGC;
979
- });
1035
+ }
980
1036
  }
981
1037
  dataCache.delete(baseRef);
982
1038
  return doMoreGC;
@@ -1015,6 +1071,7 @@ export const makeVirtualObjectManager = (
1015
1071
  if (isDurable) {
1016
1072
  insistDurableCapdata(vrm, prop, valueCD, true);
1017
1073
  }
1074
+ // eslint-disable-next-line github/array-foreach
1018
1075
  valueCD.slots.forEach(vrm.addReachableVref);
1019
1076
  capdatas[prop] = valueCD;
1020
1077
  valueMap.set(prop, value);
@@ -1030,10 +1087,63 @@ export const makeVirtualObjectManager = (
1030
1087
  val = makeRepresentative(proto, baseRef);
1031
1088
  }
1032
1089
  registerValue(baseRef, val, multifaceted);
1033
- finish?.(contextCache.get(baseRef));
1090
+ finish && finish(contextCache.get(baseRef));
1034
1091
  return val;
1035
1092
  };
1036
1093
 
1094
+ if (receiveAmplifier) {
1095
+ assert(contextProviderKit);
1096
+
1097
+ // Amplify a facet to a cohort
1098
+ const amplify = exoFacet => {
1099
+ for (const cp of values(contextProviderKit)) {
1100
+ const context = cp(exoFacet);
1101
+ if (context !== undefined) {
1102
+ return context.facets;
1103
+ }
1104
+ }
1105
+ throw Fail`Must be a facet of ${q(tag)}: ${exoFacet}`;
1106
+ };
1107
+ harden(amplify);
1108
+ receiveAmplifier(amplify);
1109
+ }
1110
+
1111
+ if (receiveInstanceTester) {
1112
+ if (multifaceted) {
1113
+ assert(contextProviderKit);
1114
+
1115
+ const isInstance = (exoFacet, facetName = undefined) => {
1116
+ if (facetName === undefined) {
1117
+ // Is exoFacet and instance of any facet of this class kit?
1118
+ return values(contextProviderKit).some(
1119
+ cp => cp(exoFacet) !== undefined,
1120
+ );
1121
+ }
1122
+ // Is this exoFacet an instance of this specific facet column
1123
+ // of this class kit?
1124
+ assert.typeof(facetName, 'string');
1125
+ const cp = contextProviderKit[facetName];
1126
+ cp !== undefined ||
1127
+ Fail`exo class kit ${q(tag)} has no facet named ${q(facetName)}`;
1128
+ return cp(exoFacet) !== undefined;
1129
+ };
1130
+ harden(isInstance);
1131
+ receiveInstanceTester(isInstance);
1132
+ } else {
1133
+ assert(contextProvider);
1134
+ // Is this exo an instance of this class?
1135
+ const isInstance = (exo, facetName = undefined) => {
1136
+ facetName === undefined ||
1137
+ Fail`facetName can only be used with an exo class kit: ${q(
1138
+ tag,
1139
+ )} has no facet ${q(facetName)}`;
1140
+ return contextProvider(exo) !== undefined;
1141
+ };
1142
+ harden(isInstance);
1143
+ receiveInstanceTester(isInstance);
1144
+ }
1145
+ }
1146
+
1037
1147
  return makeNewInstance;
1038
1148
  };
1039
1149
 
@@ -1082,6 +1192,7 @@ export const makeVirtualObjectManager = (
1082
1192
  return id;
1083
1193
  };
1084
1194
 
1195
+ /** @type {import('./vatDataTypes').VatData['defineKind']} */
1085
1196
  const defineKind = (tag, init, behavior, options) => {
1086
1197
  const kindID = `${allocateExportID()}`;
1087
1198
  saveVirtualKindDescriptor(kindID, { kindID, tag });
@@ -1097,6 +1208,7 @@ export const makeVirtualObjectManager = (
1097
1208
  );
1098
1209
  };
1099
1210
 
1211
+ /** @type {import('./vatDataTypes').VatData['defineKindMulti']} */
1100
1212
  const defineKindMulti = (tag, init, behavior, options) => {
1101
1213
  const kindID = `${allocateExportID()}`;
1102
1214
  saveVirtualKindDescriptor(kindID, { kindID, tag });
@@ -1115,7 +1227,7 @@ export const makeVirtualObjectManager = (
1115
1227
  /**
1116
1228
  *
1117
1229
  * @param {string} tag
1118
- * @returns {import('@agoric/vat-data').DurableKindHandle}
1230
+ * @returns {DurableKindHandle}
1119
1231
  */
1120
1232
  const makeKindHandle = tag => {
1121
1233
  assert(kindIDID, 'initializeKindHandleKind not called yet');
@@ -1126,9 +1238,9 @@ export const makeVirtualObjectManager = (
1126
1238
  nextInstanceIDs.set(kindID, nextInstanceID);
1127
1239
  saveDurableKindDescriptor(durableKindDescriptor);
1128
1240
  saveNextInstanceID(kindID);
1129
- /** @type {import('@agoric/vat-data').DurableKindHandle} */
1130
- // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error -- https://github.com/Agoric/agoric-sdk/issues/4620
1131
- // @ts-ignore cast
1241
+ /** @type {DurableKindHandle} */
1242
+
1243
+ // @ts-expect-error cast
1132
1244
  const kindHandle = Far('kind', {});
1133
1245
  kindHandleToID.set(kindHandle, kindID);
1134
1246
  const kindIDvref = makeBaseRef(kindIDID, kindID, true);
@@ -1136,6 +1248,7 @@ export const makeVirtualObjectManager = (
1136
1248
  return kindHandle;
1137
1249
  };
1138
1250
 
1251
+ /** @type {import('./vatDataTypes').VatData['defineDurableKind']} */
1139
1252
  const defineDurableKind = (kindHandle, init, behavior, options) => {
1140
1253
  kindHandleToID.has(kindHandle) || Fail`unknown handle ${kindHandle}`;
1141
1254
  const kindID = kindHandleToID.get(kindHandle);
@@ -1158,6 +1271,7 @@ export const makeVirtualObjectManager = (
1158
1271
  return maker;
1159
1272
  };
1160
1273
 
1274
+ /** @type {import('./vatDataTypes').VatData['defineDurableKindMulti']} */
1161
1275
  const defineDurableKindMulti = (kindHandle, init, behavior, options) => {
1162
1276
  kindHandleToID.has(kindHandle) || Fail`unknown handle ${kindHandle}`;
1163
1277
  const kindID = kindHandleToID.get(kindHandle);