| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 137a2e5 commit 51ba566
11 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,6 +24,8 @@ const { | |||
| 24 | 24 | const { fileURLToPath } = require('internal/url'); | |
| 25 | 25 | const { setGetSourceMapErrorSource } = internalBinding('errors'); | |
| 26 | 26 | ||
| 27 | + const kStackLineAt = '\n at '; | ||
| 28 | + | ||
| 27 | 29 | // Create a prettified stacktrace, inserting context from source maps | |
| 28 | 30 | // if possible. | |
| 29 | 31 | function prepareStackTraceWithSourceMaps(error, trace) { | |
@@ -40,75 +42,98 @@ function prepareStackTraceWithSourceMaps(error, trace) { | |||
| 40 | 42 | ||
| 41 | 43 | let lastSourceMap; | |
| 42 | 44 | let lastFileName; | |
| 43 | - const preparedTrace = ArrayPrototypeJoin(ArrayPrototypeMap(trace, (t, i) => { | ||
| 44 | - const str = '\n at '; | ||
| 45 | + const preparedTrace = ArrayPrototypeJoin(ArrayPrototypeMap(trace, (callSite, i) => { | ||
| 45 | 46 | try { | |
| 46 | 47 | // A stack trace will often have several call sites in a row within the | |
| 47 | 48 | // same file, cache the source map and file content accordingly: | |
| 48 | - let fileName = t.getFileName(); | ||
| 49 | + let fileName = callSite.getFileName(); | ||
| 49 | 50 | if (fileName === undefined) { | |
| 50 | - fileName = t.getEvalOrigin(); | ||
| 51 | + fileName = callSite.getEvalOrigin(); | ||
| 51 | 52 | } | |
| 52 | 53 | const sm = fileName === lastFileName ? | |
| 53 | 54 | lastSourceMap : | |
| 54 | 55 | findSourceMap(fileName); | |
| 55 | 56 | lastSourceMap = sm; | |
| 56 | 57 | lastFileName = fileName; | |
| 57 | 58 | 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 | - const name = getOriginalSymbolName(sm, trace, i); | ||
| 68 | - // Construct call site name based on: v8.dev/docs/stack-trace-api: | ||
| 69 | - const fnName = t.getFunctionName() ?? t.getMethodName(); | ||
| 70 | - const typeName = t.getTypeName(); | ||
| 71 | - const namePrefix = typeName !== null && typeName !== 'global' ? `${typeName}.` : ''; | ||
| 72 | - const originalName = `${namePrefix}${fnName || '<anonymous>'}`; | ||
| 73 | - // The original call site may have a different symbol name | ||
| 74 | - // associated with it, use it: | ||
| 75 | - const prefix = (name && name !== originalName) ? | ||
| 76 | - `${name}` : | ||
| 77 | - `${originalName}`; | ||
| 78 | - const hasName = !!(name || originalName); | ||
| 79 | - const originalSourceNoScheme = | ||
| 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])}`; | ||
| 87 | 60 | } | |
| 88 | 61 | } catch (err) { | |
| 89 | 62 | debug(err); | |
| 90 | 63 | } | |
| 91 | - return `${str}${t}`; | ||
| 64 | + return `${kStackLineAt}${callSite}`; | ||
| 92 | 65 | }), ''); | |
| 93 | 66 | return `${errorString}${preparedTrace}`; | |
| 94 | 67 | } | |
| 95 | 68 | ||
| 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 | + function serializeJSStackFrame(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 | + const name = getOriginalSymbolName(sm, callSite, callerCallSite); | ||
| 92 | + const originalSourceNoScheme = | ||
| 93 | + StringPrototypeStartsWith(originalSource, 'file://') ? | ||
| 94 | + fileURLToPath(originalSource) : originalSource; | ||
| 95 | + // Construct call site name based on: v8.dev/docs/stack-trace-api: | ||
| 96 | + const fnName = callSite.getFunctionName() ?? callSite.getMethodName(); | ||
| 97 | + | ||
| 98 | + let prefix = ''; | ||
| 99 | + if (callSite.isAsync()) { | ||
| 100 | + // Promise aggregation operation frame has no locations. This must be an | ||
| 101 | + // async stack frame. | ||
| 102 | + prefix = 'async '; | ||
| 103 | + } else if (callSite.isConstructor()) { | ||
| 104 | + prefix = 'new '; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + const typeName = callSite.getTypeName(); | ||
| 108 | + const namePrefix = typeName !== null && typeName !== 'global' ? `${typeName}.` : ''; | ||
| 109 | + const originalName = `${namePrefix}${fnName || '<anonymous>'}`; | ||
| 110 | + // The original call site may have a different symbol name | ||
| 111 | + // associated with it, use it: | ||
| 112 | + const mappedName = (name && name !== originalName) ? | ||
| 113 | + `${name}` : | ||
| 114 | + `${originalName}`; | ||
| 115 | + const hasName = !!(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 | + | ||
| 96 | 122 | // Transpilers may have removed the original symbol name used in the stack | |
| 97 | 123 | // trace, if possible restore it from the names field of the source map: | |
| 98 | - function getOriginalSymbolName(sourceMap, trace, curIndex) { | ||
| 124 | + function getOriginalSymbolName(sourceMap, callSite, callerCallSite) { | ||
| 99 | 125 | // First check for a symbol name associated with the enclosing function: | |
| 100 | 126 | const enclosingEntry = sourceMap.findEntry( | |
| 101 | - trace[curIndex].getEnclosingLineNumber() - 1, | ||
| 102 | - trace[curIndex].getEnclosingColumnNumber() - 1, | ||
| 127 | + callSite.getEnclosingLineNumber() - 1, | ||
| 128 | + callSite.getEnclosingColumnNumber() - 1, | ||
| 103 | 129 | ); | |
| 104 | 130 | if (enclosingEntry.name) return enclosingEntry.name; | |
| 105 | - // Fallback to using the symbol name attached to the next stack frame: | ||
| 106 | - const currentFileName = trace[curIndex].getFileName(); | ||
| 107 | - const nextCallSite = trace[curIndex + 1]; | ||
| 108 | - if (nextCallSite && currentFileName === nextCallSite.getFileName()) { | ||
| 131 | + // Fallback to using the symbol name attached to the caller site: | ||
| 132 | + const currentFileName = callSite.getFileName(); | ||
| 133 | + if (callerCallSite && currentFileName === callerCallSite.getFileName()) { | ||
| 109 | 134 | const { name } = sourceMap.findEntry( | |
| 110 | - nextCallSite.getLineNumber() - 1, | ||
| 111 | - nextCallSite.getColumnNumber() - 1, | ||
| 135 | + callerCallSite.getLineNumber() - 1, | ||
| 136 | + callerCallSite.getColumnNumber() - 1, | ||
| 112 | 137 | ); | |
| 113 | 138 | return name; | |
| 114 | 139 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,13 @@ | |||
| 1 | + // Flags: --enable-source-maps | ||
| 2 | + import '../../../common/index.mjs'; | ||
| 3 | + async function Throw() { | ||
| 4 | + await 0; | ||
| 5 | + throw new Error('message'); | ||
| 6 | + } | ||
| 7 | + (async function main() { | ||
| 8 | + await Promise.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 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,22 @@ | |||
| 1 | + // Flags: --enable-source-maps | ||
| 2 | + | ||
| 3 | + import '../../../common/index.mjs'; | ||
| 4 | + | ||
| 5 | + interface Foo { | ||
| 6 | + /** line | ||
| 7 | + * | ||
| 8 | + * blocks */ | ||
| 9 | + } | ||
| 10 | + | ||
| 11 | + async function Throw() { | ||
| 12 | + await 0; | ||
| 13 | + throw new Error('message') | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + (async function main() { | ||
| 17 | + await Promise.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 | ||
| Original file line number | Diff line number | Diff 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 * | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,12 @@ | |||
| 1 | + // Flags: --enable-source-maps | ||
| 2 | + import '../../../common/index.mjs'; | ||
| 3 | + class Foo { | ||
| 4 | + constructor() { | ||
| 5 | + throw new Error('message'); | ||
| 6 | + } | ||
| 7 | + } | ||
| 8 | + new Foo(); | ||
| 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 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,21 @@ | |||
| 1 | + // Flags: --enable-source-maps | ||
| 2 | + | ||
| 3 | + import '../../../common/index.mjs'; | ||
| 4 | + | ||
| 5 | + interface Block { | ||
| 6 | + /** line | ||
| 7 | + * | ||
| 8 | + * blocks */ | ||
| 9 | + } | ||
| 10 | + | ||
| 11 | + class Foo { | ||
| 12 | + constructor() { | ||
| 13 | + throw new Error('message'); | ||
| 14 | + } | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + new Foo(); | ||
| 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 | ||
| Original file line number | Diff line number | Diff 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 * | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,6 @@ | |||
| 6 | 6 | Error: goodbye | |
| 7 | 7 | at Hello (*uglify-throw-original.js:5:9) | |
| 8 | 8 | at Immediate.<anonymous> (*uglify-throw-original.js:9:3) | |
| 9 | - at process.processImmediate (node:internal*timers:483:21) | ||
| 9 | + * | ||
| 10 | 10 | ||
| 11 | 11 | Node.js * | |
| Back | FazBrowse Home | New Git URL |
0 commit comments