@agoric/swingset-liveslots 0.10.3-other-dev-8f8782b.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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Far } from '@endo/marshal';
|
|
2
|
+
import { setupTestLiveslots } from '../test/liveslots-helpers.js';
|
|
3
|
+
|
|
4
|
+
// This file contains a test harness for virtual objects. runVOTest()
|
|
5
|
+
// is to to help verify that a VO can be garbage collected and then
|
|
6
|
+
// reloaded from persistent storage while maintaining functionality.
|
|
7
|
+
|
|
8
|
+
// Testing VO swapping with runVOTest:
|
|
9
|
+
//
|
|
10
|
+
// Step 1: import the necessary harness paraphernalia
|
|
11
|
+
//
|
|
12
|
+
// import { test, runVOTest } from '@agoric/swingset-vat/tools/vo-test-harness.js';
|
|
13
|
+
//
|
|
14
|
+
// `test` is the regular Ava test object that you'd normally import from
|
|
15
|
+
// `@agoric/swingset-vat/tools/prepare-test-env-ava.js`. The test harness will
|
|
16
|
+
// import it for you, since it needs to set up some test things itself.
|
|
17
|
+
//
|
|
18
|
+
// Step 2: write three functions that you will pass to the test harness
|
|
19
|
+
//
|
|
20
|
+
// `prepare(VatData)` should perform any necessary environmental setup that the
|
|
21
|
+
// virtual object kind under test will require. In particular, this includes
|
|
22
|
+
// executing any necessary `defineKind` or `defineDurableKind` calls to
|
|
23
|
+
// establish the VO itself. The `VatData` parameter is a regular `VatData`
|
|
24
|
+
// object that can be used to obtain functions like `defineKind`.
|
|
25
|
+
//
|
|
26
|
+
// `makeTestObject()` should create and return an instance of the VO to be tested.
|
|
27
|
+
//
|
|
28
|
+
// `testTestObject(obj, phase)` should execute whatever actual testing and test
|
|
29
|
+
// assertions you care to perform to verify your VO kind. `obj` will be a
|
|
30
|
+
// reference to an in-memory representative of the virtual object being tested
|
|
31
|
+
// and `phase` will be a string, either 'before' or 'after', indicating whether
|
|
32
|
+
// this instance of the object is before or after having been swapped out of
|
|
33
|
+
// memory and then reloaded. A correctly functioning VO should, among other
|
|
34
|
+
// things, behave exactly the same in both cases.
|
|
35
|
+
//
|
|
36
|
+
// Step 3: write an Ava test that invokes the test harness
|
|
37
|
+
//
|
|
38
|
+
// The outer portion of this should be a conventional Ava test written in the
|
|
39
|
+
// conventional way, e.g.:
|
|
40
|
+
//
|
|
41
|
+
// test('test name', async t => {
|
|
42
|
+
// ...your test here...
|
|
43
|
+
// });
|
|
44
|
+
//
|
|
45
|
+
// The body of your test most likely will enclose the three functions described
|
|
46
|
+
// above, since your `testTestObject` function (and possibly the others,
|
|
47
|
+
// depending on how you code things) will need access to the `t` object in order
|
|
48
|
+
// to execute test assertions. Then, from inside your test invoke:
|
|
49
|
+
//
|
|
50
|
+
// await runVOTest(t, prepare, makeTestObject, testTestObject);
|
|
51
|
+
//
|
|
52
|
+
// This will:
|
|
53
|
+
// 1 - execute the `prepare` function
|
|
54
|
+
// 2 - create a test object instance via `makeTestObject`
|
|
55
|
+
// 3 - run `testTestObject` on the test object (this is the 'before' phase)
|
|
56
|
+
// 4 - drop all in-memory references to the test object and force a GC pass
|
|
57
|
+
// 5 - run `testTestObject` on the test object *again* (this is the 'after' phase)
|
|
58
|
+
//
|
|
59
|
+
// The key thing that the test harness provides for you is step 4, which
|
|
60
|
+
// packages up some awkward boilerplate that's a bit of mysterious if you're not
|
|
61
|
+
// already pretty familiar with how the VO GC mechanism works (or perhaps even
|
|
62
|
+
// if you are).
|
|
63
|
+
//
|
|
64
|
+
// Note: It is critical that none of your own code retain any in-memory
|
|
65
|
+
// references to the test object beyond step 3. However, another key service
|
|
66
|
+
// that the test harness provides is to detect if you did this and if so fail
|
|
67
|
+
// the test. One use of the test harness is to verify that you aren't
|
|
68
|
+
// accidentally holding such references when you didn't mean to.
|
|
69
|
+
//
|
|
70
|
+
// The SwingSet test `vo-test-harness/test-vo-test-harness.js` is a test of the
|
|
71
|
+
// VO test harness itself, but can be used as a simple example of how to set
|
|
72
|
+
// things up.
|
|
73
|
+
|
|
74
|
+
export async function runVOTest(t, prepare, makeTestObject, testTestObject) {
|
|
75
|
+
function buildRootObject(vatPowers) {
|
|
76
|
+
const { VatData } = vatPowers;
|
|
77
|
+
const { defineKind } = VatData;
|
|
78
|
+
|
|
79
|
+
const freeChecker = new WeakSet();
|
|
80
|
+
|
|
81
|
+
const makeSlug = defineKind('slug', label => ({ label }), {
|
|
82
|
+
getLabel: ({ state }) => state.label,
|
|
83
|
+
});
|
|
84
|
+
const cacheDisplacer = makeSlug('cacheDisplacer');
|
|
85
|
+
|
|
86
|
+
const makeHolder = defineKind('holder', (held = null) => ({ held }), {
|
|
87
|
+
setValue: ({ state }, value) => {
|
|
88
|
+
state.held = value;
|
|
89
|
+
},
|
|
90
|
+
getValue: ({ state }) => state.held,
|
|
91
|
+
});
|
|
92
|
+
const holder = makeHolder();
|
|
93
|
+
|
|
94
|
+
let held = null;
|
|
95
|
+
|
|
96
|
+
prepare(VatData);
|
|
97
|
+
|
|
98
|
+
function displaceCache() {
|
|
99
|
+
return cacheDisplacer.getLabel();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return Far('root', {
|
|
103
|
+
makeAndHold() {
|
|
104
|
+
held = makeTestObject();
|
|
105
|
+
freeChecker.add(held);
|
|
106
|
+
displaceCache();
|
|
107
|
+
},
|
|
108
|
+
storeHeld() {
|
|
109
|
+
holder.setValue(held);
|
|
110
|
+
displaceCache();
|
|
111
|
+
},
|
|
112
|
+
dropHeld() {
|
|
113
|
+
held = null;
|
|
114
|
+
displaceCache();
|
|
115
|
+
},
|
|
116
|
+
fetchAndHold() {
|
|
117
|
+
held = holder.getValue();
|
|
118
|
+
t.falsy(
|
|
119
|
+
freeChecker.has(held),
|
|
120
|
+
'somebody continues to hold test object',
|
|
121
|
+
);
|
|
122
|
+
displaceCache();
|
|
123
|
+
},
|
|
124
|
+
testHeld(phase) {
|
|
125
|
+
testTestObject(held, phase);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const { dispatchMessage } = await setupTestLiveslots(
|
|
131
|
+
t,
|
|
132
|
+
buildRootObject,
|
|
133
|
+
'bob',
|
|
134
|
+
{ forceGC: true, skipLogging: true },
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
await dispatchMessage('makeAndHold');
|
|
138
|
+
await dispatchMessage('testHeld', 'before');
|
|
139
|
+
await dispatchMessage('storeHeld');
|
|
140
|
+
await dispatchMessage('dropHeld');
|
|
141
|
+
await dispatchMessage('fetchAndHold');
|
|
142
|
+
await dispatchMessage('testHeld', 'after');
|
|
143
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# Change Log
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
-
|
|
6
|
-
### [0.10.3-u11.0](https://github.com/Agoric/agoric-sdk/compare/@agoric/swingset-liveslots@0.10.2...@agoric/swingset-liveslots@0.10.3-u11.0) (2023-08-24)
|
|
7
|
-
|
|
8
|
-
**Note:** Version bump only for package @agoric/swingset-liveslots
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
### [0.10.2](https://github.com/Agoric/agoric-sdk/compare/@agoric/swingset-liveslots@0.10.1...@agoric/swingset-liveslots@0.10.2) (2023-06-02)
|
|
15
|
-
|
|
16
|
-
**Note:** Version bump only for package @agoric/swingset-liveslots
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
### [0.10.1](https://github.com/Agoric/agoric-sdk/compare/@agoric/swingset-liveslots@0.10.0...@agoric/swingset-liveslots@0.10.1) (2023-05-24)
|
|
23
|
-
|
|
24
|
-
**Note:** Version bump only for package @agoric/swingset-liveslots
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
## 0.10.0 (2023-05-19)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
### Features
|
|
34
|
-
|
|
35
|
-
* better diagnostic for failed reanimate ([c0c9c94](https://github.com/Agoric/agoric-sdk/commit/c0c9c9433648d520aa2bcdbadbbfe877831567c7)), closes [Error#1](https://github.com/Agoric/Error/issues/1)
|
|
36
|
-
* **swingset-liveslots:** label virtual instances ([4191eb6](https://github.com/Agoric/agoric-sdk/commit/4191eb62d0e64048c3c715e5f71a53a747267350))
|
|
37
|
-
* add APIs for tracking/debugging undesired object retention (aka "leaks") ([0a7221b](https://github.com/Agoric/agoric-sdk/commit/0a7221b3c04f3b2894c30346fa2ea6fb0130c046)), closes [#7318](https://github.com/Agoric/agoric-sdk/issues/7318)
|
|
38
|
-
* move liveslots and specific tests to a new package ([0921a89](https://github.com/Agoric/agoric-sdk/commit/0921a8903b72cfefdf05a5906bcfb826cac1cc2f)), closes [#6596](https://github.com/Agoric/agoric-sdk/issues/6596)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
### Bug Fixes
|
|
42
|
-
|
|
43
|
-
* **liveslots:** allow new Kind upgrade to add new facets ([6bc6694](https://github.com/Agoric/agoric-sdk/commit/6bc6694968e6d2f529e7c91ec1efb11fdff2e2d3)), closes [#7437](https://github.com/Agoric/agoric-sdk/issues/7437)
|
|
44
|
-
* **liveslots:** retain WeakRefs to voAware collections ([3935723](https://github.com/Agoric/agoric-sdk/commit/393572396781afd17691e1366abeba696228a24e)), closes [#7371](https://github.com/Agoric/agoric-sdk/issues/7371)
|
|
45
|
-
* **swingset-liveslots:** prevent VOM infinite loop if `globalThis.WeakSet` etc are replaced ([d7b35e2](https://github.com/Agoric/agoric-sdk/commit/d7b35e28715a715ef510f2717e0040fa017caab4))
|
|
46
|
-
* adding dup entries to virtual sets is OK ([c81d367](https://github.com/Agoric/agoric-sdk/commit/c81d3677d8085eb4debe5baa416816ff94d582cf)), closes [#7234](https://github.com/Agoric/agoric-sdk/issues/7234)
|
|
47
|
-
* code updates for new marshal ([292f971](https://github.com/Agoric/agoric-sdk/commit/292f971769db69e61782f96638c2f687c3f95ac2))
|
|
48
|
-
* **SwingSet:** Don't send stopVat during upgrade ([5cc47d2](https://github.com/Agoric/agoric-sdk/commit/5cc47d2d8892690f8c1653630b41dd64cc42d73b)), closes [#6650](https://github.com/Agoric/agoric-sdk/issues/6650)
|
|
49
|
-
* **types:** return value of deleter ([457f576](https://github.com/Agoric/agoric-sdk/commit/457f5765b9fc0a693e6eb5e6644ddf4af3b791db))
|
|
50
|
-
* move many type definitions from swingset to liveslots ([727143d](https://github.com/Agoric/agoric-sdk/commit/727143d5562498e2e3013c34304f229b4dd11da5))
|
|
51
|
-
* move rejectAllPromises from stopVat to kernels-side upgradeVat ([d79623f](https://github.com/Agoric/agoric-sdk/commit/d79623f3fb3b87653dba1c71eb1153711c9d962c)), closes [#6694](https://github.com/Agoric/agoric-sdk/issues/6694)
|
|
52
|
-
* Move upgrade-time abandonExports responsibility into the kernel ([66ac657](https://github.com/Agoric/agoric-sdk/commit/66ac657d51d3d1be61ee4a6e9a621a664086ee57)), closes [#6696](https://github.com/Agoric/agoric-sdk/issues/6696)
|
|
53
|
-
* only the exo api change ([5cf3bf1](https://github.com/Agoric/agoric-sdk/commit/5cf3bf10a71dd02094365a66e87032e5d17d004f))
|
|
54
|
-
* **liveslots:** use Map for vrefStatus, not object ([1456e2a](https://github.com/Agoric/agoric-sdk/commit/1456e2ae006bb1c702383cedda5e5c407968840e))
|
|
55
|
-
* **swingset:** move a bunch of types from swingset to swingset-liveslots ([14f9bb0](https://github.com/Agoric/agoric-sdk/commit/14f9bb00c82c085dc647f23b6c90b26e6a0a6dfd))
|
|
56
|
-
* **swingset-liveslots:** Move promise rejection responsibility into the kernel ([dd29ff3](https://github.com/Agoric/agoric-sdk/commit/dd29ff35c5dc72efbbf7087849182aa7f04b2bb1)), closes [#6694](https://github.com/Agoric/agoric-sdk/issues/6694)
|
|
57
|
-
* add 'v'/'d' virtual/durable annotations to vrefs ([b859e92](https://github.com/Agoric/agoric-sdk/commit/b859e92fe041415d6e34250f672a10ad927aa33e)), closes [#6695](https://github.com/Agoric/agoric-sdk/issues/6695)
|
|
58
|
-
* update description of "FINALIZED" state ([809f366](https://github.com/Agoric/agoric-sdk/commit/809f3660c083467e76deb1487015cb24205a801d))
|
|
59
|
-
* without assertKeyPattern ([#7035](https://github.com/Agoric/agoric-sdk/issues/7035)) ([c9fcd7f](https://github.com/Agoric/agoric-sdk/commit/c9fcd7f82757732435cd96f3377e4fbfb6586ce7))
|
|
60
|
-
* **swingset-liveslots:** copy helper files to new liveslots package ([be7229f](https://github.com/Agoric/agoric-sdk/commit/be7229f7217c1ecc523069a57945a372f4a1e00e))
|
|
61
|
-
* **swingset-liveslots:** update imports of helper files ([0b4b38a](https://github.com/Agoric/agoric-sdk/commit/0b4b38a1f9efbb3e1e860172b0b802548d18ae2e))
|
package/test/kmarshal.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
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,360 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-await-in-loop, @jessie.js/no-nested-await, no-shadow */
|
|
2
|
-
import test from 'ava';
|
|
3
|
-
import '@endo/init/debug.js';
|
|
4
|
-
|
|
5
|
-
import { Far } from '@endo/marshal';
|
|
6
|
-
import { Fail } from '@agoric/assert';
|
|
7
|
-
import { M, provideLazy as provide } from '@agoric/store';
|
|
8
|
-
import { makePromiseKit } from '@endo/promise-kit';
|
|
9
|
-
// Disabled to avoid circular dependencies.
|
|
10
|
-
// import { makeStoreUtils } from '@agoric/vat-data/src/vat-data-bindings.js';
|
|
11
|
-
// import { makeExoUtils } from '@agoric/vat-data/src/exo-utils.js';
|
|
12
|
-
import { kslot, kser } from './kmarshal.js';
|
|
13
|
-
import { setupTestLiveslots } from './liveslots-helpers.js';
|
|
14
|
-
import { makeResolve, makeReject } from './util.js';
|
|
15
|
-
|
|
16
|
-
// eslint-disable-next-line no-unused-vars
|
|
17
|
-
const compareEntriesByKey = ([ka], [kb]) => (ka < kb ? -1 : 1);
|
|
18
|
-
|
|
19
|
-
// Paritally duplicates @agoric/vat-data to avoid circular dependencies.
|
|
20
|
-
const makeExoUtils = VatData => {
|
|
21
|
-
const { defineDurableKind, makeKindHandle, watchPromise } = VatData;
|
|
22
|
-
|
|
23
|
-
const provideKindHandle = (baggage, kindName) =>
|
|
24
|
-
provide(baggage, `${kindName}_kindHandle`, () => makeKindHandle(kindName));
|
|
25
|
-
|
|
26
|
-
const emptyRecord = harden({});
|
|
27
|
-
const initEmpty = () => emptyRecord;
|
|
28
|
-
|
|
29
|
-
const defineDurableExoClass = (
|
|
30
|
-
kindHandle,
|
|
31
|
-
interfaceGuard,
|
|
32
|
-
init,
|
|
33
|
-
methods,
|
|
34
|
-
options,
|
|
35
|
-
) =>
|
|
36
|
-
defineDurableKind(kindHandle, init, methods, {
|
|
37
|
-
...options,
|
|
38
|
-
thisfulMethods: true,
|
|
39
|
-
interfaceGuard,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const prepareExoClass = (
|
|
43
|
-
baggage,
|
|
44
|
-
kindName,
|
|
45
|
-
interfaceGuard,
|
|
46
|
-
init,
|
|
47
|
-
methods,
|
|
48
|
-
options = undefined,
|
|
49
|
-
) =>
|
|
50
|
-
defineDurableExoClass(
|
|
51
|
-
provideKindHandle(baggage, kindName),
|
|
52
|
-
interfaceGuard,
|
|
53
|
-
init,
|
|
54
|
-
methods,
|
|
55
|
-
options,
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
const prepareExo = (
|
|
59
|
-
baggage,
|
|
60
|
-
kindName,
|
|
61
|
-
interfaceGuard,
|
|
62
|
-
methods,
|
|
63
|
-
options = undefined,
|
|
64
|
-
) => {
|
|
65
|
-
const makeSingleton = prepareExoClass(
|
|
66
|
-
baggage,
|
|
67
|
-
kindName,
|
|
68
|
-
interfaceGuard,
|
|
69
|
-
initEmpty,
|
|
70
|
-
methods,
|
|
71
|
-
options,
|
|
72
|
-
);
|
|
73
|
-
return provide(baggage, `the_${kindName}`, () => makeSingleton());
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
defineDurableKind,
|
|
78
|
-
makeKindHandle,
|
|
79
|
-
watchPromise,
|
|
80
|
-
|
|
81
|
-
provideKindHandle,
|
|
82
|
-
defineDurableExoClass,
|
|
83
|
-
prepareExoClass,
|
|
84
|
-
prepareExo,
|
|
85
|
-
};
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// cf. packages/SwingSet/test/vat-durable-promise-watcher.js
|
|
89
|
-
const buildPromiseWatcherRootObject = (vatPowers, _vatParameters, baggage) => {
|
|
90
|
-
const { VatData } = vatPowers;
|
|
91
|
-
const { watchPromise } = VatData;
|
|
92
|
-
const { prepareExo } = makeExoUtils(VatData);
|
|
93
|
-
// const { makeScalarBigMapStore } = makeStoreUtils(VatData);
|
|
94
|
-
const PromiseWatcherI = M.interface('ExtraArgPromiseWatcher', {
|
|
95
|
-
onFulfilled: M.call(M.any(), M.string()).returns(),
|
|
96
|
-
onRejected: M.call(M.any(), M.string()).returns(),
|
|
97
|
-
});
|
|
98
|
-
const watcher = prepareExo(
|
|
99
|
-
baggage,
|
|
100
|
-
'DurablePromiseIgnorer',
|
|
101
|
-
PromiseWatcherI,
|
|
102
|
-
{
|
|
103
|
-
onFulfilled(_value, _name) {},
|
|
104
|
-
onRejected(_reason, _name) {},
|
|
105
|
-
},
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
const localPromises = new Map();
|
|
109
|
-
|
|
110
|
-
return Far('root', {
|
|
111
|
-
exportPromise: () => [Promise.resolve()],
|
|
112
|
-
createLocalPromise: (name, fulfillment, rejection) => {
|
|
113
|
-
!localPromises.has(name) || Fail`local promise already exists: ${name}`;
|
|
114
|
-
const { promise, resolve, reject } = makePromiseKit();
|
|
115
|
-
if (fulfillment !== undefined) {
|
|
116
|
-
resolve(fulfillment);
|
|
117
|
-
} else if (rejection !== undefined) {
|
|
118
|
-
reject(rejection);
|
|
119
|
-
}
|
|
120
|
-
localPromises.set(name, promise);
|
|
121
|
-
return `created local promise: ${name}`;
|
|
122
|
-
},
|
|
123
|
-
watchLocalPromise: name => {
|
|
124
|
-
localPromises.has(name) || Fail`local promise not found: ${name}`;
|
|
125
|
-
watchPromise(localPromises.get(name), watcher, name);
|
|
126
|
-
return `watched local promise: ${name}`;
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
};
|
|
130
|
-
const kvStoreDataV1 = Object.entries({
|
|
131
|
-
baggageID: 'o+d6/1',
|
|
132
|
-
idCounters: '{"exportID":11,"collectionID":5,"promiseID":9}',
|
|
133
|
-
kindIDID: '1',
|
|
134
|
-
storeKindIDTable:
|
|
135
|
-
'{"scalarMapStore":2,"scalarWeakMapStore":3,"scalarSetStore":4,"scalarWeakSetStore":5,"scalarDurableMapStore":6,"scalarDurableWeakMapStore":7,"scalarDurableSetStore":8,"scalarDurableWeakSetStore":9}',
|
|
136
|
-
'vc.1.sDurablePromiseIgnorer_kindHandle':
|
|
137
|
-
'{"body":"#\\"$0.Alleged: kind\\"","slots":["o+d1/10"]}',
|
|
138
|
-
'vc.1.sthe_DurablePromiseIgnorer':
|
|
139
|
-
'{"body":"#\\"$0.Alleged: DurablePromiseIgnorer\\"","slots":["o+d10/1"]}',
|
|
140
|
-
'vc.1.|entryCount': '2',
|
|
141
|
-
'vc.1.|nextOrdinal': '1',
|
|
142
|
-
'vc.1.|schemata':
|
|
143
|
-
'{"label":"baggage","body":"#{\\"keyShape\\":{\\"#tag\\":\\"match:string\\",\\"payload\\":[]}}","slots":[]}',
|
|
144
|
-
// non-durable
|
|
145
|
-
// 'vc.2.sp+6': '{"body":"#\\"&0\\"","slots":["p+6"]}',
|
|
146
|
-
// 'vc.2.|entryCount': '1',
|
|
147
|
-
// 'vc.2.|nextOrdinal': '1',
|
|
148
|
-
// 'vc.2.|schemata': '{"label":"promiseRegistrations","body":"#{\\"keyShape\\":{\\"#tag\\":\\"match:scalar\\",\\"payload\\":\\"#undefined\\"}}","slots":[]}',
|
|
149
|
-
'vc.3.|entryCount': '0',
|
|
150
|
-
'vc.3.|nextOrdinal': '1',
|
|
151
|
-
'vc.3.|schemata':
|
|
152
|
-
'{"label":"promiseWatcherByKind","body":"#{\\"keyShape\\":{\\"#tag\\":\\"match:scalar\\",\\"payload\\":\\"#undefined\\"}}","slots":[]}',
|
|
153
|
-
'vc.4.sp+6':
|
|
154
|
-
'{"body":"#[[\\"$0.Alleged: DurablePromiseIgnorer\\",\\"orphaned\\"]]","slots":["o+d10/1"]}',
|
|
155
|
-
'vc.4.sp-8':
|
|
156
|
-
'{"body":"#[[\\"$0.Alleged: DurablePromiseIgnorer\\",\\"unresolved\\"]]","slots":["o+d10/1"]}',
|
|
157
|
-
'vc.4.sp-9':
|
|
158
|
-
'{"body":"#[[\\"$0.Alleged: DurablePromiseIgnorer\\",\\"late-rejected\\"]]","slots":["o+d10/1"]}',
|
|
159
|
-
'vc.4.|entryCount': '3',
|
|
160
|
-
'vc.4.|nextOrdinal': '1',
|
|
161
|
-
'vc.4.|schemata':
|
|
162
|
-
'{"label":"watchedPromises","body":"#{\\"keyShape\\":{\\"#tag\\":\\"match:and\\",\\"payload\\":[{\\"#tag\\":\\"match:scalar\\",\\"payload\\":\\"#undefined\\"},{\\"#tag\\":\\"match:string\\",\\"payload\\":[]}]}}","slots":[]}',
|
|
163
|
-
'vom.dkind.10.descriptor':
|
|
164
|
-
'{"kindID":"10","tag":"DurablePromiseIgnorer","unfaceted":true}',
|
|
165
|
-
'vom.dkind.10.nextID': '2',
|
|
166
|
-
'vom.o+d10/1': '{}',
|
|
167
|
-
'vom.rc.o+d1/10': '1',
|
|
168
|
-
'vom.rc.o+d10/1': '3',
|
|
169
|
-
'vom.rc.o+d6/1': '1',
|
|
170
|
-
'vom.rc.o+d6/3': '1',
|
|
171
|
-
'vom.rc.o+d6/4': '1',
|
|
172
|
-
watchedPromiseTableID: 'o+d6/4',
|
|
173
|
-
watcherTableID: 'o+d6/3',
|
|
174
|
-
});
|
|
175
|
-
const kvStoreDataV1VpidsToReject = ['p+6', 'p-9'];
|
|
176
|
-
const kvStoreDataV1KeysToDelete = ['vc.4.sp+6', 'vc.4.sp-9'];
|
|
177
|
-
const kvStoreDataV1VpidsToKeep = ['p-8'];
|
|
178
|
-
const kvStoreDataV1KeysToKeep = ['vc.4.sp-8'];
|
|
179
|
-
|
|
180
|
-
test('past-incarnation watched promises', async t => {
|
|
181
|
-
const kvStore = new Map();
|
|
182
|
-
let { v, dispatch, dispatchMessage } = await setupTestLiveslots(
|
|
183
|
-
t,
|
|
184
|
-
buildPromiseWatcherRootObject,
|
|
185
|
-
'durable-promise-watcher',
|
|
186
|
-
{ kvStore },
|
|
187
|
-
);
|
|
188
|
-
let vatLogs = v.log;
|
|
189
|
-
|
|
190
|
-
// Anchor promise counters upon which the other assertions depend.
|
|
191
|
-
const firstPImport = 1;
|
|
192
|
-
// cf. src/liveslots.js:initialIDCounters
|
|
193
|
-
const firstPExport = 5;
|
|
194
|
-
let lastPImport = firstPImport - 1;
|
|
195
|
-
let lastPExport = firstPExport - 1;
|
|
196
|
-
const nextPImport = () => (lastPImport += 1);
|
|
197
|
-
const nextPExport = () => (lastPExport += 1);
|
|
198
|
-
// Ignore vatstore syscalls.
|
|
199
|
-
const getDispatchLogs = () =>
|
|
200
|
-
vatLogs.splice(0).filter(m => !m.type.startsWith('vatstore'));
|
|
201
|
-
const settlementMessage = (vpid, rejected, value) => ({
|
|
202
|
-
type: 'resolve',
|
|
203
|
-
resolutions: [[vpid, rejected, kser(value)]],
|
|
204
|
-
});
|
|
205
|
-
const fulfillmentMessage = (vpid, value) =>
|
|
206
|
-
settlementMessage(vpid, false, value);
|
|
207
|
-
const rejectionMessage = (vpid, value) =>
|
|
208
|
-
settlementMessage(vpid, true, value);
|
|
209
|
-
const subscribeMessage = vpid => ({
|
|
210
|
-
type: 'subscribe',
|
|
211
|
-
target: vpid,
|
|
212
|
-
});
|
|
213
|
-
vatLogs.length = 0;
|
|
214
|
-
await dispatchMessage('exportPromise');
|
|
215
|
-
t.deepEqual(getDispatchLogs(), [
|
|
216
|
-
fulfillmentMessage(`p-${nextPImport()}`, [kslot(`p+${nextPExport()}`)]),
|
|
217
|
-
fulfillmentMessage(`p+${lastPExport}`, undefined),
|
|
218
|
-
]);
|
|
219
|
-
|
|
220
|
-
const S = 'settlement';
|
|
221
|
-
await dispatchMessage('createLocalPromise', 'orphaned');
|
|
222
|
-
t.deepEqual(getDispatchLogs(), [
|
|
223
|
-
fulfillmentMessage(`p-${nextPImport()}`, 'created local promise: orphaned'),
|
|
224
|
-
]);
|
|
225
|
-
await dispatchMessage('createLocalPromise', 'fulfilled', S);
|
|
226
|
-
t.deepEqual(getDispatchLogs(), [
|
|
227
|
-
fulfillmentMessage(
|
|
228
|
-
`p-${nextPImport()}`,
|
|
229
|
-
'created local promise: fulfilled',
|
|
230
|
-
),
|
|
231
|
-
]);
|
|
232
|
-
await dispatchMessage('createLocalPromise', 'rejected', undefined, S);
|
|
233
|
-
t.deepEqual(getDispatchLogs(), [
|
|
234
|
-
fulfillmentMessage(`p-${nextPImport()}`, 'created local promise: rejected'),
|
|
235
|
-
]);
|
|
236
|
-
t.deepEqual(
|
|
237
|
-
lastPImport - firstPImport + 1,
|
|
238
|
-
4,
|
|
239
|
-
'imported 4 promises (1 per dispatch)',
|
|
240
|
-
);
|
|
241
|
-
t.deepEqual(lastPExport - firstPExport + 1, 1, 'exported 1 promise: first');
|
|
242
|
-
|
|
243
|
-
await dispatchMessage('watchLocalPromise', 'orphaned');
|
|
244
|
-
t.deepEqual(getDispatchLogs(), [
|
|
245
|
-
subscribeMessage(`p+${nextPExport()}`),
|
|
246
|
-
fulfillmentMessage(`p-${nextPImport()}`, 'watched local promise: orphaned'),
|
|
247
|
-
]);
|
|
248
|
-
await dispatchMessage('watchLocalPromise', 'fulfilled');
|
|
249
|
-
t.deepEqual(getDispatchLogs(), [
|
|
250
|
-
subscribeMessage(`p+${nextPExport()}`),
|
|
251
|
-
fulfillmentMessage(
|
|
252
|
-
`p-${nextPImport()}`,
|
|
253
|
-
'watched local promise: fulfilled',
|
|
254
|
-
),
|
|
255
|
-
fulfillmentMessage(`p+${lastPExport}`, S),
|
|
256
|
-
]);
|
|
257
|
-
await dispatchMessage('watchLocalPromise', 'rejected');
|
|
258
|
-
t.deepEqual(getDispatchLogs(), [
|
|
259
|
-
subscribeMessage(`p+${nextPExport()}`),
|
|
260
|
-
fulfillmentMessage(`p-${nextPImport()}`, 'watched local promise: rejected'),
|
|
261
|
-
rejectionMessage(`p+${lastPExport}`, S),
|
|
262
|
-
]);
|
|
263
|
-
t.deepEqual(
|
|
264
|
-
lastPImport - firstPImport + 1,
|
|
265
|
-
7,
|
|
266
|
-
'imported 7 promises (1 per dispatch)',
|
|
267
|
-
);
|
|
268
|
-
t.deepEqual(
|
|
269
|
-
lastPExport - firstPExport + 1,
|
|
270
|
-
4,
|
|
271
|
-
'exported 4 promises: first, orphaned, fulfilled, rejected',
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
// Simulate upgrade by starting from the non-empty kvStore.
|
|
275
|
-
// t.log(Object.fromEntries([...kvStore.entries()].sort(compareEntriesByKey)));
|
|
276
|
-
const clonedStore = new Map(kvStore);
|
|
277
|
-
({ v, dispatch, dispatchMessage } = await setupTestLiveslots(
|
|
278
|
-
t,
|
|
279
|
-
buildPromiseWatcherRootObject,
|
|
280
|
-
'durable-promise-watcher-v2',
|
|
281
|
-
{ kvStore: clonedStore, nextPromiseImportNumber: lastPImport + 1 },
|
|
282
|
-
));
|
|
283
|
-
vatLogs = v.log;
|
|
284
|
-
|
|
285
|
-
// Simulate kernel rejection of promises orphaned by termination/upgrade of their decider vat.
|
|
286
|
-
const expectedDeletions = [...clonedStore.entries()].filter(entry =>
|
|
287
|
-
entry[1].includes('orphaned'),
|
|
288
|
-
);
|
|
289
|
-
t.true(expectedDeletions.length >= 1);
|
|
290
|
-
await dispatch(
|
|
291
|
-
makeReject(`p+${firstPExport + 1}`, kser('tomorrow never came')),
|
|
292
|
-
);
|
|
293
|
-
for (const [key, value] of expectedDeletions) {
|
|
294
|
-
t.false(clonedStore.has(key), `entry should be removed: ${key}: ${value}`);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Verify that the data is still in loadable condition.
|
|
298
|
-
const finalClonedStore = new Map(clonedStore);
|
|
299
|
-
({ v, dispatch, dispatchMessage } = await setupTestLiveslots(
|
|
300
|
-
t,
|
|
301
|
-
buildPromiseWatcherRootObject,
|
|
302
|
-
'durable-promise-watcher-final',
|
|
303
|
-
{ kvStore: finalClonedStore, nextPromiseImportNumber: lastPImport + 1 },
|
|
304
|
-
));
|
|
305
|
-
vatLogs = v.log;
|
|
306
|
-
vatLogs.length = 0;
|
|
307
|
-
await dispatchMessage('createLocalPromise', 'final', S);
|
|
308
|
-
await dispatchMessage('watchLocalPromise', 'final');
|
|
309
|
-
t.deepEqual(getDispatchLogs(), [
|
|
310
|
-
fulfillmentMessage(`p-${nextPImport()}`, 'created local promise: final'),
|
|
311
|
-
subscribeMessage(`p+${nextPExport()}`),
|
|
312
|
-
fulfillmentMessage(`p-${nextPImport()}`, 'watched local promise: final'),
|
|
313
|
-
fulfillmentMessage(`p+${lastPExport}`, S),
|
|
314
|
-
]);
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
test('past-incarnation watched promises from original-format kvStore', async t => {
|
|
318
|
-
const kvStore = new Map(kvStoreDataV1);
|
|
319
|
-
for (const key of [
|
|
320
|
-
...kvStoreDataV1KeysToDelete,
|
|
321
|
-
...kvStoreDataV1KeysToKeep,
|
|
322
|
-
]) {
|
|
323
|
-
t.true(kvStore.has(key), `key must be initially present: ${key}`);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
let { v, dispatch, dispatchMessage } = await setupTestLiveslots(
|
|
327
|
-
t,
|
|
328
|
-
buildPromiseWatcherRootObject,
|
|
329
|
-
'durable-promise-watcher',
|
|
330
|
-
{ kvStore, nextPromiseImportNumber: 100 },
|
|
331
|
-
);
|
|
332
|
-
let vatLogs = v.log;
|
|
333
|
-
for (const vpid of kvStoreDataV1VpidsToReject) {
|
|
334
|
-
await dispatch(makeReject(vpid, kser('tomorrow never came')));
|
|
335
|
-
}
|
|
336
|
-
for (const key of kvStoreDataV1KeysToDelete) {
|
|
337
|
-
t.false(kvStore.has(key), `key should be removed: ${key}`);
|
|
338
|
-
}
|
|
339
|
-
for (const key of kvStoreDataV1KeysToKeep) {
|
|
340
|
-
t.true(kvStore.has(key), `key should remain: ${key}`);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Verify that the data is still in loadable condition.
|
|
344
|
-
const finalClonedStore = new Map(kvStore);
|
|
345
|
-
// eslint-disable-next-line no-unused-vars
|
|
346
|
-
({ v, dispatch, dispatchMessage } = await setupTestLiveslots(
|
|
347
|
-
t,
|
|
348
|
-
buildPromiseWatcherRootObject,
|
|
349
|
-
'durable-promise-watcher-final',
|
|
350
|
-
{ kvStore: finalClonedStore, nextPromiseImportNumber: 200 },
|
|
351
|
-
));
|
|
352
|
-
vatLogs = v.log;
|
|
353
|
-
vatLogs.length = 0;
|
|
354
|
-
for (const vpid of kvStoreDataV1VpidsToKeep) {
|
|
355
|
-
await dispatch(makeResolve(vpid, kser('finally')));
|
|
356
|
-
}
|
|
357
|
-
for (const key of kvStoreDataV1KeysToKeep) {
|
|
358
|
-
t.false(finalClonedStore.has(key), `key should be removed: ${key}`);
|
|
359
|
-
}
|
|
360
|
-
});
|