@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.
- package/README.md +2 -0
- package/package.json +27 -19
- package/src/boyd-gc.js +598 -0
- package/src/cache.js +3 -2
- package/src/capdata.js +1 -2
- package/src/collectionManager.js +219 -103
- package/src/facetiousness.js +1 -1
- package/src/index.js +4 -2
- package/src/liveslots.js +131 -301
- package/src/message.js +5 -5
- package/src/parseVatSlots.js +1 -1
- package/src/types-index.d.ts +4 -0
- package/src/types-index.js +2 -0
- package/src/types.js +8 -2
- package/src/vatstore-iterators.js +2 -0
- package/src/virtualObjectManager.js +185 -71
- package/src/virtualReferences.js +135 -26
- package/src/watchedPromises.js +67 -17
- package/test/{test-baggage.js → baggage.test.js} +1 -2
- package/test/{test-cache.js → cache.test.js} +0 -1
- package/test/clear-collection.test.js +586 -0
- package/test/{test-collection-schema-refcount.js → collection-schema-refcount.test.js} +1 -2
- package/test/{test-collection-upgrade.js → collection-upgrade.test.js} +1 -3
- package/test/{test-collections.js → collections.test.js} +158 -14
- package/test/{test-dropped-collection-weakrefs.js → dropped-collection-weakrefs.test.js} +1 -2
- package/test/dropped-weakset-9939.test.js +80 -0
- package/test/dummyMeterControl.js +1 -1
- package/test/{test-durabilityChecks.js → durabilityChecks.test.js} +4 -4
- package/test/exo-utils.js +70 -0
- package/test/{test-facetiousness.js → facetiousness.test.js} +1 -2
- package/test/gc-and-finalize.js +30 -1
- package/test/gc-before-finalizer.test.js +230 -0
- package/test/gc-helpers.js +2 -3
- package/test/{test-gc-sensitivity.js → gc-sensitivity.test.js} +2 -2
- package/test/handled-promises.test.js +506 -0
- package/test/{test-initial-vrefs.js → initial-vrefs.test.js} +2 -3
- package/test/liveslots-helpers.js +12 -7
- package/test/{test-liveslots-mock-gc.js → liveslots-mock-gc.test.js} +101 -2
- package/test/{test-liveslots-real-gc.js → liveslots-real-gc.test.js} +62 -37
- package/test/{test-liveslots.js → liveslots.test.js} +14 -15
- package/test/mock-gc.js +1 -0
- package/test/storeGC/{test-lifecycle.js → lifecycle.test.js} +2 -2
- package/test/storeGC/{test-refcount-management.js → refcount-management.test.js} +1 -2
- package/test/storeGC/{test-scalar-store-kind.js → scalar-store-kind.test.js} +0 -1
- package/test/storeGC/{test-weak-key.js → weak-key.test.js} +1 -2
- package/test/strict-test-env-upgrade.test.js +94 -0
- package/test/util.js +2 -2
- package/test/vat-environment.test.js +65 -0
- package/test/vat-util.js +2 -2
- package/test/virtual-objects/{test-cease-recognition.js → cease-recognition.test.js} +2 -2
- package/test/virtual-objects/{test-cross-facet.js → cross-facet.test.js} +5 -4
- package/test/virtual-objects/{test-empty-data.js → empty-data.test.js} +1 -2
- package/test/virtual-objects/{test-facets.js → facets.test.js} +1 -2
- package/test/virtual-objects/{test-kind-changes.js → kind-changes.test.js} +2 -2
- package/test/virtual-objects/{test-reachable-vrefs.js → reachable-vrefs.test.js} +2 -2
- package/test/virtual-objects/{test-rep-tostring.js → rep-tostring.test.js} +3 -5
- package/test/virtual-objects/{test-retain-remotable.js → retain-remotable.test.js} +25 -24
- package/test/virtual-objects/set-debug-label-instances.js +1 -1
- package/test/virtual-objects/{test-state-shape.js → state-shape.test.js} +2 -2
- package/test/virtual-objects/{test-virtualObjectGC.js → virtualObjectGC.test.js} +2 -2
- package/test/virtual-objects/{test-virtualObjectManager.js → virtualObjectManager.test.js} +126 -8
- package/test/virtual-objects/{test-vo-real-gc.js → vo-real-gc.test.js} +8 -8
- package/test/virtual-objects/{test-weakcollections-vref-handling.js → weakcollections-vref-handling.test.js} +1 -2
- package/test/{test-vo-test-harness.js → vo-test-harness.test.js} +0 -1
- package/test/{test-vpid-liveslots.js → vpid-liveslots.test.js} +105 -5
- package/test/waitUntilQuiescent.js +2 -1
- package/test/weakset-dropped-remotable.test.js +50 -0
- package/tools/fakeCollectionManager.js +44 -0
- package/tools/fakeVirtualObjectManager.js +62 -0
- package/tools/fakeVirtualSupport.js +389 -0
- package/tools/prepare-strict-test-env.js +124 -0
- package/tools/prepare-test-env.js +13 -0
- package/tools/setup-vat-data.js +96 -0
- package/tools/vo-test-harness.js +143 -0
- package/CHANGELOG.md +0 -61
- package/test/kmarshal.js +0 -79
- package/test/test-handled-promises.js +0 -360
package/src/message.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { assert, Fail } from '@
|
|
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) {
|
package/src/parseVatSlots.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Nat } from '@endo/nat';
|
|
2
|
-
import { assert, Fail } from '@
|
|
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
|
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
|
-
|
|
83
|
-
|
|
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
|
|
2
|
+
/* eslint-disable jsdoc/require-returns-type */
|
|
3
3
|
|
|
4
|
-
import {
|
|
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
|
-
/**
|
|
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 {
|
|
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 = (
|
|
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
|
|
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 ${
|
|
236
|
+
Fail`value for ${what} is not durable: slot ${b(idx)} of ${capdata}`;
|
|
267
237
|
} else {
|
|
268
|
-
Fail`${what} is not durable: slot ${
|
|
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
|
|
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 {
|
|
306
|
-
* @param {
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
990
|
+
harden(contextProviderKitVar),
|
|
947
991
|
behavior,
|
|
948
992
|
thisfulMethods,
|
|
949
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 {
|
|
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 {
|
|
1130
|
-
|
|
1131
|
-
// @ts-
|
|
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);
|