Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit51ba566

Browse files
legendecasmarco-ippolito
authored andcommitted
lib: decorate async stack trace in source maps
Decorate stack frame with 'async' and 'new' keywords based on the typeof the call site info.PR-URL:#53860Reviewed-By: James M Snell <jasnell@gmail.com>Reviewed-By: Michaël Zasso <targos@protonmail.com>Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent137a2e5 commit51ba566

11 files changed

+166
-45
lines changed

‎lib/internal/source_map/prepare_stack_trace.js

Lines changed: 68 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const {
2424
const{ fileURLToPath}=require('internal/url');
2525
const{ setGetSourceMapErrorSource}=internalBinding('errors');
2626

27+
constkStackLineAt='\n at ';
28+
2729
// Create a prettified stacktrace, inserting context from source maps
2830
// if possible.
2931
functionprepareStackTraceWithSourceMaps(error,trace){
@@ -40,75 +42,98 @@ function prepareStackTraceWithSourceMaps(error, trace) {
4042

4143
letlastSourceMap;
4244
letlastFileName;
43-
constpreparedTrace=ArrayPrototypeJoin(ArrayPrototypeMap(trace,(t,i)=>{
44-
conststr='\n at ';
45+
constpreparedTrace=ArrayPrototypeJoin(ArrayPrototypeMap(trace,(callSite,i)=>{
4546
try{
4647
// A stack trace will often have several call sites in a row within the
4748
// same file, cache the source map and file content accordingly:
48-
letfileName=t.getFileName();
49+
letfileName=callSite.getFileName();
4950
if(fileName===undefined){
50-
fileName=t.getEvalOrigin();
51+
fileName=callSite.getEvalOrigin();
5152
}
5253
constsm=fileName===lastFileName ?
5354
lastSourceMap :
5455
findSourceMap(fileName);
5556
lastSourceMap=sm;
5657
lastFileName=fileName;
5758
if(sm){
58-
// Source Map V3 lines/columns start at 0/0 whereas stack traces
59-
// start at 1/1:
60-
const{
61-
originalLine,
62-
originalColumn,
63-
originalSource,
64-
}=sm.findEntry(t.getLineNumber()-1,t.getColumnNumber()-1);
65-
if(originalSource&&originalLine!==undefined&&
66-
originalColumn!==undefined){
67-
constname=getOriginalSymbolName(sm,trace,i);
68-
// Construct call site name based on: v8.dev/docs/stack-trace-api:
69-
constfnName=t.getFunctionName()??t.getMethodName();
70-
consttypeName=t.getTypeName();
71-
constnamePrefix=typeName!==null&&typeName!=='global' ?`${typeName}.` :'';
72-
constoriginalName=`${namePrefix}${fnName||'<anonymous>'}`;
73-
// The original call site may have a different symbol name
74-
// associated with it, use it:
75-
constprefix=(name&&name!==originalName) ?
76-
`${name}` :
77-
`${originalName}`;
78-
consthasName=!!(name||originalName);
79-
constoriginalSourceNoScheme=
80-
StringPrototypeStartsWith(originalSource,'file://') ?
81-
fileURLToPath(originalSource) :originalSource;
82-
// Replace the transpiled call site with the original:
83-
return`${str}${prefix}${hasName ?' (' :''}`+
84-
`${originalSourceNoScheme}:${originalLine+1}:`+
85-
`${originalColumn+1}${hasName ?')' :''}`;
86-
}
59+
return`${kStackLineAt}${serializeJSStackFrame(sm,callSite,trace[i+1])}`;
8760
}
8861
}catch(err){
8962
debug(err);
9063
}
91-
return`${str}${t}`;
64+
return`${kStackLineAt}${callSite}`;
9265
}),'');
9366
return`${errorString}${preparedTrace}`;
9467
}
9568

69+
/**
70+
* Serialize a single call site in the stack trace.
71+
* Refer to SerializeJSStackFrame in deps/v8/src/objects/call-site-info.cc for
72+
* more details about the default ToString(CallSite).
73+
* The CallSite API is documented at https://v8.dev/docs/stack-trace-api.
74+
*@param {import('internal/source_map/source_map').SourceMap} sm
75+
*@param {CallSite} callSite - the CallSite object to be serialized
76+
*@param {CallSite} callerCallSite - caller site info
77+
*@returns {string} - the serialized call site
78+
*/
79+
functionserializeJSStackFrame(sm,callSite,callerCallSite){
80+
// Source Map V3 lines/columns start at 0/0 whereas stack traces
81+
// start at 1/1:
82+
const{
83+
originalLine,
84+
originalColumn,
85+
originalSource,
86+
}=sm.findEntry(callSite.getLineNumber()-1,callSite.getColumnNumber()-1);
87+
if(originalSource===undefined||originalLine===undefined||
88+
originalColumn===undefined){
89+
return`${callSite}`;
90+
}
91+
constname=getOriginalSymbolName(sm,callSite,callerCallSite);
92+
constoriginalSourceNoScheme=
93+
StringPrototypeStartsWith(originalSource,'file://') ?
94+
fileURLToPath(originalSource) :originalSource;
95+
// Construct call site name based on: v8.dev/docs/stack-trace-api:
96+
constfnName=callSite.getFunctionName()??callSite.getMethodName();
97+
98+
letprefix='';
99+
if(callSite.isAsync()){
100+
// Promise aggregation operation frame has no locations. This must be an
101+
// async stack frame.
102+
prefix='async ';
103+
}elseif(callSite.isConstructor()){
104+
prefix='new ';
105+
}
106+
107+
consttypeName=callSite.getTypeName();
108+
constnamePrefix=typeName!==null&&typeName!=='global' ?`${typeName}.` :'';
109+
constoriginalName=`${namePrefix}${fnName||'<anonymous>'}`;
110+
// The original call site may have a different symbol name
111+
// associated with it, use it:
112+
constmappedName=(name&&name!==originalName) ?
113+
`${name}` :
114+
`${originalName}`;
115+
consthasName=!!(name||originalName);
116+
// Replace the transpiled call site with the original:
117+
return`${prefix}${mappedName}${hasName ?' (' :''}`+
118+
`${originalSourceNoScheme}:${originalLine+1}:`+
119+
`${originalColumn+1}${hasName ?')' :''}`;
120+
}
121+
96122
// Transpilers may have removed the original symbol name used in the stack
97123
// trace, if possible restore it from the names field of the source map:
98-
functiongetOriginalSymbolName(sourceMap,trace,curIndex){
124+
functiongetOriginalSymbolName(sourceMap,callSite,callerCallSite){
99125
// First check for a symbol name associated with the enclosing function:
100126
constenclosingEntry=sourceMap.findEntry(
101-
trace[curIndex].getEnclosingLineNumber()-1,
102-
trace[curIndex].getEnclosingColumnNumber()-1,
127+
callSite.getEnclosingLineNumber()-1,
128+
callSite.getEnclosingColumnNumber()-1,
103129
);
104130
if(enclosingEntry.name)returnenclosingEntry.name;
105-
// Fallback to using the symbol name attached to the next stack frame:
106-
constcurrentFileName=trace[curIndex].getFileName();
107-
constnextCallSite=trace[curIndex+1];
108-
if(nextCallSite&&currentFileName===nextCallSite.getFileName()){
131+
// Fallback to using the symbol name attached to the caller site:
132+
constcurrentFileName=callSite.getFileName();
133+
if(callerCallSite&&currentFileName===callerCallSite.getFileName()){
109134
const{ name}=sourceMap.findEntry(
110-
nextCallSite.getLineNumber()-1,
111-
nextCallSite.getColumnNumber()-1,
135+
callerCallSite.getLineNumber()-1,
136+
callerCallSite.getColumnNumber()-1,
112137
);
113138
returnname;
114139
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Flags: --enable-source-maps
2+
import'../../../common/index.mjs';
3+
asyncfunctionThrow(){
4+
await0;
5+
thrownewError('message');
6+
}
7+
(asyncfunctionmain(){
8+
awaitPromise.all([0,1,2,Throw()]);
9+
})();
10+
// To recreate:
11+
//
12+
// npx --package typescript tsc --module nodenext --target esnext --outDir test/fixtures/source-map/output --sourceMap test/fixtures/source-map/output/source_map_throw_async_stack_trace.mts
13+
//# sourceMappingURL=source_map_throw_async_stack_trace.mjs.map

‎test/fixtures/source-map/output/source_map_throw_async_stack_trace.mjs.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Flags: --enable-source-maps
2+
3+
import'../../../common/index.mjs';
4+
5+
interfaceFoo{
6+
/** line
7+
*
8+
* blocks */
9+
}
10+
11+
asyncfunctionThrow(){
12+
await0;
13+
thrownewError('message')
14+
}
15+
16+
(asyncfunctionmain(){
17+
awaitPromise.all([0,1,2,Throw()]);
18+
})()
19+
20+
// To recreate:
21+
//
22+
// npx --package typescript tsc --module nodenext --target esnext --outDir test/fixtures/source-map/output --sourceMap test/fixtures/source-map/output/source_map_throw_async_stack_trace.mts
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
*output*source_map_throw_async_stack_trace.mts:13
2+
throw new Error('message')
3+
^
4+
5+
6+
Error: message
7+
at Throw (*output*source_map_throw_async_stack_trace.mts:13:9)
8+
at async Promise.all (index 3)
9+
at async main (*output*source_map_throw_async_stack_trace.mts:17:3)
10+
11+
Node.js *
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Flags: --enable-source-maps
2+
import'../../../common/index.mjs';
3+
classFoo{
4+
constructor(){
5+
thrownewError('message');
6+
}
7+
}
8+
newFoo();
9+
// To recreate:
10+
//
11+
// npx --package typescript tsc --module nodenext --target esnext --outDir test/fixtures/source-map/output --sourceMap test/fixtures/source-map/output/source_map_throw_construct.mts
12+
//# sourceMappingURL=source_map_throw_construct.mjs.map

‎test/fixtures/source-map/output/source_map_throw_construct.mjs.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Flags: --enable-source-maps
2+
3+
import'../../../common/index.mjs';
4+
5+
interfaceBlock{
6+
/** line
7+
*
8+
* blocks */
9+
}
10+
11+
classFoo{
12+
constructor(){
13+
thrownewError('message');
14+
}
15+
}
16+
17+
newFoo();
18+
19+
// To recreate:
20+
//
21+
// npx --package typescript tsc --module nodenext --target esnext --outDir test/fixtures/source-map/output --sourceMap test/fixtures/source-map/output/source_map_throw_construct.mts
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
*output*source_map_throw_construct.mts:13
2+
throw new Error('message');
3+
^
4+
5+
6+
Error: message
7+
at new Foo (*output*source_map_throw_construct.mts:13:11)
8+
at <anonymous> (*output*source_map_throw_construct.mts:17:1)
9+
*
10+
*
11+
*
12+
13+
Node.js *

‎test/fixtures/source-map/output/source_map_throw_set_immediate.snapshot

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
Error: goodbye
77
at Hello (*uglify-throw-original.js:5:9)
88
at Immediate.<anonymous> (*uglify-throw-original.js:9:3)
9-
at process.processImmediate (node:internal*timers:483:21)
9+
*
1010

1111
Node.js *

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp