// This file is a modified version of: // https://cs.chromium.org/chromium/src/v8/tools/SourceMap.js?rcl=dd10454c1d // from the V8 codebase. Logic specific to WebInspector is removed and linting // is made to match the Node.js style guide. // Copyright 2013 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // This is a copy from blink dev tools, see: // http://src.chromium.org/viewvc/blink/trunk/Source/devtools/front_end/SourceMap.js // revision: 153407 /* * Copyright (C) 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 'use strict'; let base64Map; const VLQ_BASE_SHIFT = 5; const VLQ_BASE_MASK = (1 << 5) - 1; const VLQ_CONTINUATION_MASK = 1 << 5; class StringCharIterator { /** * @constructor * @param {string} string */ constructor(string) { this._string = string; this._position = 0; } /** * @return {string} */ next() { return this._string.charAt(this._position++); } /** * @return {string} */ peek() { return this._string.charAt(this._position); } /** * @return {boolean} */ hasNext() { return this._position < this._string.length; } } /** * Implements Source Map V3 model. See http://code.google.com/p/closure-compiler/wiki/SourceMaps * for format description. * @constructor * @param {string} sourceMappingURL * @param {SourceMapV3} payload */ class SourceMap { #reverseMappingsBySourceURL = []; #mappings = []; #sources = {}; #sourceContentByURL = {}; /** * @constructor * @param {SourceMapV3} payload */ constructor(payload) { if (!base64Map) { const base64Digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; base64Map = {}; for (let i = 0; i < base64Digits.length; ++i) base64Map[base64Digits[i]] = i; } this.#parseMappingPayload(payload); } /** * @param {SourceMapV3} mappingPayload */ #parseMappingPayload = (mappingPayload) => { if (mappingPayload.sections) this.#parseSections(mappingPayload.sections); else this.#parseMap(mappingPayload, 0, 0); } /** * @param {Array.} sections */ #parseSections = (sections) => { for (let i = 0; i < sections.length; ++i) { const section = sections[i]; this.#parseMap(section.map, section.offset.line, section.offset.column); } } /** * @param {number} lineNumber in compiled resource * @param {number} columnNumber in compiled resource * @return {?Array} */ findEntry(lineNumber, columnNumber) { let first = 0; let count = this.#mappings.length; while (count > 1) { const step = count >> 1; const middle = first + step; const mapping = this.#mappings[middle]; if (lineNumber < mapping[0] || (lineNumber === mapping[0] && columnNumber < mapping[1])) { count = step; } else { first = middle; count -= step; } } const entry = this.#mappings[first]; if (!first && entry && (lineNumber < entry[0] || (lineNumber === entry[0] && columnNumber < entry[1]))) { return null; } 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]; } /** * @override */ #parseMap = (map, lineNumber, columnNumber) => { let sourceIndex = 0; let sourceLineNumber = 0; let sourceColumnNumber = 0; const sources = []; const originalToCanonicalURLMap = {}; for (let i = 0; i < map.sources.length; ++i) { const url = map.sources[i]; originalToCanonicalURLMap[url] = url; sources.push(url); this.#sources[url] = true; if (map.sourcesContent && map.sourcesContent[i]) this.#sourceContentByURL[url] = map.sourcesContent[i]; } const stringCharIterator = new StringCharIterator(map.mappings); let sourceURL = sources[sourceIndex]; while (true) { if (stringCharIterator.peek() === ',') stringCharIterator.next(); else { while (stringCharIterator.peek() === ';') { lineNumber += 1; columnNumber = 0; stringCharIterator.next(); } if (!stringCharIterator.hasNext()) break; } columnNumber += decodeVLQ(stringCharIterator); if (isSeparator(stringCharIterator.peek())) { this.#mappings.push([lineNumber, columnNumber]); continue; } const sourceIndexDelta = decodeVLQ(stringCharIterator); if (sourceIndexDelta) { sourceIndex += sourceIndexDelta; sourceURL = sources[sourceIndex]; } sourceLineNumber += decodeVLQ(stringCharIterator); sourceColumnNumber += decodeVLQ(stringCharIterator); if (!isSeparator(stringCharIterator.peek())) // Unused index into the names list. decodeVLQ(stringCharIterator); this.#mappings.push([lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber]); } for (let i = 0; i < this.#mappings.length; ++i) { const mapping = this.#mappings[i]; const url = mapping[2]; if (!url) continue; if (!this.#reverseMappingsBySourceURL[url]) this.#reverseMappingsBySourceURL[url] = []; const reverseMappings = this.#reverseMappingsBySourceURL[url]; const sourceLine = mapping[3]; if (!reverseMappings[sourceLine]) reverseMappings[sourceLine] = [mapping[0], mapping[1]]; } }; } /** * @param {string} char * @return {boolean} */ function isSeparator(char) { return char === ',' || char === ';'; } /** * @param {SourceMap.StringCharIterator} stringCharIterator * @return {number} */ function decodeVLQ(stringCharIterator) { // Read unsigned value. let result = 0; let shift = 0; let digit; do { digit = base64Map[stringCharIterator.next()]; result += (digit & VLQ_BASE_MASK) << shift; shift += VLQ_BASE_SHIFT; } while (digit & VLQ_CONTINUATION_MASK); // Fix the sign. const negative = result & 1; result >>= 1; return negative ? -result : result; } module.exports = { SourceMap };