@agoric/async-flow 0.1.1-upgrade-16-dev-8879538.0 → 0.1.1-upgrade-16-dev-24665a9.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 +8 -8
- package/src/replay-membrane.d.ts +38 -6
- package/src/replay-membrane.d.ts.map +1 -1
- package/src/replay-membrane.js +67 -10
- package/src/types.d.ts +3 -3
- package/src/types.d.ts.map +1 -1
- package/test/async-flow-early-completion.test.js +201 -0
- package/test/replay-membrane-eventual.test.js +91 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/async-flow",
|
|
3
|
-
"version": "0.1.1-upgrade-16-dev-
|
|
3
|
+
"version": "0.1.1-upgrade-16-dev-24665a9.0+24665a9",
|
|
4
4
|
"description": "Upgrade async functions at await points by replay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": "https://github.com/Agoric/agoric-sdk",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"author": "Agoric",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@agoric/base-zone": "0.1.1-upgrade-16-dev-
|
|
28
|
-
"@agoric/store": "0.9.3-upgrade-16-dev-
|
|
29
|
-
"@agoric/vow": "0.1.1-upgrade-16-dev-
|
|
27
|
+
"@agoric/base-zone": "0.1.1-upgrade-16-dev-24665a9.0+24665a9",
|
|
28
|
+
"@agoric/store": "0.9.3-upgrade-16-dev-24665a9.0+24665a9",
|
|
29
|
+
"@agoric/vow": "0.1.1-upgrade-16-dev-24665a9.0+24665a9",
|
|
30
30
|
"@endo/common": "^1.2.2",
|
|
31
31
|
"@endo/errors": "^1.2.2",
|
|
32
32
|
"@endo/eventual-send": "^1.2.2",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"@endo/promise-kit": "^1.1.2"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@agoric/internal": "0.3.3-upgrade-16-dev-
|
|
40
|
-
"@agoric/swingset-liveslots": "0.10.3-upgrade-16-dev-
|
|
41
|
-
"@agoric/zone": "0.2.3-upgrade-16-dev-
|
|
39
|
+
"@agoric/internal": "0.3.3-upgrade-16-dev-24665a9.0+24665a9",
|
|
40
|
+
"@agoric/swingset-liveslots": "0.10.3-upgrade-16-dev-24665a9.0+24665a9",
|
|
41
|
+
"@agoric/zone": "0.2.3-upgrade-16-dev-24665a9.0+24665a9",
|
|
42
42
|
"@endo/env-options": "^1.1.4",
|
|
43
43
|
"@endo/ses-ava": "^1.2.2",
|
|
44
44
|
"ava": "^5.3.0"
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"typeCoverage": {
|
|
63
63
|
"atLeast": 77.83
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "24665a97248b854fcb01185d360429301470ed57"
|
|
66
66
|
}
|
package/src/replay-membrane.d.ts
CHANGED
|
@@ -18,10 +18,26 @@ export function makeReplayMembrane(log: import("@endo/exo").Guarded<{
|
|
|
18
18
|
guestToHost(g: any): any;
|
|
19
19
|
hostToGuest(h: any): any;
|
|
20
20
|
}>, vowTools: {
|
|
21
|
-
when: <T, TResult1 = import("@agoric/vow").
|
|
22
|
-
|
|
21
|
+
when: <T, TResult1 = import("@agoric/vow").EUnwrap<T>, TResult2 = never>(specimenP: T, onFulfilled?: ((value: import("@agoric/vow").EUnwrap<T>) => TResult1 | PromiseLike<TResult1>) | undefined, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined) => Promise<TResult1 | TResult2>;
|
|
22
|
+
/**
|
|
23
|
+
* When replaying, this comes from interpreting the log.
|
|
24
|
+
* Otherwise, it is triggered by a watcher watching hostVow,
|
|
25
|
+
* that must also log it.
|
|
26
|
+
*
|
|
27
|
+
* @param {HostVow} hostVow
|
|
28
|
+
* @param {Host} hostFulfillment
|
|
29
|
+
*/
|
|
30
|
+
watch: <T_1 = any, TResult1_1 = T_1, TResult2_1 = never, C = any>(specimenP: import("@agoric/vow").ERef<T_1 | Vow<T_1>>, watcher?: import("@agoric/vow").Watcher<T_1, TResult1_1, TResult2_1> | undefined, watcherContext?: C | undefined) => Vow<Exclude<TResult1_1, void> | Exclude<TResult2_1, void> extends never ? TResult1_1 : Exclude<TResult1_1, void> | Exclude<TResult2_1, void>>;
|
|
31
|
+
/**
|
|
32
|
+
* When replaying, this comes from interpreting the log.
|
|
33
|
+
* Otherwise, it is triggered by a watcher watching hostVow,
|
|
34
|
+
* that must also log it.
|
|
35
|
+
*
|
|
36
|
+
* @param {HostVow} hostVow
|
|
37
|
+
* @param {Host} hostFulfillment
|
|
38
|
+
*/
|
|
23
39
|
makeVowKit: <T_2>() => import("@agoric/vow").VowKit<T_2>;
|
|
24
|
-
allVows: (vows:
|
|
40
|
+
allVows: (vows: unknown[]) => Vow<any[]>;
|
|
25
41
|
}, watchWake: (vowish: Promise<any> | Vow) => void, panic: (problem: Error) => never): {
|
|
26
42
|
hostToGuest: (specimen: Passable, label?: string | undefined) => any;
|
|
27
43
|
guestToHost: (specimen: Passable, label?: string | undefined) => any;
|
|
@@ -53,10 +69,26 @@ export type ReplayMembrane = ReturnType<(log: import("@endo/exo").Guarded<{
|
|
|
53
69
|
guestToHost(g: any): any;
|
|
54
70
|
hostToGuest(h: any): any;
|
|
55
71
|
}>, vowTools: {
|
|
56
|
-
when: <T, TResult1 = import("@agoric/vow").
|
|
57
|
-
|
|
72
|
+
when: <T, TResult1 = import("@agoric/vow").EUnwrap<T>, TResult2 = never>(specimenP: T, onFulfilled?: ((value: import("@agoric/vow").EUnwrap<T>) => TResult1 | PromiseLike<TResult1>) | undefined, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined) => Promise<TResult1 | TResult2>;
|
|
73
|
+
/**
|
|
74
|
+
* When replaying, this comes from interpreting the log.
|
|
75
|
+
* Otherwise, it is triggered by a watcher watching hostVow,
|
|
76
|
+
* that must also log it.
|
|
77
|
+
*
|
|
78
|
+
* @param {HostVow} hostVow
|
|
79
|
+
* @param {Host} hostFulfillment
|
|
80
|
+
*/
|
|
81
|
+
watch: <T_1 = any, TResult1_1 = T_1, TResult2_1 = never, C = any>(specimenP: import("@agoric/vow").ERef<T_1 | Vow<T_1>>, watcher?: import("@agoric/vow").Watcher<T_1, TResult1_1, TResult2_1> | undefined, watcherContext?: C | undefined) => Vow<Exclude<TResult1_1, void> | Exclude<TResult2_1, void> extends never ? TResult1_1 : Exclude<TResult1_1, void> | Exclude<TResult2_1, void>>;
|
|
82
|
+
/**
|
|
83
|
+
* When replaying, this comes from interpreting the log.
|
|
84
|
+
* Otherwise, it is triggered by a watcher watching hostVow,
|
|
85
|
+
* that must also log it.
|
|
86
|
+
*
|
|
87
|
+
* @param {HostVow} hostVow
|
|
88
|
+
* @param {Host} hostFulfillment
|
|
89
|
+
*/
|
|
58
90
|
makeVowKit: <T_2>() => import("@agoric/vow").VowKit<T_2>;
|
|
59
|
-
allVows: (vows:
|
|
91
|
+
allVows: (vows: unknown[]) => Vow<any[]>;
|
|
60
92
|
}, watchWake: (vowish: Promise<any> | Vow) => void, panic: (problem: Error) => never) => {
|
|
61
93
|
hostToGuest: (specimen: Passable, label?: string | undefined) => any;
|
|
62
94
|
guestToHost: (specimen: Passable, label?: string | undefined) => any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay-membrane.d.ts","sourceRoot":"","sources":["replay-membrane.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"replay-membrane.d.ts","sourceRoot":"","sources":["replay-membrane.js"],"names":[],"mappings":"AAqBO;;;;;;;;;;;;;;;;;;;;wFATuC,CAAC;IA4B7C;;;;;;;OAOG;;IAPH;;;;;;;OAOG;;oBAbM,SAAS;8CAhBkB,IAAI,SAC/B,CAAC,OAAO,EAAE,KAAK,KAAK,KAAK;;;;;;;;;;GAqdnC;6BAGa,UAAU;;;;;;;;;;;;;;;;;;;;wFA/dsB,CAAC;IA4B7C;;;;;;;OAOG;;IAPH;;;;;;;OAOG;;oBAbM,SAAS;2DAfT,CAAC,OAAO,EAAE,KAAK,KAAK,KAAK;;;;;;;;;;GAwdQ"}
|
package/src/replay-membrane.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/* eslint-disable no-use-before-define */
|
|
2
|
-
import { Fail, b, q } from '@endo/errors';
|
|
2
|
+
import { Fail, X, b, makeError, q } from '@endo/errors';
|
|
3
3
|
import { Far, Remotable, getInterfaceOf } from '@endo/pass-style';
|
|
4
4
|
import { E } from '@endo/eventual-send';
|
|
5
5
|
import { getMethodNames } from '@endo/eventual-send/utils.js';
|
|
6
|
-
import { makePromiseKit } from '@endo/promise-kit';
|
|
7
6
|
import { makeEquate } from './equate.js';
|
|
8
7
|
import { makeConvertKit } from './convert.js';
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* @import {PromiseKit} from '@endo/promise-kit'
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { fromEntries, defineProperties, assign } = Object;
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* @param {LogStore} log
|
|
@@ -31,6 +34,8 @@ export const makeReplayMembrane = (
|
|
|
31
34
|
|
|
32
35
|
let stopped = false;
|
|
33
36
|
|
|
37
|
+
const Panic = (template, ...args) => panic(makeError(X(template, ...args)));
|
|
38
|
+
|
|
34
39
|
// ////////////// Host or Interpreter to Guest ///////////////////////////////
|
|
35
40
|
|
|
36
41
|
/**
|
|
@@ -196,9 +201,61 @@ export const makeReplayMembrane = (
|
|
|
196
201
|
default: {
|
|
197
202
|
// @ts-expect-error TS correctly knows this case would be outside
|
|
198
203
|
// the type. But that's what we want to check.
|
|
199
|
-
throw
|
|
204
|
+
throw Panic`unexpected outcome kind ${q(outcome.kind)}`;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// //////////////// Eventual Send ////////////////////////////////////////////
|
|
210
|
+
|
|
211
|
+
const guestHandler = harden({
|
|
212
|
+
applyMethod(guestTarget, optVerb, guestArgs, guestReturnedP) {
|
|
213
|
+
if (optVerb === undefined) {
|
|
214
|
+
throw Panic`guest eventual call not yet supported: ${guestTarget}(${b(guestArgs)}) -> ${b(guestReturnedP)}`;
|
|
215
|
+
} else {
|
|
216
|
+
throw Panic`guest eventual send not yet supported: ${guestTarget}.${b(optVerb)}(${b(guestArgs)}) -> ${b(guestReturnedP)}`;
|
|
200
217
|
}
|
|
218
|
+
},
|
|
219
|
+
applyFunction(guestTarget, guestArgs, guestReturnedP) {
|
|
220
|
+
return guestHandler.applyMethod(
|
|
221
|
+
guestTarget,
|
|
222
|
+
undefined,
|
|
223
|
+
guestArgs,
|
|
224
|
+
guestReturnedP,
|
|
225
|
+
);
|
|
226
|
+
},
|
|
227
|
+
get(guestTarget, prop, guestReturnedP) {
|
|
228
|
+
throw Panic`guest eventual get not yet supported: ${guestTarget}.${b(prop)} -> ${b(guestReturnedP)}`;
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const makeGuestPresence = (iface, methodEntries) => {
|
|
233
|
+
let guestPresence;
|
|
234
|
+
void new HandledPromise((_res, _rej, resolveWithPresence) => {
|
|
235
|
+
guestPresence = resolveWithPresence(guestHandler);
|
|
236
|
+
}); // no unfulfilledHandler
|
|
237
|
+
if (typeof guestPresence !== 'object') {
|
|
238
|
+
throw Fail`presence expected to be object ${guestPresence}`;
|
|
201
239
|
}
|
|
240
|
+
assign(guestPresence, fromEntries(methodEntries));
|
|
241
|
+
const result = Remotable(iface, undefined, guestPresence);
|
|
242
|
+
result === guestPresence ||
|
|
243
|
+
Fail`Remotable expected to make presence in place: ${guestPresence} vs ${result}`;
|
|
244
|
+
return result;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @returns {PromiseKit<any>}
|
|
249
|
+
*/
|
|
250
|
+
const makeGuestPromiseKit = () => {
|
|
251
|
+
let resolve;
|
|
252
|
+
let reject;
|
|
253
|
+
const promise = new HandledPromise((res, rej, _resPres) => {
|
|
254
|
+
resolve = res;
|
|
255
|
+
reject = rej;
|
|
256
|
+
}, guestHandler);
|
|
257
|
+
// @ts-expect-error TS cannot infer that it is a PromiseKit
|
|
258
|
+
return harden({ promise, resolve, reject });
|
|
202
259
|
};
|
|
203
260
|
|
|
204
261
|
// //////////////// Converters ///////////////////////////////////////////////
|
|
@@ -246,9 +303,7 @@ export const makeReplayMembrane = (
|
|
|
246
303
|
name,
|
|
247
304
|
makeGuestMethod(name),
|
|
248
305
|
]);
|
|
249
|
-
|
|
250
|
-
// use HandledPromise to make gRem a remote presence for hRem
|
|
251
|
-
gRem = Remotable(guestIface, undefined, fromEntries(guestMethods));
|
|
306
|
+
gRem = makeGuestPresence(guestIface, guestMethods);
|
|
252
307
|
}
|
|
253
308
|
// See note at the top of the function to see why clearing the `hRem`
|
|
254
309
|
// variable is safe, and what invariant the above code needs to maintain so
|
|
@@ -258,10 +313,12 @@ export const makeReplayMembrane = (
|
|
|
258
313
|
};
|
|
259
314
|
harden(makeGuestForHostRemotable);
|
|
260
315
|
|
|
316
|
+
/**
|
|
317
|
+
* @param {Vow} hVow
|
|
318
|
+
* @returns {Promise}
|
|
319
|
+
*/
|
|
261
320
|
const makeGuestForHostVow = hVow => {
|
|
262
|
-
|
|
263
|
-
// use HandledPromise to make `promise` a handled promise for hVow
|
|
264
|
-
const { promise, resolve, reject } = makePromiseKit();
|
|
321
|
+
const { promise, resolve, reject } = makeGuestPromiseKit();
|
|
265
322
|
guestPromiseMap.set(promise, harden({ resolve, reject }));
|
|
266
323
|
|
|
267
324
|
watchWake(hVow);
|
package/src/types.d.ts
CHANGED
|
@@ -12,10 +12,10 @@ type GuestAsyncFunc = (...activationArgs: Guest[]) => Guest<Promise<any>>;
|
|
|
12
12
|
type HostAsyncFuncWrapper = (...activationArgs: Host[]) => HostVow;
|
|
13
13
|
type PreparationOptions = {
|
|
14
14
|
vowTools?: {
|
|
15
|
-
when: <T, TResult1 = import("@agoric/vow").
|
|
16
|
-
watch: <T_1 =
|
|
15
|
+
when: <T, TResult1 = import("@agoric/vow").EUnwrap<T>, TResult2 = never>(specimenP: T, onFulfilled?: ((value: import("@agoric/vow").EUnwrap<T>) => TResult1 | PromiseLike<TResult1>) | undefined, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined) => Promise<TResult1 | TResult2>;
|
|
16
|
+
watch: <T_1 = any, TResult1_1 = T_1, TResult2_1 = never, C = any>(specimenP: import("@agoric/vow").ERef<T_1 | Vow<T_1>>, watcher?: import("@agoric/vow").Watcher<T_1, TResult1_1, TResult2_1> | undefined, watcherContext?: C | undefined) => Vow<Exclude<TResult1_1, void> | Exclude<TResult2_1, void> extends never ? TResult1_1 : Exclude<TResult1_1, void> | Exclude<TResult2_1, void>>;
|
|
17
17
|
makeVowKit: <T_2>() => import("@agoric/vow").VowKit<T_2>;
|
|
18
|
-
allVows: (vows:
|
|
18
|
+
allVows: (vows: unknown[]) => Vow<any[]>;
|
|
19
19
|
} | undefined;
|
|
20
20
|
makeLogStore?: (() => import("@endo/exo").Guarded<{
|
|
21
21
|
reset(): void;
|
package/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.js"],"names":[],"mappings":"iBAWa,SAAS,GACrB,UAAsB,GACtB,WAAuB,GACvB,QAAoB,GACpB,MAAkB;WAMN,CAAC,gCAAD,CAAC;UAKD,CAAC,gCAAD,CAAC;;;;;aAQQ,CAAC;;;sBAIV,CAAC,GAAG,cAAc,EAAE,KAAK,EAAE,KAAK,KAAK,cAAS;4BAI9C,CAAC,GAAG,cAAc,EAAE,IAAI,EAAE,KAAK,OAAO;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.js"],"names":[],"mappings":"iBAWa,SAAS,GACrB,UAAsB,GACtB,WAAuB,GACvB,QAAoB,GACpB,MAAkB;WAMN,CAAC,gCAAD,CAAC;UAKD,CAAC,gCAAD,CAAC;;;;;aAQQ,CAAC;;;sBAIV,CAAC,GAAG,cAAc,EAAE,KAAK,EAAE,KAAK,KAAK,cAAS;4BAI9C,CAAC,GAAG,cAAc,EAAE,IAAI,EAAE,KAAK,OAAO;;;4FA1BlD,CAAA;;;wBA0BW,SAAQ;;;;;;;;;;;;;;;;;;;;;;;;mBAWP,QAAQ,GAAC,OAAO;eAIhB;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAE,OAAO,CAAC;IAAE,OAAO,EAAE,GAAG,CAAA;CAAC;cAStB,CAAC,4BADK,CAAC;SAAd,CAAC,IAAI,EACE,CAAC,AADA,KAAK,CAAC;cACd,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI;;;;;;;gBAQlB,CAAE,iEAAiE;AAC/E,EAAQ,EAAE,WAAW,EACrB,GAAS,EAAE,OAAO,EAClB,WAAiB,EAAE,IAAI,CAClB,GAAG,CACR,EAAQ,EAAE,UAAU,EACpB,GAAS,EAAE,OAAO,EAClB,MAAY,EAAE,IAAI,CACb,GAAG,CACR,EAAQ,EAAE,UAAU,EACpB,SAAe,EAAE,MAAM,EACvB,MAAY,EAAE,IAAI,CACb,GAAG,CACR,EAAQ,EAAE,SAAS,EACnB,SAAe,EAAE,MAAM,EACvB,OAAa,EAAE,IAAI,CACd,GAAG,CAAE,qEAAqE;AAC/E,EAAQ,EAAE,WAAW,EACrB,MAAY,EAAE,IAAI,EAClB,OAAa,EAAE,WAAW,GAAC,SAAS,EACpC,IAAU,EAAE,IAAI,EAAE,EAClB,SAAe,EAAE,MAAM,CAClB;gCAhGuB,mBAAmB;8BACrB,kBAAkB;0BACtB,mBAAmB;yBACV,aAAa;8BAAb,aAAa;8BAClB,gBAAgB;+BACf,gBAAgB;oCACX,sBAAsB"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// eslint-disable-next-line import/order
|
|
2
|
+
import {
|
|
3
|
+
test,
|
|
4
|
+
getBaggage,
|
|
5
|
+
annihilate,
|
|
6
|
+
nextLife,
|
|
7
|
+
} from './prepare-test-env-ava.js';
|
|
8
|
+
|
|
9
|
+
import { Fail } from '@endo/errors';
|
|
10
|
+
import { passStyleOf } from '@endo/pass-style';
|
|
11
|
+
import { makeCopyMap } from '@endo/patterns';
|
|
12
|
+
import { makePromiseKit } from '@endo/promise-kit';
|
|
13
|
+
import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
|
|
14
|
+
import { isVow } from '@agoric/vow/src/vow-utils.js';
|
|
15
|
+
import { prepareVowTools } from '@agoric/vow';
|
|
16
|
+
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
17
|
+
|
|
18
|
+
import { prepareAsyncFlowTools } from '../src/async-flow.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @import {AsyncFlow} from '../src/async-flow.js'
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {Zone} zone
|
|
26
|
+
* @param {number} [k]
|
|
27
|
+
*/
|
|
28
|
+
const prepareOrchestra = (zone, k = 1) =>
|
|
29
|
+
zone.exoClass(
|
|
30
|
+
'Orchestra',
|
|
31
|
+
undefined,
|
|
32
|
+
(factor, vow, resolver) => ({ factor, vow, resolver }),
|
|
33
|
+
{
|
|
34
|
+
scale(n) {
|
|
35
|
+
const { state } = this;
|
|
36
|
+
return k * state.factor * n;
|
|
37
|
+
},
|
|
38
|
+
vow() {
|
|
39
|
+
const { state } = this;
|
|
40
|
+
return state.vow;
|
|
41
|
+
},
|
|
42
|
+
resolve(x) {
|
|
43
|
+
const { state } = this;
|
|
44
|
+
state.resolver.resolve(x);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
/** @typedef {ReturnType<ReturnType<prepareOrchestra>>} Orchestra */
|
|
50
|
+
|
|
51
|
+
const firstLogLen = 7;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {any} t
|
|
55
|
+
* @param {Zone} zone
|
|
56
|
+
*/
|
|
57
|
+
const testFirstPlay = async (t, zone) => {
|
|
58
|
+
t.log('firstPlay started');
|
|
59
|
+
const vowTools = prepareVowTools(zone);
|
|
60
|
+
const { asyncFlow, adminAsyncFlow } = prepareAsyncFlowTools(zone, {
|
|
61
|
+
vowTools,
|
|
62
|
+
});
|
|
63
|
+
const makeOrchestra = prepareOrchestra(zone);
|
|
64
|
+
const { makeVowKit } = vowTools;
|
|
65
|
+
|
|
66
|
+
const { vow: v1, resolver: r1 } = zone.makeOnce('v1', () => makeVowKit());
|
|
67
|
+
const { vow: v2, resolver: r2 } = zone.makeOnce('v2', () => makeVowKit());
|
|
68
|
+
const { vow: v3, resolver: _r3 } = zone.makeOnce('v3', () => makeVowKit());
|
|
69
|
+
const hOrch7 = zone.makeOnce('hOrch7', () => makeOrchestra(7, v2, r2));
|
|
70
|
+
|
|
71
|
+
// purposely violate rule that guestMethod is closed.
|
|
72
|
+
const { promise: promiseStep, resolve: resolveStep } = makePromiseKit();
|
|
73
|
+
|
|
74
|
+
const { guestMethod } = {
|
|
75
|
+
async guestMethod(gOrch7, g1, _p3) {
|
|
76
|
+
t.log(' firstPlay about to await g1');
|
|
77
|
+
t.is(await g1, 'x');
|
|
78
|
+
const p2 = gOrch7.vow();
|
|
79
|
+
const prod = gOrch7.scale(3);
|
|
80
|
+
t.is(prod, 21);
|
|
81
|
+
|
|
82
|
+
let gErr;
|
|
83
|
+
try {
|
|
84
|
+
gOrch7.scale(9n);
|
|
85
|
+
} catch (e) {
|
|
86
|
+
gErr = e;
|
|
87
|
+
}
|
|
88
|
+
t.is(gErr.name, 'TypeError');
|
|
89
|
+
|
|
90
|
+
resolveStep(true);
|
|
91
|
+
t.log(' firstPlay to hang awaiting p2');
|
|
92
|
+
// awaiting a promise that won't be resolved until next incarnation
|
|
93
|
+
await p2;
|
|
94
|
+
t.fail('must not reach here in first incarnation');
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const wrapperFunc = asyncFlow(zone, 'AsyncFlow1', guestMethod);
|
|
99
|
+
|
|
100
|
+
const outcomeV = zone.makeOnce('outcomeV', () => wrapperFunc(hOrch7, v1, v3));
|
|
101
|
+
|
|
102
|
+
t.true(isVow(outcomeV));
|
|
103
|
+
r1.resolve('x');
|
|
104
|
+
|
|
105
|
+
const flow = zone.makeOnce('flow', () =>
|
|
106
|
+
adminAsyncFlow.getFlowForOutcomeVow(outcomeV),
|
|
107
|
+
);
|
|
108
|
+
t.is(passStyleOf(flow), 'remotable');
|
|
109
|
+
|
|
110
|
+
await promiseStep;
|
|
111
|
+
|
|
112
|
+
const logDump = flow.dump();
|
|
113
|
+
t.is(logDump.length, firstLogLen);
|
|
114
|
+
t.deepEqual(logDump, [
|
|
115
|
+
['doFulfill', v1, 'x'],
|
|
116
|
+
['checkCall', hOrch7, 'vow', [], 1],
|
|
117
|
+
['doReturn', 1, v2],
|
|
118
|
+
['checkCall', hOrch7, 'scale', [3], 3],
|
|
119
|
+
['doReturn', 3, 21],
|
|
120
|
+
['checkCall', hOrch7, 'scale', [9n], 5],
|
|
121
|
+
[
|
|
122
|
+
'doThrow',
|
|
123
|
+
5,
|
|
124
|
+
TypeError('Cannot mix BigInt and other types, use explicit conversions'),
|
|
125
|
+
],
|
|
126
|
+
]);
|
|
127
|
+
t.log('firstPlay done');
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Test from bug https://github.com/Agoric/agoric-sdk/issues/9465
|
|
132
|
+
*
|
|
133
|
+
* @param {any} t
|
|
134
|
+
* @param {Zone} zone
|
|
135
|
+
*/
|
|
136
|
+
const testBadShortReplay = async (t, zone) => {
|
|
137
|
+
t.log('badShortReplay started');
|
|
138
|
+
const vowTools = prepareVowTools(zone);
|
|
139
|
+
const { asyncFlow, adminAsyncFlow } = prepareAsyncFlowTools(zone, {
|
|
140
|
+
vowTools,
|
|
141
|
+
});
|
|
142
|
+
prepareOrchestra(zone);
|
|
143
|
+
const { when } = vowTools;
|
|
144
|
+
|
|
145
|
+
// purposely violate rule that guestMethod is closed.
|
|
146
|
+
const { promise: promiseStep, resolve: resolveStep } = makePromiseKit();
|
|
147
|
+
|
|
148
|
+
const { guestMethod } = {
|
|
149
|
+
async guestMethod(_gOrch7, _g1, _p3) {
|
|
150
|
+
t.log(' badReplay return early');
|
|
151
|
+
resolveStep(true);
|
|
152
|
+
return 'bad';
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// `asyncFlow` can be used simply to re-prepare the guest function
|
|
157
|
+
// by ignoring the returned wrapper function. If the wrapper function is
|
|
158
|
+
// invoked, that would be a *new* activation with a new outcome and
|
|
159
|
+
// flow, and would have nothing to do with the existing one.
|
|
160
|
+
asyncFlow(zone, 'AsyncFlow1', guestMethod);
|
|
161
|
+
|
|
162
|
+
const outcomeV = /** @type {Vow} */ (
|
|
163
|
+
zone.makeOnce('outcomeV', () => Fail`need outcomeV`)
|
|
164
|
+
);
|
|
165
|
+
const flow = /** @type {AsyncFlow} */ (
|
|
166
|
+
zone.makeOnce('flow', () => Fail`need flow`)
|
|
167
|
+
);
|
|
168
|
+
const flow1 = adminAsyncFlow.getFlowForOutcomeVow(outcomeV);
|
|
169
|
+
t.is(flow, flow1);
|
|
170
|
+
t.is(passStyleOf(flow), 'remotable');
|
|
171
|
+
|
|
172
|
+
await promiseStep;
|
|
173
|
+
|
|
174
|
+
const replayProblem = flow.getOptFatalProblem();
|
|
175
|
+
t.log(' badShortReplay failures', replayProblem);
|
|
176
|
+
t.true(replayProblem instanceof Error);
|
|
177
|
+
|
|
178
|
+
const outcome = when(outcomeV);
|
|
179
|
+
await eventLoopIteration();
|
|
180
|
+
|
|
181
|
+
t.is(await Promise.race([outcome, Promise.resolve('good')]), 'good');
|
|
182
|
+
|
|
183
|
+
t.deepEqual(
|
|
184
|
+
adminAsyncFlow.getFailures(),
|
|
185
|
+
makeCopyMap([[flow, replayProblem]]),
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
t.log('badShortReplay done');
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
test.serial.failing('test durable async-flow early completion', async t => {
|
|
192
|
+
annihilate();
|
|
193
|
+
const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
|
|
194
|
+
await testFirstPlay(t, zone1);
|
|
195
|
+
|
|
196
|
+
await eventLoopIteration();
|
|
197
|
+
|
|
198
|
+
nextLife();
|
|
199
|
+
const zone2a = makeDurableZone(getBaggage(), 'durableRoot');
|
|
200
|
+
await testBadShortReplay(t, zone2a);
|
|
201
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// eslint-disable-next-line import/order
|
|
2
|
+
import {
|
|
3
|
+
test,
|
|
4
|
+
getBaggage,
|
|
5
|
+
annihilate,
|
|
6
|
+
nextLife,
|
|
7
|
+
} from './prepare-test-env-ava.js';
|
|
8
|
+
|
|
9
|
+
import { Fail } from '@endo/errors';
|
|
10
|
+
import { prepareVowTools } from '@agoric/vow';
|
|
11
|
+
import { E } from '@endo/eventual-send';
|
|
12
|
+
// import E from '@agoric/vow/src/E.js';
|
|
13
|
+
import { makeHeapZone } from '@agoric/zone/heap.js';
|
|
14
|
+
import { makeVirtualZone } from '@agoric/zone/virtual.js';
|
|
15
|
+
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
16
|
+
|
|
17
|
+
import { prepareLogStore } from '../src/log-store.js';
|
|
18
|
+
import { prepareBijection } from '../src/bijection.js';
|
|
19
|
+
import { makeReplayMembrane } from '../src/replay-membrane.js';
|
|
20
|
+
|
|
21
|
+
const watchWake = _vowish => {};
|
|
22
|
+
const panic = problem => Fail`panic over ${problem}`;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {Zone} zone
|
|
26
|
+
*/
|
|
27
|
+
const preparePingee = zone =>
|
|
28
|
+
zone.exoClass('Pingee', undefined, () => ({}), {
|
|
29
|
+
ping(_str) {},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {ReturnType<ReturnType<preparePingee>>} Pingee
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {any} t
|
|
38
|
+
* @param {Zone} zone
|
|
39
|
+
*/
|
|
40
|
+
const testFirstPlay = async (t, zone) => {
|
|
41
|
+
const vowTools = prepareVowTools(zone);
|
|
42
|
+
const makeLogStore = prepareLogStore(zone);
|
|
43
|
+
const makeBijection = prepareBijection(zone);
|
|
44
|
+
const makePingee = preparePingee(zone);
|
|
45
|
+
|
|
46
|
+
const log = zone.makeOnce('log', () => makeLogStore());
|
|
47
|
+
const bij = zone.makeOnce('bij', makeBijection);
|
|
48
|
+
|
|
49
|
+
const mem = makeReplayMembrane(log, bij, vowTools, watchWake, panic);
|
|
50
|
+
|
|
51
|
+
t.deepEqual(log.dump(), []);
|
|
52
|
+
|
|
53
|
+
/** @type {Pingee} */
|
|
54
|
+
const pingee = zone.makeOnce('pingee', () => makePingee());
|
|
55
|
+
/** @type {Pingee} */
|
|
56
|
+
const guestPingee = mem.hostToGuest(pingee);
|
|
57
|
+
t.deepEqual(log.dump(), []);
|
|
58
|
+
|
|
59
|
+
const pingTestSendResult = t.throwsAsync(() => E(guestPingee).ping('send'), {
|
|
60
|
+
message:
|
|
61
|
+
'panic over "[Error: guest eventual send not yet supported: \\"[Alleged: Pingee guest wrapper]\\".ping([\\"send\\"]) -> \\"[Promise]\\"]"',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
guestPingee.ping('call');
|
|
65
|
+
|
|
66
|
+
await pingTestSendResult;
|
|
67
|
+
|
|
68
|
+
t.deepEqual(log.dump(), [
|
|
69
|
+
['checkCall', pingee, 'ping', ['call'], 0],
|
|
70
|
+
['doReturn', 0, undefined],
|
|
71
|
+
]);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
test.serial('test heap replay-membrane settlement', async t => {
|
|
75
|
+
const zone = makeHeapZone('heapRoot');
|
|
76
|
+
return testFirstPlay(t, zone);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test.serial('test virtual replay-membrane settlement', async t => {
|
|
80
|
+
annihilate();
|
|
81
|
+
const zone = makeVirtualZone('virtualRoot');
|
|
82
|
+
return testFirstPlay(t, zone);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test.serial('test durable replay-membrane settlement', async t => {
|
|
86
|
+
annihilate();
|
|
87
|
+
|
|
88
|
+
nextLife();
|
|
89
|
+
const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
|
|
90
|
+
return testFirstPlay(t, zone1);
|
|
91
|
+
});
|