@agoric/swingset-liveslots 0.10.3-dev-5dc325b.0 → 0.10.3-getting-started-dev-d127d1d.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/CHANGELOG.md +77 -0
- package/package.json +17 -23
- package/src/collectionManager.js +66 -135
- package/src/index.js +1 -2
- package/src/liveslots.js +74 -47
- package/src/virtualObjectManager.js +15 -61
- package/src/virtualReferences.js +0 -51
- package/src/watchedPromises.js +0 -6
- package/test/gc-helpers.js +1 -1
- package/test/kmarshal.js +79 -0
- package/test/liveslots-helpers.js +6 -6
- package/test/mock-gc.js +0 -1
- package/test/storeGC/test-lifecycle.js +2 -2
- package/test/storeGC/test-refcount-management.js +2 -1
- package/test/storeGC/test-scalar-store-kind.js +1 -0
- package/test/storeGC/test-weak-key.js +2 -1
- package/test/test-baggage.js +2 -1
- package/test/test-cache.js +1 -0
- package/test/test-collection-schema-refcount.js +2 -1
- package/test/test-collection-upgrade.js +3 -1
- package/test/test-collections.js +14 -117
- package/test/test-dropped-collection-weakrefs.js +2 -1
- package/test/test-durabilityChecks.js +3 -3
- package/test/test-facetiousness.js +2 -1
- package/test/test-gc-sensitivity.js +2 -2
- package/test/test-handled-promises.js +7 -5
- package/test/test-initial-vrefs.js +3 -2
- package/test/test-liveslots-mock-gc.js +2 -2
- package/test/test-liveslots-real-gc.js +2 -2
- package/test/test-liveslots.js +14 -13
- package/test/test-vo-test-harness.js +1 -0
- package/test/test-vpid-liveslots.js +4 -3
- package/test/util.js +2 -2
- package/test/vat-util.js +1 -1
- package/test/virtual-objects/test-cease-recognition.js +2 -2
- package/test/virtual-objects/test-cross-facet.js +2 -1
- package/test/virtual-objects/test-empty-data.js +2 -1
- package/test/virtual-objects/test-facets.js +2 -1
- package/test/virtual-objects/test-kind-changes.js +2 -2
- package/test/virtual-objects/test-reachable-vrefs.js +2 -2
- package/test/virtual-objects/test-rep-tostring.js +3 -2
- package/test/virtual-objects/test-retain-remotable.js +1 -1
- package/test/virtual-objects/test-state-shape.js +2 -2
- package/test/virtual-objects/test-virtualObjectGC.js +2 -2
- package/test/virtual-objects/test-virtualObjectManager.js +2 -6
- package/test/virtual-objects/test-vo-real-gc.js +2 -2
- package/test/virtual-objects/test-weakcollections-vref-handling.js +2 -1
- package/tools/fakeVirtualSupport.js +19 -37
- package/src/vatDataTypes.d.ts +0 -234
- package/src/vatDataTypes.js +0 -2
- package/tools/prepare-test-env.js +0 -13
- package/tools/setup-vat-data.js +0 -61
package/src/liveslots.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint @typescript-eslint/no-floating-promises: "warn" */
|
|
2
1
|
import {
|
|
3
2
|
Remotable,
|
|
4
3
|
passStyleOf,
|
|
@@ -252,10 +251,13 @@ function build(
|
|
|
252
251
|
const importsToRetire = new Set();
|
|
253
252
|
const exportsToRetire = new Set();
|
|
254
253
|
let doMore;
|
|
255
|
-
await null;
|
|
256
254
|
do {
|
|
257
255
|
doMore = false;
|
|
258
256
|
|
|
257
|
+
// Yes, we know this is an await inside a loop. Too bad. (Also, it's a
|
|
258
|
+
// `do {} while` loop, which means there's no conditional bypass of the
|
|
259
|
+
// await.)
|
|
260
|
+
// eslint-disable-next-line no-await-in-loop, @jessie.js/no-nested-await
|
|
259
261
|
await gcTools.gcAndFinalize();
|
|
260
262
|
|
|
261
263
|
// possiblyDeadSet contains a baseref for everything (Presences,
|
|
@@ -498,6 +500,54 @@ function build(
|
|
|
498
500
|
return Remotable(iface);
|
|
499
501
|
}
|
|
500
502
|
|
|
503
|
+
/**
|
|
504
|
+
* Counters to track the next number for various categories of allocation.
|
|
505
|
+
* `exportID` starts at 1 because 'o+0' is always automatically
|
|
506
|
+
* pre-assigned to the root object.
|
|
507
|
+
* `promiseID` starts at 5 as a very minor aid to debugging: when puzzling
|
|
508
|
+
* over trace logs and the like, it helps for the numbers in various species
|
|
509
|
+
* of IDs that are jumbled together to be a little out of sync and thus a
|
|
510
|
+
* little less similar to each other.
|
|
511
|
+
*/
|
|
512
|
+
const initialIDCounters = { exportID: 1, collectionID: 1, promiseID: 5 };
|
|
513
|
+
/** @type {Record<string, number>} */
|
|
514
|
+
let idCounters;
|
|
515
|
+
let idCountersAreDirty = false;
|
|
516
|
+
|
|
517
|
+
function initializeIDCounters() {
|
|
518
|
+
if (!idCounters) {
|
|
519
|
+
// the saved value might be missing, or from an older liveslots
|
|
520
|
+
// (with fewer counters), so merge it with our initial values
|
|
521
|
+
const saved = JSON.parse(syscall.vatstoreGet('idCounters') || '{}');
|
|
522
|
+
idCounters = { ...initialIDCounters, ...saved };
|
|
523
|
+
idCountersAreDirty = true;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function allocateNextID(name) {
|
|
528
|
+
if (!idCounters) {
|
|
529
|
+
// Normally `initializeIDCounters` would be called from startVat, but some
|
|
530
|
+
// tests bypass that so this is a backstop. Note that the invocation from
|
|
531
|
+
// startVat is there to make vatStore access patterns a bit more
|
|
532
|
+
// consistent from one vat to another, principally as a confusion
|
|
533
|
+
// reduction measure in service of debugging; it is not a correctness
|
|
534
|
+
// issue.
|
|
535
|
+
initializeIDCounters();
|
|
536
|
+
}
|
|
537
|
+
const result = idCounters[name];
|
|
538
|
+
result !== undefined || Fail`unknown idCounters[${name}]`;
|
|
539
|
+
idCounters[name] += 1;
|
|
540
|
+
idCountersAreDirty = true;
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function flushIDCounters() {
|
|
545
|
+
if (idCountersAreDirty) {
|
|
546
|
+
syscall.vatstoreSet('idCounters', JSON.stringify(idCounters));
|
|
547
|
+
idCountersAreDirty = false;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
501
551
|
// TODO: fix awkward non-orthogonality: allocateExportID() returns a number,
|
|
502
552
|
// allocatePromiseID() returns a slot, registerPromise() uses the slot from
|
|
503
553
|
// allocatePromiseID(), exportPassByPresence() generates a slot itself using
|
|
@@ -506,18 +556,15 @@ function build(
|
|
|
506
556
|
// use a slot from the corresponding allocateX
|
|
507
557
|
|
|
508
558
|
function allocateExportID() {
|
|
509
|
-
|
|
510
|
-
return vrm.allocateNextID('exportID');
|
|
559
|
+
return allocateNextID('exportID');
|
|
511
560
|
}
|
|
512
561
|
|
|
513
562
|
function allocateCollectionID() {
|
|
514
|
-
|
|
515
|
-
return vrm.allocateNextID('collectionID');
|
|
563
|
+
return allocateNextID('collectionID');
|
|
516
564
|
}
|
|
517
565
|
|
|
518
566
|
function allocatePromiseID() {
|
|
519
|
-
|
|
520
|
-
const promiseID = vrm.allocateNextID('promiseID');
|
|
567
|
+
const promiseID = allocateNextID('promiseID');
|
|
521
568
|
return makeVatSlot('promise', true, promiseID);
|
|
522
569
|
}
|
|
523
570
|
|
|
@@ -712,9 +759,9 @@ function build(
|
|
|
712
759
|
Fail`registerValue(${baseRef} should not receive individual facets`;
|
|
713
760
|
slotToVal.set(baseRef, new WeakRef(val));
|
|
714
761
|
if (valIsCohort) {
|
|
715
|
-
|
|
762
|
+
vrm.getFacetNames(id).forEach((name, index) => {
|
|
716
763
|
valToSlot.set(val[name], `${baseRef}:${index}`);
|
|
717
|
-
}
|
|
764
|
+
});
|
|
718
765
|
} else {
|
|
719
766
|
valToSlot.set(val, baseRef);
|
|
720
767
|
}
|
|
@@ -936,20 +983,12 @@ function build(
|
|
|
936
983
|
return null;
|
|
937
984
|
}
|
|
938
985
|
syscall.resolve(resolutions);
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
for (const resolution of resolutions) {
|
|
946
|
-
const [xvpid] = resolution;
|
|
947
|
-
maybeNewVPIDs.delete(xvpid);
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
for (const newVPID of Array.from(maybeNewVPIDs).sort()) {
|
|
951
|
-
maybeExportPromise(newVPID);
|
|
986
|
+
resolutions.forEach(([_xvpid, _isReject, resolutionCD]) => {
|
|
987
|
+
resolutionCD.slots.forEach(vref => maybeNewVPIDs.add(vref));
|
|
988
|
+
});
|
|
989
|
+
resolutions.forEach(([xvpid]) => maybeNewVPIDs.delete(xvpid));
|
|
952
990
|
}
|
|
991
|
+
Array.from(maybeNewVPIDs).sort().forEach(maybeExportPromise);
|
|
953
992
|
|
|
954
993
|
// ideally we'd wait until .then is called on p before subscribing, but
|
|
955
994
|
// the current Promise API doesn't give us a way to discover this, so we
|
|
@@ -1151,21 +1190,13 @@ function build(
|
|
|
1151
1190
|
|
|
1152
1191
|
const maybeNewVPIDs = new Set();
|
|
1153
1192
|
// if we mention a vpid, we might need to track it
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
maybeNewVPIDs.add(vref);
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1193
|
+
resolutions.forEach(([_xvpid, _isReject, resolutionCD]) => {
|
|
1194
|
+
resolutionCD.slots.forEach(vref => maybeNewVPIDs.add(vref));
|
|
1195
|
+
});
|
|
1160
1196
|
// but not if we just resolved it (including the primary)
|
|
1161
|
-
|
|
1162
|
-
const [xvpid] = resolution;
|
|
1163
|
-
maybeNewVPIDs.delete(xvpid);
|
|
1164
|
-
}
|
|
1197
|
+
resolutions.forEach(([xvpid]) => maybeNewVPIDs.delete(xvpid));
|
|
1165
1198
|
// track everything that's left
|
|
1166
|
-
|
|
1167
|
-
maybeExportPromise(newVPID);
|
|
1168
|
-
}
|
|
1199
|
+
Array.from(maybeNewVPIDs).sort().forEach(maybeExportPromise);
|
|
1169
1200
|
|
|
1170
1201
|
// only the primary can possibly be newly resolved
|
|
1171
1202
|
unregisterUnreferencedVPID(vpid);
|
|
@@ -1223,11 +1254,11 @@ function build(
|
|
|
1223
1254
|
// 'imports' is an exclusively-owned Set that holds all new
|
|
1224
1255
|
// promise vpids, both resolved and unresolved
|
|
1225
1256
|
const imports = finishCollectingPromiseImports();
|
|
1226
|
-
|
|
1257
|
+
retiredVPIDs.forEach(vpid => {
|
|
1227
1258
|
unregisterUnreferencedVPID(vpid); // unregisters if not in vdata
|
|
1228
1259
|
importedVPIDs.delete(vpid);
|
|
1229
1260
|
imports.delete(vpid); // resolved, so don't subscribe()
|
|
1230
|
-
}
|
|
1261
|
+
});
|
|
1231
1262
|
for (const vpid of Array.from(imports).sort()) {
|
|
1232
1263
|
syscall.subscribe(vpid);
|
|
1233
1264
|
}
|
|
@@ -1267,18 +1298,14 @@ function build(
|
|
|
1267
1298
|
|
|
1268
1299
|
function retireExports(vrefs) {
|
|
1269
1300
|
assert(Array.isArray(vrefs));
|
|
1270
|
-
|
|
1271
|
-
retireOneExport(vref);
|
|
1272
|
-
}
|
|
1301
|
+
vrefs.forEach(retireOneExport);
|
|
1273
1302
|
}
|
|
1274
1303
|
|
|
1275
1304
|
function retireImports(vrefs) {
|
|
1276
1305
|
assert(Array.isArray(vrefs));
|
|
1277
1306
|
vrefs.map(vref => insistVatType('object', vref));
|
|
1278
1307
|
vrefs.map(vref => assert(!parseVatSlot(vref).allocatedByVat));
|
|
1279
|
-
|
|
1280
|
-
vrm.ceaseRecognition(vref);
|
|
1281
|
-
}
|
|
1308
|
+
vrefs.forEach(vrm.ceaseRecognition);
|
|
1282
1309
|
}
|
|
1283
1310
|
|
|
1284
1311
|
// TODO: when we add notifyForward, guard against cycles
|
|
@@ -1423,7 +1450,7 @@ function build(
|
|
|
1423
1450
|
}
|
|
1424
1451
|
harden(vpow);
|
|
1425
1452
|
|
|
1426
|
-
|
|
1453
|
+
initializeIDCounters();
|
|
1427
1454
|
vom.initializeKindHandleKind();
|
|
1428
1455
|
collectionManager.initializeStoreKindInfo();
|
|
1429
1456
|
|
|
@@ -1546,7 +1573,7 @@ function build(
|
|
|
1546
1573
|
* dispatch has completed and user code has relinquished agency.
|
|
1547
1574
|
*/
|
|
1548
1575
|
function afterDispatchActions() {
|
|
1549
|
-
|
|
1576
|
+
flushIDCounters();
|
|
1550
1577
|
collectionManager.flushSchemaCache();
|
|
1551
1578
|
vom.flushStateCache();
|
|
1552
1579
|
}
|
|
@@ -1602,7 +1629,7 @@ function build(
|
|
|
1602
1629
|
// *not* directly wait for the userspace function to complete, nor for
|
|
1603
1630
|
// any promise it returns to fire.
|
|
1604
1631
|
const p = Promise.resolve(delivery).then(unmeteredDispatch);
|
|
1605
|
-
|
|
1632
|
+
p.finally(() => (complete = true));
|
|
1606
1633
|
|
|
1607
1634
|
// Instead, we wait for userspace to become idle by draining the
|
|
1608
1635
|
// promise queue. We clean up and then examine/return 'p' so any
|
|
@@ -16,19 +16,10 @@ import {
|
|
|
16
16
|
|
|
17
17
|
/** @template T @typedef {import('@agoric/vat-data').DefineKindOptions<T>} DefineKindOptions */
|
|
18
18
|
|
|
19
|
-
const { hasOwn, defineProperty, getOwnPropertyNames
|
|
19
|
+
const { hasOwn, defineProperty, getOwnPropertyNames } = Object;
|
|
20
20
|
const { ownKeys } = Reflect;
|
|
21
21
|
const { quote: q } = assert;
|
|
22
22
|
|
|
23
|
-
// See https://github.com/Agoric/agoric-sdk/issues/8005
|
|
24
|
-
// Once agoric-sdk is upgraded to depend on endo post
|
|
25
|
-
// https://github.com/endojs/endo/pull/1606 then remove this
|
|
26
|
-
// definition of `b` and say instead
|
|
27
|
-
// ```js
|
|
28
|
-
// const { quote: q, base: b } = assert;
|
|
29
|
-
// ```
|
|
30
|
-
const b = index => q(Number(index));
|
|
31
|
-
|
|
32
23
|
// import { kdebug } from './kdebug.js';
|
|
33
24
|
|
|
34
25
|
// TODO Use environment-options.js currently in ses/src after factoring it out
|
|
@@ -150,8 +141,9 @@ const makeContextCache = (makeState, makeContext) => {
|
|
|
150
141
|
* @param {*} getSlotForVal
|
|
151
142
|
* @returns {ContextProvider}
|
|
152
143
|
*/
|
|
153
|
-
const makeContextProvider = (contextCache, getSlotForVal) =>
|
|
154
|
-
harden(rep => contextCache.get(getSlotForVal(rep)));
|
|
144
|
+
const makeContextProvider = (contextCache, getSlotForVal) => {
|
|
145
|
+
return harden(rep => contextCache.get(getSlotForVal(rep)));
|
|
146
|
+
};
|
|
155
147
|
|
|
156
148
|
const makeContextProviderKit = (contextCache, getSlotForVal, facetNames) => {
|
|
157
149
|
/** @type { Record<string, any> } */
|
|
@@ -268,15 +260,15 @@ const makeFacets = (
|
|
|
268
260
|
};
|
|
269
261
|
|
|
270
262
|
const insistDurableCapdata = (vrm, what, capdata, valueFor) => {
|
|
271
|
-
|
|
263
|
+
capdata.slots.forEach((vref, idx) => {
|
|
272
264
|
if (!vrm.isDurable(vref)) {
|
|
273
265
|
if (valueFor) {
|
|
274
|
-
Fail`value for ${what} is not durable: slot ${
|
|
266
|
+
Fail`value for ${what} is not durable: slot ${q(idx)} of ${capdata}`;
|
|
275
267
|
} else {
|
|
276
|
-
Fail`${what} is not durable: slot ${
|
|
268
|
+
Fail`${what} is not durable: slot ${q(idx)} of ${capdata}`;
|
|
277
269
|
}
|
|
278
270
|
}
|
|
279
|
-
}
|
|
271
|
+
});
|
|
280
272
|
};
|
|
281
273
|
|
|
282
274
|
const insistSameCapData = (oldCD, newCD) => {
|
|
@@ -289,11 +281,11 @@ const insistSameCapData = (oldCD, newCD) => {
|
|
|
289
281
|
if (oldCD.slots.length !== newCD.slots.length) {
|
|
290
282
|
Fail`durable Kind stateShape mismatch (slots.length)`;
|
|
291
283
|
}
|
|
292
|
-
|
|
284
|
+
oldCD.slots.forEach((oldVref, idx) => {
|
|
293
285
|
if (newCD.slots[idx] !== oldVref) {
|
|
294
286
|
Fail`durable Kind stateShape mismatch (slot[${idx}])`;
|
|
295
287
|
}
|
|
296
|
-
}
|
|
288
|
+
});
|
|
297
289
|
};
|
|
298
290
|
|
|
299
291
|
/**
|
|
@@ -322,7 +314,7 @@ const insistSameCapData = (oldCD, newCD) => {
|
|
|
322
314
|
* recursion if our returned WeakMap/WeakSet wrappers are subsequently installed
|
|
323
315
|
* on globalThis.
|
|
324
316
|
*
|
|
325
|
-
* @returns a new virtual object manager.
|
|
317
|
+
* @returns {object} a new virtual object manager.
|
|
326
318
|
*
|
|
327
319
|
* The virtual object manager allows the creation of persistent objects that do
|
|
328
320
|
* not need to occupy memory when they are not in use. It provides five
|
|
@@ -715,16 +707,10 @@ export const makeVirtualObjectManager = (
|
|
|
715
707
|
durableKindDescriptor = undefined, // only for durables
|
|
716
708
|
) => {
|
|
717
709
|
const {
|
|
718
|
-
finish
|
|
710
|
+
finish,
|
|
719
711
|
stateShape = undefined,
|
|
720
712
|
thisfulMethods = false,
|
|
721
|
-
} = options;
|
|
722
|
-
let {
|
|
723
|
-
// These are "let" rather than "const" only to accommodate code
|
|
724
|
-
// below that tolerates an old version of the vat-data package.
|
|
725
|
-
// See there for more explanation.
|
|
726
713
|
interfaceGuard = undefined,
|
|
727
|
-
interfaceGuardKit = undefined,
|
|
728
714
|
} = options;
|
|
729
715
|
|
|
730
716
|
const statePrototype = {}; // Not frozen yet
|
|
@@ -745,38 +731,11 @@ export const makeVirtualObjectManager = (
|
|
|
745
731
|
switch (assessFacetiousness(behavior)) {
|
|
746
732
|
case 'one': {
|
|
747
733
|
assert(!multifaceted);
|
|
748
|
-
interfaceGuardKit === undefined ||
|
|
749
|
-
Fail`Use an interfaceGuard, not interfaceGuardKit, to protect class ${q(
|
|
750
|
-
tag,
|
|
751
|
-
)}`;
|
|
752
734
|
proposedFacetNames = undefined;
|
|
753
735
|
break;
|
|
754
736
|
}
|
|
755
737
|
case 'many': {
|
|
756
738
|
assert(multifaceted);
|
|
757
|
-
|
|
758
|
-
if (interfaceGuard && interfaceGuardKit === undefined) {
|
|
759
|
-
// This if clause is for the purpose of tolerating versions
|
|
760
|
-
// of the vata-data package that precede
|
|
761
|
-
// https://github.com/Agoric/agoric-sdk/pull/8220 .
|
|
762
|
-
// Before that PR, the options name `interfaceGuard` would
|
|
763
|
-
// actually carry the InterfaceGuardKit.
|
|
764
|
-
//
|
|
765
|
-
// Tolerating the old vat-data with the new types.
|
|
766
|
-
// at-expect-error here causes inconsistent reports, so
|
|
767
|
-
// doing the at-ts-ignore-error ritual instead.
|
|
768
|
-
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
|
|
769
|
-
// @ts-ignore
|
|
770
|
-
interfaceGuardKit = interfaceGuard;
|
|
771
|
-
interfaceGuard = undefined;
|
|
772
|
-
// The rest of the code from here makes no further compromise
|
|
773
|
-
// for that old version of the vat-data package.
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
interfaceGuard === undefined ||
|
|
777
|
-
Fail`Use an interfaceGuardKit, not an interfaceGuard, to protect class kit ${q(
|
|
778
|
-
tag,
|
|
779
|
-
)}`;
|
|
780
739
|
proposedFacetNames = ownKeys(behavior).sort();
|
|
781
740
|
break;
|
|
782
741
|
}
|
|
@@ -987,7 +946,7 @@ export const makeVirtualObjectManager = (
|
|
|
987
946
|
makeContextProviderKit(contextCache, getSlotForVal, facetNames),
|
|
988
947
|
behavior,
|
|
989
948
|
thisfulMethods,
|
|
990
|
-
|
|
949
|
+
interfaceGuard,
|
|
991
950
|
);
|
|
992
951
|
} else {
|
|
993
952
|
proto = defendPrototype(
|
|
@@ -1015,9 +974,9 @@ export const makeVirtualObjectManager = (
|
|
|
1015
974
|
let doMoreGC = false;
|
|
1016
975
|
const record = dataCache.get(baseRef);
|
|
1017
976
|
for (const valueCD of Object.values(record.capdatas)) {
|
|
1018
|
-
|
|
977
|
+
valueCD.slots.forEach(vref => {
|
|
1019
978
|
doMoreGC = vrm.removeReachableVref(vref) || doMoreGC;
|
|
1020
|
-
}
|
|
979
|
+
});
|
|
1021
980
|
}
|
|
1022
981
|
dataCache.delete(baseRef);
|
|
1023
982
|
return doMoreGC;
|
|
@@ -1056,7 +1015,6 @@ export const makeVirtualObjectManager = (
|
|
|
1056
1015
|
if (isDurable) {
|
|
1057
1016
|
insistDurableCapdata(vrm, prop, valueCD, true);
|
|
1058
1017
|
}
|
|
1059
|
-
// eslint-disable-next-line github/array-foreach
|
|
1060
1018
|
valueCD.slots.forEach(vrm.addReachableVref);
|
|
1061
1019
|
capdatas[prop] = valueCD;
|
|
1062
1020
|
valueMap.set(prop, value);
|
|
@@ -1124,7 +1082,6 @@ export const makeVirtualObjectManager = (
|
|
|
1124
1082
|
return id;
|
|
1125
1083
|
};
|
|
1126
1084
|
|
|
1127
|
-
/** @type {import('./vatDataTypes').VatData['defineKind']} */
|
|
1128
1085
|
const defineKind = (tag, init, behavior, options) => {
|
|
1129
1086
|
const kindID = `${allocateExportID()}`;
|
|
1130
1087
|
saveVirtualKindDescriptor(kindID, { kindID, tag });
|
|
@@ -1140,7 +1097,6 @@ export const makeVirtualObjectManager = (
|
|
|
1140
1097
|
);
|
|
1141
1098
|
};
|
|
1142
1099
|
|
|
1143
|
-
/** @type {import('./vatDataTypes').VatData['defineKindMulti']} */
|
|
1144
1100
|
const defineKindMulti = (tag, init, behavior, options) => {
|
|
1145
1101
|
const kindID = `${allocateExportID()}`;
|
|
1146
1102
|
saveVirtualKindDescriptor(kindID, { kindID, tag });
|
|
@@ -1180,7 +1136,6 @@ export const makeVirtualObjectManager = (
|
|
|
1180
1136
|
return kindHandle;
|
|
1181
1137
|
};
|
|
1182
1138
|
|
|
1183
|
-
/** @type {import('./vatDataTypes').VatData['defineDurableKind']} */
|
|
1184
1139
|
const defineDurableKind = (kindHandle, init, behavior, options) => {
|
|
1185
1140
|
kindHandleToID.has(kindHandle) || Fail`unknown handle ${kindHandle}`;
|
|
1186
1141
|
const kindID = kindHandleToID.get(kindHandle);
|
|
@@ -1203,7 +1158,6 @@ export const makeVirtualObjectManager = (
|
|
|
1203
1158
|
return maker;
|
|
1204
1159
|
};
|
|
1205
1160
|
|
|
1206
|
-
/** @type {import('./vatDataTypes').VatData['defineDurableKindMulti']} */
|
|
1207
1161
|
const defineDurableKindMulti = (kindHandle, init, behavior, options) => {
|
|
1208
1162
|
kindHandleToID.has(kindHandle) || Fail`unknown handle ${kindHandle}`;
|
|
1209
1163
|
const kindID = kindHandleToID.get(kindHandle);
|
package/src/virtualReferences.js
CHANGED
|
@@ -682,54 +682,6 @@ export function makeVirtualReferenceManager(
|
|
|
682
682
|
return size;
|
|
683
683
|
}
|
|
684
684
|
|
|
685
|
-
/**
|
|
686
|
-
* Counters to track the next number for various categories of allocation.
|
|
687
|
-
* `exportID` starts at 1 because 'o+0' is always automatically
|
|
688
|
-
* pre-assigned to the root object.
|
|
689
|
-
* `promiseID` starts at 5 as a very minor aid to debugging: when puzzling
|
|
690
|
-
* over trace logs and the like, it helps for the numbers in various species
|
|
691
|
-
* of IDs that are jumbled together to be a little out of sync and thus a
|
|
692
|
-
* little less similar to each other.
|
|
693
|
-
*/
|
|
694
|
-
const initialIDCounters = { exportID: 1, collectionID: 1, promiseID: 5 };
|
|
695
|
-
/** @type {Record<string, number>} */
|
|
696
|
-
let idCounters;
|
|
697
|
-
let idCountersAreDirty = false;
|
|
698
|
-
|
|
699
|
-
function initializeIDCounters() {
|
|
700
|
-
if (!idCounters) {
|
|
701
|
-
// the saved value might be missing, or from an older liveslots
|
|
702
|
-
// (with fewer counters), so merge it with our initial values
|
|
703
|
-
const saved = JSON.parse(syscall.vatstoreGet('idCounters') || '{}');
|
|
704
|
-
idCounters = { ...initialIDCounters, ...saved };
|
|
705
|
-
idCountersAreDirty = true;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
function allocateNextID(name) {
|
|
710
|
-
if (!idCounters) {
|
|
711
|
-
// Normally `initializeIDCounters` would be called from startVat, but some
|
|
712
|
-
// tests bypass that so this is a backstop. Note that the invocation from
|
|
713
|
-
// startVat is there to make vatStore access patterns a bit more
|
|
714
|
-
// consistent from one vat to another, principally as a confusion
|
|
715
|
-
// reduction measure in service of debugging; it is not a correctness
|
|
716
|
-
// issue.
|
|
717
|
-
initializeIDCounters();
|
|
718
|
-
}
|
|
719
|
-
const result = idCounters[name];
|
|
720
|
-
result !== undefined || Fail`unknown idCounters[${name}]`;
|
|
721
|
-
idCounters[name] += 1;
|
|
722
|
-
idCountersAreDirty = true;
|
|
723
|
-
return result;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
function flushIDCounters() {
|
|
727
|
-
if (idCountersAreDirty) {
|
|
728
|
-
syscall.vatstoreSet('idCounters', JSON.stringify(idCounters));
|
|
729
|
-
idCountersAreDirty = false;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
|
|
733
685
|
const testHooks = {
|
|
734
686
|
getReachableRefCount,
|
|
735
687
|
countCollectionsForWeakKey,
|
|
@@ -774,9 +726,6 @@ export function makeVirtualReferenceManager(
|
|
|
774
726
|
ceaseRecognition,
|
|
775
727
|
setDeleteCollectionEntry,
|
|
776
728
|
getRetentionStats,
|
|
777
|
-
initializeIDCounters,
|
|
778
|
-
allocateNextID,
|
|
779
|
-
flushIDCounters,
|
|
780
729
|
testHooks,
|
|
781
730
|
});
|
|
782
731
|
}
|
package/src/watchedPromises.js
CHANGED
|
@@ -147,12 +147,6 @@ export function makeWatchedPromiseManager({
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
/**
|
|
151
|
-
*
|
|
152
|
-
* @param {Promise} p
|
|
153
|
-
* @param {{onFulfilled?: Function, onRejected?: Function}} watcher
|
|
154
|
-
* @param {...any} args
|
|
155
|
-
*/
|
|
156
150
|
function watchPromise(p, watcher, ...args) {
|
|
157
151
|
// The following wrapping defers setting up the promise watcher itself to a
|
|
158
152
|
// later turn so that if the promise to be watched was the return value from
|
package/test/gc-helpers.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { Far } from '@endo/marshal';
|
|
4
4
|
import { M } from '@agoric/store';
|
|
5
|
-
import { kslot, kser } from '
|
|
5
|
+
import { kslot, kser } from './kmarshal.js';
|
|
6
6
|
import { parseVatSlot } from '../src/parseVatSlots.js';
|
|
7
7
|
|
|
8
8
|
// These tests follow the model described in
|
package/test/kmarshal.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Far, makeMarshal, passStyleOf } from '@endo/marshal';
|
|
2
|
+
import { assert } from '@agoric/assert';
|
|
3
|
+
|
|
4
|
+
// Simple wrapper for serializing and unserializing marshalled values inside the
|
|
5
|
+
// kernel, where we don't actually want to use clists nor actually allocate real
|
|
6
|
+
// objects, but instead to stay entirely within the domain of krefs. This is
|
|
7
|
+
// used to enable syntactic manipulation of serialized values while remaining
|
|
8
|
+
// agnostic about the internal details of the serialization encoding.
|
|
9
|
+
|
|
10
|
+
const refMap = new WeakMap();
|
|
11
|
+
|
|
12
|
+
export const kslot = (kref, iface) => {
|
|
13
|
+
assert.typeof(kref, 'string');
|
|
14
|
+
if (iface && iface.startsWith('Alleged: ')) {
|
|
15
|
+
// Encoder prepends "Alleged: " to iface string, but the decoder doesn't strip it
|
|
16
|
+
// Unclear whether it's the decoder or me who is wrong
|
|
17
|
+
iface = iface.slice(9);
|
|
18
|
+
}
|
|
19
|
+
if (
|
|
20
|
+
kref.startsWith('p') ||
|
|
21
|
+
kref.startsWith('kp') ||
|
|
22
|
+
kref.startsWith('lp') ||
|
|
23
|
+
kref.startsWith('rp')
|
|
24
|
+
) {
|
|
25
|
+
// TODO: temporary hack because smallcaps encodes promise references
|
|
26
|
+
// differently from remotable object references, and the current version of
|
|
27
|
+
// the smallcaps decoder annoyingly insists that if the encoded body string
|
|
28
|
+
// says a slot is a promise, then convertSlotToVal had better by damn return
|
|
29
|
+
// an actual Promise, even if, as in the intended use case here, we neither
|
|
30
|
+
// want nor need a promise, nor the overhead of a map to keep track of it
|
|
31
|
+
// with. This behavior is in service of defense against a hypothesized
|
|
32
|
+
// security issue whose exact nature has largely been forgotton in the
|
|
33
|
+
// months since it was first encountered. MarkM is currently researching
|
|
34
|
+
// what the problem was thought to have been, to see if it is real and to
|
|
35
|
+
// understand it if so. This will eventually result in either changes to
|
|
36
|
+
// the smallcaps encoding or to the marshal setup API to support the purely
|
|
37
|
+
// manipulative use case. In the meantime, this ugliness...
|
|
38
|
+
const p = new Promise(() => undefined);
|
|
39
|
+
refMap.set(p, kref);
|
|
40
|
+
return harden(p);
|
|
41
|
+
} else {
|
|
42
|
+
const o = Far(iface, {
|
|
43
|
+
iface: () => iface,
|
|
44
|
+
getKref: () => `${kref}`,
|
|
45
|
+
});
|
|
46
|
+
return o;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const krefOf = obj => {
|
|
51
|
+
const fromMap = refMap.get(obj);
|
|
52
|
+
if (fromMap) {
|
|
53
|
+
return fromMap;
|
|
54
|
+
}
|
|
55
|
+
// When krefOf() is called as part of kmarshal.serialize, marshal
|
|
56
|
+
// will only give it things that are 'remotable' (Promises and the
|
|
57
|
+
// Far objects created by kslot()). When krefOf() is called by
|
|
58
|
+
// kernel code (as part of extractSingleSlot() or the vat-comms
|
|
59
|
+
// equivalent), it ought to throw if 'obj' is not one of the Far
|
|
60
|
+
// objects created by our kslot().
|
|
61
|
+
assert.equal(passStyleOf(obj), 'remotable', obj);
|
|
62
|
+
const getKref = obj.getKref;
|
|
63
|
+
assert.typeof(getKref, 'function');
|
|
64
|
+
return getKref();
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const kmarshal = makeMarshal(krefOf, kslot, {
|
|
68
|
+
serializeBodyFormat: 'smallcaps',
|
|
69
|
+
errorTagging: 'off',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export const kser = value => kmarshal.serialize(harden(value));
|
|
73
|
+
|
|
74
|
+
export const kunser = serializedValue => kmarshal.unserialize(serializedValue);
|
|
75
|
+
|
|
76
|
+
export function makeError(message) {
|
|
77
|
+
assert.typeof(message, 'string');
|
|
78
|
+
return kser(Error(message));
|
|
79
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* global WeakRef, FinalizationRegistry */
|
|
2
|
-
import { kser } from '@agoric/kmarshal';
|
|
3
|
-
|
|
4
2
|
import engineGC from './engine-gc.js';
|
|
3
|
+
|
|
5
4
|
import { waitUntilQuiescent } from './waitUntilQuiescent.js';
|
|
6
5
|
import { makeGcAndFinalize } from './gc-and-finalize.js';
|
|
7
6
|
import { makeDummyMeterControl } from './dummyMeterControl.js';
|
|
@@ -13,11 +12,12 @@ import {
|
|
|
13
12
|
makeRetireExports,
|
|
14
13
|
makeBringOutYourDead,
|
|
15
14
|
} from './util.js';
|
|
15
|
+
import { kser } from './kmarshal.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* @param {object} [options]
|
|
19
|
-
* @param {boolean} [options.skipLogging]
|
|
20
|
-
* @param {Map<string, string>} [options.kvStore]
|
|
19
|
+
* @param {boolean} [options.skipLogging = false]
|
|
20
|
+
* @param {Map<string, string>} [options.kvStore = new Map()]
|
|
21
21
|
*/
|
|
22
22
|
export function buildSyscall(options = {}) {
|
|
23
23
|
const { skipLogging = false, kvStore: fakestore = new Map() } = options;
|
|
@@ -168,9 +168,9 @@ function makeRPMaker(nextNumber = 1) {
|
|
|
168
168
|
* @param {string} vatName
|
|
169
169
|
* @param {object} [options]
|
|
170
170
|
* @param {boolean} [options.forceGC]
|
|
171
|
-
* @param {Map<string, string>} [options.kvStore]
|
|
171
|
+
* @param {Map<string, string>} [options.kvStore = new Map()]
|
|
172
172
|
* @param {number} [options.nextPromiseImportNumber]
|
|
173
|
-
* @param {boolean} [options.skipLogging]
|
|
173
|
+
* @param {boolean} [options.skipLogging = false]
|
|
174
174
|
*/
|
|
175
175
|
export async function setupTestLiveslots(
|
|
176
176
|
t,
|
package/test/mock-gc.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import test from 'ava';
|
|
2
|
+
import '@endo/init/debug.js';
|
|
3
3
|
|
|
4
|
-
import { kslot, kunser } from '@agoric/kmarshal';
|
|
5
4
|
import {
|
|
6
5
|
setupTestLiveslots,
|
|
7
6
|
findSyscallsByType,
|
|
8
7
|
} from '../liveslots-helpers.js';
|
|
9
8
|
import { buildRootObject, mainHeldIdx, mapRef } from '../gc-helpers.js';
|
|
9
|
+
import { kslot, kunser } from '../kmarshal.js';
|
|
10
10
|
import { parseVatSlot } from '../../src/parseVatSlots.js';
|
|
11
11
|
|
|
12
12
|
// These tests follow the model described in
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from 'ava';
|
|
2
|
+
import '@endo/init/debug.js';
|
|
2
3
|
|
|
3
|
-
import { kslot } from '@agoric/kmarshal';
|
|
4
4
|
import {
|
|
5
5
|
findSyscallsByType,
|
|
6
6
|
setupTestLiveslots,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
refValString,
|
|
13
13
|
assertCollectionDeleted,
|
|
14
14
|
} from '../gc-helpers.js';
|
|
15
|
+
import { kslot } from '../kmarshal.js';
|
|
15
16
|
import { vstr } from '../util.js';
|
|
16
17
|
|
|
17
18
|
// These tests follow the model described in
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from 'ava';
|
|
2
|
+
import '@endo/init/debug.js';
|
|
2
3
|
|
|
3
|
-
import { kslot } from '@agoric/kmarshal';
|
|
4
4
|
import { setupTestLiveslots } from '../liveslots-helpers.js';
|
|
5
5
|
import {
|
|
6
6
|
buildRootObject,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
assertCollectionDeleted,
|
|
9
9
|
deduceCollectionID,
|
|
10
10
|
} from '../gc-helpers.js';
|
|
11
|
+
import { kslot } from '../kmarshal.js';
|
|
11
12
|
import { vstr } from '../util.js';
|
|
12
13
|
|
|
13
14
|
// These tests follow the model described in test-virtualObjectGC.js
|