1 /* -*- Mode: js; js-indent-level: 2; -*- */
3 * Copyright 2011 Mozilla Foundation and contributors
4 * Licensed under the New BSD license. See LICENSE or:
5 * http://opensource.org/licenses/BSD-3-Clause
7 if (typeof define !== 'function') {
8 var define = require('amdefine')(module, require);
10 define(function (require, exports, module) {
12 var util = require('./util');
14 function SourceMapConsumer(aSourceMap) {
15 var sourceMap = aSourceMap;
16 if (typeof aSourceMap === 'string') {
17 sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
20 // We do late requires because the subclasses require() this file.
21 if (sourceMap.sections != null) {
22 var indexedSourceMapConsumer = require('./indexed-source-map-consumer');
23 return new indexedSourceMapConsumer.IndexedSourceMapConsumer(sourceMap);
25 var basicSourceMapConsumer = require('./basic-source-map-consumer');
26 return new basicSourceMapConsumer.BasicSourceMapConsumer(sourceMap);
30 SourceMapConsumer.fromSourceMap = function(aSourceMap) {
31 var basicSourceMapConsumer = require('./basic-source-map-consumer');
32 return basicSourceMapConsumer.BasicSourceMapConsumer
33 .fromSourceMap(aSourceMap);
37 * The version of the source mapping spec that we are consuming.
39 SourceMapConsumer.prototype._version = 3;
42 // `__generatedMappings` and `__originalMappings` are arrays that hold the
43 // parsed mapping coordinates from the source map's "mappings" attribute. They
44 // are lazily instantiated, accessed via the `_generatedMappings` and
45 // `_originalMappings` getters respectively, and we only parse the mappings
46 // and create these arrays once queried for a source location. We jump through
47 // these hoops because there can be many thousands of mappings, and parsing
48 // them is expensive, so we only want to do it if we must.
50 // Each object in the arrays is of the form:
53 // generatedLine: The line number in the generated code,
54 // generatedColumn: The column number in the generated code,
55 // source: The path to the original source file that generated this
57 // originalLine: The line number in the original source that
58 // corresponds to this chunk of generated code,
59 // originalColumn: The column number in the original source that
60 // corresponds to this chunk of generated code,
61 // name: The name of the original symbol which generated this chunk of
65 // All properties except for `generatedLine` and `generatedColumn` can be
68 // `_generatedMappings` is ordered by the generated positions.
70 // `_originalMappings` is ordered by the original positions.
72 SourceMapConsumer.prototype.__generatedMappings = null;
73 Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
75 if (!this.__generatedMappings) {
76 this.__generatedMappings = [];
77 this.__originalMappings = [];
78 this._parseMappings(this._mappings, this.sourceRoot);
81 return this.__generatedMappings;
85 SourceMapConsumer.prototype.__originalMappings = null;
86 Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
88 if (!this.__originalMappings) {
89 this.__generatedMappings = [];
90 this.__originalMappings = [];
91 this._parseMappings(this._mappings, this.sourceRoot);
94 return this.__originalMappings;
98 SourceMapConsumer.prototype._nextCharIsMappingSeparator =
99 function SourceMapConsumer_nextCharIsMappingSeparator(aStr) {
100 var c = aStr.charAt(0);
101 return c === ";" || c === ",";
105 * Parse the mappings in a string in to a data structure which we can easily
106 * query (the ordered arrays in the `this.__generatedMappings` and
107 * `this.__originalMappings` properties).
109 SourceMapConsumer.prototype._parseMappings =
110 function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
111 throw new Error("Subclasses must implement _parseMappings");
114 SourceMapConsumer.GENERATED_ORDER = 1;
115 SourceMapConsumer.ORIGINAL_ORDER = 2;
118 * Iterate over each mapping between an original source/line/column and a
119 * generated line/column in this source map.
121 * @param Function aCallback
122 * The function that is called with each mapping.
123 * @param Object aContext
124 * Optional. If specified, this object will be the value of `this` every
125 * time that `aCallback` is called.
127 * Either `SourceMapConsumer.GENERATED_ORDER` or
128 * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
129 * iterate over the mappings sorted by the generated file's line/column
130 * order or the original's source/line/column order, respectively. Defaults to
131 * `SourceMapConsumer.GENERATED_ORDER`.
133 SourceMapConsumer.prototype.eachMapping =
134 function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
135 var context = aContext || null;
136 var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
140 case SourceMapConsumer.GENERATED_ORDER:
141 mappings = this._generatedMappings;
143 case SourceMapConsumer.ORIGINAL_ORDER:
144 mappings = this._originalMappings;
147 throw new Error("Unknown order of iteration.");
150 var sourceRoot = this.sourceRoot;
151 mappings.map(function (mapping) {
152 var source = mapping.source;
153 if (source != null && sourceRoot != null) {
154 source = util.join(sourceRoot, source);
158 generatedLine: mapping.generatedLine,
159 generatedColumn: mapping.generatedColumn,
160 originalLine: mapping.originalLine,
161 originalColumn: mapping.originalColumn,
164 }).forEach(aCallback, context);
168 * Returns all generated line and column information for the original source
169 * and line provided. The only argument is an object with the following
172 * - source: The filename of the original source.
173 * - line: The line number in the original source.
175 * and an array of objects is returned, each with the following properties:
177 * - line: The line number in the generated source, or null.
178 * - column: The column number in the generated source, or null.
180 SourceMapConsumer.prototype.allGeneratedPositionsFor =
181 function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
182 // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping
183 // returns the index of the closest mapping less than the needle. By
184 // setting needle.originalColumn to Infinity, we thus find the last
185 // mapping for the given line, provided such a mapping exists.
187 source: util.getArg(aArgs, 'source'),
188 originalLine: util.getArg(aArgs, 'line'),
189 originalColumn: Infinity
192 if (this.sourceRoot != null) {
193 needle.source = util.relative(this.sourceRoot, needle.source);
198 var index = this._findMapping(needle,
199 this._originalMappings,
202 util.compareByOriginalPositions);
204 var mapping = this._originalMappings[index];
206 while (mapping && mapping.originalLine === needle.originalLine) {
208 line: util.getArg(mapping, 'generatedLine', null),
209 column: util.getArg(mapping, 'generatedColumn', null),
210 lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
213 mapping = this._originalMappings[--index];
217 return mappings.reverse();
220 exports.SourceMapConsumer = SourceMapConsumer;