diff options
author | bcoe <bencoe@google.com> | 2020-01-05 06:17:42 +0300 |
---|---|---|
committer | Benjamin Coe <bencoe@google.com> | 2020-01-14 23:39:06 +0300 |
commit | 521b2224c3b06de811f3d8353cce5e4a69239864 (patch) | |
tree | 74cdf8cdab9a256f19389e7185b0730ad2ed642d /lib/internal/source_map | |
parent | 2bb85b85c94465246841c8f4bcd73a6bec0917a6 (diff) |
module: add API for interacting with source maps
PR-URL: https://github.com/nodejs/node/pull/31132
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'lib/internal/source_map')
-rw-r--r-- | lib/internal/source_map/prepare_stack_trace.js | 18 | ||||
-rw-r--r-- | lib/internal/source_map/source_map.js | 72 | ||||
-rw-r--r-- | lib/internal/source_map/source_map_cache.js | 12 |
3 files changed, 71 insertions, 31 deletions
diff --git a/lib/internal/source_map/prepare_stack_trace.js b/lib/internal/source_map/prepare_stack_trace.js index df559b2cdfe..037a8dc53e0 100644 --- a/lib/internal/source_map/prepare_stack_trace.js +++ b/lib/internal/source_map/prepare_stack_trace.js @@ -29,7 +29,6 @@ const prepareStackTrace = (globalThis, error, trace) => { maybeOverridePrepareStackTrace(globalThis, error, trace); if (globalOverride !== kNoOverride) return globalOverride; - const { SourceMap } = require('internal/source_map/source_map'); const errorString = ErrorToString.call(error); if (trace.length === 0) { @@ -39,16 +38,19 @@ const prepareStackTrace = (globalThis, error, trace) => { 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); + const sm = findSourceMap(t.getFileName(), error); + if (sm) { // 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) { + const { + originalLine, + originalColumn, + originalSource + } = sm.findEntry(t.getLineNumber() - 1, t.getColumnNumber() - 1); + if (originalSource && originalLine !== undefined && + originalColumn !== undefined) { str += - `\n -> ${url.replace('file://', '')}:${line + 1}:${col + 1}`; +`\n -> ${originalSource.replace('file://', '')}:${originalLine + 1}:${originalColumn + 1}`; } } } catch (err) { diff --git a/lib/internal/source_map/source_map.js b/lib/internal/source_map/source_map.js index 9044521b6d6..32fe43ac8f6 100644 --- a/lib/internal/source_map/source_map.js +++ b/lib/internal/source_map/source_map.js @@ -66,6 +66,14 @@ 'use strict'; +const { + Array +} = primordials; + +const { + ERR_INVALID_ARG_TYPE +} = require('internal/errors').codes; + let base64Map; const VLQ_BASE_SHIFT = 5; @@ -112,6 +120,7 @@ class StringCharIterator { * @param {SourceMapV3} payload */ class SourceMap { + #payload; #reverseMappingsBySourceURL = []; #mappings = []; #sources = {}; @@ -129,17 +138,25 @@ class SourceMap { for (let i = 0; i < base64Digits.length; ++i) base64Map[base64Digits[i]] = i; } - this.#parseMappingPayload(payload); + this.#payload = cloneSourceMapV3(payload); + this.#parseMappingPayload(); + } + + /** + * @return {Object} raw source map v3 payload. + */ + get payload() { + return cloneSourceMapV3(this.#payload); } /** * @param {SourceMapV3} mappingPayload */ - #parseMappingPayload = (mappingPayload) => { - if (mappingPayload.sections) - this.#parseSections(mappingPayload.sections); + #parseMappingPayload = () => { + if (this.#payload.sections) + this.#parseSections(this.#payload.sections); else - this.#parseMap(mappingPayload, 0, 0); + this.#parseMap(this.#payload, 0, 0); } /** @@ -175,24 +192,18 @@ class SourceMap { const entry = this.#mappings[first]; if (!first && entry && (lineNumber < entry[0] || (lineNumber === entry[0] && columnNumber < entry[1]))) { - return null; + return {}; + } else if (!entry) { + return {}; + } else { + return { + generatedLine: entry[0], + generatedColumn: entry[1], + originalSource: entry[2], + originalLine: entry[3], + originalColumn: entry[4] + }; } - return entry; - } - - /** - * @param {string} sourceURL of the originating resource - * @param {number} lineNumber in the originating resource - * @return {Array} - */ - findEntryReversed(sourceURL, lineNumber) { - const mappings = this.#reverseMappingsBySourceURL[sourceURL]; - for (; lineNumber < mappings.length; ++lineNumber) { - const mapping = mappings[lineNumber]; - if (mapping) - return mapping; - } - return this.#mappings[0]; } /** @@ -296,6 +307,23 @@ function decodeVLQ(stringCharIterator) { return negative ? -result : result; } +/** + * @param {SourceMapV3} payload + * @return {SourceMapV3} + */ +function cloneSourceMapV3(payload) { + if (typeof payload !== 'object') { + throw new ERR_INVALID_ARG_TYPE('payload', ['Object'], payload); + } + payload = { ...payload }; + for (const key in payload) { + if (payload.hasOwnProperty(key) && Array.isArray(payload[key])) { + payload[key] = payload[key].slice(0); + } + } + return payload; +} + module.exports = { SourceMap }; diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index 593c2c8277f..b64af7eed6e 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -37,6 +37,7 @@ const cjsSourceMapCache = new WeakMap(); const esmSourceMapCache = new Map(); const { fileURLToPath, URL } = require('url'); let Module; +let SourceMap; let experimentalSourceMaps; function maybeCacheSourceMap(filename, content, cjsModuleInstance) { @@ -222,8 +223,13 @@ function appendCJSCache(obj) { // Attempt to lookup a source map, which is either attached to a file URI, or // keyed on an error instance. +// TODO(bcoe): once WeakRefs are available in Node.js, refactor to drop +// requirement of error parameter. function findSourceMap(uri, error) { if (!Module) Module = require('internal/modules/cjs/loader').Module; + if (!SourceMap) { + SourceMap = require('internal/source_map/source_map').SourceMap; + } let sourceMap = cjsSourceMapCache.get(Module._cache[uri]); if (!uri.startsWith('file://')) uri = normalizeReferrerURL(uri); if (sourceMap === undefined) { @@ -235,7 +241,11 @@ function findSourceMap(uri, error) { sourceMap = candidateSourceMap; } } - return sourceMap; + if (sourceMap && sourceMap.data) { + return new SourceMap(sourceMap.data); + } else { + return undefined; + } } module.exports = { |