| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent bf9bcef commit f7a6235
18 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,7 @@ test/message/esm_display_syntax_error.mjs | |||
| 6 | 6 | tools/icu | |
| 7 | 7 | tools/lint-md/lint-md.mjs | |
| 8 | 8 | benchmark/tmp | |
| 9 | + benchmark/fixtures | ||
| 9 | 10 | doc/**/*.js | |
| 10 | 11 | !doc/api_assets/*.js | |
| 11 | 12 | !.eslintrc.js | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,34 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common.js'); | ||
| 4 | + const modPath = require.resolve('../fixtures/simple-error-stack.js'); | ||
| 5 | + | ||
| 6 | + const bench = common.createBenchmark(main, { | ||
| 7 | + method: ['without-sourcemap', 'sourcemap'], | ||
| 8 | + n: [1e5], | ||
| 9 | + }); | ||
| 10 | + | ||
| 11 | + function runN(n) { | ||
| 12 | + delete require.cache[modPath]; | ||
| 13 | + const mod = require(modPath); | ||
| 14 | + bench.start(); | ||
| 15 | + for (let i = 0; i < n; i++) { | ||
| 16 | + mod.simpleErrorStack(); | ||
| 17 | + } | ||
| 18 | + bench.end(n); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + function main({ n, method }) { | ||
| 22 | + switch (method) { | ||
| 23 | + case 'without-sourcemap': | ||
| 24 | + process.setSourceMapsEnabled(false); | ||
| 25 | + runN(n); | ||
| 26 | + break; | ||
| 27 | + case 'sourcemap': | ||
| 28 | + process.setSourceMapsEnabled(true); | ||
| 29 | + runN(n); | ||
| 30 | + break; | ||
| 31 | + default: | ||
| 32 | + throw new Error(`Unexpected method "${method}"`); | ||
| 33 | + } | ||
| 34 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,17 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + // Compile with `tsc --inlineSourceMap benchmark/fixtures/simple-error-stack.ts`. | ||
| 4 | + | ||
| 5 | + const lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; | ||
| 6 | + | ||
| 7 | + function simpleErrorStack() { | ||
| 8 | + try { | ||
| 9 | + (lorem as any).BANG(); | ||
| 10 | + } catch (e) { | ||
| 11 | + return e.stack; | ||
| 12 | + } | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + export { | ||
| 16 | + simpleErrorStack, | ||
| 17 | + }; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,6 +25,7 @@ const { | |||
| 25 | 25 | kIsNodeError, | |
| 26 | 26 | } = require('internal/errors'); | |
| 27 | 27 | const { fileURLToPath } = require('internal/url'); | |
| 28 | + const { setGetSourceMapErrorSource } = internalBinding('errors'); | ||
| 28 | 29 | ||
| 29 | 30 | // Create a prettified stacktrace, inserting context from source maps | |
| 30 | 31 | // if possible. | |
@@ -53,7 +54,6 @@ const prepareStackTrace = (globalThis, error, trace) => { | |||
| 53 | 54 | return errorString; | |
| 54 | 55 | } | |
| 55 | 56 | ||
| 56 | - let errorSource = ''; | ||
| 57 | 57 | let lastSourceMap; | |
| 58 | 58 | let lastFileName; | |
| 59 | 59 | const preparedTrace = ArrayPrototypeJoin(ArrayPrototypeMap(trace, (t, i) => { | |
@@ -62,14 +62,12 @@ const prepareStackTrace = (globalThis, error, trace) => { | |||
| 62 | 62 | // A stack trace will often have several call sites in a row within the | |
| 63 | 63 | // same file, cache the source map and file content accordingly: | |
| 64 | 64 | let fileName = t.getFileName(); | |
| 65 | - let generated = false; | ||
| 66 | 65 | if (fileName === undefined) { | |
| 67 | 66 | fileName = t.getEvalOrigin(); | |
| 68 | - generated = true; | ||
| 69 | 67 | } | |
| 70 | 68 | const sm = fileName === lastFileName ? | |
| 71 | 69 | lastSourceMap : | |
| 72 | - findSourceMap(fileName, generated); | ||
| 70 | + findSourceMap(fileName); | ||
| 73 | 71 | lastSourceMap = sm; | |
| 74 | 72 | lastFileName = fileName; | |
| 75 | 73 | if (sm) { | |
@@ -83,14 +81,6 @@ const prepareStackTrace = (globalThis, error, trace) => { | |||
| 83 | 81 | if (originalSource && originalLine !== undefined && | |
| 84 | 82 | originalColumn !== undefined) { | |
| 85 | 83 | const name = getOriginalSymbolName(sm, trace, i); | |
| 86 | - if (i === 0) { | ||
| 87 | - errorSource = getErrorSource( | ||
| 88 | - sm, | ||
| 89 | - originalSource, | ||
| 90 | - originalLine, | ||
| 91 | - originalColumn | ||
| 92 | - ); | ||
| 93 | - } | ||
| 94 | 84 | // Construct call site name based on: v8.dev/docs/stack-trace-api: | |
| 95 | 85 | const fnName = t.getFunctionName() ?? t.getMethodName(); | |
| 96 | 86 | const typeName = t.getTypeName(); | |
@@ -116,7 +106,7 @@ const prepareStackTrace = (globalThis, error, trace) => { | |||
| 116 | 106 | } | |
| 117 | 107 | return `${str}${t}`; | |
| 118 | 108 | }), ''); | |
| 119 | - return `${errorSource}${errorString}\n at ${preparedTrace}`; | ||
| 109 | + return `${errorString}\n at ${preparedTrace}`; | ||
| 120 | 110 | }; | |
| 121 | 111 | ||
| 122 | 112 | // Transpilers may have removed the original symbol name used in the stack | |
@@ -155,7 +145,7 @@ function getErrorSource( | |||
| 155 | 145 | fileURLToPath(originalSourcePath) : originalSourcePath; | |
| 156 | 146 | const source = getOriginalSource( | |
| 157 | 147 | sourceMap.payload, | |
| 158 | - originalSourcePathNoScheme | ||
| 148 | + originalSourcePath | ||
| 159 | 149 | ); | |
| 160 | 150 | const lines = RegExpPrototypeSymbolSplit(/\r?\n/, source, originalLine + 1); | |
| 161 | 151 | const line = lines[originalLine]; | |
@@ -178,28 +168,46 @@ function getErrorSource( | |||
| 178 | 168 | ||
| 179 | 169 | function getOriginalSource(payload, originalSourcePath) { | |
| 180 | 170 | let source; | |
| 181 | - const originalSourcePathNoScheme = | ||
| 182 | - StringPrototypeStartsWith(originalSourcePath, 'file://') ? | ||
| 183 | - fileURLToPath(originalSourcePath) : originalSourcePath; | ||
| 171 | + // payload.sources has been normalized to be an array of absolute urls. | ||
| 184 | 172 | const sourceContentIndex = | |
| 185 | 173 | ArrayPrototypeIndexOf(payload.sources, originalSourcePath); | |
| 186 | 174 | if (payload.sourcesContent?.[sourceContentIndex]) { | |
| 187 | 175 | // First we check if the original source content was provided in the | |
| 188 | 176 | // source map itself: | |
| 189 | 177 | source = payload.sourcesContent[sourceContentIndex]; | |
| 190 | - } else { | ||
| 178 | + } else if (StringPrototypeStartsWith(originalSourcePath, 'file://')) { | ||
| 191 | 179 | // If no sourcesContent was found, attempt to load the original source | |
| 192 | 180 | // from disk: | |
| 181 | + debug(`read source of ${originalSourcePath} from filesystem`); | ||
| 182 | + const originalSourcePathNoScheme = fileURLToPath(originalSourcePath); | ||
| 193 | 183 | try { | |
| 194 | 184 | source = readFileSync(originalSourcePathNoScheme, 'utf8'); | |
| 195 | 185 | } catch (err) { | |
| 196 | 186 | debug(err); | |
| 197 | 187 | source = ''; | |
| 198 | 188 | } | |
| 189 | + } else { | ||
| 190 | + source = ''; | ||
| 199 | 191 | } | |
| 200 | 192 | return source; | |
| 201 | 193 | } | |
| 202 | 194 | ||
| 195 | + function getSourceMapErrorSource(fileName, lineNumber, columnNumber) { | ||
| 196 | + const sm = findSourceMap(fileName); | ||
| 197 | + if (sm === null) { | ||
| 198 | + return; | ||
| 199 | + } | ||
| 200 | + const { | ||
| 201 | + originalLine, | ||
| 202 | + originalColumn, | ||
| 203 | + originalSource, | ||
| 204 | + } = sm.findEntry(lineNumber - 1, columnNumber); | ||
| 205 | + const errorSource = getErrorSource(sm, originalSource, originalLine, originalColumn); | ||
| 206 | + return errorSource; | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + setGetSourceMapErrorSource(getSourceMapErrorSource); | ||
| 210 | + | ||
| 203 | 211 | module.exports = { | |
| 204 | 212 | prepareStackTrace, | |
| 205 | 213 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -41,6 +41,8 @@ const esmSourceMapCache = new SafeMap(); | |||
| 41 | 41 | // The generated sources is not mutable, so we can use a Map without memory concerns: | |
| 42 | 42 | const generatedSourceMapCache = new SafeMap(); | |
| 43 | 43 | const kLeadingProtocol = /^\w+:\/\//; | |
| 44 | + const kSourceMappingURLMagicComment = /\/[*/]#\s+sourceMappingURL=(?<sourceMappingURL>[^\s]+)/; | ||
| 45 | + const kSourceURLMagicComment = /\/[*/]#\s+sourceURL=(?<sourceURL>[^\s]+)/; | ||
| 44 | 46 | ||
| 45 | 47 | const { fileURLToPath, pathToFileURL, URL } = require('internal/url'); | |
| 46 | 48 | let SourceMap; | |
@@ -77,7 +79,22 @@ function setSourceMapsEnabled(val) { | |||
| 77 | 79 | sourceMapsEnabled = val; | |
| 78 | 80 | } | |
| 79 | 81 | ||
| 80 | - function maybeCacheSourceMap(filename, content, cjsModuleInstance, isGeneratedSource) { | ||
| 82 | + function extractSourceURLMagicComment(content) { | ||
| 83 | + const matchSourceURL = RegExpPrototypeExec( | ||
| 84 | + kSourceURLMagicComment, | ||
| 85 | + content | ||
| 86 | + ); | ||
| 87 | + if (matchSourceURL === null) { | ||
| 88 | + return null; | ||
| 89 | + } | ||
| 90 | + let sourceURL = matchSourceURL.groups.sourceURL; | ||
| 91 | + if (sourceURL != null && RegExpPrototypeExec(kLeadingProtocol, sourceURL) === null) { | ||
| 92 | + sourceURL = pathToFileURL(sourceURL).href; | ||
| 93 | + } | ||
| 94 | + return sourceURL; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + function maybeCacheSourceMap(filename, content, cjsModuleInstance, isGeneratedSource, sourceURL) { | ||
| 81 | 98 | const sourceMapsEnabled = getSourceMapsEnabled(); | |
| 82 | 99 | if (!(process.env.NODE_V8_COVERAGE || sourceMapsEnabled)) return; | |
| 83 | 100 | try { | |
@@ -87,10 +104,10 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance, isGeneratedSo | |||
| 87 | 104 | debug(err); | |
| 88 | 105 | return; | |
| 89 | 106 | } | |
| 90 | - const match = RegExpPrototypeExec( | ||
| 91 | - /\/[*/]#\s+sourceMappingURL=(?<sourceMappingURL>[^\s]+)/, | ||
| 92 | - content, | ||
| 93 | - ); | ||
| 107 | + const match = RegExpPrototypeExec(kSourceMappingURLMagicComment, content); | ||
| 108 | + if (sourceURL === undefined) { | ||
| 109 | + sourceURL = extractSourceURLMagicComment(content); | ||
| 110 | + } | ||
| 94 | 111 | if (match) { | |
| 95 | 112 | const data = dataFromUrl(filename, match.groups.sourceMappingURL); | |
| 96 | 113 | const url = data ? null : match.groups.sourceMappingURL; | |
@@ -99,22 +116,33 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance, isGeneratedSo | |||
| 99 | 116 | filename, | |
| 100 | 117 | lineLengths: lineLengths(content), | |
| 101 | 118 | data, | |
| 102 | - url | ||
| 119 | + url, | ||
| 120 | + sourceURL, | ||
| 103 | 121 | }); | |
| 104 | 122 | } else if (isGeneratedSource) { | |
| 105 | - generatedSourceMapCache.set(filename, { | ||
| 123 | + const entry = { | ||
| 106 | 124 | lineLengths: lineLengths(content), | |
| 107 | 125 | data, | |
| 108 | - url | ||
| 109 | - }); | ||
| 126 | + url, | ||
| 127 | + sourceURL | ||
| 128 | + }; | ||
| 129 | + generatedSourceMapCache.set(filename, entry); | ||
| 130 | + if (sourceURL) { | ||
| 131 | + generatedSourceMapCache.set(sourceURL, entry); | ||
| 132 | + } | ||
| 110 | 133 | } else { | |
| 111 | 134 | // If there is no cjsModuleInstance and is not generated source assume we are in a | |
| 112 | 135 | // "modules/esm" context. | |
| 113 | - esmSourceMapCache.set(filename, { | ||
| 136 | + const entry = { | ||
| 114 | 137 | lineLengths: lineLengths(content), | |
| 115 | 138 | data, | |
| 116 | - url | ||
| 117 | - }); | ||
| 139 | + url, | ||
| 140 | + sourceURL, | ||
| 141 | + }; | ||
| 142 | + esmSourceMapCache.set(filename, entry); | ||
| 143 | + if (sourceURL) { | ||
| 144 | + esmSourceMapCache.set(sourceURL, entry); | ||
| 145 | + } | ||
| 118 | 146 | } | |
| 119 | 147 | } | |
| 120 | 148 | } | |
@@ -123,19 +151,12 @@ function maybeCacheGeneratedSourceMap(content) { | |||
| 123 | 151 | const sourceMapsEnabled = getSourceMapsEnabled(); | |
| 124 | 152 | if (!(process.env.NODE_V8_COVERAGE || sourceMapsEnabled)) return; | |
| 125 | 153 | ||
| 126 | - const matchSourceURL = RegExpPrototypeExec( | ||
| 127 | - /\/[*/]#\s+sourceURL=(?<sourceURL>[^\s]+)/, | ||
| 128 | - content | ||
| 129 | - ); | ||
| 130 | - if (matchSourceURL == null) { | ||
| 154 | + const sourceURL = extractSourceURLMagicComment(content); | ||
| 155 | + if (sourceURL === null) { | ||
| 131 | 156 | return; | |
| 132 | 157 | } | |
| 133 | - let sourceURL = matchSourceURL.groups.sourceURL; | ||
| 134 | - if (RegExpPrototypeExec(kLeadingProtocol, sourceURL) === null) { | ||
| 135 | - sourceURL = pathToFileURL(sourceURL).href; | ||
| 136 | - } | ||
| 137 | 158 | try { | |
| 138 | - maybeCacheSourceMap(sourceURL, content, null, true); | ||
| 159 | + maybeCacheSourceMap(sourceURL, content, null, true, sourceURL); | ||
| 139 | 160 | } catch (err) { | |
| 140 | 161 | // This can happen if the filename is not a valid URL. | |
| 141 | 162 | // If we fail to cache the source map, we should not fail the whole process. | |
@@ -254,33 +275,29 @@ function appendCJSCache(obj) { | |||
| 254 | 275 | } | |
| 255 | 276 | } | |
| 256 | 277 | ||
| 257 | - function findSourceMap(sourceURL, isGenerated) { | ||
| 278 | + function findSourceMap(sourceURL) { | ||
| 258 | 279 | if (RegExpPrototypeExec(kLeadingProtocol, sourceURL) === null) { | |
| 259 | 280 | sourceURL = pathToFileURL(sourceURL).href; | |
| 260 | 281 | } | |
| 261 | 282 | if (!SourceMap) { | |
| 262 | 283 | SourceMap = require('internal/source_map/source_map').SourceMap; | |
| 263 | 284 | } | |
| 264 | - let sourceMap; | ||
| 265 | - if (isGenerated) { | ||
| 266 | - sourceMap = generatedSourceMapCache.get(sourceURL); | ||
| 267 | - } else { | ||
| 268 | - sourceMap = esmSourceMapCache.get(sourceURL); | ||
| 269 | - if (sourceMap === undefined) { | ||
| 270 | - for (const value of cjsSourceMapCache) { | ||
| 271 | - const filename = ObjectGetValueSafe(value, 'filename'); | ||
| 272 | - if (sourceURL === filename) { | ||
| 273 | - sourceMap = { | ||
| 274 | - data: ObjectGetValueSafe(value, 'data') | ||
| 275 | - }; | ||
| 276 | - } | ||
| 285 | + let sourceMap = esmSourceMapCache.get(sourceURL) ?? generatedSourceMapCache.get(sourceURL); | ||
| 286 | + if (sourceMap === undefined) { | ||
| 287 | + for (const value of cjsSourceMapCache) { | ||
| 288 | + const filename = ObjectGetValueSafe(value, 'filename'); | ||
| 289 | + const cachedSourceURL = ObjectGetValueSafe(value, 'sourceURL'); | ||
| 290 | + if (sourceURL === filename || sourceURL === cachedSourceURL) { | ||
| 291 | + sourceMap = { | ||
| 292 | + data: ObjectGetValueSafe(value, 'data') | ||
| 293 | + }; | ||
| 277 | 294 | } | |
| 278 | 295 | } | |
| 279 | 296 | } | |
| 280 | 297 | if (sourceMap && sourceMap.data) { | |
| 281 | 298 | return new SourceMap(sourceMap.data); | |
| 282 | 299 | } | |
| 283 | - return undefined; | ||
| 300 | + return null; | ||
| 284 | 301 | } | |
| 285 | 302 | ||
| 286 | 303 | module.exports = { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -525,6 +525,7 @@ class NoArrayBufferZeroFillScope { | |||
| 525 | 525 | V(enhance_fatal_stack_after_inspector, v8::Function) \ | |
| 526 | 526 | V(enhance_fatal_stack_before_inspector, v8::Function) \ | |
| 527 | 527 | V(fs_use_promises_symbol, v8::Symbol) \ | |
| 528 | + V(get_source_map_error_source, v8::Function) \ | ||
| 528 | 529 | V(host_import_module_dynamically_callback, v8::Function) \ | |
| 529 | 530 | V(host_initialize_import_meta_object_callback, v8::Function) \ | |
| 530 | 531 | V(http2session_on_altsvc_function, v8::Function) \ | |
| Back | FazBrowse Home | New Git URL |
0 commit comments