@@ -17,45 +17,77 @@ const { parse, query } = require('./compiler')
1717module . exports = { waitForAsyncEnd }
1818
1919/**
20- * Injects a wait for `ctx.asyncEndPromise` into a generated `tracePromise`
21- * wrapper's native-Promise fulfillment handler .
20+ * Injects settlement-specific asyncEnd waits into a generated `tracePromise`
21+ * wrapper's native-Promise handlers .
2222 *
2323 * @param {object } _state
2424 * @param {import('estree').CallExpression } node
2525 * @returns {void }
2626 */
2727function waitForAsyncEnd ( _state , node ) {
2828 const onFulfilled = node . arguments [ 0 ]
29- const statements = onFulfilled ?. body ?. body
29+ const onRejected = node . arguments [ 1 ]
3030
31- if ( ! statements || query ( onFulfilled . body , '[id.name=__apm$asyncEndPromise]' ) . length > 0 ) {
31+ if ( ! onFulfilled ?. body || ! onRejected ? .body ) {
3232 return
3333 }
3434
35- const returnIndex = statements . findIndex ( statement =>
36- statement . type === 'ReturnStatement' && statement . argument
35+ injectAsyncEndCallbackWait ( onFulfilled . body , 'ReturnStatement' , 'resolveCallback' )
36+ injectAsyncEndCallbackWait ( onRejected . body , 'ThrowStatement' , 'rejectCallback' )
37+ }
38+
39+ /**
40+ * Injects a settlement-specific callback wait before an exit.
41+ *
42+ * @param {import('estree').BlockStatement } body
43+ * @param {'ReturnStatement'|'ThrowStatement' } exitType
44+ * @param {'resolveCallback'|'rejectCallback' } callbackProperty
45+ * @returns {void }
46+ */
47+ function injectAsyncEndCallbackWait ( body , exitType , callbackProperty ) {
48+ const callbackVariable = `__apm$${ callbackProperty } `
49+ if ( query ( body , `[id.name=${ callbackVariable } ]` ) . length > 0 ) {
50+ return
51+ }
52+
53+ const exitIndex = body . body . findIndex ( statement =>
54+ statement . type === exitType && statement . argument
3755 )
3856
39- // The generated fulfillment handler always ends in a return; a miss means the
57+ // The generated settlement handlers always end in a return or throw ; a miss means the
4058 // upstream template changed and the caller's try/catch falls back to the
4159 // unwrapped source.
42- assert ( returnIndex !== - 1 , ' waitForAsyncEnd: no return statement to wait on' )
60+ assert ( exitIndex !== - 1 , ` waitForAsyncEnd: no ${ exitType } to wait on` )
4361
62+ // This runs inside tracePromise's native-Promise settlement handler. The
63+ // Promise adapts subscriber callback completion to that existing chain.
4464 const waitStatements = parse ( `
4565 function wrapper () {
46- const __apm$asyncEndPromise = __apm$ctx.asyncEndPromise ;
47- if (__apm$asyncEndPromise && typeof __apm$asyncEndPromise.then === 'function') {
48- return __apm$asyncEndPromise .then(() => __apm$result, () => __apm$result);
66+ const ${ callbackVariable } = __apm$ctx.${ callbackProperty } ;
67+ if (typeof ${ callbackVariable } === 'function') {
68+ return new Promise( ${ callbackVariable } ) .then(() => __apm$result, () => __apm$result);
4969 }
5070 }
5171 ` ) . body [ 0 ] . body . body
5272
53- // Resolve to whatever the fulfillment handler returns (its return argument),
54- // so a subscriber that reassigned `__apm$ctx.result` in `asyncEnd` still wins.
55- const returnArgument = statements [ returnIndex ] . argument
56- const { arguments : onSettled } = waitStatements [ 1 ] . consequent . body [ 0 ] . argument
57- onSettled [ 0 ] . body = clone ( returnArgument )
58- onSettled [ 1 ] . body = clone ( returnArgument )
73+ const exitArgument = body . body [ exitIndex ] . argument
74+ const callbackIf = waitStatements [ 1 ]
75+ const { arguments : onCallbackSettled } = callbackIf . consequent . body [ 0 ] . argument
76+
77+ if ( exitType === 'ThrowStatement' ) {
78+ for ( const handler of onCallbackSettled ) {
79+ handler . body = {
80+ type : 'BlockStatement' ,
81+ body : [ {
82+ type : 'ThrowStatement' ,
83+ argument : clone ( exitArgument ) ,
84+ } ] ,
85+ }
86+ }
87+ } else {
88+ onCallbackSettled [ 0 ] . body = clone ( exitArgument )
89+ onCallbackSettled [ 1 ] . body = clone ( exitArgument )
90+ }
5991
60- statements . splice ( returnIndex , 0 , ...waitStatements )
92+ body . body . splice ( exitIndex , 0 , ...waitStatements )
6193}
0 commit comments