@agoric/swingset-liveslots 0.10.3-u11.0 → 0.10.3-u12.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 +16 -0
- package/package.json +17 -17
- package/tools/fakeCollectionManager.js +44 -0
- package/tools/fakeVirtualObjectManager.js +60 -0
- package/tools/fakeVirtualSupport.js +357 -0
- package/tools/vo-test-harness.js +143 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
### [0.10.3-u12.0](https://github.com/Agoric/agoric-sdk/compare/@agoric/swingset-liveslots@0.10.3-u11wf.0...@agoric/swingset-liveslots@0.10.3-u12.0) (2023-11-10)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @agoric/swingset-liveslots
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### [0.10.3-u11wf.0](https://github.com/Agoric/agoric-sdk/compare/@agoric/swingset-liveslots@0.10.3-u11.0...@agoric/swingset-liveslots@0.10.3-u11wf.0) (2023-09-23)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @agoric/swingset-liveslots
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
### [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
23
|
|
|
8
24
|
**Note:** Version bump only for package @agoric/swingset-liveslots
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/swingset-liveslots",
|
|
3
|
-
"version": "0.10.3-
|
|
3
|
+
"version": "0.10.3-u12.0",
|
|
4
4
|
"description": "SwingSet ocap support layer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -17,28 +17,28 @@
|
|
|
17
17
|
"lint:eslint": "eslint ."
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@agoric/assert": "^0.6.0",
|
|
21
|
-
"@agoric/internal": "^0.
|
|
22
|
-
"@agoric/store": "^0.9.3-
|
|
23
|
-
"@agoric/vat-data": "^0.5.3-
|
|
24
|
-
"@endo/eventual-send": "
|
|
25
|
-
"@endo/exo": "
|
|
26
|
-
"@endo/
|
|
27
|
-
"@endo/
|
|
28
|
-
"@endo/
|
|
29
|
-
"@endo/
|
|
30
|
-
"@endo/
|
|
31
|
-
"@endo/
|
|
20
|
+
"@agoric/assert": "^0.6.1-u11wf.0",
|
|
21
|
+
"@agoric/internal": "^0.4.0-u12.0",
|
|
22
|
+
"@agoric/store": "^0.9.3-u12.0",
|
|
23
|
+
"@agoric/vat-data": "^0.5.3-u12.0",
|
|
24
|
+
"@endo/eventual-send": "0.17.2",
|
|
25
|
+
"@endo/exo": "0.2.2",
|
|
26
|
+
"@endo/far": "0.2.18",
|
|
27
|
+
"@endo/init": "0.5.56",
|
|
28
|
+
"@endo/marshal": "0.8.5",
|
|
29
|
+
"@endo/nat": "4.1.27",
|
|
30
|
+
"@endo/pass-style": "0.1.3",
|
|
31
|
+
"@endo/patterns": "0.2.2",
|
|
32
|
+
"@endo/promise-kit": "0.2.56"
|
|
32
33
|
},
|
|
33
|
-
"
|
|
34
|
-
"@endo/far": "^0.2.18",
|
|
35
|
-
"@endo/ses-ava": "^0.2.40",
|
|
34
|
+
"devDependencies": {
|
|
36
35
|
"ava": "^5.2.0"
|
|
37
36
|
},
|
|
38
37
|
"files": [
|
|
39
38
|
"src/**/*.js",
|
|
40
39
|
"src/**/*.d.ts",
|
|
41
40
|
"test/**/*.js",
|
|
41
|
+
"tools",
|
|
42
42
|
"exported.js"
|
|
43
43
|
],
|
|
44
44
|
"repository": {
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"access": "public"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "ee5a5fdad9187a6b1b7b26b772b6564c0db3062e"
|
|
65
65
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { makeCollectionManager } from '../src/collectionManager.js';
|
|
2
|
+
|
|
3
|
+
export function makeFakeCollectionManager(vrm, fakeStuff, _options = {}) {
|
|
4
|
+
const {
|
|
5
|
+
makeScalarBigMapStore,
|
|
6
|
+
makeScalarBigWeakMapStore,
|
|
7
|
+
makeScalarBigSetStore,
|
|
8
|
+
makeScalarBigWeakSetStore,
|
|
9
|
+
provideBaggage,
|
|
10
|
+
initializeStoreKindInfo,
|
|
11
|
+
flushSchemaCache,
|
|
12
|
+
} = makeCollectionManager(
|
|
13
|
+
fakeStuff.syscall,
|
|
14
|
+
vrm,
|
|
15
|
+
fakeStuff.allocateExportID,
|
|
16
|
+
fakeStuff.allocateCollectionID,
|
|
17
|
+
fakeStuff.convertValToSlot,
|
|
18
|
+
fakeStuff.convertSlotToVal,
|
|
19
|
+
fakeStuff.registerEntry,
|
|
20
|
+
fakeStuff.marshal.serialize,
|
|
21
|
+
fakeStuff.marshal.unserialize,
|
|
22
|
+
fakeStuff.assertAcceptableSyscallCapdataSize,
|
|
23
|
+
);
|
|
24
|
+
initializeStoreKindInfo();
|
|
25
|
+
|
|
26
|
+
const normalCM = {
|
|
27
|
+
makeScalarBigMapStore,
|
|
28
|
+
makeScalarBigWeakMapStore,
|
|
29
|
+
makeScalarBigSetStore,
|
|
30
|
+
makeScalarBigWeakSetStore,
|
|
31
|
+
provideBaggage,
|
|
32
|
+
flushSchemaCache,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const debugTools = {
|
|
36
|
+
getValForSlot: fakeStuff.getValForSlot,
|
|
37
|
+
setValForSlot: fakeStuff.setValForSlot,
|
|
38
|
+
registerEntry: fakeStuff.registerEntry,
|
|
39
|
+
deleteEntry: fakeStuff.deleteEntry,
|
|
40
|
+
dumpStore: fakeStuff.dumpStore,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return harden({ ...normalCM, ...debugTools });
|
|
44
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { makeVirtualObjectManager } from '../src/virtualObjectManager.js';
|
|
2
|
+
|
|
3
|
+
// Note: `fakeVirtualObjectManager` is something of a misnomer here. The
|
|
4
|
+
// virtual object manager being used to implement this realized by the actual
|
|
5
|
+
// virtual object manager code. What's being faked is everything else the
|
|
6
|
+
// virtual object manager is embedded in, i.e., the kernel and the rest of
|
|
7
|
+
// liveslots. In particular, this module can be (and is, and is intended to be)
|
|
8
|
+
// used for unit tests for the virtual object manager itself. What you get back
|
|
9
|
+
// from `makeFakeVirtualObjectManager` can't be used to program as if you were
|
|
10
|
+
// running in a vat because the rest of the vat environment is not present, but
|
|
11
|
+
// it *will* execute virtual object manager operations in the same way that the
|
|
12
|
+
// real one will because underneath it *is* the real one.
|
|
13
|
+
|
|
14
|
+
export function makeFakeVirtualObjectManager(vrm, fakeStuff) {
|
|
15
|
+
const {
|
|
16
|
+
initializeKindHandleKind,
|
|
17
|
+
defineKind,
|
|
18
|
+
defineKindMulti,
|
|
19
|
+
defineDurableKind,
|
|
20
|
+
defineDurableKindMulti,
|
|
21
|
+
makeKindHandle,
|
|
22
|
+
VirtualObjectAwareWeakMap,
|
|
23
|
+
VirtualObjectAwareWeakSet,
|
|
24
|
+
flushStateCache,
|
|
25
|
+
canBeDurable,
|
|
26
|
+
} = makeVirtualObjectManager(
|
|
27
|
+
fakeStuff.syscall,
|
|
28
|
+
vrm,
|
|
29
|
+
fakeStuff.allocateExportID,
|
|
30
|
+
fakeStuff.getSlotForVal,
|
|
31
|
+
fakeStuff.requiredValForSlot,
|
|
32
|
+
fakeStuff.registerEntry,
|
|
33
|
+
fakeStuff.marshal.serialize,
|
|
34
|
+
fakeStuff.marshal.unserialize,
|
|
35
|
+
fakeStuff.assertAcceptableSyscallCapdataSize,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const normalVOM = {
|
|
39
|
+
initializeKindHandleKind,
|
|
40
|
+
defineKind,
|
|
41
|
+
defineKindMulti,
|
|
42
|
+
defineDurableKind,
|
|
43
|
+
defineDurableKindMulti,
|
|
44
|
+
makeKindHandle,
|
|
45
|
+
canBeDurable,
|
|
46
|
+
VirtualObjectAwareWeakMap,
|
|
47
|
+
VirtualObjectAwareWeakSet,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const debugTools = {
|
|
51
|
+
getValForSlot: fakeStuff.getValForSlot,
|
|
52
|
+
setValForSlot: fakeStuff.setValForSlot,
|
|
53
|
+
registerEntry: fakeStuff.registerEntry,
|
|
54
|
+
deleteEntry: fakeStuff.deleteEntry,
|
|
55
|
+
flushStateCache,
|
|
56
|
+
dumpStore: fakeStuff.dumpStore,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return harden({ ...normalVOM, ...debugTools });
|
|
60
|
+
}
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/* global WeakRef */
|
|
2
|
+
/* eslint-disable max-classes-per-file */
|
|
3
|
+
import { makeMarshal } from '@endo/marshal';
|
|
4
|
+
import { assert } from '@agoric/assert';
|
|
5
|
+
import { parseVatSlot } from '../src/parseVatSlots.js';
|
|
6
|
+
|
|
7
|
+
import { makeVirtualReferenceManager } from '../src/virtualReferences.js';
|
|
8
|
+
import { makeWatchedPromiseManager } from '../src/watchedPromises.js';
|
|
9
|
+
import { makeFakeVirtualObjectManager } from './fakeVirtualObjectManager.js';
|
|
10
|
+
import { makeFakeCollectionManager } from './fakeCollectionManager.js';
|
|
11
|
+
|
|
12
|
+
class FakeFinalizationRegistry {
|
|
13
|
+
// eslint-disable-next-line no-useless-constructor, no-empty-function
|
|
14
|
+
constructor() {}
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line class-methods-use-this
|
|
17
|
+
register(_target, _heldValue, _unregisterToken) {}
|
|
18
|
+
|
|
19
|
+
// eslint-disable-next-line class-methods-use-this
|
|
20
|
+
unregister(_unregisterToken) {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class FakeWeakRef {
|
|
24
|
+
constructor(target) {
|
|
25
|
+
this.target = target;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
deref() {
|
|
29
|
+
return this.target; // strong ref
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const RealWeakRef = WeakRef;
|
|
34
|
+
|
|
35
|
+
export function makeFakeLiveSlotsStuff(options = {}) {
|
|
36
|
+
let vrm;
|
|
37
|
+
function setVrm(vrmToUse) {
|
|
38
|
+
assert(!vrm, 'vrm already configured');
|
|
39
|
+
vrm = vrmToUse;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
fakeStore = new Map(),
|
|
44
|
+
weak = false,
|
|
45
|
+
log,
|
|
46
|
+
FinalizationRegistry = FakeFinalizationRegistry,
|
|
47
|
+
WeakRef = FakeWeakRef, // VRM uses this
|
|
48
|
+
addToPossiblyDeadSet = () => {},
|
|
49
|
+
addToPossiblyRetiredSet = () => {},
|
|
50
|
+
} = options;
|
|
51
|
+
|
|
52
|
+
let sortedKeys;
|
|
53
|
+
let priorKeyReturned;
|
|
54
|
+
let priorKeyIndex;
|
|
55
|
+
|
|
56
|
+
function s(v) {
|
|
57
|
+
switch (typeof v) {
|
|
58
|
+
case 'symbol':
|
|
59
|
+
return v.toString();
|
|
60
|
+
case 'bigint':
|
|
61
|
+
return `${v}n`;
|
|
62
|
+
default:
|
|
63
|
+
return `${v}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function ensureSorted() {
|
|
68
|
+
if (!sortedKeys) {
|
|
69
|
+
sortedKeys = [];
|
|
70
|
+
for (const key of fakeStore.keys()) {
|
|
71
|
+
sortedKeys.push(key);
|
|
72
|
+
}
|
|
73
|
+
sortedKeys.sort((k1, k2) => k1.localeCompare(k2));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function clearGetNextKeyCache() {
|
|
78
|
+
priorKeyReturned = undefined;
|
|
79
|
+
priorKeyIndex = -1;
|
|
80
|
+
}
|
|
81
|
+
clearGetNextKeyCache();
|
|
82
|
+
|
|
83
|
+
function clearSorted() {
|
|
84
|
+
sortedKeys = undefined;
|
|
85
|
+
clearGetNextKeyCache();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function dumpStore() {
|
|
89
|
+
ensureSorted();
|
|
90
|
+
const result = [];
|
|
91
|
+
for (const key of sortedKeys) {
|
|
92
|
+
result.push([key, fakeStore.get(key)]);
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const syscall = {
|
|
98
|
+
vatstoreGet(key) {
|
|
99
|
+
const result = fakeStore.get(key);
|
|
100
|
+
if (log) {
|
|
101
|
+
log.push(`get ${s(key)} => ${s(result)}`);
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
},
|
|
105
|
+
vatstoreGetNextKey(priorKey) {
|
|
106
|
+
assert.typeof(priorKey, 'string');
|
|
107
|
+
ensureSorted();
|
|
108
|
+
// TODO: binary search for priorKey (maybe missing), then get
|
|
109
|
+
// the one after that. For now we go simple and slow. But cache
|
|
110
|
+
// a starting point, because the main use case is a full
|
|
111
|
+
// iteration. OTOH, the main use case also deletes everything,
|
|
112
|
+
// which will clobber the cache on each deletion, so it might
|
|
113
|
+
// not help.
|
|
114
|
+
const start = priorKeyReturned === priorKey ? priorKeyIndex : 0;
|
|
115
|
+
let result;
|
|
116
|
+
for (let i = start; i < sortedKeys.length; i += 1) {
|
|
117
|
+
const key = sortedKeys[i];
|
|
118
|
+
if (key > priorKey) {
|
|
119
|
+
priorKeyReturned = key;
|
|
120
|
+
priorKeyIndex = i;
|
|
121
|
+
result = key;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (!result) {
|
|
126
|
+
// reached end without finding the key, so clear our cache
|
|
127
|
+
clearGetNextKeyCache();
|
|
128
|
+
}
|
|
129
|
+
if (log) {
|
|
130
|
+
log.push(`getNextKey ${s(priorKey)} => ${s(result)}`);
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
},
|
|
134
|
+
vatstoreSet(key, value) {
|
|
135
|
+
if (log) {
|
|
136
|
+
log.push(`set ${s(key)} ${s(value)}`);
|
|
137
|
+
}
|
|
138
|
+
if (!fakeStore.has(key)) {
|
|
139
|
+
clearSorted();
|
|
140
|
+
}
|
|
141
|
+
fakeStore.set(key, value);
|
|
142
|
+
},
|
|
143
|
+
vatstoreDelete(key) {
|
|
144
|
+
if (log) {
|
|
145
|
+
log.push(`delete ${s(key)}`);
|
|
146
|
+
}
|
|
147
|
+
if (fakeStore.has(key)) {
|
|
148
|
+
clearSorted();
|
|
149
|
+
}
|
|
150
|
+
fakeStore.delete(key);
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
let nextExportID = 1;
|
|
155
|
+
function allocateExportID() {
|
|
156
|
+
const exportID = nextExportID;
|
|
157
|
+
nextExportID += 1;
|
|
158
|
+
return exportID;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let nextCollectionID = 1;
|
|
162
|
+
function allocateCollectionID() {
|
|
163
|
+
const collectionID = nextCollectionID;
|
|
164
|
+
nextCollectionID += 1;
|
|
165
|
+
return collectionID;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// note: The real liveslots slotToVal() maps slots (vrefs) to a WeakRef,
|
|
169
|
+
// and the WeakRef may or may not contain the target value. Use
|
|
170
|
+
// options={weak:true} to match that behavior, or the default weak:false to
|
|
171
|
+
// keep strong references.
|
|
172
|
+
const valToSlot = new WeakMap();
|
|
173
|
+
const slotToVal = new Map();
|
|
174
|
+
|
|
175
|
+
function getSlotForVal(val) {
|
|
176
|
+
return valToSlot.get(val);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function getValForSlot(slot) {
|
|
180
|
+
const d = slotToVal.get(slot);
|
|
181
|
+
return d && (weak ? d.deref() : d);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function requiredValForSlot(slot) {
|
|
185
|
+
const val = getValForSlot(slot);
|
|
186
|
+
assert(val, `${slot} must have a value`);
|
|
187
|
+
return val;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function setValForSlot(slot, val) {
|
|
191
|
+
slotToVal.set(slot, weak ? new RealWeakRef(val) : val);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function convertValToSlot(val) {
|
|
195
|
+
if (!valToSlot.has(val)) {
|
|
196
|
+
const slot = `o+${allocateExportID()}`;
|
|
197
|
+
valToSlot.set(val, slot);
|
|
198
|
+
setValForSlot(slot, val);
|
|
199
|
+
}
|
|
200
|
+
return valToSlot.get(val);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function convertSlotToVal(slot) {
|
|
204
|
+
const { type, id, virtual, durable, facet, baseRef } = parseVatSlot(slot);
|
|
205
|
+
assert.equal(type, 'object');
|
|
206
|
+
let val = getValForSlot(baseRef);
|
|
207
|
+
if (val) {
|
|
208
|
+
if (virtual || durable) {
|
|
209
|
+
if (facet !== undefined) {
|
|
210
|
+
return vrm.getFacet(id, val, facet);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return val;
|
|
214
|
+
}
|
|
215
|
+
if (virtual || durable) {
|
|
216
|
+
if (vrm) {
|
|
217
|
+
val = vrm.reanimate(slot);
|
|
218
|
+
if (facet !== undefined) {
|
|
219
|
+
return vrm.getFacet(id, val, facet);
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
assert.fail('fake liveSlots stuff configured without vrm');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return val;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const marshal = makeMarshal(convertValToSlot, convertSlotToVal, {
|
|
229
|
+
serializeBodyFormat: 'smallcaps',
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
function registerEntry(baseRef, val, valIsCohort) {
|
|
233
|
+
setValForSlot(baseRef, val);
|
|
234
|
+
if (valIsCohort) {
|
|
235
|
+
const { id } = parseVatSlot(baseRef);
|
|
236
|
+
vrm.getFacetNames(id).forEach((name, index) => {
|
|
237
|
+
valToSlot.set(val[name], `${baseRef}:${index}`);
|
|
238
|
+
});
|
|
239
|
+
} else {
|
|
240
|
+
valToSlot.set(val, baseRef);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function deleteEntry(slot, val) {
|
|
245
|
+
if (!val) {
|
|
246
|
+
val = getValForSlot(slot);
|
|
247
|
+
}
|
|
248
|
+
slotToVal.delete(slot);
|
|
249
|
+
valToSlot.delete(val);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function assertAcceptableSyscallCapdataSize(_capdatas) {}
|
|
253
|
+
|
|
254
|
+
const maybeExportPromise = _vref => false;
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
syscall,
|
|
258
|
+
allocateExportID,
|
|
259
|
+
allocateCollectionID,
|
|
260
|
+
getSlotForVal,
|
|
261
|
+
requiredValForSlot,
|
|
262
|
+
getValForSlot,
|
|
263
|
+
setValForSlot,
|
|
264
|
+
registerEntry,
|
|
265
|
+
valToSlot,
|
|
266
|
+
slotToVal,
|
|
267
|
+
convertValToSlot,
|
|
268
|
+
convertSlotToVal,
|
|
269
|
+
marshal,
|
|
270
|
+
deleteEntry,
|
|
271
|
+
FinalizationRegistry,
|
|
272
|
+
WeakRef,
|
|
273
|
+
addToPossiblyDeadSet,
|
|
274
|
+
addToPossiblyRetiredSet,
|
|
275
|
+
dumpStore,
|
|
276
|
+
setVrm,
|
|
277
|
+
assertAcceptableSyscallCapdataSize,
|
|
278
|
+
maybeExportPromise,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function makeFakeVirtualReferenceManager(
|
|
283
|
+
fakeStuff,
|
|
284
|
+
relaxDurabilityRules = true,
|
|
285
|
+
) {
|
|
286
|
+
return makeVirtualReferenceManager(
|
|
287
|
+
fakeStuff.syscall,
|
|
288
|
+
fakeStuff.getSlotForVal,
|
|
289
|
+
fakeStuff.getValForSlot,
|
|
290
|
+
fakeStuff.FinalizationRegistry,
|
|
291
|
+
fakeStuff.WeakRef,
|
|
292
|
+
fakeStuff.addToPossiblyDeadSet,
|
|
293
|
+
fakeStuff.addToPossiblyRetiredSet,
|
|
294
|
+
relaxDurabilityRules,
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function makeFakeWatchedPromiseManager(
|
|
299
|
+
vrm,
|
|
300
|
+
vom,
|
|
301
|
+
collectionManager,
|
|
302
|
+
fakeStuff,
|
|
303
|
+
) {
|
|
304
|
+
return makeWatchedPromiseManager({
|
|
305
|
+
syscall: fakeStuff.syscall,
|
|
306
|
+
vrm,
|
|
307
|
+
vom,
|
|
308
|
+
collectionManager,
|
|
309
|
+
convertValToSlot: fakeStuff.convertValToSlot,
|
|
310
|
+
convertSlotToVal: fakeStuff.convertSlotToVal,
|
|
311
|
+
maybeExportPromise: fakeStuff.maybeExportPromise,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Configure virtual stuff with relaxed durability rules and fake liveslots
|
|
316
|
+
*
|
|
317
|
+
* @param {object} [options]
|
|
318
|
+
* @param {number} [options.cacheSize=3]
|
|
319
|
+
* @param {boolean} [options.relaxDurabilityRules=true]
|
|
320
|
+
*/
|
|
321
|
+
export function makeFakeVirtualStuff(options = {}) {
|
|
322
|
+
const actualOptions = {
|
|
323
|
+
relaxDurabilityRules: true,
|
|
324
|
+
...options,
|
|
325
|
+
};
|
|
326
|
+
const { relaxDurabilityRules } = actualOptions;
|
|
327
|
+
const fakeStuff = makeFakeLiveSlotsStuff(actualOptions);
|
|
328
|
+
const vrm = makeFakeVirtualReferenceManager(fakeStuff, relaxDurabilityRules);
|
|
329
|
+
const vom = makeFakeVirtualObjectManager(vrm, fakeStuff);
|
|
330
|
+
vom.initializeKindHandleKind();
|
|
331
|
+
fakeStuff.setVrm(vrm);
|
|
332
|
+
const cm = makeFakeCollectionManager(vrm, fakeStuff, actualOptions);
|
|
333
|
+
const wpm = makeFakeWatchedPromiseManager(vrm, vom, cm, fakeStuff);
|
|
334
|
+
return { fakeStuff, vrm, vom, cm, wpm };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function makeStandaloneFakeVirtualObjectManager(options = {}) {
|
|
338
|
+
const fakeStuff = makeFakeLiveSlotsStuff(options);
|
|
339
|
+
const { relaxDurabilityRules = true } = options;
|
|
340
|
+
const vrm = makeFakeVirtualReferenceManager(fakeStuff, relaxDurabilityRules);
|
|
341
|
+
const vom = makeFakeVirtualObjectManager(vrm, fakeStuff);
|
|
342
|
+
vom.initializeKindHandleKind();
|
|
343
|
+
fakeStuff.setVrm(vrm);
|
|
344
|
+
return vom;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function makeStandaloneFakeCollectionManager(options = {}) {
|
|
348
|
+
const fakeStuff = makeFakeLiveSlotsStuff(options);
|
|
349
|
+
const { relaxDurabilityRules = true } = options;
|
|
350
|
+
const vrm = makeFakeVirtualReferenceManager(fakeStuff, relaxDurabilityRules);
|
|
351
|
+
return makeFakeCollectionManager(vrm, fakeStuff, options);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export {
|
|
355
|
+
makeStandaloneFakeVirtualObjectManager as makeFakeVirtualObjectManager,
|
|
356
|
+
makeStandaloneFakeCollectionManager as makeFakeCollectionManager,
|
|
357
|
+
};
|
|
@@ -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
|
+
}
|