@agoric/swingset-liveslots 0.10.3-upgrade-19-dev-5428c4d.0 → 0.10.3-upgrade-20-dev-31be299.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/package.json +16 -16
- package/src/collectionManager.d.ts +1 -0
- package/src/collectionManager.d.ts.map +1 -1
- package/src/collectionManager.js +1 -0
- package/src/liveslots.js +2 -2
- package/src/message.d.ts +10 -6
- package/src/message.d.ts.map +1 -1
- package/src/message.js +7 -3
- package/src/types.d.ts +8 -3
- package/src/types.d.ts.map +1 -1
- package/src/types.js +6 -5
- package/src/virtualObjectManager.d.ts.map +1 -1
- package/src/virtualObjectManager.js +70 -14
- package/src/watchedPromises.d.ts.map +1 -1
- package/src/watchedPromises.js +10 -13
- package/test/gc-helpers.js +2 -2
- package/test/handled-promises.test.js +529 -163
- package/test/initial-vrefs.test.js +12 -18
- package/test/liveslots-helpers.d.ts +1 -0
- package/test/liveslots-helpers.d.ts.map +1 -1
- package/test/liveslots-helpers.js +1 -0
- package/test/liveslots-real-gc.test.js +2 -2
- package/test/liveslots.test.js +3 -3
- package/test/storeGC/lifecycle.test.js +13 -12
- package/test/util.d.ts +1 -1
- package/test/util.d.ts.map +1 -1
- package/test/util.js +2 -2
- package/test/virtual-objects/state-shape.test.js +312 -221
- package/test/virtual-objects/virtualObjectGC.test.js +37 -36
- package/test/virtual-objects/virtualObjectManager.test.js +41 -63
- package/test/vo-test-harness.test.js +13 -9
- package/tools/fakeVirtualSupport.d.ts.map +1 -1
- package/tools/setup-vat-data.d.ts.map +1 -1
- package/tools/setup-vat-data.js +0 -1
- package/tools/vo-test-harness.d.ts +31 -0
- package/tools/vo-test-harness.d.ts.map +1 -1
- package/tools/vo-test-harness.js +21 -0
- package/test/watch-promise.test.js +0 -42
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import test from 'ava';
|
|
3
3
|
|
|
4
|
+
import { Fail } from '@endo/errors';
|
|
4
5
|
import { Far } from '@endo/marshal';
|
|
5
|
-
import { kser, kslot } from '@agoric/kmarshal';
|
|
6
|
+
import { kser, kslot, kunser } from '@agoric/kmarshal';
|
|
6
7
|
import { M } from '@agoric/store';
|
|
7
8
|
import { makeLiveSlots } from '../../src/liveslots.js';
|
|
8
9
|
import { buildSyscall } from '../liveslots-helpers.js';
|
|
@@ -10,160 +11,176 @@ import { makeStartVat, makeMessage } from '../util.js';
|
|
|
10
11
|
import { makeMockGC } from '../mock-gc.js';
|
|
11
12
|
import { makeFakeVirtualStuff } from '../../tools/fakeVirtualSupport.js';
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
/** @import {VatOneResolution} from '../../src/types.js'; */
|
|
15
|
+
/** @import {VatData} from '../../src/vatDataTypes.js'; */
|
|
16
|
+
|
|
17
|
+
let lastPnum = 100;
|
|
18
|
+
/**
|
|
19
|
+
* Send a "message" syscall into a liveslots instance and return the
|
|
20
|
+
* unserialized result, or an error if the result is unsettled or rejected.
|
|
21
|
+
*
|
|
22
|
+
* @param {ReturnType<typeof makeLiveSlots>} ls
|
|
23
|
+
* @param {ReturnType<typeof buildSyscall>['log']} syscallLog
|
|
24
|
+
* @param {[target: unknown, method: string | symbol, args?: unknown[]]} message
|
|
25
|
+
*/
|
|
26
|
+
const dispatchForResult = async (ls, syscallLog, message) => {
|
|
27
|
+
const oldSyscallCount = syscallLog.length;
|
|
28
|
+
lastPnum += 1;
|
|
29
|
+
const resultVpid = `p-${lastPnum}`;
|
|
30
|
+
const vdo = makeMessage(...message.concat(undefined).slice(0, 3), resultVpid);
|
|
31
|
+
await ls.dispatch(vdo);
|
|
32
|
+
const newSyscalls = syscallLog.slice(oldSyscallCount);
|
|
33
|
+
/** @type {VatOneResolution[]} */
|
|
34
|
+
const newResolutions = newSyscalls.flatMap(vso =>
|
|
35
|
+
vso.type === 'resolve' ? vso.resolutions : [],
|
|
36
|
+
);
|
|
37
|
+
const [_vpid, isRejection, capdata] =
|
|
38
|
+
newResolutions.find(resolution => resolution[0] === resultVpid) ||
|
|
39
|
+
Fail`unsettled by syscalls ${syscallLog.slice(oldSyscallCount)}`;
|
|
40
|
+
if (isRejection) throw Error('rejected', { cause: kunser(capdata) });
|
|
41
|
+
return kunser(capdata);
|
|
42
|
+
};
|
|
22
43
|
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
44
|
+
const eph1 = Far('ephemeral1');
|
|
45
|
+
const eph2 = Far('ephemeral2');
|
|
46
|
+
|
|
47
|
+
const initHolder = fields => ({ ...fields });
|
|
48
|
+
const holderMethods = {
|
|
49
|
+
get: ({ state }, ...fields) =>
|
|
50
|
+
// We require fields to be explicit because they are currently defined on
|
|
51
|
+
// the state *prototype*.
|
|
52
|
+
Object.fromEntries(
|
|
53
|
+
fields.flatMap(key => (key in state ? [[key, state[key]]] : [])),
|
|
54
|
+
),
|
|
55
|
+
set: ({ state }, fields) => {
|
|
56
|
+
Object.assign(state, fields);
|
|
57
|
+
},
|
|
26
58
|
};
|
|
59
|
+
/**
|
|
60
|
+
* Define a virtual or durable kind for getting and setting state constrained
|
|
61
|
+
* by the provided shape.
|
|
62
|
+
*
|
|
63
|
+
* @template {VatData.defineKind | VatData.defineDurableKind} D
|
|
64
|
+
* @param {D} defineKind
|
|
65
|
+
* @param {Parameters<D>[0]} kindIdentifier
|
|
66
|
+
* @param {import('@endo/patterns').Pattern} stateShape
|
|
67
|
+
*/
|
|
68
|
+
const defineHolder = (defineKind, kindIdentifier, stateShape) =>
|
|
69
|
+
defineKind(kindIdentifier, initHolder, holderMethods, { stateShape });
|
|
27
70
|
|
|
28
71
|
// virtual/durable Kinds can specify a 'stateShape', which should be
|
|
29
|
-
// enforced
|
|
30
|
-
|
|
31
|
-
|
|
72
|
+
// enforced at both initialization and subsequent state changes
|
|
73
|
+
const testStateShape = test.macro((t, valueShape, config) => {
|
|
74
|
+
const { goodValues, badValues, throwsExpectation } = config;
|
|
32
75
|
const { vom } = makeFakeVirtualStuff();
|
|
33
76
|
const { defineKind } = vom;
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// M.remotable() requires any Remotable
|
|
69
|
-
const remotableFail = { message: /Must be a remotable/ };
|
|
70
|
-
const makeD = defineKind('kindD', init, behavior, { stateShape: remotable });
|
|
71
|
-
const d = makeD(eph1);
|
|
72
|
-
makeD(eph2);
|
|
73
|
-
t.throws(() => makeD(1), remotableFail);
|
|
74
|
-
t.throws(() => makeD('string'), remotableFail);
|
|
75
|
-
d.set(eph2);
|
|
76
|
-
t.throws(() => d.set(2), remotableFail);
|
|
77
|
-
t.throws(() => d.set('string'), remotableFail);
|
|
78
|
-
|
|
79
|
-
// using a specific Remotable object requires that exact object
|
|
80
|
-
const eph1Fail = { message: /Must be:.*Alleged: ephemeral1/ };
|
|
81
|
-
const makeE = defineKind('kindE', init, behavior, { stateShape: eph });
|
|
82
|
-
const e = makeE(eph1);
|
|
83
|
-
t.throws(() => makeE(eph2), eph1Fail);
|
|
84
|
-
t.throws(() => makeE(1), eph1Fail);
|
|
85
|
-
t.throws(() => makeE('string'), eph1Fail);
|
|
86
|
-
e.set(eph1);
|
|
87
|
-
t.throws(() => e.set(eph2), eph1Fail);
|
|
88
|
-
t.throws(() => e.set(2), eph1Fail);
|
|
89
|
-
t.throws(() => e.set('string'), eph1Fail);
|
|
77
|
+
const makeHolder = defineHolder(defineKind, 'kindTag', { value: valueShape });
|
|
78
|
+
const instance = goodValues.map(value => makeHolder({ value })).at(-1);
|
|
79
|
+
for (const value of goodValues) {
|
|
80
|
+
instance.set({ value });
|
|
81
|
+
}
|
|
82
|
+
for (const value of badValues || []) {
|
|
83
|
+
t.throws(() => makeHolder({ value }), throwsExpectation);
|
|
84
|
+
t.throws(() => instance.set({ value }), throwsExpectation);
|
|
85
|
+
}
|
|
86
|
+
t.pass();
|
|
87
|
+
});
|
|
88
|
+
test('constrain state shape - M.any()', testStateShape, M.any(), {
|
|
89
|
+
goodValues: [eph1, eph2, 1, 2, 'string'],
|
|
90
|
+
});
|
|
91
|
+
test('constrain state shape - M.number()', testStateShape, M.number(), {
|
|
92
|
+
goodValues: [1],
|
|
93
|
+
badValues: [eph1, 'string'],
|
|
94
|
+
throwsExpectation: { message: /Must be a number/ },
|
|
95
|
+
});
|
|
96
|
+
test('constrain state shape - M.string()', testStateShape, M.string(), {
|
|
97
|
+
goodValues: ['string'],
|
|
98
|
+
badValues: [eph1, 1, 2],
|
|
99
|
+
throwsExpectation: { message: /Must be a string/ },
|
|
100
|
+
});
|
|
101
|
+
test('constrain state shape - M.remotable()', testStateShape, M.remotable(), {
|
|
102
|
+
goodValues: [eph1, eph2],
|
|
103
|
+
badValues: ['string', 1, 2],
|
|
104
|
+
throwsExpectation: { message: /Must be a remotable/ },
|
|
105
|
+
});
|
|
106
|
+
test('constrain state shape - specific remotable', testStateShape, eph1, {
|
|
107
|
+
goodValues: [eph1],
|
|
108
|
+
badValues: ['string', 1, 2],
|
|
109
|
+
throwsExpectation: { message: /Must be:.*Alleged: ephemeral1/ },
|
|
90
110
|
});
|
|
91
111
|
|
|
92
112
|
// durable Kinds serialize and store their stateShape, which must
|
|
93
113
|
// itself be durable
|
|
94
|
-
|
|
95
114
|
test('durable state shape', t => {
|
|
96
115
|
// note: relaxDurabilityRules defaults to true in fake tools
|
|
97
116
|
const { vom } = makeFakeVirtualStuff({ relaxDurabilityRules: false });
|
|
98
117
|
const { makeKindHandle, defineDurableKind } = vom;
|
|
99
118
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
119
|
+
let kindNumber = 0;
|
|
120
|
+
const defineNextKind = valueShape => {
|
|
121
|
+
kindNumber += 1;
|
|
122
|
+
const kh = makeKindHandle(`kind${kindNumber}`);
|
|
123
|
+
return defineHolder(defineDurableKind, kh, { value: valueShape });
|
|
103
124
|
};
|
|
104
125
|
|
|
105
|
-
const makeKind1 =
|
|
106
|
-
makeKind1();
|
|
126
|
+
const makeKind1 = defineNextKind();
|
|
127
|
+
makeKind1({ value: undefined });
|
|
107
128
|
|
|
108
|
-
const makeKind2 =
|
|
129
|
+
const makeKind2 = defineNextKind();
|
|
109
130
|
makeKind2();
|
|
110
131
|
|
|
111
|
-
const makeKind3 =
|
|
132
|
+
const makeKind3 = defineNextKind(M.any());
|
|
112
133
|
const obj3 = makeKind3();
|
|
113
134
|
|
|
114
|
-
const makeKind4 =
|
|
115
|
-
const obj4 = makeKind4('string');
|
|
135
|
+
const makeKind4 = defineNextKind(M.string());
|
|
136
|
+
const obj4 = makeKind4({ value: 'string' });
|
|
116
137
|
|
|
117
|
-
const makeKind5 =
|
|
138
|
+
const makeKind5 = defineNextKind(M.remotable());
|
|
118
139
|
const durableValueFail = { message: /value for "value" is not durable/ };
|
|
119
|
-
t.throws(() => makeKind5(eph1), durableValueFail);
|
|
140
|
+
t.throws(() => makeKind5({ value: eph1 }), durableValueFail);
|
|
120
141
|
|
|
121
142
|
const durableShapeFail = { message: /stateShape.*is not durable: slot 0 of/ };
|
|
122
|
-
t.throws(() =>
|
|
143
|
+
t.throws(() => defineNextKind(eph1), durableShapeFail);
|
|
123
144
|
|
|
124
|
-
const makeKind7 =
|
|
125
|
-
makeKind7(obj4);
|
|
145
|
+
const makeKind7 = defineNextKind(obj4); // obj4 is durable
|
|
146
|
+
makeKind7({ value: obj4 });
|
|
126
147
|
const specificRemotableFail = { message: /kind3.*Must be:.*kind4/ };
|
|
127
|
-
t.throws(() => makeKind7(obj3), specificRemotableFail);
|
|
148
|
+
t.throws(() => makeKind7({ value: obj3 }), specificRemotableFail);
|
|
128
149
|
});
|
|
129
150
|
|
|
130
151
|
// durable Kinds maintain refcounts on their serialized stateShape
|
|
131
|
-
|
|
132
152
|
test('durable stateShape refcounts', async t => {
|
|
133
153
|
const kvStore = new Map();
|
|
134
154
|
const { syscall: sc1 } = buildSyscall({ kvStore });
|
|
135
155
|
const gcTools = makeMockGC();
|
|
136
156
|
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
await ls1.dispatch(startVat1);
|
|
156
|
-
const rootA = 'o+0';
|
|
157
|
-
|
|
158
|
-
const standard1Vref = 'o-1';
|
|
159
|
-
await ls1.dispatch(makeMessage(rootA, 'accept', []));
|
|
160
|
-
t.falsy(ls1.testHooks.getReachableRefCount(standard1Vref));
|
|
157
|
+
const ls1 = makeLiveSlots(sc1, 'vatA', {}, {}, gcTools, undefined, () => {
|
|
158
|
+
const buildRootObject = (vatPowers, _vatParameters, baggage) => {
|
|
159
|
+
const { VatData } = vatPowers;
|
|
160
|
+
const { makeKindHandle, defineDurableKind } = VatData;
|
|
161
|
+
|
|
162
|
+
return Far('root', {
|
|
163
|
+
acceptRef: _ref => {}, // assigns a vref
|
|
164
|
+
defineDurableKind: valueShape => {
|
|
165
|
+
const kh = makeKindHandle('shaped');
|
|
166
|
+
baggage.init('kh', kh);
|
|
167
|
+
defineHolder(defineDurableKind, kh, { value: valueShape });
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
return { buildRootObject };
|
|
172
|
+
});
|
|
173
|
+
await ls1.dispatch(makeStartVat());
|
|
174
|
+
const root = 'o+0';
|
|
161
175
|
|
|
162
|
-
|
|
176
|
+
// accepting a ref but doing nothing with it does not increment its refcount
|
|
177
|
+
const vref1 = 'o-1';
|
|
178
|
+
await ls1.dispatch(makeMessage(root, 'acceptRef', [kslot(vref1)]));
|
|
179
|
+
t.is(ls1.testHooks.getReachableRefCount(vref1), 0);
|
|
163
180
|
|
|
164
|
-
// using
|
|
165
|
-
|
|
166
|
-
t.is(ls1.testHooks.getReachableRefCount(
|
|
181
|
+
// ...but using it in stateShape does
|
|
182
|
+
await ls1.dispatch(makeMessage(root, 'defineDurableKind', [kslot(vref1)]));
|
|
183
|
+
t.is(ls1.testHooks.getReachableRefCount(vref1), 1);
|
|
167
184
|
|
|
168
185
|
// ------
|
|
169
186
|
|
|
@@ -171,128 +188,202 @@ test('durable stateShape refcounts', async t => {
|
|
|
171
188
|
const clonedStore = new Map(kvStore);
|
|
172
189
|
const { syscall: sc2 } = buildSyscall({ kvStore: clonedStore });
|
|
173
190
|
|
|
174
|
-
function build2(vatPowers, vatParameters, baggage) {
|
|
175
|
-
const { VatData } = vatPowers;
|
|
176
|
-
const { defineDurableKind } = VatData;
|
|
177
|
-
const { standard2 } = vatParameters;
|
|
178
|
-
const kh = baggage.get('kh');
|
|
179
|
-
const stateShape = { value: standard2 };
|
|
180
|
-
defineDurableKind(kh, init, behavior, { stateShape });
|
|
181
|
-
|
|
182
|
-
return Far('root', {});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
191
|
// to test refcount increment/decrement, we need to override the
|
|
186
192
|
// usual rule that the new version must exactly match the original
|
|
187
193
|
// stateShape
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const vp = { standard2: kslot(standard2Vref) };
|
|
202
|
-
const startVat2 = makeStartVat(kser(vp));
|
|
203
|
-
await ls2.dispatch(startVat2);
|
|
204
|
-
|
|
205
|
-
// redefining the durable kind, with a different 'standard' object,
|
|
206
|
-
// will decrement the standard1 refcount, and increment that of
|
|
207
|
-
// standard2
|
|
194
|
+
const opts = { allowStateShapeChanges: true };
|
|
195
|
+
const ls2 = makeLiveSlots(sc2, 'vatA', {}, opts, gcTools, undefined, () => {
|
|
196
|
+
const buildRootObject = (vatPowers, vatParameters, baggage) => {
|
|
197
|
+
const { VatData } = vatPowers;
|
|
198
|
+
const { defineDurableKind } = VatData;
|
|
199
|
+
const { valueShape } = vatParameters;
|
|
200
|
+
const kh = baggage.get('kh');
|
|
201
|
+
defineHolder(defineDurableKind, kh, { value: valueShape });
|
|
202
|
+
|
|
203
|
+
return Far('root', {});
|
|
204
|
+
};
|
|
205
|
+
return { buildRootObject };
|
|
206
|
+
});
|
|
208
207
|
|
|
209
|
-
|
|
210
|
-
|
|
208
|
+
// redefining the durable kind's stateShape to replace its vref1 reference
|
|
209
|
+
// with a vref2 reference will decrement the former refcount and increment the
|
|
210
|
+
// latter
|
|
211
|
+
const vref2 = 'o-2';
|
|
212
|
+
const vatParameters = { valueShape: kslot(vref2) };
|
|
213
|
+
await ls2.dispatch(makeStartVat(kser(vatParameters)));
|
|
214
|
+
t.is(ls2.testHooks.getReachableRefCount(vref1), 0);
|
|
215
|
+
t.is(ls2.testHooks.getReachableRefCount(vref2), 1);
|
|
211
216
|
});
|
|
212
217
|
|
|
213
|
-
test('durable stateShape must match', async t => {
|
|
214
|
-
const
|
|
215
|
-
const { syscall: sc1 } = buildSyscall({ kvStore });
|
|
218
|
+
test('durable stateShape must match or extend', async t => {
|
|
219
|
+
const store1 = new Map();
|
|
220
|
+
const { syscall: sc1, log: log1 } = buildSyscall({ kvStore: store1 });
|
|
216
221
|
const gcTools = makeMockGC();
|
|
217
222
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
await ls1.dispatch(
|
|
236
|
-
const
|
|
223
|
+
const fields = ['x', 'x2', 'y']; // but "x2" is not initially present
|
|
224
|
+
const ls1 = makeLiveSlots(sc1, 'vatA', {}, {}, gcTools, undefined, () => {
|
|
225
|
+
const buildRootObject = (vatPowers, _vatParameters, baggage) => {
|
|
226
|
+
const { VatData } = vatPowers;
|
|
227
|
+
const { makeKindHandle, defineDurableKind } = VatData;
|
|
228
|
+
|
|
229
|
+
return Far('root', {
|
|
230
|
+
makeShapedDurable: (x, y) => {
|
|
231
|
+
const kh = makeKindHandle('shaped');
|
|
232
|
+
baggage.init('kh', kh);
|
|
233
|
+
const makeInstance = defineHolder(defineDurableKind, kh, { x, y });
|
|
234
|
+
return makeInstance(harden({ x, y }));
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
};
|
|
238
|
+
return { buildRootObject };
|
|
239
|
+
});
|
|
240
|
+
await ls1.dispatch(makeStartVat());
|
|
241
|
+
const root = 'o+0';
|
|
237
242
|
|
|
238
243
|
const vref1 = 'o-1';
|
|
239
244
|
const vref2 = 'o-2';
|
|
240
|
-
|
|
241
|
-
|
|
245
|
+
const passables = new Map([
|
|
246
|
+
[vref1, kslot(vref1, vref1)],
|
|
247
|
+
[vref2, kslot(vref2, vref2)],
|
|
248
|
+
]);
|
|
249
|
+
const instance1 = await dispatchForResult(ls1, log1, [
|
|
250
|
+
root,
|
|
251
|
+
'makeShapedDurable',
|
|
252
|
+
[passables.get(vref1), passables.get(vref2)],
|
|
253
|
+
]);
|
|
254
|
+
const objRef = instance1.getKref();
|
|
255
|
+
const state1 = await dispatchForResult(ls1, log1, [objRef, 'get', fields]);
|
|
256
|
+
t.deepEqual(
|
|
257
|
+
kser(state1),
|
|
258
|
+
kser({ x: passables.get(vref1), y: passables.get(vref2) }),
|
|
242
259
|
);
|
|
243
260
|
|
|
244
|
-
// the first version's state is { x: vref1, y: vref2 }
|
|
245
|
-
|
|
246
261
|
// ------
|
|
247
262
|
|
|
248
263
|
// Simulate upgrade by starting from the non-empty kvStore.
|
|
249
|
-
const
|
|
250
|
-
const { syscall: sc2 } = buildSyscall({ kvStore:
|
|
264
|
+
const store2 = new Map(store1);
|
|
265
|
+
const { syscall: sc2, log: log2 } = buildSyscall({ kvStore: store2 });
|
|
251
266
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
{
|
|
267
|
-
|
|
267
|
+
// we do *not* override allowStateShapeChanges
|
|
268
|
+
// const opts = { allowStateShapeChanges: true };
|
|
269
|
+
const opts = undefined;
|
|
270
|
+
const stateShapeMismatch = { message: /durable Kind stateShape mismatch/ };
|
|
271
|
+
const ls2 = makeLiveSlots(sc2, 'vatA', {}, opts, gcTools, undefined, () => {
|
|
272
|
+
const buildRootObject = (vatPowers, vatParameters, baggage) => {
|
|
273
|
+
const { VatData } = vatPowers;
|
|
274
|
+
const { x, y } = vatParameters;
|
|
275
|
+
const kh = baggage.get('kh');
|
|
276
|
+
const redefineDurableKind = stateShape =>
|
|
277
|
+
defineHolder(VatData.defineDurableKind, kh, stateShape);
|
|
278
|
+
// several shapes that are not compatible
|
|
279
|
+
const badShapes = [
|
|
280
|
+
{ x, y: M.any() },
|
|
281
|
+
{ x },
|
|
282
|
+
{ x, y, z: M.string() },
|
|
283
|
+
{ x: M.or(x, M.string()), y },
|
|
284
|
+
{ x: y, y: x }, // wrong slots
|
|
285
|
+
];
|
|
286
|
+
for (const stateShape of badShapes) {
|
|
287
|
+
t.throws(() => redefineDurableKind(stateShape), stateShapeMismatch);
|
|
288
|
+
}
|
|
289
|
+
// the correct shape
|
|
290
|
+
redefineDurableKind({ x, y });
|
|
291
|
+
|
|
292
|
+
return Far('root', {});
|
|
268
293
|
};
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
trial(shape3);
|
|
272
|
-
trial(shape4);
|
|
273
|
-
trial(shape5);
|
|
274
|
-
const stateShape = { x: obj1, y: obj2 }; // the correct shape
|
|
275
|
-
defineDurableKind(kh, init, behavior, { stateShape });
|
|
276
|
-
t.pass();
|
|
277
|
-
|
|
278
|
-
return Far('root', {});
|
|
279
|
-
}
|
|
294
|
+
return { buildRootObject };
|
|
295
|
+
});
|
|
280
296
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
sc2,
|
|
287
|
-
'vatA',
|
|
288
|
-
{},
|
|
289
|
-
options,
|
|
290
|
-
gcTools,
|
|
291
|
-
undefined,
|
|
292
|
-
makeNS2,
|
|
293
|
-
);
|
|
297
|
+
const vatParameters2 = { x: passables.get(vref1), y: passables.get(vref2) };
|
|
298
|
+
await ls2.dispatch(makeStartVat(kser(vatParameters2)));
|
|
299
|
+
|
|
300
|
+
const state2 = await dispatchForResult(ls2, log2, [objRef, 'get', fields]);
|
|
301
|
+
t.deepEqual(kser(state2), kser(state1));
|
|
294
302
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
303
|
+
// ------
|
|
304
|
+
|
|
305
|
+
// Now upgrade again, first to add a new optional stateShape field x2 and then
|
|
306
|
+
// to preserve the new shape.
|
|
307
|
+
const store3 = new Map(store2);
|
|
308
|
+
const { syscall: sc3, log: log3 } = buildSyscall({ kvStore: store3 });
|
|
309
|
+
const ls3 = makeLiveSlots(sc3, 'vatA', {}, {}, gcTools, undefined, () => {
|
|
310
|
+
const buildRootObject = ({ VatData }, vatParameters, baggage) => {
|
|
311
|
+
const { x, y } = vatParameters;
|
|
312
|
+
const kh = baggage.get('kh');
|
|
313
|
+
const redefineDurableKind = stateShape =>
|
|
314
|
+
defineHolder(VatData.defineDurableKind, kh, stateShape);
|
|
315
|
+
const badShapes = [{ x, x2: x, y }];
|
|
316
|
+
for (const stateShape of badShapes) {
|
|
317
|
+
t.throws(() => redefineDurableKind(stateShape), stateShapeMismatch);
|
|
318
|
+
}
|
|
319
|
+
redefineDurableKind({ x, x2: M.or(M.undefined(), x), y });
|
|
320
|
+
return Far('root', {});
|
|
321
|
+
};
|
|
322
|
+
return { buildRootObject };
|
|
323
|
+
});
|
|
324
|
+
const vatParameters3 = { x: passables.get(vref1), y: passables.get(vref2) };
|
|
325
|
+
await ls3.dispatch(makeStartVat(kser(vatParameters3)));
|
|
326
|
+
|
|
327
|
+
const state3 = await dispatchForResult(ls3, log3, [objRef, 'get', fields]);
|
|
328
|
+
t.deepEqual(kser(state3), kser({ ...state1, x2: undefined }));
|
|
329
|
+
const makeVerificationCase = (x, x2, y, throwsMessage) => [
|
|
330
|
+
`{ x: ${x}, x2: ${x2}, y: ${y} }`,
|
|
331
|
+
{ x: passables.get(x), x2: passables.get(x2), y: passables.get(y) },
|
|
332
|
+
throwsMessage,
|
|
333
|
+
];
|
|
334
|
+
const verifications = [
|
|
335
|
+
makeVerificationCase(vref1, vref2, vref2, 'rejected'),
|
|
336
|
+
makeVerificationCase(vref1, vref1, vref2),
|
|
337
|
+
makeVerificationCase(vref1, undefined, vref2),
|
|
338
|
+
];
|
|
339
|
+
let lastState = state3;
|
|
340
|
+
for (const [label, value, message] of verifications) {
|
|
341
|
+
const tryUpdate = () =>
|
|
342
|
+
dispatchForResult(ls3, log3, [objRef, 'set', [value]]);
|
|
343
|
+
await (message
|
|
344
|
+
? t.throwsAsync(tryUpdate, { message }, `${label} ${message}`)
|
|
345
|
+
: tryUpdate());
|
|
346
|
+
const state = await dispatchForResult(ls3, log3, [objRef, 'get', fields]);
|
|
347
|
+
const expectState = message ? lastState : value;
|
|
348
|
+
t.deepEqual(kser(state), kser(expectState), `${label}`);
|
|
349
|
+
lastState = state;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const store4 = new Map(store3);
|
|
353
|
+
const { syscall: sc4, log: log4 } = buildSyscall({ kvStore: store4 });
|
|
354
|
+
const ls4 = makeLiveSlots(sc4, 'vatA', {}, {}, gcTools, undefined, () => {
|
|
355
|
+
const buildRootObject = ({ VatData }, vatParameters, baggage) => {
|
|
356
|
+
const { x, y } = vatParameters;
|
|
357
|
+
const kh = baggage.get('kh');
|
|
358
|
+
const redefineDurableKind = stateShape =>
|
|
359
|
+
defineHolder(VatData.defineDurableKind, kh, stateShape);
|
|
360
|
+
const badShapes = [
|
|
361
|
+
// Removing optionality from x2.
|
|
362
|
+
{ x, x2: x, y },
|
|
363
|
+
// Equivalent but not *equal* to M.or(M.undefined(), x).
|
|
364
|
+
{ x, x2: M.or(undefined, x), y },
|
|
365
|
+
{ x, x2: M.or(x, M.undefined()), y },
|
|
366
|
+
];
|
|
367
|
+
for (const stateShape of badShapes) {
|
|
368
|
+
t.throws(() => redefineDurableKind(stateShape), stateShapeMismatch);
|
|
369
|
+
}
|
|
370
|
+
redefineDurableKind({ x, x2: M.or(M.undefined(), x), y });
|
|
371
|
+
return Far('root', {});
|
|
372
|
+
};
|
|
373
|
+
return { buildRootObject };
|
|
374
|
+
});
|
|
375
|
+
const vatParameters4 = { x: passables.get(vref1), y: passables.get(vref2) };
|
|
376
|
+
await ls4.dispatch(makeStartVat(kser(vatParameters4)));
|
|
377
|
+
|
|
378
|
+
for (const [label, value, message] of verifications) {
|
|
379
|
+
const tryUpdate = () =>
|
|
380
|
+
dispatchForResult(ls4, log4, [objRef, 'set', [value]]);
|
|
381
|
+
await (message
|
|
382
|
+
? t.throwsAsync(tryUpdate, { message }, `${label} ${message}`)
|
|
383
|
+
: tryUpdate());
|
|
384
|
+
const state = await dispatchForResult(ls4, log4, [objRef, 'get', fields]);
|
|
385
|
+
const expectState = message ? lastState : value;
|
|
386
|
+
t.deepEqual(kser(state), kser(expectState), `${label}`);
|
|
387
|
+
lastState = state;
|
|
388
|
+
}
|
|
298
389
|
});
|