@agoric/async-flow 0.1.1-dev-f0f1a3e.0 → 0.1.1-dev-4adf64f.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/async-flow.d.ts +2 -2
- package/src/log-store.d.ts +2 -2
- package/src/replay-membrane.d.ts +9 -8
- package/src/replay-membrane.d.ts.map +1 -1
- package/src/replay-membrane.js +257 -24
- package/src/type-guards.d.ts.map +1 -1
- package/src/type-guards.js +21 -7
- package/src/types.d.ts +1 -1
- package/src/types.d.ts.map +1 -1
- package/src/types.js +24 -0
- package/test/bad-host.test.js +2 -2
- package/test/replay-membrane-eventual.test.js +118 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/async-flow",
|
|
3
|
-
"version": "0.1.1-dev-
|
|
3
|
+
"version": "0.1.1-dev-4adf64f.0+4adf64f",
|
|
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,10 +24,10 @@
|
|
|
24
24
|
"author": "Agoric",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@agoric/base-zone": "0.1.1-dev-
|
|
28
|
-
"@agoric/internal": "0.3.3-dev-
|
|
29
|
-
"@agoric/store": "0.9.3-dev-
|
|
30
|
-
"@agoric/vow": "0.1.1-dev-
|
|
27
|
+
"@agoric/base-zone": "0.1.1-dev-4adf64f.0+4adf64f",
|
|
28
|
+
"@agoric/internal": "0.3.3-dev-4adf64f.0+4adf64f",
|
|
29
|
+
"@agoric/store": "0.9.3-dev-4adf64f.0+4adf64f",
|
|
30
|
+
"@agoric/vow": "0.1.1-dev-4adf64f.0+4adf64f",
|
|
31
31
|
"@endo/common": "^1.2.2",
|
|
32
32
|
"@endo/errors": "^1.2.2",
|
|
33
33
|
"@endo/eventual-send": "^1.2.2",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"@endo/promise-kit": "^1.1.2"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@agoric/swingset-liveslots": "0.10.3-dev-
|
|
41
|
-
"@agoric/zone": "0.2.3-dev-
|
|
40
|
+
"@agoric/swingset-liveslots": "0.10.3-dev-4adf64f.0+4adf64f",
|
|
41
|
+
"@agoric/zone": "0.2.3-dev-4adf64f.0+4adf64f",
|
|
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": "4adf64fd53a1a3c68ca52728710830201c9a4418"
|
|
66
66
|
}
|
package/src/async-flow.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export function prepareAsyncFlowTools(outerZone: Zone, outerOptions?: Preparatio
|
|
|
16
16
|
restart(eager?: boolean | undefined): void;
|
|
17
17
|
wake(): void;
|
|
18
18
|
getOutcome(): import("@agoric/vow").Vow<any>;
|
|
19
|
-
dump(): ([op: "doFulfill", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, fulfillment: import("@endo/pass-style").Passable] | [op: "doReject", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, reason: import("@endo/pass-style").Passable] | [op: "doReturn", callIndex: number, result: import("@endo/pass-style").Passable] | [op: "doThrow", callIndex: number, problem: import("@endo/pass-style").Passable] | [op: "checkCall", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number])[];
|
|
19
|
+
dump(): ([op: "doFulfill", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, fulfillment: import("@endo/pass-style").Passable] | [op: "doReject", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, reason: import("@endo/pass-style").Passable] | [op: "doReturn", callIndex: number, result: import("@endo/pass-style").Passable] | [op: "doThrow", callIndex: number, problem: import("@endo/pass-style").Passable] | [op: "checkCall", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSendOnly", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSend", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number])[];
|
|
20
20
|
getOptFatalProblem(): any;
|
|
21
21
|
};
|
|
22
22
|
admin: {
|
|
@@ -58,7 +58,7 @@ export type AsyncFlowTools = ReturnType<(outerZone: Zone, outerOptions?: Prepara
|
|
|
58
58
|
restart(eager?: boolean | undefined): void;
|
|
59
59
|
wake(): void;
|
|
60
60
|
getOutcome(): import("@agoric/vow").Vow<any>;
|
|
61
|
-
dump(): ([op: "doFulfill", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, fulfillment: import("@endo/pass-style").Passable] | [op: "doReject", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, reason: import("@endo/pass-style").Passable] | [op: "doReturn", callIndex: number, result: import("@endo/pass-style").Passable] | [op: "doThrow", callIndex: number, problem: import("@endo/pass-style").Passable] | [op: "checkCall", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number])[];
|
|
61
|
+
dump(): ([op: "doFulfill", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, fulfillment: import("@endo/pass-style").Passable] | [op: "doReject", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, reason: import("@endo/pass-style").Passable] | [op: "doReturn", callIndex: number, result: import("@endo/pass-style").Passable] | [op: "doThrow", callIndex: number, problem: import("@endo/pass-style").Passable] | [op: "checkCall", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSendOnly", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSend", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number])[];
|
|
62
62
|
getOptFatalProblem(): any;
|
|
63
63
|
};
|
|
64
64
|
admin: {
|
package/src/log-store.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export function prepareLogStore(zone: Zone): () => import("@endo/exo").Guarded<{
|
|
|
7
7
|
peekEntry(): LogEntry;
|
|
8
8
|
nextEntry(): LogEntry;
|
|
9
9
|
pushEntry(entry: any): number;
|
|
10
|
-
dump(): ([op: "doFulfill", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, fulfillment: import("@endo/pass-style").Passable] | [op: "doReject", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, reason: import("@endo/pass-style").Passable] | [op: "doReturn", callIndex: number, result: import("@endo/pass-style").Passable] | [op: "doThrow", callIndex: number, problem: import("@endo/pass-style").Passable] | [op: "checkCall", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number])[];
|
|
10
|
+
dump(): ([op: "doFulfill", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, fulfillment: import("@endo/pass-style").Passable] | [op: "doReject", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, reason: import("@endo/pass-style").Passable] | [op: "doReturn", callIndex: number, result: import("@endo/pass-style").Passable] | [op: "doThrow", callIndex: number, problem: import("@endo/pass-style").Passable] | [op: "checkCall", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSendOnly", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSend", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number])[];
|
|
11
11
|
promiseReplayDone(): Promise<undefined>;
|
|
12
12
|
}>;
|
|
13
13
|
export type LogStore = ReturnType<ReturnType<(zone: Zone) => () => import("@endo/exo").Guarded<{
|
|
@@ -19,7 +19,7 @@ export type LogStore = ReturnType<ReturnType<(zone: Zone) => () => import("@endo
|
|
|
19
19
|
peekEntry(): LogEntry;
|
|
20
20
|
nextEntry(): LogEntry;
|
|
21
21
|
pushEntry(entry: any): number;
|
|
22
|
-
dump(): ([op: "doFulfill", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, fulfillment: import("@endo/pass-style").Passable] | [op: "doReject", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, reason: import("@endo/pass-style").Passable] | [op: "doReturn", callIndex: number, result: import("@endo/pass-style").Passable] | [op: "doThrow", callIndex: number, problem: import("@endo/pass-style").Passable] | [op: "checkCall", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number])[];
|
|
22
|
+
dump(): ([op: "doFulfill", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, fulfillment: import("@endo/pass-style").Passable] | [op: "doReject", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, reason: import("@endo/pass-style").Passable] | [op: "doReturn", callIndex: number, result: import("@endo/pass-style").Passable] | [op: "doThrow", callIndex: number, problem: import("@endo/pass-style").Passable] | [op: "checkCall", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSendOnly", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSend", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number])[];
|
|
23
23
|
promiseReplayDone(): Promise<undefined>;
|
|
24
24
|
}>>>;
|
|
25
25
|
import type { Zone } from '@agoric/base-zone';
|
package/src/replay-membrane.d.ts
CHANGED
|
@@ -5,13 +5,13 @@ export function makeReplayMembrane({ log, bijection, vowTools, watchWake, panic,
|
|
|
5
5
|
watchWake: (vowish: Promise<any> | Vow) => void;
|
|
6
6
|
panic: (problem: Error) => never;
|
|
7
7
|
}): {
|
|
8
|
-
hostToGuest: (specimen:
|
|
9
|
-
guestToHost: (specimen:
|
|
8
|
+
hostToGuest: (specimen: Passable, label?: string | undefined) => any;
|
|
9
|
+
guestToHost: (specimen: Passable, label?: string | undefined) => any;
|
|
10
10
|
wake: () => void;
|
|
11
11
|
stop: () => void;
|
|
12
12
|
} & import("@endo/pass-style").RemotableObject<`Alleged: ${string}`> & import("@endo/eventual-send").RemotableBrand<{}, {
|
|
13
|
-
hostToGuest: (specimen:
|
|
14
|
-
guestToHost: (specimen:
|
|
13
|
+
hostToGuest: (specimen: Passable, label?: string | undefined) => any;
|
|
14
|
+
guestToHost: (specimen: Passable, label?: string | undefined) => any;
|
|
15
15
|
wake: () => void;
|
|
16
16
|
stop: () => void;
|
|
17
17
|
}>;
|
|
@@ -22,13 +22,13 @@ export type ReplayMembrane = ReturnType<({ log, bijection, vowTools, watchWake,
|
|
|
22
22
|
watchWake: (vowish: Promise<any> | Vow) => void;
|
|
23
23
|
panic: (problem: Error) => never;
|
|
24
24
|
}) => {
|
|
25
|
-
hostToGuest: (specimen:
|
|
26
|
-
guestToHost: (specimen:
|
|
25
|
+
hostToGuest: (specimen: Passable, label?: string | undefined) => any;
|
|
26
|
+
guestToHost: (specimen: Passable, label?: string | undefined) => any;
|
|
27
27
|
wake: () => void;
|
|
28
28
|
stop: () => void;
|
|
29
29
|
} & import("@endo/pass-style").RemotableObject<`Alleged: ${string}`> & import("@endo/eventual-send").RemotableBrand<{}, {
|
|
30
|
-
hostToGuest: (specimen:
|
|
31
|
-
guestToHost: (specimen:
|
|
30
|
+
hostToGuest: (specimen: Passable, label?: string | undefined) => any;
|
|
31
|
+
guestToHost: (specimen: Passable, label?: string | undefined) => any;
|
|
32
32
|
wake: () => void;
|
|
33
33
|
stop: () => void;
|
|
34
34
|
}>>;
|
|
@@ -36,4 +36,5 @@ import type { LogStore } from '../src/log-store.js';
|
|
|
36
36
|
import type { Bijection } from '../src/bijection.js';
|
|
37
37
|
import type { VowTools } from '@agoric/vow';
|
|
38
38
|
import type { Vow } from '@agoric/vow';
|
|
39
|
+
import type { Passable } from '@endo/pass-style';
|
|
39
40
|
//# sourceMappingURL=replay-membrane.d.ts.map
|
|
@@ -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":"AAsCO,oFANJ;IAAsB,GAAG,EAAjB,QAAQ;IACO,SAAS,EAAxB,SAAS;IACK,QAAQ,EAAtB,QAAQ;IAC6B,SAAS,EAA9C,CAAC,MAAM,EAAE,eAAU,GAAG,KAAK,IAAI;IACA,KAAK,EAApC,CAAC,OAAO,EAAE,KAAK,KAAK,KAAK;CACnC;;;;;;;;;;GAusBA;6BAGa,UAAU,mDA/sBrB;IAAsB,GAAG,EAAjB,QAAQ;IACO,SAAS,EAAxB,SAAS;IACK,QAAQ,EAAtB,QAAQ;IAC6B,SAAS,EAA9C,CAAC,MAAM,EAAE,eAAU,GAAG,KAAK,IAAI;IACA,KAAK,EAApC,CAAC,OAAO,EAAE,KAAK,KAAK,KAAK;CACnC;;;;;;;;;;GA0sB2C;8BAxtBjB,qBAAqB;+BACpB,qBAAqB;8BAFT,aAAa;yBAAb,aAAa;8BADD,kBAAkB"}
|
package/src/replay-membrane.js
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
/* eslint-disable no-use-before-define */
|
|
2
2
|
import { Fail, X, b, makeError, q } from '@endo/errors';
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
Far,
|
|
5
|
+
Remotable,
|
|
6
|
+
getInterfaceOf,
|
|
7
|
+
getTag,
|
|
8
|
+
makeTagged,
|
|
9
|
+
passStyleOf,
|
|
10
|
+
} from '@endo/pass-style';
|
|
5
11
|
import { E } from '@endo/eventual-send';
|
|
12
|
+
import { throwLabeled } from '@endo/common/throw-labeled.js';
|
|
13
|
+
import { heapVowE } from '@agoric/vow/vat.js';
|
|
6
14
|
import { getMethodNames } from '@endo/eventual-send/utils.js';
|
|
15
|
+
import { objectMap } from '@endo/common/object-map.js';
|
|
7
16
|
import { isVow } from '@agoric/vow/src/vow-utils.js';
|
|
8
17
|
import { makeEquate } from './equate.js';
|
|
9
18
|
import { makeConvertKit } from './convert.js';
|
|
10
19
|
|
|
11
20
|
/**
|
|
12
21
|
* @import {PromiseKit} from '@endo/promise-kit'
|
|
13
|
-
* @import {
|
|
22
|
+
* @import {Passable, PassableCap, CopyTagged} from '@endo/pass-style'
|
|
23
|
+
* @import {Vow, VowTools, VowKit} from '@agoric/vow'
|
|
14
24
|
* @import {LogStore} from '../src/log-store.js';
|
|
15
25
|
* @import {Bijection} from '../src/bijection.js';
|
|
16
26
|
* @import {Host, HostVow, LogEntry, Outcome} from '../src/types.js';
|
|
@@ -33,7 +43,7 @@ export const makeReplayMembrane = ({
|
|
|
33
43
|
watchWake,
|
|
34
44
|
panic,
|
|
35
45
|
}) => {
|
|
36
|
-
const { when, watch } = vowTools;
|
|
46
|
+
const { when, watch, makeVowKit } = vowTools;
|
|
37
47
|
|
|
38
48
|
const equate = makeEquate(bijection);
|
|
39
49
|
|
|
@@ -127,18 +137,50 @@ export const makeReplayMembrane = ({
|
|
|
127
137
|
|
|
128
138
|
// ///////////// Guest to Host or consume log ////////////////////////////////
|
|
129
139
|
|
|
140
|
+
/**
|
|
141
|
+
* The host is not supposed to expose host-side promises to the membrane,
|
|
142
|
+
* since they cannot be stored durably or survive upgrade. We cannot just
|
|
143
|
+
* automatically wrap any such host promises with host vows, because that
|
|
144
|
+
* would mask upgrade hazards if an upgrade happens before the vow settles.
|
|
145
|
+
* However, during the transition, the current host APIs called by
|
|
146
|
+
* orchestration still return many promises. We want to generate diagnostics
|
|
147
|
+
* when we encounter them, but for now, automatically convert them to
|
|
148
|
+
* host vow anyway, just so integration testing can proceed to reveal
|
|
149
|
+
* additional problems beyond these.
|
|
150
|
+
*
|
|
151
|
+
* @param {Passable} h
|
|
152
|
+
*/
|
|
130
153
|
const tolerateHostPromiseToVow = h => {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
154
|
+
const passStyle = passStyleOf(h);
|
|
155
|
+
switch (passStyle) {
|
|
156
|
+
case 'promise': {
|
|
157
|
+
const e = Error('where warning happened');
|
|
158
|
+
console.log('Warning for now: vow expected, not promise', h, e);
|
|
159
|
+
// TODO remove this stopgap. Here for now because host-side
|
|
160
|
+
// promises are everywhere!
|
|
161
|
+
// Note: A good place to set a breakpoint, or to uncomment the
|
|
162
|
+
// `debugger;` line, to work around bundling.
|
|
163
|
+
// debugger;
|
|
164
|
+
return watch(h);
|
|
165
|
+
}
|
|
166
|
+
case 'copyRecord': {
|
|
167
|
+
const o = /** @type {object} */ (h);
|
|
168
|
+
return objectMap(o, tolerateHostPromiseToVow);
|
|
169
|
+
}
|
|
170
|
+
case 'copyArray': {
|
|
171
|
+
const a = /** @type {Array} */ (h);
|
|
172
|
+
return harden(a.map(tolerateHostPromiseToVow));
|
|
173
|
+
}
|
|
174
|
+
case 'tagged': {
|
|
175
|
+
const t = /** @type {CopyTagged} */ (h);
|
|
176
|
+
if (isVow(t)) {
|
|
177
|
+
return h;
|
|
178
|
+
}
|
|
179
|
+
return makeTagged(getTag(t), tolerateHostPromiseToVow(t.payload));
|
|
180
|
+
}
|
|
181
|
+
default: {
|
|
182
|
+
return h;
|
|
183
|
+
}
|
|
142
184
|
}
|
|
143
185
|
};
|
|
144
186
|
|
|
@@ -150,6 +192,7 @@ export const makeReplayMembrane = ({
|
|
|
150
192
|
: hostTarget(...hostArgs);
|
|
151
193
|
// This is a temporary kludge anyway. But note that it only
|
|
152
194
|
// catches the case where the promise is at the top of hostResult.
|
|
195
|
+
harden(hostResult);
|
|
153
196
|
hostResult = tolerateHostPromiseToVow(hostResult);
|
|
154
197
|
// Try converting here just to route the error correctly
|
|
155
198
|
hostToGuest(hostResult, `converting ${optVerb || 'host'} result`);
|
|
@@ -233,14 +276,193 @@ export const makeReplayMembrane = ({
|
|
|
233
276
|
|
|
234
277
|
// //////////////// Eventual Send ////////////////////////////////////////////
|
|
235
278
|
|
|
279
|
+
/**
|
|
280
|
+
* @param {PassableCap} hostTarget
|
|
281
|
+
* @param {string | undefined} optVerb
|
|
282
|
+
* @param {Passable[]} hostArgs
|
|
283
|
+
*/
|
|
284
|
+
const performSendOnly = (hostTarget, optVerb, hostArgs) => {
|
|
285
|
+
try {
|
|
286
|
+
optVerb
|
|
287
|
+
? heapVowE.sendOnly(hostTarget)[optVerb](...hostArgs)
|
|
288
|
+
: // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
|
|
289
|
+
// @ts-ignore once we changed this from E to heapVowE,
|
|
290
|
+
// typescript started complaining that heapVowE(hostTarget)
|
|
291
|
+
// is not callable. I'm not sure if this is a just a typing bug
|
|
292
|
+
// in heapVowE or also reflects a runtime deficiency. But this
|
|
293
|
+
// case it not used yet anyway. We disable it
|
|
294
|
+
// with at-ts-ignore rather than at-ts-expect-error because
|
|
295
|
+
// the dependency-graph tests complains that the latter is unused.
|
|
296
|
+
heapVowE.sendOnly(hostTarget)(...hostArgs);
|
|
297
|
+
} catch (hostProblem) {
|
|
298
|
+
throw Panic`internal: eventual sendOnly synchrously failed ${hostProblem}`;
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* @param {PassableCap} hostTarget
|
|
304
|
+
* @param {string | undefined} optVerb
|
|
305
|
+
* @param {Passable[]} hostArgs
|
|
306
|
+
* @param {number} callIndex
|
|
307
|
+
* @param {VowKit} hostResultKit
|
|
308
|
+
* @param {Promise} guestReturnedP
|
|
309
|
+
* @returns {Outcome}
|
|
310
|
+
*/
|
|
311
|
+
const performSend = (
|
|
312
|
+
hostTarget,
|
|
313
|
+
optVerb,
|
|
314
|
+
hostArgs,
|
|
315
|
+
callIndex,
|
|
316
|
+
hostResultKit,
|
|
317
|
+
guestReturnedP,
|
|
318
|
+
) => {
|
|
319
|
+
const { vow, resolver } = hostResultKit;
|
|
320
|
+
try {
|
|
321
|
+
const hostPromise = optVerb
|
|
322
|
+
? heapVowE(hostTarget)[optVerb](...hostArgs)
|
|
323
|
+
: // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
|
|
324
|
+
// @ts-ignore once we changed this from E to heapVowE,
|
|
325
|
+
// typescript started complaining that heapVowE(hostTarget)
|
|
326
|
+
// is not callable. I'm not sure if this is a just a typing bug
|
|
327
|
+
// in heapVowE or also reflects a runtime deficiency. But this
|
|
328
|
+
// case it not used yet anyway. We disable it
|
|
329
|
+
// with at-ts-ignore rather than at-ts-expect-error because
|
|
330
|
+
// the dependency-graph tests complains that the latter is unused.
|
|
331
|
+
heapVowE(hostTarget)(...hostArgs);
|
|
332
|
+
resolver.resolve(hostPromise); // TODO does this always work?
|
|
333
|
+
} catch (hostProblem) {
|
|
334
|
+
throw Panic`internal: eventual send synchrously failed ${hostProblem}`;
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
const entry = harden(['doReturn', callIndex, vow]);
|
|
338
|
+
log.pushEntry(entry);
|
|
339
|
+
const guestPromise = makeGuestForHostVow(vow, guestReturnedP);
|
|
340
|
+
// Note that `guestPromise` is not registered in the bijection since
|
|
341
|
+
// guestReturnedP is already the guest for vow. Rather, the handler
|
|
342
|
+
// returns guestPromise to resolve guestReturnedP to guestPromise.
|
|
343
|
+
doReturn(callIndex, vow);
|
|
344
|
+
return harden({
|
|
345
|
+
kind: 'return',
|
|
346
|
+
result: guestPromise,
|
|
347
|
+
});
|
|
348
|
+
} catch (problem) {
|
|
349
|
+
throw panic(problem);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
236
353
|
const guestHandler = harden({
|
|
354
|
+
applyMethodSendOnly(guestTarget, optVerb, guestArgs) {
|
|
355
|
+
const callIndex = log.getIndex();
|
|
356
|
+
if (stopped || !bijection.hasGuest(guestTarget)) {
|
|
357
|
+
Fail`Sent from a previous run: ${guestTarget}`;
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
const guestEntry = harden([
|
|
361
|
+
'checkSendOnly',
|
|
362
|
+
guestTarget,
|
|
363
|
+
optVerb,
|
|
364
|
+
guestArgs,
|
|
365
|
+
callIndex,
|
|
366
|
+
]);
|
|
367
|
+
if (log.isReplaying()) {
|
|
368
|
+
const entry = log.nextEntry();
|
|
369
|
+
try {
|
|
370
|
+
equate(guestEntry, entry);
|
|
371
|
+
} catch (equateErr) {
|
|
372
|
+
// TODO consider Richard Gibson's suggestion for a better way
|
|
373
|
+
// to keep track of the error labeling.
|
|
374
|
+
throwLabeled(
|
|
375
|
+
equateErr,
|
|
376
|
+
`replay ${callIndex}:
|
|
377
|
+
${q(guestEntry)}
|
|
378
|
+
vs ${q(entry)}
|
|
379
|
+
`,
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
const entry = guestToHost(guestEntry);
|
|
384
|
+
log.pushEntry(entry);
|
|
385
|
+
const [_op, hostTarget, _optVerb, hostArgs, _callIndex] = entry;
|
|
386
|
+
performSendOnly(hostTarget, optVerb, hostArgs);
|
|
387
|
+
}
|
|
388
|
+
} catch (fatalError) {
|
|
389
|
+
throw panic(fatalError);
|
|
390
|
+
}
|
|
391
|
+
},
|
|
237
392
|
applyMethod(guestTarget, optVerb, guestArgs, guestReturnedP) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
393
|
+
const callIndex = log.getIndex();
|
|
394
|
+
if (stopped || !bijection.hasGuest(guestTarget)) {
|
|
395
|
+
Fail`Sent from a previous run: ${guestTarget}`;
|
|
396
|
+
}
|
|
397
|
+
const hostResultKit = makeVowKit();
|
|
398
|
+
const g = bijection.unwrapInit(guestReturnedP, hostResultKit.vow);
|
|
399
|
+
g === guestReturnedP ||
|
|
400
|
+
Fail`internal: guestReturnedP should not unwrap: ${g} vs ${guestReturnedP}`;
|
|
401
|
+
/** @type {Outcome} */
|
|
402
|
+
let outcome;
|
|
403
|
+
try {
|
|
404
|
+
const guestEntry = harden([
|
|
405
|
+
'checkSend',
|
|
406
|
+
guestTarget,
|
|
407
|
+
optVerb,
|
|
408
|
+
guestArgs,
|
|
409
|
+
callIndex,
|
|
410
|
+
]);
|
|
411
|
+
if (log.isReplaying()) {
|
|
412
|
+
const entry = log.nextEntry();
|
|
413
|
+
try {
|
|
414
|
+
equate(guestEntry, entry);
|
|
415
|
+
} catch (equateErr) {
|
|
416
|
+
// TODO consider Richard Gibson's suggestion for a better way
|
|
417
|
+
// to keep track of the error labeling.
|
|
418
|
+
throwLabeled(
|
|
419
|
+
equateErr,
|
|
420
|
+
`replay ${callIndex}:
|
|
421
|
+
${q(guestEntry)}
|
|
422
|
+
vs ${q(entry)}
|
|
423
|
+
`,
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
outcome = /** @type {Outcome} */ (nestInterpreter(callIndex));
|
|
427
|
+
} else {
|
|
428
|
+
const entry = guestToHost(guestEntry);
|
|
429
|
+
log.pushEntry(entry);
|
|
430
|
+
const [_op, hostTarget, _optVerb, hostArgs, _callIndex] = entry;
|
|
431
|
+
nestInterpreter(callIndex);
|
|
432
|
+
outcome = performSend(
|
|
433
|
+
hostTarget,
|
|
434
|
+
optVerb,
|
|
435
|
+
hostArgs,
|
|
436
|
+
callIndex,
|
|
437
|
+
hostResultKit,
|
|
438
|
+
guestReturnedP,
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
} catch (fatalError) {
|
|
442
|
+
throw panic(fatalError);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
switch (outcome.kind) {
|
|
446
|
+
case 'return': {
|
|
447
|
+
return outcome.result;
|
|
448
|
+
}
|
|
449
|
+
case 'throw': {
|
|
450
|
+
throw outcome.problem;
|
|
451
|
+
}
|
|
452
|
+
default: {
|
|
453
|
+
// @ts-expect-error TS correctly knows this case would be outside
|
|
454
|
+
// the type. But that's what we want to check.
|
|
455
|
+
throw Panic`unexpected outcome kind ${q(outcome.kind)}`;
|
|
456
|
+
}
|
|
242
457
|
}
|
|
243
458
|
},
|
|
459
|
+
applyFunctionSendOnly(guestTarget, guestArgs) {
|
|
460
|
+
return guestHandler.applyMethodSendOnly(
|
|
461
|
+
guestTarget,
|
|
462
|
+
undefined,
|
|
463
|
+
guestArgs,
|
|
464
|
+
);
|
|
465
|
+
},
|
|
244
466
|
applyFunction(guestTarget, guestArgs, guestReturnedP) {
|
|
245
467
|
return guestHandler.applyMethod(
|
|
246
468
|
guestTarget,
|
|
@@ -249,6 +471,9 @@ export const makeReplayMembrane = ({
|
|
|
249
471
|
guestReturnedP,
|
|
250
472
|
);
|
|
251
473
|
},
|
|
474
|
+
getSendOnly(guestTarget, prop) {
|
|
475
|
+
throw Panic`guest eventual getSendOnly not yet supported: ${guestTarget}.${b(prop)}`;
|
|
476
|
+
},
|
|
252
477
|
get(guestTarget, prop, guestReturnedP) {
|
|
253
478
|
throw Panic`guest eventual get not yet supported: ${guestTarget}.${b(prop)} -> ${b(guestReturnedP)}`;
|
|
254
479
|
},
|
|
@@ -340,13 +565,21 @@ export const makeReplayMembrane = ({
|
|
|
340
565
|
|
|
341
566
|
/**
|
|
342
567
|
* @param {Vow} hVow
|
|
343
|
-
* @
|
|
568
|
+
* @param {Promise} [promiseKey]
|
|
569
|
+
* If provided, use this promise as the key in the guestPromiseMap
|
|
570
|
+
* rather than the returned promise. This only happens when the
|
|
571
|
+
* promiseKey ends up forwarded to the returned promise anyway, so
|
|
572
|
+
* associating it with this resolve/reject pair is not incorrect.
|
|
573
|
+
* It is needed when `promiseKey` is also entered into the bijection
|
|
574
|
+
* paired with hVow.
|
|
575
|
+
* @returns {Promise}
|
|
344
576
|
*/
|
|
345
|
-
const makeGuestForHostVow = hVow => {
|
|
577
|
+
const makeGuestForHostVow = (hVow, promiseKey = undefined) => {
|
|
346
578
|
hVow = tolerateHostPromiseToVow(hVow);
|
|
347
579
|
isVow(hVow) || Fail`vow expected ${hVow}`;
|
|
348
580
|
const { promise, resolve, reject } = makeGuestPromiseKit();
|
|
349
|
-
|
|
581
|
+
promiseKey ??= promise;
|
|
582
|
+
guestPromiseMap.set(promiseKey, harden({ resolve, reject }));
|
|
350
583
|
|
|
351
584
|
watchWake(hVow);
|
|
352
585
|
|
|
@@ -370,7 +603,7 @@ export const makeReplayMembrane = ({
|
|
|
370
603
|
hVow,
|
|
371
604
|
async hostFulfillment => {
|
|
372
605
|
await log.promiseReplayDone(); // should never reject
|
|
373
|
-
if (!stopped && guestPromiseMap.get(
|
|
606
|
+
if (!stopped && guestPromiseMap.get(promiseKey) !== 'settled') {
|
|
374
607
|
/** @type {LogEntry} */
|
|
375
608
|
const entry = harden(['doFulfill', hVow, hostFulfillment]);
|
|
376
609
|
log.pushEntry(entry);
|
|
@@ -385,7 +618,7 @@ export const makeReplayMembrane = ({
|
|
|
385
618
|
},
|
|
386
619
|
async hostReason => {
|
|
387
620
|
await log.promiseReplayDone(); // should never reject
|
|
388
|
-
if (!stopped && guestPromiseMap.get(
|
|
621
|
+
if (!stopped && guestPromiseMap.get(promiseKey) !== 'settled') {
|
|
389
622
|
/** @type {LogEntry} */
|
|
390
623
|
const entry = harden(['doReject', hVow, hostReason]);
|
|
391
624
|
log.pushEntry(entry);
|
package/src/type-guards.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-guards.d.ts","sourceRoot":"","sources":["type-guards.js"],"names":[],"mappings":"AAGA,8DAME;AAEF,gEAA6D;AAE7D,
|
|
1
|
+
{"version":3,"file":"type-guards.d.ts","sourceRoot":"","sources":["type-guards.js"],"names":[],"mappings":"AAGA,8DAME;AAEF,gEAA6D;AAE7D,6DAsDE"}
|
package/src/type-guards.js
CHANGED
|
@@ -23,6 +23,13 @@ export const LogEntryShape = M.or(
|
|
|
23
23
|
// M.number(),
|
|
24
24
|
// ],
|
|
25
25
|
// [
|
|
26
|
+
// 'doSendOnly',
|
|
27
|
+
// M.or(M.remotable('host wrapper of guest target'), VowShape),
|
|
28
|
+
// M.opt(PropertyKeyShape),
|
|
29
|
+
// M.arrayOf(M.any()),
|
|
30
|
+
// M.number(),
|
|
31
|
+
// ],
|
|
32
|
+
// [
|
|
26
33
|
// 'doSend',
|
|
27
34
|
// M.or(M.remotable('host wrapper of guest target'), VowShape),
|
|
28
35
|
// M.opt(PropertyKeyShape),
|
|
@@ -42,13 +49,20 @@ export const LogEntryShape = M.or(
|
|
|
42
49
|
M.arrayOf(M.any()),
|
|
43
50
|
M.number(),
|
|
44
51
|
],
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
[
|
|
53
|
+
'checkSendOnly',
|
|
54
|
+
M.or(M.remotable('host target'), VowShape),
|
|
55
|
+
M.opt(PropertyKeyShape),
|
|
56
|
+
M.arrayOf(M.any()),
|
|
57
|
+
M.number(),
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
'checkSend',
|
|
61
|
+
M.or(M.remotable('host target'), VowShape),
|
|
62
|
+
M.opt(PropertyKeyShape),
|
|
63
|
+
M.arrayOf(M.any()),
|
|
64
|
+
M.number(),
|
|
65
|
+
],
|
|
52
66
|
// ['checkReturn', M.number(), M.any()],
|
|
53
67
|
// ['checkThrow', M.number(), M.any()],
|
|
54
68
|
);
|
package/src/types.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ export type Ephemera<S extends WeakKey = WeakKey, V extends unknown = any> = {
|
|
|
49
49
|
*/
|
|
50
50
|
export type LogEntry = [// ///////////////// From Host to Guest /////////////////////////
|
|
51
51
|
op: "doFulfill", vow: HostVow, fulfillment: Host] | [op: "doReject", vow: HostVow, reason: Host] | [op: "doReturn", callIndex: number, result: Host] | [op: "doThrow", callIndex: number, problem: Host] | [// ///////////////////// From Guest to Host /////////////////////////
|
|
52
|
-
op: "checkCall", target: Host, optVerb: PropertyKey | undefined, args: Host[], callIndex: number];
|
|
52
|
+
op: "checkCall", target: Host, optVerb: PropertyKey | undefined, args: Host[], callIndex: number] | [op: "checkSendOnly", target: Host, optVerb: PropertyKey | undefined, args: Host[], callIndex: number] | [op: "checkSend", target: Host, optVerb: PropertyKey | undefined, args: Host[], callIndex: number];
|
|
53
53
|
import type { Passable } from '@endo/pass-style';
|
|
54
54
|
import type { Vow } from '@agoric/vow';
|
|
55
55
|
import type { LogStore } from './log-store.js';
|
package/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.js"],"names":[],"mappings":"wBAYa,SAAS,GACrB,UAAsB,GACtB,WAAuB,GACvB,QAAoB,GACpB,MAAkB;;;;;;;;kBAWC,CAAC,0BACR,CAAC;iBAIW,CAAC,SAAb,QAAU,eACV,CAAC;;;;;oBAOW,CAAC,SAAb,QAAU,eACV,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;6BAIZ,CAAC,GAAG,cAAc,EAAE,KAAK,EAAE,KAAK,KAAK,cAAS;mCAI9C,CAAC,GAAG,cAAc,EAAE,IAAI,EAAE,KAAK,OAAO;;;;;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.js"],"names":[],"mappings":"wBAYa,SAAS,GACrB,UAAsB,GACtB,WAAuB,GACvB,QAAoB,GACpB,MAAkB;;;;;;;;kBAWC,CAAC,0BACR,CAAC;iBAIW,CAAC,SAAb,QAAU,eACV,CAAC;;;;;oBAOW,CAAC,SAAb,QAAU,eACV,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;6BAIZ,CAAC,GAAG,cAAc,EAAE,KAAK,EAAE,KAAK,KAAK,cAAS;mCAI9C,CAAC,GAAG,cAAc,EAAE,IAAI,EAAE,KAAK,OAAO;;;;;;;wCAyDxC,GAAG;;0BAnDM,QAAQ;2BACR,SAAS;;;;;;0BAKhB,QAAQ,GAAC,OAAO;sBAIhB;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAE,OAAO,CAAC;IAAE,OAAO,EAAE,GAAG,CAAA;CAAC;qBAKnB,CAAC,SAAX,OAAQ,YACF,CAAC;SAEP,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;cACd,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI;;;;;;;uBAQlB,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,GAAG,CACR,EAAQ,EAAE,eAAe,EACzB,MAAY,EAAE,IAAI,EAClB,OAAa,EAAE,WAAW,GAAC,SAAS,EACpC,IAAU,EAAE,IAAI,EAAE,EAClB,SAAe,EAAE,MAAM,CAClB,GAAG,CACR,EAAQ,EAAE,WAAW,EACrB,MAAY,EAAE,IAAI,EAClB,OAAa,EAAE,WAAW,GAAC,SAAS,EACpC,IAAU,EAAE,IAAI,EAAE,EAClB,SAAe,EAAE,MAAM,CAClB;8BAjHqB,kBAAkB;yBACb,aAAa;8BAClB,gBAAgB;+BACf,gBAAgB"}
|
package/src/types.js
CHANGED
|
@@ -103,6 +103,18 @@ export {};
|
|
|
103
103
|
* optVerb: PropertyKey|undefined,
|
|
104
104
|
* args: Host[],
|
|
105
105
|
* callIndex: number
|
|
106
|
+
* ] | [
|
|
107
|
+
* op: 'checkSendOnly',
|
|
108
|
+
* target: Host,
|
|
109
|
+
* optVerb: PropertyKey|undefined,
|
|
110
|
+
* args: Host[],
|
|
111
|
+
* callIndex: number
|
|
112
|
+
* ] | [
|
|
113
|
+
* op: 'checkSend',
|
|
114
|
+
* target: Host,
|
|
115
|
+
* optVerb: PropertyKey|undefined,
|
|
116
|
+
* args: Host[],
|
|
117
|
+
* callIndex: number
|
|
106
118
|
* ]} LogEntry
|
|
107
119
|
*/
|
|
108
120
|
|
|
@@ -127,6 +139,12 @@ export {};
|
|
|
127
139
|
* args: Host[],
|
|
128
140
|
* callIndex: number
|
|
129
141
|
* ] | [
|
|
142
|
+
* op: 'doSendOnly',
|
|
143
|
+
* target: Host,
|
|
144
|
+
* optVerb: PropertyKey|undefined,
|
|
145
|
+
* args: Host[],
|
|
146
|
+
* callIndex: number
|
|
147
|
+
* ] | [
|
|
130
148
|
* op: 'doSend',
|
|
131
149
|
* target: Host,
|
|
132
150
|
* optVerb: PropertyKey|undefined,
|
|
@@ -155,6 +173,12 @@ export {};
|
|
|
155
173
|
* args: Host[],
|
|
156
174
|
* callIndex: number
|
|
157
175
|
* ] | [
|
|
176
|
+
* op: 'checkSendOnly',
|
|
177
|
+
* target: Host,
|
|
178
|
+
* optVerb: PropertyKey|undefined,
|
|
179
|
+
* args: Host[],
|
|
180
|
+
* callIndex: number
|
|
181
|
+
* ] | [
|
|
158
182
|
* op: 'checkSend',
|
|
159
183
|
* target: Host,
|
|
160
184
|
* optVerb: PropertyKey|undefined,
|
package/test/bad-host.test.js
CHANGED
|
@@ -141,7 +141,7 @@ const testBadHostReplay1 = async (t, zone) => {
|
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
message:
|
|
144
|
-
'
|
|
144
|
+
'Remotables must be explicitly declared: "[Function nonPassableFunc]"',
|
|
145
145
|
},
|
|
146
146
|
);
|
|
147
147
|
t.log(' badHost replay1 guest error caused by host error', gErr);
|
|
@@ -177,7 +177,7 @@ const testBadHostReplay1 = async (t, zone) => {
|
|
|
177
177
|
'doThrow',
|
|
178
178
|
2,
|
|
179
179
|
Error(
|
|
180
|
-
'
|
|
180
|
+
'Remotables must be explicitly declared: "[Function nonPassableFunc]"',
|
|
181
181
|
),
|
|
182
182
|
],
|
|
183
183
|
['checkCall', badHost, 'badMethod', [], 4],
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from './prepare-test-env-ava.js';
|
|
8
8
|
|
|
9
9
|
import { Fail } from '@endo/errors';
|
|
10
|
+
import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
|
|
10
11
|
import { prepareVowTools } from '@agoric/vow';
|
|
11
12
|
import { E } from '@endo/eventual-send';
|
|
12
13
|
// import E from '@agoric/vow/src/E.js';
|
|
@@ -46,9 +47,12 @@ const preparePingee = zone =>
|
|
|
46
47
|
*/
|
|
47
48
|
const testFirstPlay = async (t, zone) => {
|
|
48
49
|
const vowTools = prepareVowTools(zone);
|
|
50
|
+
const { makeVowKit } = vowTools;
|
|
49
51
|
const makeLogStore = prepareLogStore(zone);
|
|
50
52
|
const makeBijection = prepareBijection(zone);
|
|
51
53
|
const makePingee = preparePingee(zone);
|
|
54
|
+
const { vow: v1, resolver: r1 } = zone.makeOnce('v1', () => makeVowKit());
|
|
55
|
+
const { vow: _v2, resolver: _r2 } = zone.makeOnce('v2', () => makeVowKit());
|
|
52
56
|
|
|
53
57
|
const log = zone.makeOnce('log', () => makeLogStore());
|
|
54
58
|
const bijection = zone.makeOnce('bij', makeBijection);
|
|
@@ -61,6 +65,7 @@ const testFirstPlay = async (t, zone) => {
|
|
|
61
65
|
panic,
|
|
62
66
|
});
|
|
63
67
|
|
|
68
|
+
const p1 = mem.hostToGuest(v1);
|
|
64
69
|
t.deepEqual(log.dump(), []);
|
|
65
70
|
|
|
66
71
|
/** @type {Pingee} */
|
|
@@ -69,18 +74,122 @@ const testFirstPlay = async (t, zone) => {
|
|
|
69
74
|
const guestPingee = mem.hostToGuest(pingee);
|
|
70
75
|
t.deepEqual(log.dump(), []);
|
|
71
76
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
const p = E(guestPingee).ping('send');
|
|
78
|
+
const pOnly = E.sendOnly(guestPingee).ping('sendOnly');
|
|
79
|
+
t.is(pOnly, undefined);
|
|
80
|
+
|
|
81
|
+
guestPingee.ping('call');
|
|
82
|
+
|
|
83
|
+
t.is(await p, undefined);
|
|
84
|
+
const dump = log.dump();
|
|
85
|
+
const v3 = dump[3][2];
|
|
86
|
+
t.deepEqual(dump, [
|
|
87
|
+
['checkCall', pingee, 'ping', ['call'], 0],
|
|
88
|
+
['doReturn', 0, undefined],
|
|
89
|
+
['checkSend', pingee, 'ping', ['send'], 2],
|
|
90
|
+
['doReturn', 2, v3],
|
|
91
|
+
['checkSendOnly', pingee, 'ping', ['sendOnly'], 4],
|
|
92
|
+
['doFulfill', v3, undefined],
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
r1.resolve('x');
|
|
96
|
+
t.is(await p1, 'x');
|
|
97
|
+
|
|
98
|
+
t.deepEqual(log.dump(), [
|
|
99
|
+
['checkCall', pingee, 'ping', ['call'], 0],
|
|
100
|
+
['doReturn', 0, undefined],
|
|
101
|
+
['checkSend', pingee, 'ping', ['send'], 2],
|
|
102
|
+
['doReturn', 2, v3],
|
|
103
|
+
['checkSendOnly', pingee, 'ping', ['sendOnly'], 4],
|
|
104
|
+
['doFulfill', v3, undefined],
|
|
105
|
+
['doFulfill', v1, 'x'],
|
|
106
|
+
]);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {any} t
|
|
111
|
+
* @param {Zone} zone
|
|
112
|
+
*/
|
|
113
|
+
const testReplay = async (t, zone) => {
|
|
114
|
+
const vowTools = prepareVowTools(zone);
|
|
115
|
+
prepareLogStore(zone);
|
|
116
|
+
prepareBijection(zone);
|
|
117
|
+
preparePingee(zone);
|
|
118
|
+
const { vow: v1 } = zone.makeOnce('v1', () => Fail`need v1`);
|
|
119
|
+
const { vow: v2, resolver: r2 } = zone.makeOnce('v2', () => Fail`need v2`);
|
|
120
|
+
|
|
121
|
+
const log = /** @type {LogStore} */ (
|
|
122
|
+
zone.makeOnce('log', () => Fail`need log`)
|
|
123
|
+
);
|
|
124
|
+
const bijection = /** @type {Bijection} */ (
|
|
125
|
+
zone.makeOnce('bij', () => Fail`need bij`)
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const pingee = zone.makeOnce('pingee', () => Fail`need pingee`);
|
|
129
|
+
|
|
130
|
+
const dump = log.dump();
|
|
131
|
+
const v3 = dump[3][2];
|
|
132
|
+
t.deepEqual(dump, [
|
|
133
|
+
['checkCall', pingee, 'ping', ['call'], 0],
|
|
134
|
+
['doReturn', 0, undefined],
|
|
135
|
+
['checkSend', pingee, 'ping', ['send'], 2],
|
|
136
|
+
['doReturn', 2, v3],
|
|
137
|
+
['checkSendOnly', pingee, 'ping', ['sendOnly'], 4],
|
|
138
|
+
['doFulfill', v3, undefined],
|
|
139
|
+
['doFulfill', v1, 'x'],
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
const mem = makeReplayMembrane({
|
|
143
|
+
log,
|
|
144
|
+
bijection,
|
|
145
|
+
vowTools,
|
|
146
|
+
watchWake,
|
|
147
|
+
panic,
|
|
75
148
|
});
|
|
149
|
+
t.true(log.isReplaying());
|
|
150
|
+
t.is(log.getIndex(), 0);
|
|
151
|
+
|
|
152
|
+
const guestPingee = mem.hostToGuest(pingee);
|
|
153
|
+
const p2 = mem.hostToGuest(v2);
|
|
154
|
+
// @ts-expect-error TS doesn't know that r2 is a resolver
|
|
155
|
+
r2.resolve('y');
|
|
156
|
+
await eventLoopIteration();
|
|
157
|
+
|
|
158
|
+
const p1 = mem.hostToGuest(v1);
|
|
159
|
+
mem.wake();
|
|
160
|
+
t.true(log.isReplaying());
|
|
161
|
+
t.is(log.getIndex(), 0);
|
|
162
|
+
t.deepEqual(log.dump(), [
|
|
163
|
+
['checkCall', pingee, 'ping', ['call'], 0],
|
|
164
|
+
['doReturn', 0, undefined],
|
|
165
|
+
['checkSend', pingee, 'ping', ['send'], 2],
|
|
166
|
+
['doReturn', 2, v3],
|
|
167
|
+
['checkSendOnly', pingee, 'ping', ['sendOnly'], 4],
|
|
168
|
+
['doFulfill', v3, undefined],
|
|
169
|
+
['doFulfill', v1, 'x'],
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
E(guestPingee).ping('send');
|
|
173
|
+
// TODO Once https://github.com/endojs/endo/issues/2336 is fixed,
|
|
174
|
+
// the following `void` should not be needed. But strangely, TS isn't
|
|
175
|
+
// telling me a `void` is needed above, which is also incorrect.
|
|
176
|
+
void E.sendOnly(guestPingee).ping('sendOnly');
|
|
76
177
|
|
|
77
178
|
guestPingee.ping('call');
|
|
78
179
|
|
|
79
|
-
await
|
|
180
|
+
t.is(await p1, 'x');
|
|
181
|
+
t.is(await p2, 'y');
|
|
182
|
+
t.false(log.isReplaying());
|
|
80
183
|
|
|
81
184
|
t.deepEqual(log.dump(), [
|
|
82
185
|
['checkCall', pingee, 'ping', ['call'], 0],
|
|
83
186
|
['doReturn', 0, undefined],
|
|
187
|
+
['checkSend', pingee, 'ping', ['send'], 2],
|
|
188
|
+
['doReturn', 2, v3],
|
|
189
|
+
['checkSendOnly', pingee, 'ping', ['sendOnly'], 4],
|
|
190
|
+
['doFulfill', v3, undefined],
|
|
191
|
+
['doFulfill', v1, 'x'],
|
|
192
|
+
['doFulfill', v2, 'y'],
|
|
84
193
|
]);
|
|
85
194
|
};
|
|
86
195
|
|
|
@@ -100,5 +209,9 @@ test.serial('test durable replay-membrane settlement', async t => {
|
|
|
100
209
|
|
|
101
210
|
nextLife();
|
|
102
211
|
const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
|
|
103
|
-
|
|
212
|
+
await testFirstPlay(t, zone1);
|
|
213
|
+
|
|
214
|
+
nextLife();
|
|
215
|
+
const zone3 = makeDurableZone(getBaggage(), 'durableRoot');
|
|
216
|
+
return testReplay(t, zone3);
|
|
104
217
|
});
|