diff options
author | bcoe <bencoe@google.com> | 2019-10-06 11:37:42 -0700 |
---|---|---|
committer | Rich Trott <rtrott@gmail.com> | 2019-10-13 18:58:21 -0700 |
commit | 4ca61f40fed31d590e4d624551044fe7cc7efd42 (patch) | |
tree | 8bb59868ac3566ae37e0146578535ad042978790 /lib | |
parent | f8f6a21580544146d5a8527333e1130b336dc094 (diff) | |
download | android-node-v8-4ca61f40fed31d590e4d624551044fe7cc7efd42.tar.gz android-node-v8-4ca61f40fed31d590e4d624551044fe7cc7efd42.tar.bz2 android-node-v8-4ca61f40fed31d590e4d624551044fe7cc7efd42.zip |
process: add lineLength to source-map-cache
Without the line lengths of in-memory transpiled source, it's not
possible to convert from byte ofsets to line/column offsets.
PR-URL: https://github.com/nodejs/node/pull/29863
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: David Carlier <devnexen@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/internal/bootstrap/pre_execution.js | 2 | ||||
-rw-r--r-- | lib/internal/source_map/prepare_stack_trace.js | 52 | ||||
-rw-r--r-- | lib/internal/source_map/source_map_cache.js | 75 |
3 files changed, 77 insertions, 52 deletions
diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index c1636d87f4..1126fbcdd7 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -25,7 +25,7 @@ function prepareMainThreadExecution(expandArgv1 = false) { // prepareStackTrace method, replacing the default in errors.js. if (getOptionValue('--enable-source-maps')) { const { prepareStackTrace } = - require('internal/source_map/source_map_cache'); + require('internal/source_map/prepare_stack_trace'); const { setPrepareStackTraceCallback } = internalBinding('errors'); setPrepareStackTraceCallback(prepareStackTrace); } diff --git a/lib/internal/source_map/prepare_stack_trace.js b/lib/internal/source_map/prepare_stack_trace.js new file mode 100644 index 0000000000..d6c5fce60a --- /dev/null +++ b/lib/internal/source_map/prepare_stack_trace.js @@ -0,0 +1,52 @@ +'use strict'; + +const debug = require('internal/util/debuglog').debuglog('source_map'); +const { findSourceMap } = require('internal/source_map/source_map_cache'); +const { overrideStackTrace } = require('internal/errors'); + +// Create a prettified stacktrace, inserting context from source maps +// if possible. +const ErrorToString = Error.prototype.toString; // Capture original toString. +const prepareStackTrace = (globalThis, error, trace) => { + // API for node internals to override error stack formatting + // without interfering with userland code. + // TODO(bcoe): add support for source-maps to repl. + if (overrideStackTrace.has(error)) { + const f = overrideStackTrace.get(error); + overrideStackTrace.delete(error); + return f(error, trace); + } + + const { SourceMap } = require('internal/source_map/source_map'); + const errorString = ErrorToString.call(error); + + if (trace.length === 0) { + return errorString; + } + const preparedTrace = trace.map((t, i) => { + let str = i !== 0 ? '\n at ' : ''; + str = `${str}${t}`; + try { + const sourceMap = findSourceMap(t.getFileName(), error); + if (sourceMap && sourceMap.data) { + const sm = new SourceMap(sourceMap.data); + // Source Map V3 lines/columns use zero-based offsets whereas, in + // stack traces, they start at 1/1. + const [, , url, line, col] = + sm.findEntry(t.getLineNumber() - 1, t.getColumnNumber() - 1); + if (url && line !== undefined && col !== undefined) { + str += + `\n -> ${url.replace('file://', '')}:${line + 1}:${col + 1}`; + } + } + } catch (err) { + debug(err.stack); + } + return str; + }); + return `${errorString}\n at ${preparedTrace.join('')}`; +}; + +module.exports = { + prepareStackTrace, +}; diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index 94a4165546..0e77203e9d 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -17,7 +17,6 @@ const cjsSourceMapCache = new WeakMap(); // on filenames. const esmSourceMapCache = new Map(); const { fileURLToPath, URL } = require('url'); -const { overrideStackTrace } = require('internal/errors'); let experimentalSourceMaps; function maybeCacheSourceMap(filename, content, cjsModuleInstance) { @@ -38,18 +37,22 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance) { const match = content.match(/\/[*/]#\s+sourceMappingURL=(?<sourceMappingURL>[^\s]+)/); if (match) { + const data = dataFromUrl(basePath, match.groups.sourceMappingURL); + const url = data ? null : match.groups.sourceMappingURL; if (cjsModuleInstance) { cjsSourceMapCache.set(cjsModuleInstance, { filename, - url: match.groups.sourceMappingURL, - data: dataFromUrl(basePath, match.groups.sourceMappingURL) + lineLengths: lineLengths(content), + data, + url }); } else { // If there is no cjsModuleInstance assume we are in a // "modules/esm" context. esmSourceMapCache.set(filename, { - url: match.groups.sourceMappingURL, - data: dataFromUrl(basePath, match.groups.sourceMappingURL) + lineLengths: lineLengths(content), + data, + url }); } } @@ -73,6 +76,18 @@ function dataFromUrl(basePath, sourceMappingURL) { } } +// Cache the length of each line in the file that a source map was extracted +// from. This allows translation from byte offset V8 coverage reports, +// to line/column offset Source Map V3. +function lineLengths(content) { + // We purposefully keep \r as part of the line-length calculation, in + // cases where there is a \r\n separator, so that this can be taken into + // account in coverage calculations. + return content.split(/\n|\u2028|\u2029/).map((line) => { + return line.length; + }); +} + function sourceMapFromFile(sourceMapFile) { try { const content = fs.readFileSync(sourceMapFile, 'utf8'); @@ -161,56 +176,14 @@ function appendCJSCache(obj) { const value = cjsSourceMapCache.get(Module._cache[key]); if (value) { obj[`file://${key}`] = { - url: value.url, - data: value.data + lineLengths: value.lineLengths, + data: value.data, + url: value.url }; } }); } -// Create a prettified stacktrace, inserting context from source maps -// if possible. -const ErrorToString = Error.prototype.toString; // Capture original toString. -const prepareStackTrace = (globalThis, error, trace) => { - // API for node internals to override error stack formatting - // without interfering with userland code. - // TODO(bcoe): add support for source-maps to repl. - if (overrideStackTrace.has(error)) { - const f = overrideStackTrace.get(error); - overrideStackTrace.delete(error); - return f(error, trace); - } - - const { SourceMap } = require('internal/source_map/source_map'); - const errorString = ErrorToString.call(error); - - if (trace.length === 0) { - return errorString; - } - const preparedTrace = trace.map((t, i) => { - let str = i !== 0 ? '\n at ' : ''; - str = `${str}${t}`; - try { - const sourceMap = findSourceMap(t.getFileName(), error); - if (sourceMap && sourceMap.data) { - const sm = new SourceMap(sourceMap.data); - // Source Map V3 lines/columns use zero-based offsets whereas, in - // stack traces, they start at 1/1. - const [, , url, line, col] = - sm.findEntry(t.getLineNumber() - 1, t.getColumnNumber() - 1); - if (url && line !== undefined && col !== undefined) { - str += - `\n -> ${url.replace('file://', '')}:${line + 1}:${col + 1}`; - } - } - } catch (err) { - debug(err.stack); - } - return str; - }); - return `${errorString}\n at ${preparedTrace.join('')}`; -}; - // Attempt to lookup a source map, which is either attached to a file URI, or // keyed on an error instance. function findSourceMap(uri, error) { @@ -230,8 +203,8 @@ function findSourceMap(uri, error) { } module.exports = { + findSourceMap, maybeCacheSourceMap, - prepareStackTrace, rekeySourceMap, sourceMapCacheToObject, }; |