blob: f3abade75f03316cc74230cd32b75e1c348a2d4c [file] [log] [blame]
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
if (typeof define !== 'function') {
var define = require('amdefine')(module, require);
}
define(function (require, exports, module) {
var util = require('./util');
function SourceMapConsumer(aSourceMap) {
var sourceMap = aSourceMap;
if (typeof aSourceMap === 'string') {
sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
}
// We do late requires because the subclasses require() this file.
if (sourceMap.sections != null) {
var indexedSourceMapConsumer = require('./indexed-source-map-consumer');
return new indexedSourceMapConsumer.IndexedSourceMapConsumer(sourceMap);
} else {
var basicSourceMapConsumer = require('./basic-source-map-consumer');
return new basicSourceMapConsumer.BasicSourceMapConsumer(sourceMap);
}
}
SourceMapConsumer.fromSourceMap = function(aSourceMap) {
var basicSourceMapConsumer = require('./basic-source-map-consumer');
return basicSourceMapConsumer.BasicSourceMapConsumer
.fromSourceMap(aSourceMap);
}
/**
* The version of the source mapping spec that we are consuming.
*/
SourceMapConsumer.prototype._version = 3;
// `__generatedMappings` and `__originalMappings` are arrays that hold the
// parsed mapping coordinates from the source map's "mappings" attribute. They
// are lazily instantiated, accessed via the `_generatedMappings` and
// `_originalMappings` getters respectively, and we only parse the mappings
// and create these arrays once queried for a source location. We jump through
// these hoops because there can be many thousands of mappings, and parsing
// them is expensive, so we only want to do it if we must.
//
// Each object in the arrays is of the form:
//
// {
// generatedLine: The line number in the generated code,
// generatedColumn: The column number in the generated code,
// source: The path to the original source file that generated this
// chunk of code,
// originalLine: The line number in the original source that
// corresponds to this chunk of generated code,
// originalColumn: The column number in the original source that
// corresponds to this chunk of generated code,
// name: The name of the original symbol which generated this chunk of
// code.
// }
//
// All properties except for `generatedLine` and `generatedColumn` can be
// `null`.
//
// `_generatedMappings` is ordered by the generated positions.
//
// `_originalMappings` is ordered by the original positions.
SourceMapConsumer.prototype.__generatedMappings = null;
Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
get: function () {
if (!this.__generatedMappings) {
this.__generatedMappings = [];
this.__originalMappings = [];
this._parseMappings(this._mappings, this.sourceRoot);
}
return this.__generatedMappings;
}
});
SourceMapConsumer.prototype.__originalMappings = null;
Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
get: function () {
if (!this.__originalMappings) {
this.__generatedMappings = [];
this.__originalMappings = [];
this._parseMappings(this._mappings, this.sourceRoot);
}
return this.__originalMappings;
}
});
SourceMapConsumer.prototype._nextCharIsMappingSeparator =
function SourceMapConsumer_nextCharIsMappingSeparator(aStr) {
var c = aStr.charAt(0);
return c === ";" || c === ",";
};
/**
* Parse the mappings in a string in to a data structure which we can easily
* query (the ordered arrays in the `this.__generatedMappings` and
* `this.__originalMappings` properties).
*/
SourceMapConsumer.prototype._parseMappings =
function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
throw new Error("Subclasses must implement _parseMappings");
};
SourceMapConsumer.GENERATED_ORDER = 1;
SourceMapConsumer.ORIGINAL_ORDER = 2;
/**
* Iterate over each mapping between an original source/line/column and a
* generated line/column in this source map.
*
* @param Function aCallback
* The function that is called with each mapping.
* @param Object aContext
* Optional. If specified, this object will be the value of `this` every
* time that `aCallback` is called.
* @param aOrder
* Either `SourceMapConsumer.GENERATED_ORDER` or
* `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
* iterate over the mappings sorted by the generated file's line/column
* order or the original's source/line/column order, respectively. Defaults to
* `SourceMapConsumer.GENERATED_ORDER`.
*/
SourceMapConsumer.prototype.eachMapping =
function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
var context = aContext || null;
var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
var mappings;
switch (order) {
case SourceMapConsumer.GENERATED_ORDER:
mappings = this._generatedMappings;
break;
case SourceMapConsumer.ORIGINAL_ORDER:
mappings = this._originalMappings;
break;
default:
throw new Error("Unknown order of iteration.");
}
var sourceRoot = this.sourceRoot;
mappings.map(function (mapping) {
var source = mapping.source;
if (source != null && sourceRoot != null) {
source = util.join(sourceRoot, source);
}
return {
source: source,
generatedLine: mapping.generatedLine,
generatedColumn: mapping.generatedColumn,
originalLine: mapping.originalLine,
originalColumn: mapping.originalColumn,
name: mapping.name
};
}).forEach(aCallback, context);
};
/**
* Returns all generated line and column information for the original source
* and line provided. The only argument is an object with the following
* properties:
*
* - source: The filename of the original source.
* - line: The line number in the original source.
*
* and an array of objects is returned, each with the following properties:
*
* - line: The line number in the generated source, or null.
* - column: The column number in the generated source, or null.
*/
SourceMapConsumer.prototype.allGeneratedPositionsFor =
function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
// When there is no exact match, BasicSourceMapConsumer.prototype._findMapping
// returns the index of the closest mapping less than the needle. By
// setting needle.originalColumn to Infinity, we thus find the last
// mapping for the given line, provided such a mapping exists.
var needle = {
source: util.getArg(aArgs, 'source'),
originalLine: util.getArg(aArgs, 'line'),
originalColumn: Infinity
};
if (this.sourceRoot != null) {
needle.source = util.relative(this.sourceRoot, needle.source);
}
var mappings = [];
var index = this._findMapping(needle,
this._originalMappings,
"originalLine",
"originalColumn",
util.compareByOriginalPositions);
if (index >= 0) {
var mapping = this._originalMappings[index];
while (mapping && mapping.originalLine === needle.originalLine) {
mappings.push({
line: util.getArg(mapping, 'generatedLine', null),
column: util.getArg(mapping, 'generatedColumn', null),
lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
});
mapping = this._originalMappings[--index];
}
}
return mappings.reverse();
};
exports.SourceMapConsumer = SourceMapConsumer;
});