@@ -252,7 +252,11 @@ function findCalledFunctionNameFromStackTrace(
252252    const  url  =  devirtualizeURL ( callsite [ 1 ] ) ; 
253253    const  lineNumber  =  callsite [ 2 ] ; 
254254    const  columnNumber  =  callsite [ 3 ] ; 
255-     if  ( filterStackFrame ( url ,  functionName ,  lineNumber ,  columnNumber ) )  { 
255+     if  ( 
256+       filterStackFrame ( url ,  functionName ,  lineNumber ,  columnNumber )  && 
257+       // Don't consider anonymous code first party even if the filter wants to include them in the stack. 
258+       url  !==  '' 
259+     )  { 
256260      if  ( bestMatch  ===  '' )  { 
257261        // If we had no good stack frames for internal calls, just use the last 
258262        // first party function name. 
@@ -308,7 +312,10 @@ function hasUnfilteredFrame(request: Request, stack: ReactStackTrace): boolean {
308312    const  isAsync  =  callsite [ 6 ] ; 
309313    if  ( 
310314      ! isAsync  && 
311-       filterStackFrame ( url ,  functionName ,  lineNumber ,  columnNumber ) 
315+       filterStackFrame ( url ,  functionName ,  lineNumber ,  columnNumber )  && 
316+       // Ignore anonymous stack frames like internals. They are also not in first party 
317+       // code even though it might be useful to include them in the final stack. 
318+       url  !==  '' 
312319    )  { 
313320      return true ; 
314321    } 
@@ -367,7 +374,10 @@ export function isAwaitInUserspace(
367374    const  url  =  devirtualizeURL ( callsite [ 1 ] ) ; 
368375    const  lineNumber  =  callsite [ 2 ] ; 
369376    const  columnNumber  =  callsite [ 3 ] ; 
370-     return  filterStackFrame ( url ,  functionName ,  lineNumber ,  columnNumber ) ; 
377+     return  ( 
378+       filterStackFrame ( url ,  functionName ,  lineNumber ,  columnNumber )  && 
379+       url  !==  '' 
380+     ) ; 
371381  } 
372382  return false;
373383} 
@@ -2347,6 +2357,7 @@ function visitAsyncNode(
23472357      } 
23482358      const awaited = node.awaited;
23492359      let match: void | null | PromiseNode | IONode = previousIONode;
2360+       const promise = node.promise.deref();
23502361      if (awaited !== null) { 
23512362        const  ioNode  =  visitAsyncNode ( request ,  task ,  awaited ,  visited ,  cutOff ) ; 
23522363        if  ( ioNode  ===  undefined )  { 
@@ -2361,26 +2372,39 @@ function visitAsyncNode(
23612372          if  ( ioNode . tag  ===  PROMISE_NODE )  { 
23622373            // If the ioNode was a Promise, then that means we found one in user space since otherwise 
23632374            // we would've returned an IO node. We assume this has the best stack. 
2375+             // Note: This might also be a Promise with a displayName but potentially a worse stack. 
2376+             // We could potentially favor the outer Promise if it has a stack but not the inner. 
23642377            match  =  ioNode ; 
23652378          }  else  if  ( 
2366-             node . stack  ===  null  || 
2367-             ! hasUnfilteredFrame ( request ,  node . stack ) 
2379+             ( node . stack  !==  null  &&  hasUnfilteredFrame ( request ,  node . stack ) )  || 
2380+             ( promise  !==  undefined  && 
2381+               // $FlowFixMe[prop-missing] 
2382+               typeof  promise . displayName  ===  'string'  && 
2383+               ( ioNode . stack  ===  null  || 
2384+                 ! hasUnfilteredFrame ( request ,  ioNode . stack ) ) ) 
23682385          ) { 
2386+             // If this Promise has a stack trace then we favor that over the I/O node since we're 
2387+             // mainly dealing with Promises as the abstraction. 
2388+             // If it has no stack but at least has a displayName and the io doesn't have a better 
2389+             // stack anyway, then also use this Promise instead since at least it has a name. 
2390+             match  =  node ; 
2391+           }  else { 
23692392            // If this Promise was created inside only third party code, then try to use 
23702393            // the inner I/O node instead. This could happen if third party calls into first 
23712394            // party to perform some I/O. 
23722395            match  =  ioNode ; 
2373-           }  else  { 
2374-             match =  node ; 
23752396          } 
23762397        }  else  if  ( request . status  ===  ABORTING )  { 
23772398          if  ( node . start  <  request . abortTime  &&  node . end  >  request . abortTime ) { 
23782399            // We aborted this render. If this Promise spanned the abort time it was probably the 
23792400            // Promise that was aborted. This won't necessarily have I/O associated with it but 
23802401            // it's a point of interest. 
23812402            if  ( 
2382-               node . stack  !==  null  && 
2383-               hasUnfilteredFrame ( request ,  node . stack ) 
2403+               ( node . stack  !==  null  && 
2404+                 hasUnfilteredFrame ( request ,  node . stack ) )  || 
2405+               ( promise  !==  undefined  && 
2406+                 // $FlowFixMe[prop-missing] 
2407+                 typeof  promise . displayName  ===  'string' ) 
23842408            ) { 
23852409              match  =  node ; 
23862410            } 
@@ -2389,7 +2413,6 @@ function visitAsyncNode(
23892413      } 
23902414      // We need to forward after we visit awaited nodes because what ever I/O we requested that's 
23912415      // the thing that generated this node and its virtual children. 
2392-       const  promise  =  node . promise . deref ( ) ; 
23932416      if  ( promise  !==  undefined )  { 
23942417        const  debugInfo  =  promise . _debugInfo ; 
23952418        if  ( debugInfo  !=  null  &&  ! visited . has ( debugInfo ) )  { 
@@ -4497,17 +4520,33 @@ function serializeIONode(
44974520
44984521  let  stack  =  null ; 
44994522  let  name  =  '' ; 
4523+   if  ( ioNode . promise  !==  null )  { 
4524+     // Pick an explicit name from the Promise itself if it exists. 
4525+     // Note that we don't use the promiseRef passed in since that's sometimes the awaiting Promise 
4526+     // which is the value observed but it's likely not the one with the name on it. 
4527+     const  promise =  ioNode . promise . deref ( ) ; 
4528+     if  ( 
4529+       promise  !==  undefined  && 
4530+       // $FlowFixMe[prop-missing] 
4531+       typeof  promise . displayName  ===  'string' 
4532+     )  { 
4533+       name =  promise . displayName ; 
4534+     } 
4535+   } 
45004536  if  ( ioNode . stack  !==  null )  { 
45014537    // The stack can contain some leading internal frames for the construction of the promise that we skip. 
45024538    const  fullStack =  stripLeadingPromiseCreationFrames ( ioNode . stack ) ; 
45034539    stack  =  filterStackTrace ( request ,  fullStack ) ; 
4504-     name  =  findCalledFunctionNameFromStackTrace ( request ,  fullStack ) ; 
4505-     // The name can include the object that this was called on but sometimes that's 
4506-     // just unnecessary context. 
4507-     if  ( name . startsWith ( 'Window.' ) )  { 
4508-       name =  name . slice ( 7 ) ; 
4509-     }  else  if  ( name . startsWith ( '<anonymous>.' ) )  { 
4510-       name =  name . slice ( 7 ) ; 
4540+     if  ( name  ===  '' )  { 
4541+       // If we didn't have an explicit name, try finding one from the stack. 
4542+       name =  findCalledFunctionNameFromStackTrace ( request ,  fullStack ) ; 
4543+       // The name can include the object that this was called on but sometimes that's 
4544+       // just unnecessary context. 
4545+       if  ( name . startsWith ( 'Window.' ) )  { 
4546+         name =  name . slice ( 7 ) ; 
4547+       }  else  if  ( name . startsWith ( '<anonymous>.' ) )  { 
4548+         name =  name . slice ( 7 ) ; 
4549+       } 
45114550    } 
45124551  } 
45134552  const  owner  =  ioNode . owner ; 
0 commit comments