-
Notifications
You must be signed in to change notification settings - Fork 206
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Deep stacks for
E.when
and for handled async operations (#1987)
- Loading branch information
Showing
7 changed files
with
135 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// @ts-nocheck | ||
/* global assert globalThis */ | ||
|
||
// NOTE: We can't import these because they're not in scope before lockdown. | ||
// import { assert, details as d } from '@agoric/assert'; | ||
|
||
// WARNING: Global Mutable State! | ||
// This state is communicated to `assert` that makes it available to the | ||
// causal console, which affects the console log output. Normally we | ||
// regard the ability to see console log output as a meta-level privilege | ||
// analogous to the ability to debug. Aside from that, this module should | ||
// not have any observably mutable state. | ||
|
||
let hiddenPriorError; | ||
let hiddenCurrentTurn = 0; | ||
let hiddenCurrentEvent = 0; | ||
|
||
/** | ||
* @typedef {((...args: any[]) => any) | void} TurnStarterFn | ||
* An optional function that is not this-sensitive, expected to be called at | ||
* bottom of stack to start a new turn. | ||
*/ | ||
|
||
/** | ||
* Given a list of `TurnStarterFn`s, returns a list of `TurnStarterFn`s whose | ||
* `this`-free call behaviors are not observably different to those that | ||
* cannot see console output. The only purpose is to cause additional | ||
* information to appear on the console. | ||
* | ||
* The call to `trackTurns` is itself a sending event, that occurs in some call | ||
* stack in some turn number at some event number within that turn. Each call | ||
* to any of the returned `TurnStartFn`s is a receiving event that begins a new | ||
* turn. This sending event caused each of those receiving events. | ||
* | ||
* @param {TurnStarterFn[]} funcs | ||
* @returns {TurnStarterFn[]} | ||
*/ | ||
export const trackTurns = funcs => { | ||
if (typeof globalThis === 'undefined' || !globalThis.assert) { | ||
return funcs; | ||
} | ||
hiddenCurrentEvent += 1; | ||
const sendingError = new Error( | ||
`Event: ${hiddenCurrentTurn}.${hiddenCurrentEvent}`, | ||
); | ||
if (hiddenPriorError !== undefined) { | ||
assert.note(sendingError, assert.details`Caused by: ${hiddenPriorError}`); | ||
} | ||
|
||
return funcs.map( | ||
func => | ||
func && | ||
((...args) => { | ||
hiddenPriorError = sendingError; | ||
hiddenCurrentTurn += 1; | ||
hiddenCurrentEvent = 0; | ||
try { | ||
return func(...args); | ||
} catch (err) { | ||
assert.note( | ||
err, | ||
assert.details`Thrown from: ${hiddenPriorError}:${hiddenCurrentTurn}.${hiddenCurrentEvent}`, | ||
); | ||
throw err; | ||
} finally { | ||
hiddenPriorError = undefined; | ||
} | ||
}), | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// This file does not start with "test-" because it is useful as an | ||
// automated test. Rather, its purpose is just to run it to see what a | ||
// deep stack looks like. | ||
|
||
import '@agoric/install-ses'; | ||
import test from 'ava'; | ||
import { assert } from '@agoric/assert'; | ||
import { E } from './get-hp'; | ||
|
||
const { freeze } = Object; | ||
|
||
const carol = freeze({ | ||
bar: () => assert.fail('Wut?'), | ||
}); | ||
|
||
const bob = freeze({ | ||
foo: carolP => E(carolP).bar(), | ||
}); | ||
|
||
const alice = freeze({ | ||
test: () => E(bob).foo(carol), | ||
}); | ||
|
||
test('deep-stacks E', t => { | ||
const q = alice.test(); | ||
return q.catch(reason => { | ||
t.assert(reason instanceof Error); | ||
console.log('expected failure', reason); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// This file does not start with "test-" because it is useful as an | ||
// automated test. Rather, its purpose is just to run it to see what a | ||
// deep stack looks like. | ||
|
||
import '@agoric/install-ses'; | ||
import test from 'ava'; | ||
import { assert } from '@agoric/assert'; | ||
import { E } from './get-hp'; | ||
|
||
test('deep-stacks when', t => { | ||
let r; | ||
const p = new Promise(res => (r = res)); | ||
const q = E.when(p, v1 => E.when(v1 + 1, v2 => assert.equal(v2, 22))); | ||
r(33); | ||
return q.catch(reason => { | ||
t.assert(reason instanceof Error); | ||
console.log('expected failure', reason); | ||
}); | ||
}); |