Ejdjdososs/fable-ai
0
1/* -*- Mode: js; js-indent-level: 2; -*- */2/*3 * Copyright 2011 Mozilla Foundation and contributors4 * Licensed under the New BSD license. See LICENSE or:5 * http://opensource.org/licenses/BSD-3-Clause6 */7 8var util = require('./util');9var binarySearch = require('./binary-search');10var ArraySet = require('./array-set').ArraySet;11var base64VLQ = require('./base64-vlq');12var quickSort = require('./quick-sort').quickSort;13 14function SourceMapConsumer(aSourceMap, aSourceMapURL) {15 var sourceMap = aSourceMap;16 if (typeof aSourceMap === 'string') {17 sourceMap = util.parseSourceMapInput(aSourceMap);18 }19 20 return sourceMap.sections != null21 ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)22 : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);23}24 25SourceMapConsumer.fromSourceMap = function(aSourceMap, aSourceMapURL) {26 return BasicSourceMapConsumer.fromSourceMap(aSourceMap, aSourceMapURL);27}28 29/**30 * The version of the source mapping spec that we are consuming.31 */32SourceMapConsumer.prototype._version = 3;33 34// `__generatedMappings` and `__originalMappings` are arrays that hold the35// parsed mapping coordinates from the source map's "mappings" attribute. They36// are lazily instantiated, accessed via the `_generatedMappings` and37// `_originalMappings` getters respectively, and we only parse the mappings38// and create these arrays once queried for a source location. We jump through39// these hoops because there can be many thousands of mappings, and parsing40// them is expensive, so we only want to do it if we must.41//42// Each object in the arrays is of the form:43//44// {45// generatedLine: The line number in the generated code,46// generatedColumn: The column number in the generated code,47// source: The path to the original source file that generated this48// chunk of code,49// originalLine: The line number in the original source that50// corresponds to this chunk of generated code,51// originalColumn: The column number in the original source that52// corresponds to this chunk of generated code,53// name: The name of the original symbol which generated this chunk of54// code.55// }56//57// All properties except for `generatedLine` and `generatedColumn` can be58// `null`.59//60// `_generatedMappings` is ordered by the generated positions.61//62// `_originalMappings` is ordered by the original positions.63 64SourceMapConsumer.prototype.__generatedMappings = null;65Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {66 configurable: true,67 enumerable: true,68 get: function () {69 if (!this.__generatedMappings) {70 this._parseMappings(this._mappings, this.sourceRoot);71 }72 73 return this.__generatedMappings;74 }75});76 77SourceMapConsumer.prototype.__originalMappings = null;78Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {79 configurable: true,80 enumerable: true,81 get: function () {82 if (!this.__originalMappings) {83 this._parseMappings(this._mappings, this.sourceRoot);84 }85 86 return this.__originalMappings;87 }88});89 90SourceMapConsumer.prototype._charIsMappingSeparator =91 function SourceMapConsumer_charIsMappingSeparator(aStr, index) {92 var c = aStr.charAt(index);93 return c === ";" || c === ",";94 };95 96/**97 * Parse the mappings in a string in to a data structure which we can easily98 * query (the ordered arrays in the `this.__generatedMappings` and99 * `this.__originalMappings` properties).100 */101SourceMapConsumer.prototype._parseMappings =102 function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {103 throw new Error("Subclasses must implement _parseMappings");104 };105 106SourceMapConsumer.GENERATED_ORDER = 1;107SourceMapConsumer.ORIGINAL_ORDER = 2;108 109SourceMapConsumer.GREATEST_LOWER_BOUND = 1;110SourceMapConsumer.LEAST_UPPER_BOUND = 2;111 112/**113 * Iterate over each mapping between an original source/line/column and a114 * generated line/column in this source map.115 *116 * @param Function aCallback117 * The function that is called with each mapping.118 * @param Object aContext119 * Optional. If specified, this object will be the value of `this` every120 * time that `aCallback` is called.121 * @param aOrder122 * Either `SourceMapConsumer.GENERATED_ORDER` or123 * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to124 * iterate over the mappings sorted by the generated file's line/column125 * order or the original's source/line/column order, respectively. Defaults to126 * `SourceMapConsumer.GENERATED_ORDER`.127 */128SourceMapConsumer.prototype.eachMapping =129 function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {130 var context = aContext || null;131 var order = aOrder || SourceMapConsumer.GENERATED_ORDER;132 133 var mappings;134 switch (order) {135 case SourceMapConsumer.GENERATED_ORDER:136 mappings = this._generatedMappings;137 break;138 case SourceMapConsumer.ORIGINAL_ORDER:139 mappings = this._originalMappings;140 break;141 default:142 throw new Error("Unknown order of iteration.");143 }144 145 var sourceRoot = this.sourceRoot;146 var boundCallback = aCallback.bind(context);147 var names = this._names;148 var sources = this._sources;149 var sourceMapURL = this._sourceMapURL;150 151 for (var i = 0, n = mappings.length; i < n; i++) {152 var mapping = mappings[i];153 var source = mapping.source === null ? null : sources.at(mapping.source);154 if(source !== null) {155 source = util.computeSourceURL(sourceRoot, source, sourceMapURL);156 }157 boundCallback({158 source: source,159 generatedLine: mapping.generatedLine,160 generatedColumn: mapping.generatedColumn,161 originalLine: mapping.originalLine,162 originalColumn: mapping.originalColumn,163 name: mapping.name === null ? null : names.at(mapping.name)164 });165 }166 };167 168/**169 * Returns all generated line and column information for the original source,170 * line, and column provided. If no column is provided, returns all mappings171 * corresponding to a either the line we are searching for or the next172 * closest line that has any mappings. Otherwise, returns all mappings173 * corresponding to the given line and either the column we are searching for174 * or the next closest column that has any offsets.175 *176 * The only argument is an object with the following properties:177 *178 * - source: The filename of the original source.179 * - line: The line number in the original source. The line number is 1-based.180 * - column: Optional. the column number in the original source.181 * The column number is 0-based.182 *183 * and an array of objects is returned, each with the following properties:184 *185 * - line: The line number in the generated source, or null. The186 * line number is 1-based.187 * - column: The column number in the generated source, or null.188 * The column number is 0-based.189 */190SourceMapConsumer.prototype.allGeneratedPositionsFor =191 function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {192 var line = util.getArg(aArgs, 'line');193 194 // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping195 // returns the index of the closest mapping less than the needle. By196 // setting needle.originalColumn to 0, we thus find the last mapping for197 // the given line, provided such a mapping exists.198 var needle = {199 source: util.getArg(aArgs, 'source'),200 originalLine: line,201 originalColumn: util.getArg(aArgs, 'column', 0)202 };203 204 needle.source = this._findSourceIndex(needle.source);205 if (needle.source < 0) {206 return [];207 }208 209 var mappings = [];210 211 var index = this._findMapping(needle,212 this._originalMappings,213 "originalLine",214 "originalColumn",215 util.compareByOriginalPositions,216 binarySearch.LEAST_UPPER_BOUND);217 if (index >= 0) {218 var mapping = this._originalMappings[index];219 220 if (aArgs.column === undefined) {221 var originalLine = mapping.originalLine;222 223 // Iterate until either we run out of mappings, or we run into224 // a mapping for a different line than the one we found. Since225 // mappings are sorted, this is guaranteed to find all mappings for226 // the line we found.227 while (mapping && mapping.originalLine === originalLine) {228 mappings.push({229 line: util.getArg(mapping, 'generatedLine', null),230 column: util.getArg(mapping, 'generatedColumn', null),231 lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)232 });233 234 mapping = this._originalMappings[++index];235 }236 } else {237 var originalColumn = mapping.originalColumn;238 239 // Iterate until either we run out of mappings, or we run into240 // a mapping for a different line than the one we were searching for.241 // Since mappings are sorted, this is guaranteed to find all mappings for242 // the line we are searching for.243 while (mapping &&244 mapping.originalLine === line &&245 mapping.originalColumn == originalColumn) {246 mappings.push({247 line: util.getArg(mapping, 'generatedLine', null),248 column: util.getArg(mapping, 'generatedColumn', null),249 lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)250 });251 252 mapping = this._originalMappings[++index];253 }254 }255 }256 257 return mappings;258 };259 260exports.SourceMapConsumer = SourceMapConsumer;261 262/**263 * A BasicSourceMapConsumer instance represents a parsed source map which we can264 * query for information about the original file positions by giving it a file265 * position in the generated source.266 *267 * The first parameter is the raw source map (either as a JSON string, or268 * already parsed to an object). According to the spec, source maps have the269 * following attributes:270 *271 * - version: Which version of the source map spec this map is following.272 * - sources: An array of URLs to the original source files.273 * - names: An array of identifiers which can be referrenced by individual mappings.274 * - sourceRoot: Optional. The URL root from which all sources are relative.275 * - sourcesContent: Optional. An array of contents of the original source files.276 * - mappings: A string of base64 VLQs which contain the actual mappings.277 * - file: Optional. The generated file this source map is associated with.278 *279 * Here is an example source map, taken from the source map spec[0]:280 *281 * {282 * version : 3,283 * file: "out.js",284 * sourceRoot : "",285 * sources: ["foo.js", "bar.js"],286 * names: ["src", "maps", "are", "fun"],287 * mappings: "AA,AB;;ABCDE;"288 * }289 *290 * The second parameter, if given, is a string whose value is the URL291 * at which the source map was found. This URL is used to compute the292 * sources array.293 *294 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#295 */296function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {297 var sourceMap = aSourceMap;298 if (typeof aSourceMap === 'string') {299 sourceMap = util.parseSourceMapInput(aSourceMap);300 }301 302 var version = util.getArg(sourceMap, 'version');303 var sources = util.getArg(sourceMap, 'sources');304 // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which305 // requires the array) to play nice here.306 var names = util.getArg(sourceMap, 'names', []);307 var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);308 var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);309 var mappings = util.getArg(sourceMap, 'mappings');310 var file = util.getArg(sourceMap, 'file', null);311 312 // Once again, Sass deviates from the spec and supplies the version as a313 // string rather than a number, so we use loose equality checking here.314 if (version != this._version) {315 throw new Error('Unsupported version: ' + version);316 }317 318 if (sourceRoot) {319 sourceRoot = util.normalize(sourceRoot);320 }321 322 sources = sources323 .map(String)324 // Some source maps produce relative source paths like "./foo.js" instead of325 // "foo.js". Normalize these first so that future comparisons will succeed.326 // See bugzil.la/1090768.327 .map(util.normalize)328 // Always ensure that absolute sources are internally stored relative to329 // the source root, if the source root is absolute. Not doing this would330 // be particularly problematic when the source root is a prefix of the331 // source (valid, but why??). See github issue #199 and bugzil.la/1188982.332 .map(function (source) {333 return sourceRoot && util.isAbsolute(sourceRoot) && util.isAbsolute(source)334 ? util.relative(sourceRoot, source)335 : source;336 });337 338 // Pass `true` below to allow duplicate names and sources. While source maps339 // are intended to be compressed and deduplicated, the TypeScript compiler340 // sometimes generates source maps with duplicates in them. See Github issue341 // #72 and bugzil.la/889492.342 this._names = ArraySet.fromArray(names.map(String), true);343 this._sources = ArraySet.fromArray(sources, true);344 345 this._absoluteSources = this._sources.toArray().map(function (s) {346 return util.computeSourceURL(sourceRoot, s, aSourceMapURL);347 });348 349 this.sourceRoot = sourceRoot;350 this.sourcesContent = sourcesContent;351 this._mappings = mappings;352 this._sourceMapURL = aSourceMapURL;353 this.file = file;354}355 356BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);357BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;358 359/**360 * Utility function to find the index of a source. Returns -1 if not361 * found.362 */363BasicSourceMapConsumer.prototype._findSourceIndex = function(aSource) {364 var relativeSource = aSource;365 if (this.sourceRoot != null) {366 relativeSource = util.relative(this.sourceRoot, relativeSource);367 }368 369 if (this._sources.has(relativeSource)) {370 return this._sources.indexOf(relativeSource);371 }372 373 // Maybe aSource is an absolute URL as returned by |sources|. In374 // this case we can't simply undo the transform.375 var i;376 for (i = 0; i < this._absoluteSources.length; ++i) {377 if (this._absoluteSources[i] == aSource) {378 return i;379 }380 }381 382 return -1;383};384 385/**386 * Create a BasicSourceMapConsumer from a SourceMapGenerator.387 *388 * @param SourceMapGenerator aSourceMap389 * The source map that will be consumed.390 * @param String aSourceMapURL391 * The URL at which the source map can be found (optional)392 * @returns BasicSourceMapConsumer393 */394BasicSourceMapConsumer.fromSourceMap =395 function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {396 var smc = Object.create(BasicSourceMapConsumer.prototype);397 398 var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);399 var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);400 smc.sourceRoot = aSourceMap._sourceRoot;401 smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),402 smc.sourceRoot);403 smc.file = aSourceMap._file;404 smc._sourceMapURL = aSourceMapURL;405 smc._absoluteSources = smc._sources.toArray().map(function (s) {406 return util.computeSourceURL(smc.sourceRoot, s, aSourceMapURL);407 });408 409 // Because we are modifying the entries (by converting string sources and410 // names to indices into the sources and names ArraySets), we have to make411 // a copy of the entry or else bad things happen. Shared mutable state412 // strikes again! See github issue #191.413 414 var generatedMappings = aSourceMap._mappings.toArray().slice();415 var destGeneratedMappings = smc.__generatedMappings = [];416 var destOriginalMappings = smc.__originalMappings = [];417 418 for (var i = 0, length = generatedMappings.length; i < length; i++) {419 var srcMapping = generatedMappings[i];420 var destMapping = new Mapping;421 destMapping.generatedLine = srcMapping.generatedLine;422 destMapping.generatedColumn = srcMapping.generatedColumn;423 424 if (srcMapping.source) {425 destMapping.source = sources.indexOf(srcMapping.source);426 destMapping.originalLine = srcMapping.originalLine;427 destMapping.originalColumn = srcMapping.originalColumn;428 429 if (srcMapping.name) {430 destMapping.name = names.indexOf(srcMapping.name);431 }432 433 destOriginalMappings.push(destMapping);434 }435 436 destGeneratedMappings.push(destMapping);437 }438 439 quickSort(smc.__originalMappings, util.compareByOriginalPositions);440 441 return smc;442 };443 444/**445 * The version of the source mapping spec that we are consuming.446 */447BasicSourceMapConsumer.prototype._version = 3;448 449/**450 * The list of original sources.451 */452Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {453 get: function () {454 return this._absoluteSources.slice();455 }456});457 458/**459 * Provide the JIT with a nice shape / hidden class.460 */461function Mapping() {462 this.generatedLine = 0;463 this.generatedColumn = 0;464 this.source = null;465 this.originalLine = null;466 this.originalColumn = null;467 this.name = null;468}469 470/**471 * Parse the mappings in a string in to a data structure which we can easily472 * query (the ordered arrays in the `this.__generatedMappings` and473 * `this.__originalMappings` properties).474 */475 476const compareGenerated = util.compareByGeneratedPositionsDeflatedNoLine;477function sortGenerated(array, start) {478 let l = array.length;479 let n = array.length - start;480 if (n <= 1) {481 return;482 } else if (n == 2) {483 let a = array[start];484 let b = array[start + 1];485 if (compareGenerated(a, b) > 0) {486 array[start] = b;487 array[start + 1] = a;488 }489 } else if (n < 20) {490 for (let i = start; i < l; i++) {491 for (let j = i; j > start; j--) {492 let a = array[j - 1];493 let b = array[j];494 if (compareGenerated(a, b) <= 0) {495 break;496 }497 array[j - 1] = b;498 array[j] = a;499 }500 }501 } else {502 quickSort(array, compareGenerated, start);503 }504}505BasicSourceMapConsumer.prototype._parseMappings =506 function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {507 var generatedLine = 1;508 var previousGeneratedColumn = 0;509 var previousOriginalLine = 0;510 var previousOriginalColumn = 0;511 var previousSource = 0;512 var previousName = 0;513 var length = aStr.length;514 var index = 0;515 var cachedSegments = {};516 var temp = {};517 var originalMappings = [];518 var generatedMappings = [];519 var mapping, str, segment, end, value;520 521 let subarrayStart = 0;522 while (index < length) {523 if (aStr.charAt(index) === ';') {524 generatedLine++;525 index++;526 previousGeneratedColumn = 0;527 528 sortGenerated(generatedMappings, subarrayStart);529 subarrayStart = generatedMappings.length;530 }531 else if (aStr.charAt(index) === ',') {532 index++;533 }534 else {535 mapping = new Mapping();536 mapping.generatedLine = generatedLine;537 538 for (end = index; end < length; end++) {539 if (this._charIsMappingSeparator(aStr, end)) {540 break;541 }542 }543 str = aStr.slice(index, end);544 545 segment = [];546 while (index < end) {547 base64VLQ.decode(aStr, index, temp);548 value = temp.value;549 index = temp.rest;550 segment.push(value);551 }552 553 if (segment.length === 2) {554 throw new Error('Found a source, but no line and column');555 }556 557 if (segment.length === 3) {558 throw new Error('Found a source and line, but no column');559 }560 561 // Generated column.562 mapping.generatedColumn = previousGeneratedColumn + segment[0];563 previousGeneratedColumn = mapping.generatedColumn;564 565 if (segment.length > 1) {566 // Original source.567 mapping.source = previousSource + segment[1];568 previousSource += segment[1];569 570 // Original line.571 mapping.originalLine = previousOriginalLine + segment[2];572 previousOriginalLine = mapping.originalLine;573 // Lines are stored 0-based574 mapping.originalLine += 1;575 576 // Original column.577 mapping.originalColumn = previousOriginalColumn + segment[3];578 previousOriginalColumn = mapping.originalColumn;579 580 if (segment.length > 4) {581 // Original name.582 mapping.name = previousName + segment[4];583 previousName += segment[4];584 }585 }586 587 generatedMappings.push(mapping);588 if (typeof mapping.originalLine === 'number') {589 let currentSource = mapping.source;590 while (originalMappings.length <= currentSource) {591 originalMappings.push(null);592 }593 if (originalMappings[currentSource] === null) {594 originalMappings[currentSource] = [];595 }596 originalMappings[currentSource].push(mapping);597 }598 }599 }600 601 sortGenerated(generatedMappings, subarrayStart);602 this.__generatedMappings = generatedMappings;603 604 for (var i = 0; i < originalMappings.length; i++) {605 if (originalMappings[i] != null) {606 quickSort(originalMappings[i], util.compareByOriginalPositionsNoSource);607 }608 }609 this.__originalMappings = [].concat(...originalMappings);610 };611 612/**613 * Find the mapping that best matches the hypothetical "needle" mapping that614 * we are searching for in the given "haystack" of mappings.615 */616BasicSourceMapConsumer.prototype._findMapping =617 function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,618 aColumnName, aComparator, aBias) {619 // To return the position we are searching for, we must first find the620 // mapping for the given position and then return the opposite position it621 // points to. Because the mappings are sorted, we can use binary search to622 // find the best mapping.623 624 if (aNeedle[aLineName] <= 0) {625 throw new TypeError('Line must be greater than or equal to 1, got '626 + aNeedle[aLineName]);627 }628 if (aNeedle[aColumnName] < 0) {629 throw new TypeError('Column must be greater than or equal to 0, got '630 + aNeedle[aColumnName]);631 }632 633 return binarySearch.search(aNeedle, aMappings, aComparator, aBias);634 };635 636/**637 * Compute the last column for each generated mapping. The last column is638 * inclusive.639 */640BasicSourceMapConsumer.prototype.computeColumnSpans =641 function SourceMapConsumer_computeColumnSpans() {642 for (var index = 0; index < this._generatedMappings.length; ++index) {643 var mapping = this._generatedMappings[index];644 645 // Mappings do not contain a field for the last generated columnt. We646 // can come up with an optimistic estimate, however, by assuming that647 // mappings are contiguous (i.e. given two consecutive mappings, the648 // first mapping ends where the second one starts).649 if (index + 1 < this._generatedMappings.length) {650 var nextMapping = this._generatedMappings[index + 1];651 652 if (mapping.generatedLine === nextMapping.generatedLine) {653 mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1;654 continue;655 }656 }657 658 // The last mapping for each line spans the entire line.659 mapping.lastGeneratedColumn = Infinity;660 }661 };662 663/**664 * Returns the original source, line, and column information for the generated665 * source's line and column positions provided. The only argument is an object666 * with the following properties:667 *668 * - line: The line number in the generated source. The line number669 * is 1-based.670 * - column: The column number in the generated source. The column671 * number is 0-based.672 * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or673 * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the674 * closest element that is smaller than or greater than the one we are675 * searching for, respectively, if the exact element cannot be found.676 * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.677 *678 * and an object is returned with the following properties:679 *680 * - source: The original source file, or null.681 * - line: The line number in the original source, or null. The682 * line number is 1-based.683 * - column: The column number in the original source, or null. The684 * column number is 0-based.685 * - name: The original identifier, or null.686 */687BasicSourceMapConsumer.prototype.originalPositionFor =688 function SourceMapConsumer_originalPositionFor(aArgs) {689 var needle = {690 generatedLine: util.getArg(aArgs, 'line'),691 generatedColumn: util.getArg(aArgs, 'column')692 };693 694 var index = this._findMapping(695 needle,696 this._generatedMappings,697 "generatedLine",698 "generatedColumn",699 util.compareByGeneratedPositionsDeflated,700 util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND)701 );702 703 if (index >= 0) {704 var mapping = this._generatedMappings[index];705 706 if (mapping.generatedLine === needle.generatedLine) {707 var source = util.getArg(mapping, 'source', null);708 if (source !== null) {709 source = this._sources.at(source);710 source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);711 }712 var name = util.getArg(mapping, 'name', null);713 if (name !== null) {714 name = this._names.at(name);715 }716 return {717 source: source,718 line: util.getArg(mapping, 'originalLine', null),719 column: util.getArg(mapping, 'originalColumn', null),720 name: name721 };722 }723 }724 725 return {726 source: null,727 line: null,728 column: null,729 name: null730 };731 };732 733/**734 * Return true if we have the source content for every source in the source735 * map, false otherwise.736 */737BasicSourceMapConsumer.prototype.hasContentsOfAllSources =738 function BasicSourceMapConsumer_hasContentsOfAllSources() {739 if (!this.sourcesContent) {740 return false;741 }742 return this.sourcesContent.length >= this._sources.size() &&743 !this.sourcesContent.some(function (sc) { return sc == null; });744 };745 746/**747 * Returns the original source content. The only argument is the url of the748 * original source file. Returns null if no original source content is749 * available.750 */751BasicSourceMapConsumer.prototype.sourceContentFor =752 function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {753 if (!this.sourcesContent) {754 return null;755 }756 757 var index = this._findSourceIndex(aSource);758 if (index >= 0) {759 return this.sourcesContent[index];760 }761 762 var relativeSource = aSource;763 if (this.sourceRoot != null) {764 relativeSource = util.relative(this.sourceRoot, relativeSource);765 }766 767 var url;768 if (this.sourceRoot != null769 && (url = util.urlParse(this.sourceRoot))) {770 // XXX: file:// URIs and absolute paths lead to unexpected behavior for771 // many users. We can help them out when they expect file:// URIs to772 // behave like it would if they were running a local HTTP server. See773 // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.774 var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");775 if (url.scheme == "file"776 && this._sources.has(fileUriAbsPath)) {777 return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]778 }779 780 if ((!url.path || url.path == "/")781 && this._sources.has("/" + relativeSource)) {782 return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];783 }784 }785 786 // This function is used recursively from787 // IndexedSourceMapConsumer.prototype.sourceContentFor. In that case, we788 // don't want to throw if we can't find the source - we just want to789 // return null, so we provide a flag to exit gracefully.790 if (nullOnMissing) {791 return null;792 }793 else {794 throw new Error('"' + relativeSource + '" is not in the SourceMap.');795 }796 };797 798/**799 * Returns the generated line and column information for the original source,800 * line, and column positions provided. The only argument is an object with801 * the following properties:802 *803 * - source: The filename of the original source.804 * - line: The line number in the original source. The line number805 * is 1-based.806 * - column: The column number in the original source. The column807 * number is 0-based.808 * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or809 * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the810 * closest element that is smaller than or greater than the one we are811 * searching for, respectively, if the exact element cannot be found.812 * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.813 *814 * and an object is returned with the following properties:815 *816 * - line: The line number in the generated source, or null. The817 * line number is 1-based.818 * - column: The column number in the generated source, or null.819 * The column number is 0-based.820 */821BasicSourceMapConsumer.prototype.generatedPositionFor =822 function SourceMapConsumer_generatedPositionFor(aArgs) {823 var source = util.getArg(aArgs, 'source');824 source = this._findSourceIndex(source);825 if (source < 0) {826 return {827 line: null,828 column: null,829 lastColumn: null830 };831 }832 833 var needle = {834 source: source,835 originalLine: util.getArg(aArgs, 'line'),836 originalColumn: util.getArg(aArgs, 'column')837 };838 839 var index = this._findMapping(840 needle,841 this._originalMappings,842 "originalLine",843 "originalColumn",844 util.compareByOriginalPositions,845 util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND)846 );847 848 if (index >= 0) {849 var mapping = this._originalMappings[index];850 851 if (mapping.source === needle.source) {852 return {853 line: util.getArg(mapping, 'generatedLine', null),854 column: util.getArg(mapping, 'generatedColumn', null),855 lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)856 };857 }858 }859 860 return {861 line: null,862 column: null,863 lastColumn: null864 };865 };866 867exports.BasicSourceMapConsumer = BasicSourceMapConsumer;868 869/**870 * An IndexedSourceMapConsumer instance represents a parsed source map which871 * we can query for information. It differs from BasicSourceMapConsumer in872 * that it takes "indexed" source maps (i.e. ones with a "sections" field) as873 * input.874 *875 * The first parameter is a raw source map (either as a JSON string, or already876 * parsed to an object). According to the spec for indexed source maps, they877 * have the following attributes:878 *879 * - version: Which version of the source map spec this map is following.880 * - file: Optional. The generated file this source map is associated with.881 * - sections: A list of section definitions.882 *883 * Each value under the "sections" field has two fields:884 * - offset: The offset into the original specified at which this section885 * begins to apply, defined as an object with a "line" and "column"886 * field.887 * - map: A source map definition. This source map could also be indexed,888 * but doesn't have to be.889 *890 * Instead of the "map" field, it's also possible to have a "url" field891 * specifying a URL to retrieve a source map from, but that's currently892 * unsupported.893 *894 * Here's an example source map, taken from the source map spec[0], but895 * modified to omit a section which uses the "url" field.896 *897 * {898 * version : 3,899 * file: "app.js",900 * sections: [{901 * offset: {line:100, column:10},902 * map: {903 * version : 3,904 * file: "section.js",905 * sources: ["foo.js", "bar.js"],906 * names: ["src", "maps", "are", "fun"],907 * mappings: "AAAA,E;;ABCDE;"908 * }909 * }],910 * }911 *912 * The second parameter, if given, is a string whose value is the URL913 * at which the source map was found. This URL is used to compute the914 * sources array.915 *916 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt917 */918function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {919 var sourceMap = aSourceMap;920 if (typeof aSourceMap === 'string') {921 sourceMap = util.parseSourceMapInput(aSourceMap);922 }923 924 var version = util.getArg(sourceMap, 'version');925 var sections = util.getArg(sourceMap, 'sections');926 927 if (version != this._version) {928 throw new Error('Unsupported version: ' + version);929 }930 931 this._sources = new ArraySet();932 this._names = new ArraySet();933 934 var lastOffset = {935 line: -1,936 column: 0937 };938 this._sections = sections.map(function (s) {939 if (s.url) {940 // The url field will require support for asynchronicity.941 // See https://github.com/mozilla/source-map/issues/16942 throw new Error('Support for url field in sections not implemented.');943 }944 var offset = util.getArg(s, 'offset');945 var offsetLine = util.getArg(offset, 'line');946 var offsetColumn = util.getArg(offset, 'column');947 948 if (offsetLine < lastOffset.line ||949 (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) {950 throw new Error('Section offsets must be ordered and non-overlapping.');951 }952 lastOffset = offset;953 954 return {955 generatedOffset: {956 // The offset fields are 0-based, but we use 1-based indices when957 // encoding/decoding from VLQ.958 generatedLine: offsetLine + 1,959 generatedColumn: offsetColumn + 1960 },961 consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)962 }963 });964}965 966IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);967IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer;968 969/**970 * The version of the source mapping spec that we are consuming.971 */972IndexedSourceMapConsumer.prototype._version = 3;973 974/**975 * The list of original sources.976 */977Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {978 get: function () {979 var sources = [];980 for (var i = 0; i < this._sections.length; i++) {981 for (var j = 0; j < this._sections[i].consumer.sources.length; j++) {982 sources.push(this._sections[i].consumer.sources[j]);983 }984 }985 return sources;986 }987});988 989/**990 * Returns the original source, line, and column information for the generated991 * source's line and column positions provided. The only argument is an object992 * with the following properties:993 *994 * - line: The line number in the generated source. The line number995 * is 1-based.996 * - column: The column number in the generated source. The column997 * number is 0-based.998 *999 * and an object is returned with the following properties:1000 *1001 * - source: The original source file, or null.1002 * - line: The line number in the original source, or null. The1003 * line number is 1-based.1004 * - column: The column number in the original source, or null. The1005 * column number is 0-based.1006 * - name: The original identifier, or null.1007 */1008IndexedSourceMapConsumer.prototype.originalPositionFor =1009 function IndexedSourceMapConsumer_originalPositionFor(aArgs) {1010 var needle = {1011 generatedLine: util.getArg(aArgs, 'line'),1012 generatedColumn: util.getArg(aArgs, 'column')1013 };1014 1015 // Find the section containing the generated position we're trying to map1016 // to an original position.1017 var sectionIndex = binarySearch.search(needle, this._sections,1018 function(needle, section) {1019 var cmp = needle.generatedLine - section.generatedOffset.generatedLine;1020 if (cmp) {1021 return cmp;1022 }1023 1024 return (needle.generatedColumn -1025 section.generatedOffset.generatedColumn);1026 });1027 var section = this._sections[sectionIndex];1028 1029 if (!section) {1030 return {1031 source: null,1032 line: null,1033 column: null,1034 name: null1035 };1036 }1037 1038 return section.consumer.originalPositionFor({1039 line: needle.generatedLine -1040 (section.generatedOffset.generatedLine - 1),1041 column: needle.generatedColumn -1042 (section.generatedOffset.generatedLine === needle.generatedLine1043 ? section.generatedOffset.generatedColumn - 11044 : 0),1045 bias: aArgs.bias1046 });1047 };1048 1049/**1050 * Return true if we have the source content for every source in the source1051 * map, false otherwise.1052 */1053IndexedSourceMapConsumer.prototype.hasContentsOfAllSources =1054 function IndexedSourceMapConsumer_hasContentsOfAllSources() {1055 return this._sections.every(function (s) {1056 return s.consumer.hasContentsOfAllSources();1057 });1058 };1059 1060/**1061 * Returns the original source content. The only argument is the url of the1062 * original source file. Returns null if no original source content is1063 * available.1064 */1065IndexedSourceMapConsumer.prototype.sourceContentFor =1066 function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {1067 for (var i = 0; i < this._sections.length; i++) {1068 var section = this._sections[i];1069 1070 var content = section.consumer.sourceContentFor(aSource, true);1071 if (content || content === '') {1072 return content;1073 }1074 }1075 if (nullOnMissing) {1076 return null;1077 }1078 else {1079 throw new Error('"' + aSource + '" is not in the SourceMap.');1080 }1081 };1082 1083/**1084 * Returns the generated line and column information for the original source,1085 * line, and column positions provided. The only argument is an object with1086 * the following properties:1087 *1088 * - source: The filename of the original source.1089 * - line: The line number in the original source. The line number1090 * is 1-based.1091 * - column: The column number in the original source. The column1092 * number is 0-based.1093 *1094 * and an object is returned with the following properties:1095 *1096 * - line: The line number in the generated source, or null. The1097 * line number is 1-based. 1098 * - column: The column number in the generated source, or null.1099 * The column number is 0-based.1100 */1101IndexedSourceMapConsumer.prototype.generatedPositionFor =1102 function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {1103 for (var i = 0; i < this._sections.length; i++) {1104 var section = this._sections[i];1105 1106 // Only consider this section if the requested source is in the list of1107 // sources of the consumer.1108 if (section.consumer._findSourceIndex(util.getArg(aArgs, 'source')) === -1) {1109 continue;1110 }1111 var generatedPosition = section.consumer.generatedPositionFor(aArgs);1112 if (generatedPosition) {1113 var ret = {1114 line: generatedPosition.line +1115 (section.generatedOffset.generatedLine - 1),1116 column: generatedPosition.column +1117 (section.generatedOffset.generatedLine === generatedPosition.line1118 ? section.generatedOffset.generatedColumn - 11119 : 0)1120 };1121 return ret;1122 }1123 }1124 1125 return {1126 line: null,1127 column: null1128 };1129 };1130 1131/**1132 * Parse the mappings in a string in to a data structure which we can easily1133 * query (the ordered arrays in the `this.__generatedMappings` and1134 * `this.__originalMappings` properties).1135 */1136IndexedSourceMapConsumer.prototype._parseMappings =1137 function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) {1138 this.__generatedMappings = [];1139 this.__originalMappings = [];1140 for (var i = 0; i < this._sections.length; i++) {1141 var section = this._sections[i];1142 var sectionMappings = section.consumer._generatedMappings;1143 for (var j = 0; j < sectionMappings.length; j++) {1144 var mapping = sectionMappings[j];1145 1146 var source = section.consumer._sources.at(mapping.source);1147 if(source !== null) {1148 source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);1149 }1150 this._sources.add(source);1151 source = this._sources.indexOf(source);1152 1153 var name = null;1154 if (mapping.name) {1155 name = section.consumer._names.at(mapping.name);1156 this._names.add(name);1157 name = this._names.indexOf(name);1158 }1159 1160 // The mappings coming from the consumer for the section have1161 // generated positions relative to the start of the section, so we1162 // need to offset them to be relative to the start of the concatenated1163 // generated file.1164 var adjustedMapping = {1165 source: source,1166 generatedLine: mapping.generatedLine +1167 (section.generatedOffset.generatedLine - 1),1168 generatedColumn: mapping.generatedColumn +1169 (section.generatedOffset.generatedLine === mapping.generatedLine1170 ? section.generatedOffset.generatedColumn - 11171 : 0),1172 originalLine: mapping.originalLine,1173 originalColumn: mapping.originalColumn,1174 name: name1175 };1176 1177 this.__generatedMappings.push(adjustedMapping);1178 if (typeof adjustedMapping.originalLine === 'number') {1179 this.__originalMappings.push(adjustedMapping);1180 }1181 }1182 }1183 1184 quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated);1185 quickSort(this.__originalMappings, util.compareByOriginalPositions);1186 };1187 1188exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;1189 