@agoric/swingset-liveslots 0.10.3-u19.1 → 0.10.3-u20.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
|
@@ -45,34 +45,50 @@ const buildPromiseWatcherRootObject = (vatPowers, vatParameters, baggage) => {
|
|
|
45
45
|
|
|
46
46
|
const root = Far('root', {
|
|
47
47
|
getPromise: name => {
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
knownPromises.has(name) || Fail`promise not found: ${name}`;
|
|
49
|
+
const { promise } = knownPromises.get(name);
|
|
50
50
|
return { promise };
|
|
51
51
|
},
|
|
52
52
|
importPromise: (name, promise) => {
|
|
53
53
|
!knownPromises.has(name) || Fail`promise already exists: ${name}`;
|
|
54
|
-
knownPromises.set(name, promise);
|
|
54
|
+
knownPromises.set(name, { promise });
|
|
55
55
|
return `imported promise: ${name}`;
|
|
56
56
|
},
|
|
57
57
|
createLocalPromise: (name, fulfillment, rejection) => {
|
|
58
58
|
!knownPromises.has(name) || Fail`promise already exists: ${name}`;
|
|
59
59
|
const { promise, resolve, reject } = makePromiseKit();
|
|
60
|
+
let resolvers = {};
|
|
60
61
|
if (fulfillment !== undefined) {
|
|
61
62
|
resolve(fulfillment);
|
|
62
63
|
} else if (rejection !== undefined) {
|
|
63
64
|
reject(rejection);
|
|
65
|
+
} else {
|
|
66
|
+
resolvers = { resolve, reject };
|
|
64
67
|
}
|
|
65
|
-
knownPromises.set(name, promise);
|
|
68
|
+
knownPromises.set(name, { promise, ...resolvers });
|
|
66
69
|
return `created local promise: ${name}`;
|
|
67
70
|
},
|
|
71
|
+
resolveLocalPromise: (name, rejection, value) => {
|
|
72
|
+
knownPromises.has(name) || Fail`promise not found: ${name}`;
|
|
73
|
+
const { resolve, reject, promise } = knownPromises.get(name);
|
|
74
|
+
(resolve && reject) || Fail`promise not resolvable: ${name}`;
|
|
75
|
+
(rejection ? reject : resolve)(value);
|
|
76
|
+
knownPromises.set(name, { promise });
|
|
77
|
+
return `resolved promise: ${name}`;
|
|
78
|
+
},
|
|
68
79
|
watchPromise: name => {
|
|
69
80
|
knownPromises.has(name) || Fail`promise not found: ${name}`;
|
|
70
|
-
watchPromise(knownPromises.get(name), watcher, name);
|
|
81
|
+
watchPromise(knownPromises.get(name).promise, watcher, name);
|
|
71
82
|
return `watched promise: ${name}`;
|
|
72
83
|
},
|
|
73
84
|
getWatchResolution: name => {
|
|
74
85
|
return watchResolutions.get(name);
|
|
75
86
|
},
|
|
87
|
+
sendToPromise: (name, method, ...args) => {
|
|
88
|
+
knownPromises.has(name) || Fail`promise not found: ${name}`;
|
|
89
|
+
const { promise } = knownPromises.get(name);
|
|
90
|
+
return HandledPromise.applyMethod(promise, method, args);
|
|
91
|
+
},
|
|
76
92
|
});
|
|
77
93
|
|
|
78
94
|
const startOperations = vatParameters?.startOperations || [];
|
|
@@ -132,6 +148,29 @@ const kvStoreDataV1KeysToDelete = ['vc.4.sp+6', 'vc.4.sp-9'];
|
|
|
132
148
|
const kvStoreDataV1VpidsToKeep = ['p-8'];
|
|
133
149
|
const kvStoreDataV1KeysToKeep = ['vc.4.sp-8'];
|
|
134
150
|
|
|
151
|
+
// Ignore vatstore syscalls.
|
|
152
|
+
const extractDispatchLogs = vatLogs =>
|
|
153
|
+
vatLogs.splice(0).filter(m => !m.type.startsWith('vatstore'));
|
|
154
|
+
|
|
155
|
+
const settlementMessage = (vpid, rejected, value) => ({
|
|
156
|
+
type: 'resolve',
|
|
157
|
+
resolutions: [[vpid, rejected, kser(value)]],
|
|
158
|
+
});
|
|
159
|
+
const fulfillmentMessage = (vpid, value) =>
|
|
160
|
+
settlementMessage(vpid, false, value);
|
|
161
|
+
const rejectionMessage = (vpid, value) => settlementMessage(vpid, true, value);
|
|
162
|
+
const subscribeMessage = vpid => ({
|
|
163
|
+
type: 'subscribe',
|
|
164
|
+
target: vpid,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
/** @param {`p-${number}` | `p+${number}`} p */
|
|
168
|
+
const extractPNum = p => {
|
|
169
|
+
const r = /^p[-+](\d+)$/.exec(p);
|
|
170
|
+
if (!r) throw Fail`Invalid promise ${p}`;
|
|
171
|
+
return parseInt(r[1], 10);
|
|
172
|
+
};
|
|
173
|
+
|
|
135
174
|
test('past-incarnation watched promises', async t => {
|
|
136
175
|
const S = 'settlement';
|
|
137
176
|
// Anchor promise counters upon which the other assertions depend.
|
|
@@ -139,14 +178,14 @@ test('past-incarnation watched promises', async t => {
|
|
|
139
178
|
// cf. src/liveslots.js:initialIDCounters
|
|
140
179
|
const firstPExport = 5;
|
|
141
180
|
|
|
142
|
-
const startImportedP = firstPImport - 2
|
|
181
|
+
const startImportedP = `p-${firstPImport - 2}`;
|
|
143
182
|
|
|
144
183
|
const v1StartOperations = [
|
|
145
184
|
['createLocalPromise', 'start orphaned'],
|
|
146
185
|
['watchPromise', 'start orphaned'],
|
|
147
186
|
['createLocalPromise', 'start fulfilled', S],
|
|
148
187
|
['watchPromise', 'start fulfilled'],
|
|
149
|
-
['importPromise', 'start imported', kslot(
|
|
188
|
+
['importPromise', 'start imported', kslot(startImportedP)],
|
|
150
189
|
['watchPromise', 'start imported'],
|
|
151
190
|
];
|
|
152
191
|
const kvStore = new Map();
|
|
@@ -166,172 +205,133 @@ test('past-incarnation watched promises', async t => {
|
|
|
166
205
|
);
|
|
167
206
|
let vatLogs = v.log;
|
|
168
207
|
|
|
169
|
-
|
|
170
|
-
let
|
|
171
|
-
let dispatches = 0;
|
|
172
|
-
const exportedPromises = new Set();
|
|
173
|
-
const v1OrphanedPExports = [];
|
|
174
|
-
const nextPImport = () => (lastPImport += 1);
|
|
175
|
-
const nextPExport = () => (lastPExport += 1);
|
|
208
|
+
/** @type {`p-${number}`} */
|
|
209
|
+
let rp = `p-0`;
|
|
176
210
|
/** @type {typeof rawDispatch} */
|
|
177
|
-
const dispatchMessage = (...args) => {
|
|
178
|
-
|
|
179
|
-
return
|
|
211
|
+
const dispatchMessage = async (...args) => {
|
|
212
|
+
rp = /** @type {`p-${number}`} */ (await rawDispatch(...args));
|
|
213
|
+
return rp;
|
|
180
214
|
};
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
215
|
+
let lastPExport = firstPExport - 1;
|
|
216
|
+
const exportedPromises = new Map();
|
|
217
|
+
const recordNextExportedPromise = name => {
|
|
218
|
+
const p = `p+${(lastPExport += 1)}`;
|
|
219
|
+
exportedPromises.set(p, name);
|
|
220
|
+
return p;
|
|
221
|
+
};
|
|
222
|
+
const recordExportedPromiseNotification = p => {
|
|
223
|
+
t.true(exportedPromises.delete(p));
|
|
224
|
+
return p;
|
|
184
225
|
};
|
|
185
|
-
// Ignore vatstore syscalls.
|
|
186
|
-
const getDispatchLogs = () =>
|
|
187
|
-
vatLogs.splice(0).filter(m => !m.type.startsWith('vatstore'));
|
|
188
|
-
const settlementMessage = (vpid, rejected, value) => ({
|
|
189
|
-
type: 'resolve',
|
|
190
|
-
resolutions: [[vpid, rejected, kser(value)]],
|
|
191
|
-
});
|
|
192
|
-
const fulfillmentMessage = (vpid, value) =>
|
|
193
|
-
settlementMessage(vpid, false, value);
|
|
194
|
-
const rejectionMessage = (vpid, value) =>
|
|
195
|
-
settlementMessage(vpid, true, value);
|
|
196
|
-
const subscribeMessage = vpid => ({
|
|
197
|
-
type: 'subscribe',
|
|
198
|
-
target: vpid,
|
|
199
|
-
});
|
|
200
226
|
|
|
201
227
|
// startVat logs
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
subscribeMessage(
|
|
208
|
-
|
|
209
|
-
subscribeMessage(`p+${lastPExport}`),
|
|
210
|
-
fulfillmentMessage(`p+${lastPExport}`, S),
|
|
228
|
+
const startOrphanedP = recordNextExportedPromise('start orphaned');
|
|
229
|
+
const startFulfilledP = recordNextExportedPromise('start fulfilled');
|
|
230
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
231
|
+
subscribeMessage(startImportedP),
|
|
232
|
+
subscribeMessage(startOrphanedP),
|
|
233
|
+
subscribeMessage(startFulfilledP),
|
|
234
|
+
fulfillmentMessage(startFulfilledP, S),
|
|
211
235
|
]);
|
|
212
236
|
await dispatchMessage('createLocalPromise', 'exported', S);
|
|
213
|
-
t.deepEqual(
|
|
214
|
-
fulfillmentMessage(
|
|
237
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
238
|
+
fulfillmentMessage(rp, 'created local promise: exported'),
|
|
215
239
|
]);
|
|
216
|
-
await dispatchMessage('getPromise',
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
240
|
+
await dispatchMessage('getPromise', 'exported');
|
|
241
|
+
const exportedP = recordNextExportedPromise('exported');
|
|
242
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
243
|
+
fulfillmentMessage(rp, {
|
|
244
|
+
promise: kslot(exportedP),
|
|
220
245
|
}),
|
|
221
|
-
fulfillmentMessage(
|
|
246
|
+
fulfillmentMessage(recordExportedPromiseNotification(exportedP), S),
|
|
222
247
|
]);
|
|
223
|
-
const importedP = firstPImport - 1
|
|
224
|
-
await dispatchMessage('importPromise', 'imported', kslot(
|
|
225
|
-
t.deepEqual(
|
|
226
|
-
subscribeMessage(
|
|
227
|
-
fulfillmentMessage(
|
|
248
|
+
const importedP = `p-${firstPImport - 1}`;
|
|
249
|
+
await dispatchMessage('importPromise', 'imported', kslot(importedP));
|
|
250
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
251
|
+
subscribeMessage(importedP),
|
|
252
|
+
fulfillmentMessage(rp, 'imported promise: imported'),
|
|
228
253
|
]);
|
|
229
254
|
await dispatchMessage('createLocalPromise', 'orphaned');
|
|
230
|
-
t.deepEqual(
|
|
231
|
-
fulfillmentMessage(
|
|
255
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
256
|
+
fulfillmentMessage(rp, 'created local promise: orphaned'),
|
|
232
257
|
]);
|
|
233
258
|
await dispatchMessage('createLocalPromise', 'orphaned exported');
|
|
234
|
-
|
|
235
|
-
'
|
|
236
|
-
|
|
237
|
-
);
|
|
238
|
-
const orphanedExportedP =
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
`p-${nextPImport()}`,
|
|
243
|
-
'created local promise: orphaned exported',
|
|
244
|
-
),
|
|
245
|
-
fulfillmentMessage(`p-${nextPImport()}`, {
|
|
246
|
-
promise: kslot(`p+${orphanedExportedP}`),
|
|
259
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
260
|
+
fulfillmentMessage(rp, 'created local promise: orphaned exported'),
|
|
261
|
+
]);
|
|
262
|
+
await dispatchMessage('getPromise', 'orphaned exported');
|
|
263
|
+
const orphanedExportedP = recordNextExportedPromise('orphaned exported');
|
|
264
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
265
|
+
fulfillmentMessage(rp, {
|
|
266
|
+
promise: kslot(orphanedExportedP),
|
|
247
267
|
}),
|
|
248
268
|
]);
|
|
249
269
|
await dispatchMessage('createLocalPromise', 'fulfilled', S);
|
|
250
|
-
t.deepEqual(
|
|
251
|
-
fulfillmentMessage(
|
|
252
|
-
`p-${nextPImport()}`,
|
|
253
|
-
'created local promise: fulfilled',
|
|
254
|
-
),
|
|
270
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
271
|
+
fulfillmentMessage(rp, 'created local promise: fulfilled'),
|
|
255
272
|
]);
|
|
256
273
|
await dispatchMessage('createLocalPromise', 'rejected', undefined, S);
|
|
257
|
-
t.deepEqual(
|
|
258
|
-
fulfillmentMessage(
|
|
274
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
275
|
+
fulfillmentMessage(rp, 'created local promise: rejected'),
|
|
259
276
|
]);
|
|
260
|
-
t.is(
|
|
261
|
-
lastPImport - firstPImport + 1,
|
|
262
|
-
dispatches,
|
|
263
|
-
`imported ${dispatches} promises (1 per dispatch)`,
|
|
264
|
-
);
|
|
265
|
-
t.is(
|
|
266
|
-
lastPExport - firstPExport + 1,
|
|
267
|
-
exportedPromises.size,
|
|
268
|
-
`exported ${exportedPromises.size} promises: ${[...exportedPromises].join(', ')}`,
|
|
269
|
-
);
|
|
270
277
|
|
|
271
|
-
await dispatchMessage('watchPromise',
|
|
272
|
-
|
|
273
|
-
t.deepEqual(
|
|
274
|
-
subscribeMessage(
|
|
275
|
-
fulfillmentMessage(
|
|
278
|
+
await dispatchMessage('watchPromise', 'orphaned');
|
|
279
|
+
const orphanedP = recordNextExportedPromise('orphaned');
|
|
280
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
281
|
+
subscribeMessage(orphanedP),
|
|
282
|
+
fulfillmentMessage(rp, 'watched promise: orphaned'),
|
|
276
283
|
]);
|
|
277
|
-
await dispatchMessage('watchPromise',
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
fulfillmentMessage(
|
|
284
|
+
await dispatchMessage('watchPromise', 'fulfilled');
|
|
285
|
+
const fulfilledP = recordNextExportedPromise('fulfilled');
|
|
286
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
287
|
+
subscribeMessage(fulfilledP),
|
|
288
|
+
fulfillmentMessage(rp, 'watched promise: fulfilled'),
|
|
289
|
+
fulfillmentMessage(recordExportedPromiseNotification(fulfilledP), S),
|
|
282
290
|
]);
|
|
283
|
-
await dispatchMessage('watchPromise',
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
291
|
+
await dispatchMessage('watchPromise', 'rejected');
|
|
292
|
+
const rejectedP = recordNextExportedPromise('rejected');
|
|
293
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
294
|
+
subscribeMessage(rejectedP),
|
|
295
|
+
fulfillmentMessage(rp, 'watched promise: rejected'),
|
|
296
|
+
rejectionMessage(recordExportedPromiseNotification(rejectedP), S),
|
|
288
297
|
]);
|
|
289
298
|
await dispatchMessage('watchPromise', 'imported');
|
|
290
|
-
t.deepEqual(
|
|
299
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
291
300
|
// no subscribe, we already did at import
|
|
292
|
-
fulfillmentMessage(
|
|
301
|
+
fulfillmentMessage(rp, 'watched promise: imported'),
|
|
293
302
|
]);
|
|
294
303
|
await dispatchMessage('getWatchResolution', 'fulfilled');
|
|
295
|
-
t.deepEqual(
|
|
296
|
-
fulfillmentMessage(
|
|
304
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
305
|
+
fulfillmentMessage(rp, {
|
|
297
306
|
status: 'fulfilled',
|
|
298
307
|
value: S,
|
|
299
308
|
}),
|
|
300
309
|
]);
|
|
301
310
|
await dispatchMessage('getWatchResolution', 'rejected');
|
|
302
|
-
t.deepEqual(
|
|
303
|
-
fulfillmentMessage(
|
|
311
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
312
|
+
fulfillmentMessage(rp, {
|
|
304
313
|
status: 'rejected',
|
|
305
314
|
reason: S,
|
|
306
315
|
}),
|
|
307
316
|
]);
|
|
308
317
|
await dispatchMessage('getWatchResolution', 'start fulfilled');
|
|
309
|
-
t.deepEqual(
|
|
310
|
-
fulfillmentMessage(
|
|
318
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
319
|
+
fulfillmentMessage(rp, {
|
|
311
320
|
status: 'fulfilled',
|
|
312
321
|
value: S,
|
|
313
322
|
}),
|
|
314
323
|
]);
|
|
315
324
|
|
|
316
|
-
|
|
317
|
-
lastPImport - firstPImport + 1,
|
|
318
|
-
dispatches,
|
|
319
|
-
`imported ${dispatches} promises (1 per dispatch)`,
|
|
320
|
-
);
|
|
321
|
-
t.is(
|
|
322
|
-
lastPExport - firstPExport + 1,
|
|
323
|
-
exportedPromises.size,
|
|
324
|
-
`exported ${exportedPromises.size} promises: ${[...exportedPromises].join(', ')}`,
|
|
325
|
-
);
|
|
325
|
+
const v2FirstPromise = extractPNum(rp) + 1;
|
|
326
326
|
|
|
327
327
|
// Simulate upgrade by starting from the non-empty kvStore.
|
|
328
328
|
// t.log(Object.fromEntries([...kvStore.entries()].sort(compareEntriesByKey)));
|
|
329
329
|
const clonedStore = new Map(kvStore);
|
|
330
|
-
const startImported2P = firstPImport - 3
|
|
330
|
+
const startImported2P = `p-${firstPImport - 3}`;
|
|
331
331
|
const v2StartOperations = [
|
|
332
|
-
['importPromise', 'start imported 2', kslot(
|
|
333
|
-
['importPromise', 'imported', kslot(
|
|
334
|
-
['importPromise', 'orphaned exported', kslot(
|
|
332
|
+
['importPromise', 'start imported 2', kslot(startImported2P)], // import of new promise
|
|
333
|
+
['importPromise', 'imported', kslot(importedP)], // import previously imported and watched promise
|
|
334
|
+
['importPromise', 'orphaned exported', kslot(orphanedExportedP)], // import previously exported but unwatched promise
|
|
335
335
|
['watchPromise', 'orphaned exported'],
|
|
336
336
|
];
|
|
337
337
|
({
|
|
@@ -344,45 +344,44 @@ test('past-incarnation watched promises', async t => {
|
|
|
344
344
|
'durable-promise-watcher-v2',
|
|
345
345
|
{
|
|
346
346
|
kvStore: clonedStore,
|
|
347
|
-
nextPromiseImportNumber:
|
|
347
|
+
nextPromiseImportNumber: v2FirstPromise,
|
|
348
348
|
vatParameters: { startOperations: v2StartOperations },
|
|
349
349
|
},
|
|
350
350
|
));
|
|
351
351
|
vatLogs = v.log;
|
|
352
352
|
|
|
353
353
|
// startVat logs
|
|
354
|
-
t.deepEqual(
|
|
355
|
-
subscribeMessage(
|
|
356
|
-
subscribeMessage(
|
|
357
|
-
subscribeMessage(
|
|
354
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
355
|
+
subscribeMessage(startImported2P),
|
|
356
|
+
subscribeMessage(importedP),
|
|
357
|
+
subscribeMessage(orphanedExportedP),
|
|
358
358
|
]);
|
|
359
359
|
// Simulate kernel rejection of promises orphaned by termination/upgrade of their decider vat.
|
|
360
360
|
const expectedDeletions = [...clonedStore.entries()].filter(entry =>
|
|
361
361
|
entry[1].includes('orphaned'),
|
|
362
362
|
);
|
|
363
363
|
t.true(expectedDeletions.length >= 1);
|
|
364
|
-
for (const orphanedPExport of
|
|
365
|
-
await dispatch(
|
|
366
|
-
makeReject(`p+${orphanedPExport}`, kser('tomorrow never came')),
|
|
367
|
-
);
|
|
364
|
+
for (const [orphanedPExport] of exportedPromises) {
|
|
365
|
+
await dispatch(makeReject(orphanedPExport, kser('tomorrow never came')));
|
|
368
366
|
}
|
|
367
|
+
exportedPromises.clear();
|
|
369
368
|
await dispatchMessage('getWatchResolution', 'orphaned');
|
|
370
|
-
t.deepEqual(
|
|
371
|
-
fulfillmentMessage(
|
|
369
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
370
|
+
fulfillmentMessage(rp, {
|
|
372
371
|
status: 'rejected',
|
|
373
372
|
reason: 'tomorrow never came',
|
|
374
373
|
}),
|
|
375
374
|
]);
|
|
376
375
|
await dispatchMessage('getWatchResolution', 'start orphaned');
|
|
377
|
-
t.deepEqual(
|
|
378
|
-
fulfillmentMessage(
|
|
376
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
377
|
+
fulfillmentMessage(rp, {
|
|
379
378
|
status: 'rejected',
|
|
380
379
|
reason: 'tomorrow never came',
|
|
381
380
|
}),
|
|
382
381
|
]);
|
|
383
382
|
await dispatchMessage('getWatchResolution', 'orphaned exported');
|
|
384
|
-
t.deepEqual(
|
|
385
|
-
fulfillmentMessage(
|
|
383
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
384
|
+
fulfillmentMessage(rp, {
|
|
386
385
|
status: 'rejected',
|
|
387
386
|
reason: 'tomorrow never came',
|
|
388
387
|
}),
|
|
@@ -391,46 +390,50 @@ test('past-incarnation watched promises', async t => {
|
|
|
391
390
|
t.false(clonedStore.has(key), `entry should be removed: ${key}: ${value}`);
|
|
392
391
|
}
|
|
393
392
|
// Simulate resolution of imported promises watched in previous incarnation
|
|
394
|
-
await dispatch(makeResolve(
|
|
393
|
+
await dispatch(makeResolve(importedP, kser(undefined)));
|
|
395
394
|
await dispatchMessage('getWatchResolution', 'imported');
|
|
396
|
-
t.deepEqual(
|
|
397
|
-
fulfillmentMessage(
|
|
395
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
396
|
+
fulfillmentMessage(rp, {
|
|
398
397
|
status: 'fulfilled',
|
|
399
398
|
value: undefined,
|
|
400
399
|
}),
|
|
401
400
|
]);
|
|
402
|
-
await dispatch(makeResolve(
|
|
401
|
+
await dispatch(makeResolve(startImportedP, kser(undefined)));
|
|
403
402
|
await dispatchMessage('getWatchResolution', 'start imported');
|
|
404
|
-
t.deepEqual(
|
|
405
|
-
fulfillmentMessage(
|
|
403
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
404
|
+
fulfillmentMessage(rp, {
|
|
406
405
|
status: 'fulfilled',
|
|
407
406
|
value: undefined,
|
|
408
407
|
}),
|
|
409
408
|
]);
|
|
410
|
-
await dispatch(makeResolve(
|
|
409
|
+
await dispatch(makeResolve(startImported2P, kser(undefined)));
|
|
411
410
|
await dispatchMessage('getWatchResolution', 'start imported 2');
|
|
412
|
-
t.deepEqual(
|
|
413
|
-
fulfillmentMessage(
|
|
411
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
412
|
+
fulfillmentMessage(rp, undefined),
|
|
414
413
|
]);
|
|
415
414
|
// simulate resolution of imported promise watched after resolution
|
|
416
415
|
await dispatchMessage('watchPromise', 'start imported 2');
|
|
417
|
-
|
|
416
|
+
const startImported2ReexportedP =
|
|
417
|
+
recordNextExportedPromise('start imported 2');
|
|
418
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
418
419
|
// Promise was previously resolved, so it is re-exported
|
|
419
|
-
subscribeMessage(
|
|
420
|
+
subscribeMessage(startImported2ReexportedP),
|
|
421
|
+
fulfillmentMessage(rp, 'watched promise: start imported 2'),
|
|
420
422
|
fulfillmentMessage(
|
|
421
|
-
|
|
422
|
-
|
|
423
|
+
recordExportedPromiseNotification(startImported2ReexportedP),
|
|
424
|
+
undefined,
|
|
423
425
|
),
|
|
424
|
-
fulfillmentMessage(`p+${lastPExport}`, undefined),
|
|
425
426
|
]);
|
|
426
427
|
await dispatchMessage('getWatchResolution', 'start imported 2');
|
|
427
|
-
t.deepEqual(
|
|
428
|
-
fulfillmentMessage(
|
|
428
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
429
|
+
fulfillmentMessage(rp, {
|
|
429
430
|
status: 'fulfilled',
|
|
430
431
|
value: undefined,
|
|
431
432
|
}),
|
|
432
433
|
]);
|
|
433
434
|
|
|
435
|
+
const finalFirstPromise = extractPNum(rp) + 1;
|
|
436
|
+
|
|
434
437
|
// Verify that the data is still in loadable condition.
|
|
435
438
|
const finalClonedStore = new Map(clonedStore);
|
|
436
439
|
({
|
|
@@ -441,19 +444,25 @@ test('past-incarnation watched promises', async t => {
|
|
|
441
444
|
t,
|
|
442
445
|
buildPromiseWatcherRootObject,
|
|
443
446
|
'durable-promise-watcher-final',
|
|
444
|
-
{ kvStore: finalClonedStore, nextPromiseImportNumber:
|
|
447
|
+
{ kvStore: finalClonedStore, nextPromiseImportNumber: finalFirstPromise },
|
|
445
448
|
));
|
|
446
449
|
vatLogs = v.log;
|
|
447
450
|
vatLogs.length = 0;
|
|
451
|
+
t.deepEqual([...exportedPromises], [], 'exportedPromises is empty');
|
|
448
452
|
await dispatchMessage('createLocalPromise', 'final', S);
|
|
453
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
454
|
+
fulfillmentMessage(rp, 'created local promise: final'),
|
|
455
|
+
]);
|
|
449
456
|
await dispatchMessage('watchPromise', 'final');
|
|
457
|
+
const finalP = recordNextExportedPromise('final');
|
|
458
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
459
|
+
subscribeMessage(finalP),
|
|
460
|
+
fulfillmentMessage(rp, 'watched promise: final'),
|
|
461
|
+
fulfillmentMessage(recordExportedPromiseNotification(finalP), S),
|
|
462
|
+
]);
|
|
450
463
|
await dispatchMessage('getWatchResolution', 'final');
|
|
451
|
-
t.deepEqual(
|
|
452
|
-
fulfillmentMessage(
|
|
453
|
-
subscribeMessage(`p+${nextPExport()}`),
|
|
454
|
-
fulfillmentMessage(`p-${nextPImport()}`, 'watched promise: final'),
|
|
455
|
-
fulfillmentMessage(`p+${lastPExport}`, S),
|
|
456
|
-
fulfillmentMessage(`p-${nextPImport()}`, {
|
|
464
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
465
|
+
fulfillmentMessage(rp, {
|
|
457
466
|
status: 'fulfilled',
|
|
458
467
|
value: S,
|
|
459
468
|
}),
|
|
@@ -504,3 +513,360 @@ test('past-incarnation watched promises from original-format kvStore', async t =
|
|
|
504
513
|
t.false(finalClonedStore.has(key), `key should be removed: ${key}`);
|
|
505
514
|
}
|
|
506
515
|
});
|
|
516
|
+
|
|
517
|
+
test('watched local promises should not leak slotToVal entries', async t => {
|
|
518
|
+
const S = 'settlement';
|
|
519
|
+
// cf. src/liveslots.js:initialIDCounters
|
|
520
|
+
const firstPExport = 5;
|
|
521
|
+
|
|
522
|
+
const {
|
|
523
|
+
v: { log: vatLogs },
|
|
524
|
+
dispatchMessage,
|
|
525
|
+
testHooks,
|
|
526
|
+
} = await setupTestLiveslots(t, buildPromiseWatcherRootObject, 'vatA');
|
|
527
|
+
const { slotToVal } = testHooks;
|
|
528
|
+
const initial = slotToVal.size;
|
|
529
|
+
|
|
530
|
+
let lastPExport = firstPExport - 1;
|
|
531
|
+
const nextPExport = () => `p+${(lastPExport += 1)}`;
|
|
532
|
+
|
|
533
|
+
let rp;
|
|
534
|
+
|
|
535
|
+
// Watch already resolved promise
|
|
536
|
+
rp = await dispatchMessage('createLocalPromise', 'p1', S);
|
|
537
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
538
|
+
fulfillmentMessage(rp, 'created local promise: p1'),
|
|
539
|
+
]);
|
|
540
|
+
rp = await dispatchMessage('watchPromise', 'p1');
|
|
541
|
+
const p1 = nextPExport();
|
|
542
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
543
|
+
subscribeMessage(p1),
|
|
544
|
+
fulfillmentMessage(rp, 'watched promise: p1'),
|
|
545
|
+
fulfillmentMessage(p1, S),
|
|
546
|
+
]);
|
|
547
|
+
t.is(slotToVal.size, initial); // exported promise did not leak
|
|
548
|
+
rp = await dispatchMessage('getWatchResolution', 'p1');
|
|
549
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
550
|
+
fulfillmentMessage(rp, {
|
|
551
|
+
status: 'fulfilled',
|
|
552
|
+
value: S,
|
|
553
|
+
}),
|
|
554
|
+
]);
|
|
555
|
+
|
|
556
|
+
// Watch subsequently resolved promise
|
|
557
|
+
rp = await dispatchMessage('createLocalPromise', 'p2');
|
|
558
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
559
|
+
fulfillmentMessage(rp, 'created local promise: p2'),
|
|
560
|
+
]);
|
|
561
|
+
t.is(slotToVal.size, initial);
|
|
562
|
+
|
|
563
|
+
rp = await dispatchMessage('watchPromise', 'p2');
|
|
564
|
+
const p2 = nextPExport();
|
|
565
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
566
|
+
subscribeMessage(p2),
|
|
567
|
+
fulfillmentMessage(rp, 'watched promise: p2'),
|
|
568
|
+
]);
|
|
569
|
+
t.is(slotToVal.size, initial + 1); // exported promise
|
|
570
|
+
|
|
571
|
+
rp = await dispatchMessage('resolveLocalPromise', 'p2', false, S);
|
|
572
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
573
|
+
fulfillmentMessage(p2, S),
|
|
574
|
+
fulfillmentMessage(rp, 'resolved promise: p2'),
|
|
575
|
+
]);
|
|
576
|
+
t.is(slotToVal.size, initial); // exported promise did not leak
|
|
577
|
+
|
|
578
|
+
rp = await dispatchMessage('getWatchResolution', 'p2');
|
|
579
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
580
|
+
fulfillmentMessage(rp, {
|
|
581
|
+
status: 'fulfilled',
|
|
582
|
+
value: S,
|
|
583
|
+
}),
|
|
584
|
+
]);
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
// Remaining case for https://github.com/Agoric/agoric-sdk/issues/10756
|
|
588
|
+
// The workaround doesn't handle this case because it learns about the
|
|
589
|
+
// settlement before the virtual object system
|
|
590
|
+
// See https://github.com/Agoric/agoric-sdk/issues/10757
|
|
591
|
+
test('watched imported promises should not leak slotToVal entries', async t => {
|
|
592
|
+
const S = 'settlement';
|
|
593
|
+
// cf. src/liveslots.js:initialIDCounters
|
|
594
|
+
const firstPExport = 5;
|
|
595
|
+
|
|
596
|
+
const {
|
|
597
|
+
v: { log: vatLogs },
|
|
598
|
+
dispatch,
|
|
599
|
+
dispatchMessage,
|
|
600
|
+
nextPImport,
|
|
601
|
+
testHooks,
|
|
602
|
+
} = await setupTestLiveslots(t, buildPromiseWatcherRootObject, 'vatA');
|
|
603
|
+
const { slotToVal } = testHooks;
|
|
604
|
+
const initial = slotToVal.size;
|
|
605
|
+
|
|
606
|
+
let lastPExport = firstPExport - 1;
|
|
607
|
+
const nextPExport = () => `p+${(lastPExport += 1)}`;
|
|
608
|
+
|
|
609
|
+
let rp;
|
|
610
|
+
|
|
611
|
+
// Watch already imported promise
|
|
612
|
+
const importedP = nextPImport();
|
|
613
|
+
rp = await dispatchMessage('importPromise', 'importedP', kslot(importedP));
|
|
614
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
615
|
+
subscribeMessage(importedP),
|
|
616
|
+
fulfillmentMessage(rp, 'imported promise: importedP'),
|
|
617
|
+
]);
|
|
618
|
+
t.is(slotToVal.size, initial + 1); // imported promise
|
|
619
|
+
|
|
620
|
+
rp = await dispatchMessage('watchPromise', 'importedP');
|
|
621
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
622
|
+
fulfillmentMessage(rp, 'watched promise: importedP'),
|
|
623
|
+
]);
|
|
624
|
+
t.is(slotToVal.size, initial + 1); // imported promise
|
|
625
|
+
|
|
626
|
+
await dispatch(makeResolve(importedP, kser(S)));
|
|
627
|
+
t.deepEqual(extractDispatchLogs(vatLogs), []);
|
|
628
|
+
t.is(slotToVal.size, initial); // should not leak
|
|
629
|
+
|
|
630
|
+
rp = await dispatchMessage('getPromise', 'importedP');
|
|
631
|
+
const reexportedP = nextPExport(); // Should allocate a new exported promise
|
|
632
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
633
|
+
fulfillmentMessage(rp, { promise: kslot(reexportedP) }),
|
|
634
|
+
fulfillmentMessage(reexportedP, S),
|
|
635
|
+
]);
|
|
636
|
+
t.is(slotToVal.size, initial); // reexported promise did not leak
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
test('known imported promises in resolutions should not leak slotToVal entries', async t => {
|
|
640
|
+
const S = 'settlement';
|
|
641
|
+
// cf. src/liveslots.js:initialIDCounters
|
|
642
|
+
const firstPExport = 5;
|
|
643
|
+
|
|
644
|
+
const {
|
|
645
|
+
v: { log: vatLogs },
|
|
646
|
+
dispatch,
|
|
647
|
+
dispatchMessage,
|
|
648
|
+
nextPImport,
|
|
649
|
+
testHooks,
|
|
650
|
+
} = await setupTestLiveslots(t, buildPromiseWatcherRootObject, 'vatA');
|
|
651
|
+
const { slotToVal } = testHooks;
|
|
652
|
+
const initial = slotToVal.size;
|
|
653
|
+
|
|
654
|
+
let lastPExport = firstPExport - 1;
|
|
655
|
+
const nextPExport = () => `p+${(lastPExport += 1)}`;
|
|
656
|
+
|
|
657
|
+
let rp;
|
|
658
|
+
|
|
659
|
+
const importedP = nextPImport();
|
|
660
|
+
rp = await dispatchMessage('importPromise', 'importedP', kslot(importedP));
|
|
661
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
662
|
+
subscribeMessage(importedP),
|
|
663
|
+
fulfillmentMessage(rp, 'imported promise: importedP'),
|
|
664
|
+
]);
|
|
665
|
+
t.is(slotToVal.size, initial + 1); // imported promise
|
|
666
|
+
|
|
667
|
+
rp = await dispatchMessage('getPromise', 'importedP');
|
|
668
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
669
|
+
fulfillmentMessage(rp, { promise: kslot(importedP) }),
|
|
670
|
+
]);
|
|
671
|
+
t.is(slotToVal.size, initial + 1); // imported promise
|
|
672
|
+
|
|
673
|
+
await dispatch(makeResolve(importedP, kser(S)));
|
|
674
|
+
t.deepEqual(extractDispatchLogs(vatLogs), []);
|
|
675
|
+
t.is(slotToVal.size, initial); // promise no longer imported
|
|
676
|
+
|
|
677
|
+
// The first getPromise causes liveslots to learn about the resolution value
|
|
678
|
+
// of importedP since liveslots currently only tracks known promise
|
|
679
|
+
// resolutions on export.
|
|
680
|
+
rp = await dispatchMessage('getPromise', 'importedP');
|
|
681
|
+
const reexportedP = nextPExport();
|
|
682
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
683
|
+
fulfillmentMessage(rp, { promise: kslot(reexportedP) }),
|
|
684
|
+
fulfillmentMessage(reexportedP, S), // liveslots learns about the resolution
|
|
685
|
+
]);
|
|
686
|
+
t.is(slotToVal.size, initial);
|
|
687
|
+
|
|
688
|
+
// The second getPromise verifies that liveslots does not leak when notifying
|
|
689
|
+
// a known resolved promise. It allocates a new exported promise because the
|
|
690
|
+
// resolution was previously notified
|
|
691
|
+
rp = await dispatchMessage('getPromise', 'importedP');
|
|
692
|
+
const reexportedP2 = nextPExport(); // allocates a new export promise
|
|
693
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
694
|
+
{
|
|
695
|
+
type: 'resolve',
|
|
696
|
+
resolutions: [
|
|
697
|
+
[rp, false, kser({ promise: kslot(reexportedP2) })],
|
|
698
|
+
[reexportedP2, false, kser(S)], // one shot resolution notification
|
|
699
|
+
],
|
|
700
|
+
},
|
|
701
|
+
]);
|
|
702
|
+
t.is(slotToVal.size, initial); // did not leak reexportedP2
|
|
703
|
+
|
|
704
|
+
// The 3rd getPromise ensures that the 2nd fully cleaned up, and that liveslots
|
|
705
|
+
// doesn't attempt to re-use a the previously exported promise
|
|
706
|
+
rp = await dispatchMessage('getPromise', 'importedP');
|
|
707
|
+
const reexportedP3 = nextPExport(); // allocates a new export promise
|
|
708
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
709
|
+
{
|
|
710
|
+
type: 'resolve',
|
|
711
|
+
resolutions: [
|
|
712
|
+
[rp, false, kser({ promise: kslot(reexportedP3) })],
|
|
713
|
+
[reexportedP3, false, kser(S)], // still one shot resolution
|
|
714
|
+
],
|
|
715
|
+
},
|
|
716
|
+
]);
|
|
717
|
+
t.is(slotToVal.size, initial); // did not leak reexportedP3
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
test('known exported promises in resolutions should not leak slotToVal entries', async t => {
|
|
721
|
+
const S = 'settlement';
|
|
722
|
+
// cf. src/liveslots.js:initialIDCounters
|
|
723
|
+
const firstPExport = 5;
|
|
724
|
+
|
|
725
|
+
const {
|
|
726
|
+
v: { log: vatLogs },
|
|
727
|
+
dispatchMessage,
|
|
728
|
+
testHooks,
|
|
729
|
+
} = await setupTestLiveslots(t, buildPromiseWatcherRootObject, 'vatA');
|
|
730
|
+
const { slotToVal } = testHooks;
|
|
731
|
+
const initial = slotToVal.size;
|
|
732
|
+
|
|
733
|
+
let lastPExport = firstPExport - 1;
|
|
734
|
+
const nextPExport = () => `p+${(lastPExport += 1)}`;
|
|
735
|
+
|
|
736
|
+
let rp;
|
|
737
|
+
|
|
738
|
+
rp = await dispatchMessage('createLocalPromise', 'localP');
|
|
739
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
740
|
+
fulfillmentMessage(rp, 'created local promise: localP'),
|
|
741
|
+
]);
|
|
742
|
+
t.is(slotToVal.size, initial);
|
|
743
|
+
|
|
744
|
+
rp = await dispatchMessage('getPromise', 'localP');
|
|
745
|
+
const localP = nextPExport();
|
|
746
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
747
|
+
fulfillmentMessage(rp, { promise: kslot(localP) }),
|
|
748
|
+
]);
|
|
749
|
+
t.is(slotToVal.size, initial + 1); // exported promise
|
|
750
|
+
|
|
751
|
+
rp = await dispatchMessage('resolveLocalPromise', 'localP', false, S);
|
|
752
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
753
|
+
fulfillmentMessage(localP, S),
|
|
754
|
+
fulfillmentMessage(rp, 'resolved promise: localP'),
|
|
755
|
+
]);
|
|
756
|
+
t.is(slotToVal.size, initial); // promise no longer exported
|
|
757
|
+
|
|
758
|
+
// The first getPromise verifies that liveslots does not leak when notifying
|
|
759
|
+
// a known resolved promise. It allocates a new exported promise because the
|
|
760
|
+
// resolution was previously notified
|
|
761
|
+
rp = await dispatchMessage('getPromise', 'localP');
|
|
762
|
+
const reexportedP = nextPExport();
|
|
763
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
764
|
+
{
|
|
765
|
+
type: 'resolve',
|
|
766
|
+
resolutions: [
|
|
767
|
+
[rp, false, kser({ promise: kslot(reexportedP) })],
|
|
768
|
+
[reexportedP, false, kser(S)], // one shot resolution notification
|
|
769
|
+
],
|
|
770
|
+
},
|
|
771
|
+
]);
|
|
772
|
+
t.is(slotToVal.size, initial);
|
|
773
|
+
|
|
774
|
+
// The second getPromise ensures that previous fully cleaned up, and that
|
|
775
|
+
// liveslots doesn't attempt to re-use a the previously exported promise
|
|
776
|
+
rp = await dispatchMessage('getPromise', 'localP');
|
|
777
|
+
const reexportedP2 = nextPExport(); // allocates a new export promise
|
|
778
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
779
|
+
{
|
|
780
|
+
type: 'resolve',
|
|
781
|
+
resolutions: [
|
|
782
|
+
[rp, false, kser({ promise: kslot(reexportedP2) })],
|
|
783
|
+
[reexportedP2, false, kser(S)], // still one shot resolution
|
|
784
|
+
],
|
|
785
|
+
},
|
|
786
|
+
]);
|
|
787
|
+
t.is(slotToVal.size, initial); // did not leak reexportedP2
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
test('known promises in message sends should not leak slotToVal entries', async t => {
|
|
791
|
+
const S = 'settlement';
|
|
792
|
+
// cf. src/liveslots.js:initialIDCounters
|
|
793
|
+
const firstPExport = 5;
|
|
794
|
+
|
|
795
|
+
const targetO = `o-1`;
|
|
796
|
+
|
|
797
|
+
const {
|
|
798
|
+
v: { log: vatLogs },
|
|
799
|
+
dispatch,
|
|
800
|
+
dispatchMessage,
|
|
801
|
+
testHooks,
|
|
802
|
+
} = await setupTestLiveslots(t, buildPromiseWatcherRootObject, 'vatA', {
|
|
803
|
+
vatParameters: {
|
|
804
|
+
startOperations: [['createLocalPromise', 'target', kslot(targetO)]],
|
|
805
|
+
},
|
|
806
|
+
});
|
|
807
|
+
const { slotToVal } = testHooks;
|
|
808
|
+
const initial = slotToVal.size;
|
|
809
|
+
|
|
810
|
+
let lastPExport = firstPExport - 1;
|
|
811
|
+
const nextPExport = () => `p+${(lastPExport += 1)}`;
|
|
812
|
+
|
|
813
|
+
let rp;
|
|
814
|
+
|
|
815
|
+
rp = await dispatchMessage('createLocalPromise', 'targetP');
|
|
816
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
817
|
+
fulfillmentMessage(rp, 'created local promise: targetP'),
|
|
818
|
+
]);
|
|
819
|
+
t.is(slotToVal.size, initial);
|
|
820
|
+
|
|
821
|
+
rp = await dispatchMessage('createLocalPromise', 'localP');
|
|
822
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
823
|
+
fulfillmentMessage(rp, 'created local promise: localP'),
|
|
824
|
+
]);
|
|
825
|
+
t.is(slotToVal.size, initial);
|
|
826
|
+
|
|
827
|
+
rp = await dispatchMessage('getPromise', 'localP');
|
|
828
|
+
const localP = nextPExport();
|
|
829
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
830
|
+
fulfillmentMessage(rp, { promise: kslot(localP) }),
|
|
831
|
+
]);
|
|
832
|
+
t.is(slotToVal.size, initial + 1); // localP promise
|
|
833
|
+
|
|
834
|
+
rp = await dispatchMessage('sendToPromise', 'targetP', 'foo', kslot(localP));
|
|
835
|
+
const sendToPromiseResultP = rp;
|
|
836
|
+
t.deepEqual(extractDispatchLogs(vatLogs), []);
|
|
837
|
+
t.is(slotToVal.size, initial + 2); // localP and sendToPromiseResultP promises
|
|
838
|
+
|
|
839
|
+
rp = await dispatchMessage('resolveLocalPromise', 'localP', false, S);
|
|
840
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
841
|
+
fulfillmentMessage(localP, S),
|
|
842
|
+
fulfillmentMessage(rp, 'resolved promise: localP'),
|
|
843
|
+
]);
|
|
844
|
+
t.is(slotToVal.size, initial + 1); // sendToPromiseResultP promise
|
|
845
|
+
|
|
846
|
+
rp = await dispatchMessage(
|
|
847
|
+
'resolveLocalPromise',
|
|
848
|
+
'targetP',
|
|
849
|
+
false,
|
|
850
|
+
kslot(targetO),
|
|
851
|
+
);
|
|
852
|
+
const reexportedP = nextPExport(); // Allocate a new promise for importedP
|
|
853
|
+
const sendResultP = nextPExport();
|
|
854
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
855
|
+
fulfillmentMessage(rp, 'resolved promise: targetP'), // no await so first syscall
|
|
856
|
+
{
|
|
857
|
+
type: 'send',
|
|
858
|
+
targetSlot: targetO,
|
|
859
|
+
methargs: kser(['foo', [kslot(reexportedP)]]),
|
|
860
|
+
resultSlot: sendResultP,
|
|
861
|
+
},
|
|
862
|
+
fulfillmentMessage(reexportedP, S), // known resolutions comes before subscribe of send result
|
|
863
|
+
subscribeMessage(sendResultP),
|
|
864
|
+
]);
|
|
865
|
+
t.is(slotToVal.size, initial + 2); // sendToPromiseResultP and sendResultP promises
|
|
866
|
+
|
|
867
|
+
await dispatch(makeResolve(sendResultP, kser(S)));
|
|
868
|
+
t.deepEqual(extractDispatchLogs(vatLogs), [
|
|
869
|
+
fulfillmentMessage(sendToPromiseResultP, S),
|
|
870
|
+
]);
|
|
871
|
+
t.is(slotToVal.size, initial);
|
|
872
|
+
});
|