@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,11 +1,13 @@
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';
8
9
  import { Nat } from '@endo/nat';
10
+ import { kindOf } from '@endo/patterns';
9
11
  import { parseVatSlot, makeBaseRef } from './parseVatSlots.js';
10
12
  import { enumerateKeysWithPrefix } from './vatstore-iterators.js';
11
13
  import { makeCache } from './cache.js';
@@ -14,17 +16,24 @@ import {
14
16
  checkAndUpdateFacetiousness,
15
17
  } from './facetiousness.js';
16
18
 
17
- /** @template T @typedef {import('@agoric/vat-data').DefineKindOptions<T>} DefineKindOptions */
19
+ /**
20
+ * @import {DurableKindHandle} from '@agoric/swingset-liveslots'
21
+ * @import {DefineKindOptions} from '@agoric/swingset-liveslots'
22
+ * @import {ClassContextProvider, KitContextProvider} from '@endo/exo'
23
+ * @import {ToCapData, FromCapData} from '@endo/marshal';
24
+ * @import {Pattern} from '@endo/patterns';
25
+ * @import {SwingSetCapData} from './types.js';
26
+ */
18
27
 
19
- const { hasOwn, defineProperty, getOwnPropertyNames } = Object;
28
+ const {
29
+ hasOwn,
30
+ defineProperty,
31
+ getOwnPropertyNames,
32
+ values,
33
+ entries,
34
+ fromEntries,
35
+ } = Object;
20
36
  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
37
 
29
38
  // Turn on to give each exo instance its own toStringTag value which exposes
30
39
  // the SwingSet vref.
@@ -33,9 +42,7 @@ const env = (globalThis.process || {}).env || {};
33
42
  // confidential object-creation activity, so this must not be something
34
43
  // that unprivileged vat code (including unprivileged contracts) can do
35
44
  // for themselves.
36
- const LABEL_INSTANCES = (env.DEBUG || '')
37
- .split(':')
38
- .includes('label-instances');
45
+ const LABEL_INSTANCES = environmentOptionsListHas('DEBUG', 'label-instances');
39
46
 
40
47
  // This file implements the "Virtual Objects" system, currently documented in
41
48
  // {@link https://github.com/Agoric/agoric-sdk/blob/master/packages/SwingSet/docs/virtual-objects.md})
@@ -132,40 +139,6 @@ const makeContextCache = (makeState, makeContext) => {
132
139
  return makeCache(readBacking, writeBacking, deleteBacking);
133
140
  };
134
141
 
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
142
  // The management of single Representatives (i.e. defineKind) is very similar
170
143
  // to that of a cohort of facets (i.e. defineKindMulti). In this description,
171
144
  // we use "self/facets" to refer to either 'self' or 'facets', as appropriate
@@ -260,32 +233,67 @@ const makeFacets = (
260
233
  };
261
234
 
262
235
  const insistDurableCapdata = (vrm, what, capdata, valueFor) => {
263
- capdata.slots.forEach((vref, idx) => {
236
+ for (const [idx, vref] of entries(capdata.slots)) {
264
237
  if (!vrm.isDurable(vref)) {
265
238
  if (valueFor) {
266
- Fail`value for ${what} is not durable: slot ${q(idx)} of ${capdata}`;
239
+ Fail`value for ${what} is not durable: slot ${b(idx)} of ${capdata}`;
267
240
  } else {
268
- Fail`${what} is not durable: slot ${q(idx)} of ${capdata}`;
241
+ Fail`${what} is not durable: slot ${b(idx)} of ${capdata}`;
269
242
  }
270
243
  }
271
- });
244
+ }
272
245
  };
273
246
 
274
- const insistSameCapData = (oldCD, newCD) => {
275
- // NOTE: this assumes both were marshalled with the same format
276
- // (e.g. smallcaps vs pre-smallcaps). To somewhat tolerate new
277
- // formats, we'd need to `serialize(unserialize(oldCD))`.
247
+ const isUndefinedPatt = patt =>
248
+ patt === undefined ||
249
+ (kindOf(patt) === 'match:kind' && patt.payload === 'undefined');
250
+
251
+ /**
252
+ * Assert that a new stateShape either matches the old, or only differs in the
253
+ * addition of new clearly-optional top-level fields as conveyed by an
254
+ * [Endo `M.or`]{@link https://endojs.github.io/endo/interfaces/_endo_patterns.PatternMatchers.html#or}
255
+ * Pattern with at least one `undefined` alternative.
256
+ *
257
+ * @param {SwingSetCapData} oldCD
258
+ * @param {SwingSetCapData} newCD
259
+ * @param {{ newShape: Record<string, Pattern>, serialize: ToCapData<string>, unserialize: FromCapData<string> }} powers
260
+ */
261
+ const insistCompatibleShapeCapData = (
262
+ oldCD,
263
+ newCD,
264
+ { newShape, serialize, unserialize },
265
+ ) => {
278
266
  if (oldCD.body !== newCD.body) {
279
- Fail`durable Kind stateShape mismatch (body)`;
267
+ // Allow introduction of any clearly-optional new field at top level.
268
+ const oldShape =
269
+ unserialize(oldCD) ||
270
+ Fail`durable Kind stateShape mismatch (no old shape)`;
271
+ passStyleOf(oldShape) === 'copyRecord' ||
272
+ Fail`durable Kind stateShape mismatch (invalid old shape)`;
273
+ assertPattern(oldShape);
274
+ for (const [name, oldPatt] of Object.entries(oldShape)) {
275
+ // Assert presence and CapData shape, but save slots for their own clause.
276
+ (Object.hasOwn(newShape, name) &&
277
+ serialize(newShape[name]).body === serialize(oldPatt).body) ||
278
+ Fail`durable Kind stateShape mismatch (body ${name})`;
279
+ }
280
+ for (const [name, newPatt] of Object.entries(newShape)) {
281
+ if (Object.hasOwn(oldShape, name)) continue;
282
+ kindOf(newPatt) === 'match:or' ||
283
+ Fail`durable Kind stateShape mismatch (body new field ${name})`;
284
+ // @ts-expect-error A "match:or" pattern has a `payload` array of Patterns
285
+ newPatt.payload.some(patt => isUndefinedPatt(patt)) ||
286
+ Fail`durable Kind stateShape mismatch (body ${name})`;
287
+ }
280
288
  }
281
289
  if (oldCD.slots.length !== newCD.slots.length) {
282
290
  Fail`durable Kind stateShape mismatch (slots.length)`;
283
291
  }
284
- oldCD.slots.forEach((oldVref, idx) => {
292
+ for (const [idx, oldVref] of entries(oldCD.slots)) {
285
293
  if (newCD.slots[idx] !== oldVref) {
286
294
  Fail`durable Kind stateShape mismatch (slot[${idx}])`;
287
295
  }
288
- });
296
+ }
289
297
  };
290
298
 
291
299
  /**
@@ -302,11 +310,11 @@ const insistSameCapData = (oldCD, newCD) => {
302
310
  * @param {(slot: string) => object} requiredValForSlot
303
311
  * @param {*} registerValue Function to register a new slot+value in liveSlot's
304
312
  * 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
313
+ * @param {ToCapData<string>} serialize Serializer for this vat
314
+ * @param {FromCapData<string>} unserialize Unserializer for this vat
307
315
  * @param {*} assertAcceptableSyscallCapdataSize Function to check for oversized
308
316
  * syscall params
309
- * @param {import('./types').LiveSlotsOptions} [liveSlotsOptions]
317
+ * @param {import('./types.js').LiveSlotsOptions} [liveSlotsOptions]
310
318
  * @param {{ WeakMap: typeof WeakMap, WeakSet: typeof WeakSet }} [powers]
311
319
  * Specifying the underlying WeakMap/WeakSet objects to wrap with
312
320
  * VirtualObjectAwareWeakMap/Set. By default, capture the ones currently
@@ -314,7 +322,7 @@ const insistSameCapData = (oldCD, newCD) => {
314
322
  * recursion if our returned WeakMap/WeakSet wrappers are subsequently installed
315
323
  * on globalThis.
316
324
  *
317
- * @returns {object} a new virtual object manager.
325
+ * @returns a new virtual object manager.
318
326
  *
319
327
  * The virtual object manager allows the creation of persistent objects that do
320
328
  * not need to occupy memory when they are not in use. It provides five
@@ -562,7 +570,7 @@ export const makeVirtualObjectManager = (
562
570
  * tag: string,
563
571
  * unfaceted?: boolean,
564
572
  * facets?: string[],
565
- * stateShapeCapData?: import('./types.js').SwingSetCapData
573
+ * stateShapeCapData?: SwingSetCapData
566
574
  * }} DurableKindDescriptor
567
575
  */
568
576
 
@@ -707,10 +715,18 @@ export const makeVirtualObjectManager = (
707
715
  durableKindDescriptor = undefined, // only for durables
708
716
  ) => {
709
717
  const {
710
- finish,
718
+ finish = undefined,
711
719
  stateShape = undefined,
720
+ receiveAmplifier = undefined,
721
+ receiveInstanceTester = undefined,
712
722
  thisfulMethods = false,
723
+ } = options;
724
+ let {
725
+ // These are "let" rather than "const" only to accommodate code
726
+ // below that tolerates an old version of the vat-data package.
727
+ // See there for more explanation.
713
728
  interfaceGuard = undefined,
729
+ interfaceGuardKit = undefined,
714
730
  } = options;
715
731
 
716
732
  const statePrototype = {}; // Not frozen yet
@@ -731,11 +747,35 @@ export const makeVirtualObjectManager = (
731
747
  switch (assessFacetiousness(behavior)) {
732
748
  case 'one': {
733
749
  assert(!multifaceted);
750
+ interfaceGuardKit === undefined ||
751
+ Fail`Use an interfaceGuard, not interfaceGuardKit, to protect class ${q(
752
+ tag,
753
+ )}`;
734
754
  proposedFacetNames = undefined;
735
755
  break;
736
756
  }
737
757
  case 'many': {
738
758
  assert(multifaceted);
759
+
760
+ if (interfaceGuard && interfaceGuardKit === undefined) {
761
+ // This if clause is for the purpose of tolerating versions
762
+ // of the vata-data package that precede
763
+ // https://github.com/Agoric/agoric-sdk/pull/8220 .
764
+ // Before that PR, the options name `interfaceGuard` would
765
+ // actually carry the InterfaceGuardKit.
766
+ //
767
+ // Tolerating the old vat-data with the new types.
768
+ // @ts-expect-error
769
+ interfaceGuardKit = interfaceGuard;
770
+ interfaceGuard = undefined;
771
+ // The rest of the code from here makes no further compromise
772
+ // for that old version of the vat-data package.
773
+ }
774
+
775
+ interfaceGuard === undefined ||
776
+ Fail`Use an interfaceGuardKit, not an interfaceGuard, to protect class kit ${q(
777
+ tag,
778
+ )}`;
739
779
  proposedFacetNames = ownKeys(behavior).sort();
740
780
  break;
741
781
  }
@@ -763,6 +803,11 @@ export const makeVirtualObjectManager = (
763
803
  Fail`A stateShape must be a copyRecord: ${q(stateShape)}`;
764
804
  assertPattern(stateShape);
765
805
 
806
+ if (!multifaceted) {
807
+ receiveAmplifier === undefined ||
808
+ Fail`Only facets of an exo class kit can be amplified, not ${q(tag)}`;
809
+ }
810
+
766
811
  let facetNames;
767
812
 
768
813
  if (isDurable) {
@@ -796,7 +841,11 @@ export const makeVirtualObjectManager = (
796
841
 
797
842
  const oldStateShapeSlots = oldShapeCD ? oldShapeCD.slots : [];
798
843
  if (oldShapeCD && !allowStateShapeChanges) {
799
- insistSameCapData(oldShapeCD, newShapeCD);
844
+ insistCompatibleShapeCapData(oldShapeCD, newShapeCD, {
845
+ newShape: /** @type {Record<string, Pattern>} */ (stateShape),
846
+ serialize,
847
+ unserialize,
848
+ });
800
849
  }
801
850
  const newStateShapeSlots = newShapeCD.slots;
802
851
  vrm.updateReferenceCounts(oldStateShapeSlots, newStateShapeSlots);
@@ -850,9 +899,13 @@ export const makeVirtualObjectManager = (
850
899
  return harden({
851
900
  get() {
852
901
  const baseRef = getBaseRef(this);
853
- const { valueMap, capdatas } = dataCache.get(baseRef);
902
+ const record = dataCache.get(baseRef);
903
+ assert(record !== undefined);
904
+ const { capdatas, valueMap } = record;
854
905
  if (!valueMap.has(prop)) {
855
- const value = harden(unserialize(capdatas[prop]));
906
+ const value = hasOwn(capdatas, prop)
907
+ ? harden(unserialize(capdatas[prop]))
908
+ : undefined;
856
909
  checkStatePropertyValue(value, prop);
857
910
  valueMap.set(prop, value);
858
911
  }
@@ -867,12 +920,20 @@ export const makeVirtualObjectManager = (
867
920
  insistDurableCapdata(vrm, prop, capdata, true);
868
921
  }
869
922
  const record = dataCache.get(baseRef); // mutable
870
- const oldSlots = record.capdatas[prop].slots;
923
+ assert(record !== undefined);
924
+ const { capdatas, valueMap } = record;
925
+ const oldSlots = hasOwn(capdatas, prop) ? capdatas[prop].slots : [];
871
926
  const newSlots = capdata.slots;
872
927
  vrm.updateReferenceCounts(oldSlots, newSlots);
873
- record.capdatas[prop] = capdata; // modify in place ..
874
- record.valueMap.set(prop, value);
875
- dataCache.set(baseRef, record); // .. but mark as dirty
928
+ // modify in place, but mark as dirty
929
+ defineProperty(capdatas, prop, {
930
+ value: capdata,
931
+ writable: true,
932
+ enumerable: true,
933
+ configurable: false,
934
+ });
935
+ valueMap.set(prop, value);
936
+ dataCache.set(baseRef, record);
876
937
  },
877
938
  enumerable: true,
878
939
  configurable: false,
@@ -890,7 +951,9 @@ export const makeVirtualObjectManager = (
890
951
  const makeState = baseRef => {
891
952
  const state = { __proto__: statePrototype };
892
953
  if (stateShape === undefined) {
893
- for (const prop of ownKeys(dataCache.get(baseRef).capdatas)) {
954
+ const record = dataCache.get(baseRef);
955
+ assert(record !== undefined);
956
+ for (const prop of ownKeys(record.capdatas)) {
894
957
  assert(typeof prop === 'string');
895
958
  checkStateProperty(prop);
896
959
  defineProperty(state, prop, makeFieldDescriptor(prop));
@@ -940,18 +1003,57 @@ export const makeVirtualObjectManager = (
940
1003
  // and into method-invocation time (which is not).
941
1004
 
942
1005
  let proto;
1006
+ /** @type {ClassContextProvider | undefined} */
1007
+ let contextProviderVar;
1008
+ /** @type { Record<string, KitContextProvider> | undefined } */
1009
+ let contextProviderKitVar;
1010
+
943
1011
  if (multifaceted) {
1012
+ contextProviderKitVar = fromEntries(
1013
+ facetNames.map((name, index) => [
1014
+ name,
1015
+ rep => {
1016
+ const vref = getSlotForVal(rep);
1017
+ if (vref === undefined) {
1018
+ return undefined;
1019
+ }
1020
+ const { baseRef, facet } = parseVatSlot(vref);
1021
+
1022
+ // Without this check, an attacker (with access to both
1023
+ // cohort1.facetA and cohort2.facetB)
1024
+ // could effectively forge access to
1025
+ // cohort1.facetB and cohort2.facetA.
1026
+ // They could not forge the identity of those two
1027
+ // objects, but they could invoke all their equivalent methods,
1028
+ // by using e.g.
1029
+ // cohort1.facetA.foo.apply(cohort2.facetB, [...args])
1030
+ if (Number(facet) !== index) {
1031
+ return undefined;
1032
+ }
1033
+
1034
+ return harden(contextCache.get(baseRef));
1035
+ },
1036
+ ]),
1037
+ );
1038
+
944
1039
  proto = defendPrototypeKit(
945
1040
  tag,
946
- makeContextProviderKit(contextCache, getSlotForVal, facetNames),
1041
+ harden(contextProviderKitVar),
947
1042
  behavior,
948
1043
  thisfulMethods,
949
- interfaceGuard,
1044
+ interfaceGuardKit,
950
1045
  );
951
1046
  } else {
1047
+ contextProviderVar = rep => {
1048
+ const slot = getSlotForVal(rep);
1049
+ if (slot === undefined) {
1050
+ return undefined;
1051
+ }
1052
+ return harden(contextCache.get(slot));
1053
+ };
952
1054
  proto = defendPrototype(
953
1055
  tag,
954
- makeContextProvider(contextCache, getSlotForVal),
1056
+ harden(contextProviderVar),
955
1057
  behavior,
956
1058
  thisfulMethods,
957
1059
  interfaceGuard,
@@ -959,6 +1061,10 @@ export const makeVirtualObjectManager = (
959
1061
  }
960
1062
  harden(proto);
961
1063
 
1064
+ // All this to let typescript know that it won't vary during a closure
1065
+ const contextProvider = contextProviderVar;
1066
+ const contextProviderKit = contextProviderKitVar;
1067
+
962
1068
  // this builds new Representatives, both when creating a new instance and
963
1069
  // for reanimating an existing one when the old rep gets GCed
964
1070
 
@@ -973,10 +1079,11 @@ export const makeVirtualObjectManager = (
973
1079
  const deleteStoredVO = baseRef => {
974
1080
  let doMoreGC = false;
975
1081
  const record = dataCache.get(baseRef);
1082
+ assert(record !== undefined);
976
1083
  for (const valueCD of Object.values(record.capdatas)) {
977
- valueCD.slots.forEach(vref => {
1084
+ for (const vref of valueCD.slots) {
978
1085
  doMoreGC = vrm.removeReachableVref(vref) || doMoreGC;
979
- });
1086
+ }
980
1087
  }
981
1088
  dataCache.delete(baseRef);
982
1089
  return doMoreGC;
@@ -1015,8 +1122,14 @@ export const makeVirtualObjectManager = (
1015
1122
  if (isDurable) {
1016
1123
  insistDurableCapdata(vrm, prop, valueCD, true);
1017
1124
  }
1125
+ // eslint-disable-next-line github/array-foreach
1018
1126
  valueCD.slots.forEach(vrm.addReachableVref);
1019
- capdatas[prop] = valueCD;
1127
+ defineProperty(capdatas, prop, {
1128
+ value: valueCD,
1129
+ writable: true,
1130
+ enumerable: true,
1131
+ configurable: false,
1132
+ });
1020
1133
  valueMap.set(prop, value);
1021
1134
  }
1022
1135
  // dataCache contents remain mutable: state setter modifies in-place
@@ -1030,10 +1143,63 @@ export const makeVirtualObjectManager = (
1030
1143
  val = makeRepresentative(proto, baseRef);
1031
1144
  }
1032
1145
  registerValue(baseRef, val, multifaceted);
1033
- finish?.(contextCache.get(baseRef));
1146
+ finish && finish(contextCache.get(baseRef));
1034
1147
  return val;
1035
1148
  };
1036
1149
 
1150
+ if (receiveAmplifier) {
1151
+ assert(contextProviderKit);
1152
+
1153
+ // Amplify a facet to a cohort
1154
+ const amplify = exoFacet => {
1155
+ for (const cp of values(contextProviderKit)) {
1156
+ const context = cp(exoFacet);
1157
+ if (context !== undefined) {
1158
+ return context.facets;
1159
+ }
1160
+ }
1161
+ throw Fail`Must be a facet of ${q(tag)}: ${exoFacet}`;
1162
+ };
1163
+ harden(amplify);
1164
+ receiveAmplifier(amplify);
1165
+ }
1166
+
1167
+ if (receiveInstanceTester) {
1168
+ if (multifaceted) {
1169
+ assert(contextProviderKit);
1170
+
1171
+ const isInstance = (exoFacet, facetName = undefined) => {
1172
+ if (facetName === undefined) {
1173
+ // Is exoFacet and instance of any facet of this class kit?
1174
+ return values(contextProviderKit).some(
1175
+ cp => cp(exoFacet) !== undefined,
1176
+ );
1177
+ }
1178
+ // Is this exoFacet an instance of this specific facet column
1179
+ // of this class kit?
1180
+ assert.typeof(facetName, 'string');
1181
+ const cp = contextProviderKit[facetName];
1182
+ cp !== undefined ||
1183
+ Fail`exo class kit ${q(tag)} has no facet named ${q(facetName)}`;
1184
+ return cp(exoFacet) !== undefined;
1185
+ };
1186
+ harden(isInstance);
1187
+ receiveInstanceTester(isInstance);
1188
+ } else {
1189
+ assert(contextProvider);
1190
+ // Is this exo an instance of this class?
1191
+ const isInstance = (exo, facetName = undefined) => {
1192
+ facetName === undefined ||
1193
+ Fail`facetName can only be used with an exo class kit: ${q(
1194
+ tag,
1195
+ )} has no facet ${q(facetName)}`;
1196
+ return contextProvider(exo) !== undefined;
1197
+ };
1198
+ harden(isInstance);
1199
+ receiveInstanceTester(isInstance);
1200
+ }
1201
+ }
1202
+
1037
1203
  return makeNewInstance;
1038
1204
  };
1039
1205
 
@@ -1082,6 +1248,7 @@ export const makeVirtualObjectManager = (
1082
1248
  return id;
1083
1249
  };
1084
1250
 
1251
+ /** @type {import('./vatDataTypes').VatData['defineKind']} */
1085
1252
  const defineKind = (tag, init, behavior, options) => {
1086
1253
  const kindID = `${allocateExportID()}`;
1087
1254
  saveVirtualKindDescriptor(kindID, { kindID, tag });
@@ -1097,6 +1264,7 @@ export const makeVirtualObjectManager = (
1097
1264
  );
1098
1265
  };
1099
1266
 
1267
+ /** @type {import('./vatDataTypes').VatData['defineKindMulti']} */
1100
1268
  const defineKindMulti = (tag, init, behavior, options) => {
1101
1269
  const kindID = `${allocateExportID()}`;
1102
1270
  saveVirtualKindDescriptor(kindID, { kindID, tag });
@@ -1115,7 +1283,7 @@ export const makeVirtualObjectManager = (
1115
1283
  /**
1116
1284
  *
1117
1285
  * @param {string} tag
1118
- * @returns {import('@agoric/vat-data').DurableKindHandle}
1286
+ * @returns {DurableKindHandle}
1119
1287
  */
1120
1288
  const makeKindHandle = tag => {
1121
1289
  assert(kindIDID, 'initializeKindHandleKind not called yet');
@@ -1126,9 +1294,9 @@ export const makeVirtualObjectManager = (
1126
1294
  nextInstanceIDs.set(kindID, nextInstanceID);
1127
1295
  saveDurableKindDescriptor(durableKindDescriptor);
1128
1296
  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
1297
+ /** @type {DurableKindHandle} */
1298
+
1299
+ // @ts-expect-error cast
1132
1300
  const kindHandle = Far('kind', {});
1133
1301
  kindHandleToID.set(kindHandle, kindID);
1134
1302
  const kindIDvref = makeBaseRef(kindIDID, kindID, true);
@@ -1136,6 +1304,7 @@ export const makeVirtualObjectManager = (
1136
1304
  return kindHandle;
1137
1305
  };
1138
1306
 
1307
+ /** @type {import('./vatDataTypes').VatData['defineDurableKind']} */
1139
1308
  const defineDurableKind = (kindHandle, init, behavior, options) => {
1140
1309
  kindHandleToID.has(kindHandle) || Fail`unknown handle ${kindHandle}`;
1141
1310
  const kindID = kindHandleToID.get(kindHandle);
@@ -1158,6 +1327,7 @@ export const makeVirtualObjectManager = (
1158
1327
  return maker;
1159
1328
  };
1160
1329
 
1330
+ /** @type {import('./vatDataTypes').VatData['defineDurableKindMulti']} */
1161
1331
  const defineDurableKindMulti = (kindHandle, init, behavior, options) => {
1162
1332
  kindHandleToID.has(kindHandle) || Fail`unknown handle ${kindHandle}`;
1163
1333
  const kindID = kindHandleToID.get(kindHandle);
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @param {*} syscall Vat's syscall object, used to access the vatstore operations.
3
+ * @param {(val: object) => string | undefined} getSlotForVal A function that returns the
4
+ * object ID (vref) for a given object, if any. their corresponding export
5
+ * IDs
6
+ * @param {(slot: string) => object} requiredValForSlot A function that
7
+ * converts an object ID (vref) to an object.
8
+ * @param {*} FinalizationRegistry Powerful JavaScript intrinsic normally denied
9
+ * by SES
10
+ * @param {*} WeakRef Powerful JavaScript intrinsic normally denied
11
+ * by SES
12
+ * @param {*} addToPossiblyDeadSet Function to record objects whose deaths
13
+ * should be reinvestigated
14
+ * @param {*} addToPossiblyRetiredSet Function to record dead objects whose
15
+ * retirement should be reinvestigated
16
+ * @param {boolean} relaxDurabilityRules True IFF the associated swingset is
17
+ * running with relaxed durability rules
18
+ */
19
+ export function makeVirtualReferenceManager(syscall: any, getSlotForVal: (val: object) => string | undefined, requiredValForSlot: (slot: string) => object, FinalizationRegistry: any, WeakRef: any, addToPossiblyDeadSet: any, addToPossiblyRetiredSet: any, relaxDurabilityRules: boolean): {
20
+ registerDroppedCollection: (target: any, descriptor: any) => void;
21
+ isDurable: (vref: string) => boolean;
22
+ isDurableKind: (kindID: string) => boolean;
23
+ registerKind: (kindID: string, reanimator?: (string: any, boolean: any) => object, deleter?: (string: any) => boolean, durable?: boolean) => void;
24
+ rememberFacetNames: (kindID: string, facetNames: string[] | null) => void;
25
+ getFacet: (kindID: any, facets: any, facetIndex: any) => any;
26
+ getFacetNames: (kindID: any) => any;
27
+ reanimate: (baseRef: string) => any;
28
+ addReachableVref: (vref: any) => void;
29
+ removeReachableVref: (vref: any) => boolean;
30
+ updateReferenceCounts: (beforeSlots: any, afterSlots: any) => void;
31
+ getReachablePromiseRefCount: (p: any) => number;
32
+ addRecognizableValue: (value: any, recognizer: string | (Map<string, any> | Set<string>), recognizerIsVirtual?: boolean) => void;
33
+ removeRecognizableVref: (vref: string, recognizer: string | (Map<string, any> | Set<string>), recognizerIsVirtual?: boolean) => void;
34
+ removeRecognizableValue: (value: any, recognizer: string | (Map<string, any> | Set<string>), recognizerIsVirtual?: boolean) => void;
35
+ vrefKey: (value: any) => string | undefined;
36
+ isPresenceReachable: (vref: string) => boolean;
37
+ isVrefRecognizable: (vref: any) => boolean;
38
+ setExportStatus: (vref: any, exportStatus: any) => void;
39
+ isVirtualObjectReachable: (baseRef: string) => boolean;
40
+ deleteVirtualObject: (baseRef: string) => [boolean, string[]];
41
+ ceaseRecognition: (vref: string) => boolean;
42
+ setDeleteCollectionEntry: (fn: any) => void;
43
+ getRetentionStats: () => {
44
+ remotableRefCounts: number;
45
+ vrefRecognizers: number;
46
+ kindInfoTable: number;
47
+ };
48
+ initializeIDCounters: () => void;
49
+ allocateNextID: (name: any) => number;
50
+ flushIDCounters: () => void;
51
+ testHooks: {
52
+ getReachableRefCount: (vref: any) => number | undefined;
53
+ countCollectionsForWeakKey: (vref: any) => number;
54
+ getDroppedCollectionRegistry: () => any;
55
+ remotableRefCounts: Map<any, number>;
56
+ vrefRecognizers: Map<string, Set<Map<string, any> | Set<string>>>;
57
+ kindInfoTable: Map<any, any>;
58
+ };
59
+ };
60
+ export type VirtualReferenceManager = ReturnType<typeof makeVirtualReferenceManager>;
61
+ //# sourceMappingURL=virtualReferences.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virtualReferences.d.ts","sourceRoot":"","sources":["virtualReferences.js"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;GAiBG;AACH,qDAjBW,GAAC,iBACD,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,sBAGnC,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,wBAExB,GAAC,WAED,GAAC,wBAED,GAAC,2BAED,GAAC,wBAED,OAAO;;sBAqQL,MAAM,KAEJ,OAAO;4BAfT,MAAM,KAEJ,OAAO;2BA9CT,MAAM,eACN,CAAC,MAAM,KAAA,EAAE,OAAO,KAAA,KAAK,MAAM,YAC3B,CAAC,MAAM,KAAA,KAAK,OAAO,YACnB,OAAO;iCASP,MAAM,cACN,MAAM,EAAE,GAAC,IAAI;;;yBA8Eb,MAAM;;;;;kCAmPN,GAAC,cACD,MAAM,mCAAW,wBACjB,OAAO;mCA2BP,MAAM,cACN,MAAM,mCAAW,wBACjB,OAAO;qCAgCP,GAAC,cACD,MAAM,mCAAW,wBACjB,OAAO;;gCAvIP,MAAM,KAEJ,OAAO;;;wCAzaT,MAAM,KAEJ,OAAO;mCAoBT,MAAM,KAEJ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;6BAwiBrB,MAAM,KAEJ,OAAO;;;;;;;;;;;;;;;;;;EA+LrB;sCACa,UAAU,CAAC,OAAO,2BAA2B,CAAC"}