source: frontend/node_modules/source-map/lib/source-map-consumer.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 33.5 KB
RevLine 
[9af201e]1/* -*- Mode: js; js-indent-level: 2; -*- */
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
6 */
7
8const util = require("./util");
9const binarySearch = require("./binary-search");
10const ArraySet = require("./array-set").ArraySet;
11const base64VLQ = require("./base64-vlq"); // eslint-disable-line no-unused-vars
12const readWasm = require("../lib/read-wasm");
13const wasm = require("./wasm");
14
15const INTERNAL = Symbol("smcInternal");
16
17class SourceMapConsumer {
18 constructor(aSourceMap, aSourceMapURL) {
19 // If the constructor was called by super(), just return Promise<this>.
20 // Yes, this is a hack to retain the pre-existing API of the base-class
21 // constructor also being an async factory function.
22 if (aSourceMap == INTERNAL) {
23 return Promise.resolve(this);
24 }
25
26 return _factory(aSourceMap, aSourceMapURL);
27 }
28
29 static initialize(opts) {
30 readWasm.initialize(opts["lib/mappings.wasm"]);
31 }
32
33 static fromSourceMap(aSourceMap, aSourceMapURL) {
34 return _factoryBSM(aSourceMap, aSourceMapURL);
35 }
36
37 /**
38 * Construct a new `SourceMapConsumer` from `rawSourceMap` and `sourceMapUrl`
39 * (see the `SourceMapConsumer` constructor for details. Then, invoke the `async
40 * function f(SourceMapConsumer) -> T` with the newly constructed consumer, wait
41 * for `f` to complete, call `destroy` on the consumer, and return `f`'s return
42 * value.
43 *
44 * You must not use the consumer after `f` completes!
45 *
46 * By using `with`, you do not have to remember to manually call `destroy` on
47 * the consumer, since it will be called automatically once `f` completes.
48 *
49 * ```js
50 * const xSquared = await SourceMapConsumer.with(
51 * myRawSourceMap,
52 * null,
53 * async function (consumer) {
54 * // Use `consumer` inside here and don't worry about remembering
55 * // to call `destroy`.
56 *
57 * const x = await whatever(consumer);
58 * return x * x;
59 * }
60 * );
61 *
62 * // You may not use that `consumer` anymore out here; it has
63 * // been destroyed. But you can use `xSquared`.
64 * console.log(xSquared);
65 * ```
66 */
67 static async with(rawSourceMap, sourceMapUrl, f) {
68 const consumer = await new SourceMapConsumer(rawSourceMap, sourceMapUrl);
69 try {
70 return await f(consumer);
71 } finally {
72 consumer.destroy();
73 }
74 }
75
76 /**
77 * Iterate over each mapping between an original source/line/column and a
78 * generated line/column in this source map.
79 *
80 * @param Function aCallback
81 * The function that is called with each mapping.
82 * @param Object aContext
83 * Optional. If specified, this object will be the value of `this` every
84 * time that `aCallback` is called.
85 * @param aOrder
86 * Either `SourceMapConsumer.GENERATED_ORDER` or
87 * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
88 * iterate over the mappings sorted by the generated file's line/column
89 * order or the original's source/line/column order, respectively. Defaults to
90 * `SourceMapConsumer.GENERATED_ORDER`.
91 */
92 eachMapping(aCallback, aContext, aOrder) {
93 throw new Error("Subclasses must implement eachMapping");
94 }
95
96 /**
97 * Returns all generated line and column information for the original source,
98 * line, and column provided. If no column is provided, returns all mappings
99 * corresponding to a either the line we are searching for or the next
100 * closest line that has any mappings. Otherwise, returns all mappings
101 * corresponding to the given line and either the column we are searching for
102 * or the next closest column that has any offsets.
103 *
104 * The only argument is an object with the following properties:
105 *
106 * - source: The filename of the original source.
107 * - line: The line number in the original source. The line number is 1-based.
108 * - column: Optional. the column number in the original source.
109 * The column number is 0-based.
110 *
111 * and an array of objects is returned, each with the following properties:
112 *
113 * - line: The line number in the generated source, or null. The
114 * line number is 1-based.
115 * - column: The column number in the generated source, or null.
116 * The column number is 0-based.
117 */
118 allGeneratedPositionsFor(aArgs) {
119 throw new Error("Subclasses must implement allGeneratedPositionsFor");
120 }
121
122 destroy() {
123 throw new Error("Subclasses must implement destroy");
124 }
125}
126
127/**
128 * The version of the source mapping spec that we are consuming.
129 */
130SourceMapConsumer.prototype._version = 3;
131SourceMapConsumer.GENERATED_ORDER = 1;
132SourceMapConsumer.ORIGINAL_ORDER = 2;
133
134SourceMapConsumer.GREATEST_LOWER_BOUND = 1;
135SourceMapConsumer.LEAST_UPPER_BOUND = 2;
136
137exports.SourceMapConsumer = SourceMapConsumer;
138
139/**
140 * A BasicSourceMapConsumer instance represents a parsed source map which we can
141 * query for information about the original file positions by giving it a file
142 * position in the generated source.
143 *
144 * The first parameter is the raw source map (either as a JSON string, or
145 * already parsed to an object). According to the spec, source maps have the
146 * following attributes:
147 *
148 * - version: Which version of the source map spec this map is following.
149 * - sources: An array of URLs to the original source files.
150 * - names: An array of identifiers which can be referenced by individual mappings.
151 * - sourceRoot: Optional. The URL root from which all sources are relative.
152 * - sourcesContent: Optional. An array of contents of the original source files.
153 * - mappings: A string of base64 VLQs which contain the actual mappings.
154 * - file: Optional. The generated file this source map is associated with.
155 *
156 * Here is an example source map, taken from the source map spec[0]:
157 *
158 * {
159 * version : 3,
160 * file: "out.js",
161 * sourceRoot : "",
162 * sources: ["foo.js", "bar.js"],
163 * names: ["src", "maps", "are", "fun"],
164 * mappings: "AA,AB;;ABCDE;"
165 * }
166 *
167 * The second parameter, if given, is a string whose value is the URL
168 * at which the source map was found. This URL is used to compute the
169 * sources array.
170 *
171 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
172 */
173class BasicSourceMapConsumer extends SourceMapConsumer {
174 constructor(aSourceMap, aSourceMapURL) {
175 return super(INTERNAL).then(that => {
176 let sourceMap = aSourceMap;
177 if (typeof aSourceMap === "string") {
178 sourceMap = util.parseSourceMapInput(aSourceMap);
179 }
180
181 const version = util.getArg(sourceMap, "version");
182 const sources = util.getArg(sourceMap, "sources").map(String);
183 // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
184 // requires the array) to play nice here.
185 const names = util.getArg(sourceMap, "names", []);
186 const sourceRoot = util.getArg(sourceMap, "sourceRoot", null);
187 const sourcesContent = util.getArg(sourceMap, "sourcesContent", null);
188 const mappings = util.getArg(sourceMap, "mappings");
189 const file = util.getArg(sourceMap, "file", null);
190 const x_google_ignoreList = util.getArg(
191 sourceMap,
192 "x_google_ignoreList",
193 null
194 );
195
196 // Once again, Sass deviates from the spec and supplies the version as a
197 // string rather than a number, so we use loose equality checking here.
198 if (version != that._version) {
199 throw new Error("Unsupported version: " + version);
200 }
201
202 that._sourceLookupCache = new Map();
203
204 // Pass `true` below to allow duplicate names and sources. While source maps
205 // are intended to be compressed and deduplicated, the TypeScript compiler
206 // sometimes generates source maps with duplicates in them. See Github issue
207 // #72 and bugzil.la/889492.
208 that._names = ArraySet.fromArray(names.map(String), true);
209 that._sources = ArraySet.fromArray(sources, true);
210
211 that._absoluteSources = ArraySet.fromArray(
212 that._sources.toArray().map(function (s) {
213 return util.computeSourceURL(sourceRoot, s, aSourceMapURL);
214 }),
215 true
216 );
217
218 that.sourceRoot = sourceRoot;
219 that.sourcesContent = sourcesContent;
220 that._mappings = mappings;
221 that._sourceMapURL = aSourceMapURL;
222 that.file = file;
223 that.x_google_ignoreList = x_google_ignoreList;
224
225 that._computedColumnSpans = false;
226 that._mappingsPtr = 0;
227 that._wasm = null;
228
229 return wasm().then(w => {
230 that._wasm = w;
231 return that;
232 });
233 });
234 }
235
236 /**
237 * Utility function to find the index of a source. Returns -1 if not
238 * found.
239 */
240 _findSourceIndex(aSource) {
241 // In the most common usecases, we'll be constantly looking up the index for the same source
242 // files, so we cache the index lookup to avoid constantly recomputing the full URLs.
243 const cachedIndex = this._sourceLookupCache.get(aSource);
244 if (typeof cachedIndex === "number") {
245 return cachedIndex;
246 }
247
248 // Treat the source as map-relative overall by default.
249 const sourceAsMapRelative = util.computeSourceURL(
250 null,
251 aSource,
252 this._sourceMapURL
253 );
254 if (this._absoluteSources.has(sourceAsMapRelative)) {
255 const index = this._absoluteSources.indexOf(sourceAsMapRelative);
256 this._sourceLookupCache.set(aSource, index);
257 return index;
258 }
259
260 // Fall back to treating the source as sourceRoot-relative.
261 const sourceAsSourceRootRelative = util.computeSourceURL(
262 this.sourceRoot,
263 aSource,
264 this._sourceMapURL
265 );
266 if (this._absoluteSources.has(sourceAsSourceRootRelative)) {
267 const index = this._absoluteSources.indexOf(sourceAsSourceRootRelative);
268 this._sourceLookupCache.set(aSource, index);
269 return index;
270 }
271
272 // To avoid this cache growing forever, we do not cache lookup misses.
273 return -1;
274 }
275
276 /**
277 * Create a BasicSourceMapConsumer from a SourceMapGenerator.
278 *
279 * @param SourceMapGenerator aSourceMap
280 * The source map that will be consumed.
281 * @param String aSourceMapURL
282 * The URL at which the source map can be found (optional)
283 * @returns BasicSourceMapConsumer
284 */
285 static fromSourceMap(aSourceMap, aSourceMapURL) {
286 return new BasicSourceMapConsumer(aSourceMap.toString());
287 }
288
289 get sources() {
290 return this._absoluteSources.toArray();
291 }
292
293 _getMappingsPtr() {
294 if (this._mappingsPtr === 0) {
295 this._parseMappings();
296 }
297
298 return this._mappingsPtr;
299 }
300
301 /**
302 * Parse the mappings in a string in to a data structure which we can easily
303 * query (the ordered arrays in the `this.__generatedMappings` and
304 * `this.__originalMappings` properties).
305 */
306 _parseMappings() {
307 const aStr = this._mappings;
308 const size = aStr.length;
309
310 // Interpret signed result of allocate_mappings as unsigned, otherwise
311 // addresses higher than 2GB will be negative.
312 const mappingsBufPtr = this._wasm.exports.allocate_mappings(size) >>> 0;
313 const mappingsBuf = new Uint8Array(
314 this._wasm.exports.memory.buffer,
315 mappingsBufPtr,
316 size
317 );
318 for (let i = 0; i < size; i++) {
319 mappingsBuf[i] = aStr.charCodeAt(i);
320 }
321
322 const mappingsPtr = this._wasm.exports.parse_mappings(mappingsBufPtr);
323
324 if (!mappingsPtr) {
325 const error = this._wasm.exports.get_last_error();
326 let msg = `Error parsing mappings (code ${error}): `;
327
328 // XXX: keep these error codes in sync with `wasm-mappings`.
329 switch (error) {
330 case 1:
331 msg +=
332 "the mappings contained a negative line, column, source index, or name index";
333 break;
334 case 2:
335 msg += "the mappings contained a number larger than 2**32";
336 break;
337 case 3:
338 msg += "reached EOF while in the middle of parsing a VLQ";
339 break;
340 case 4:
341 msg += "invalid base 64 character while parsing a VLQ";
342 break;
343 default:
344 msg += "unknown error code";
345 break;
346 }
347
348 throw new Error(msg);
349 }
350
351 this._mappingsPtr = mappingsPtr;
352 }
353
354 eachMapping(aCallback, aContext, aOrder) {
355 const context = aContext || null;
356 const order = aOrder || SourceMapConsumer.GENERATED_ORDER;
357
358 this._wasm.withMappingCallback(
359 mapping => {
360 if (mapping.source !== null) {
361 mapping.source = this._absoluteSources.at(mapping.source);
362
363 if (mapping.name !== null) {
364 mapping.name = this._names.at(mapping.name);
365 }
366 }
367 if (this._computedColumnSpans && mapping.lastGeneratedColumn === null) {
368 mapping.lastGeneratedColumn = Infinity;
369 }
370
371 aCallback.call(context, mapping);
372 },
373 () => {
374 switch (order) {
375 case SourceMapConsumer.GENERATED_ORDER:
376 this._wasm.exports.by_generated_location(this._getMappingsPtr());
377 break;
378 case SourceMapConsumer.ORIGINAL_ORDER:
379 this._wasm.exports.by_original_location(this._getMappingsPtr());
380 break;
381 default:
382 throw new Error("Unknown order of iteration.");
383 }
384 }
385 );
386 }
387
388 allGeneratedPositionsFor(aArgs) {
389 let source = util.getArg(aArgs, "source");
390 const originalLine = util.getArg(aArgs, "line");
391 const originalColumn = aArgs.column || 0;
392
393 source = this._findSourceIndex(source);
394 if (source < 0) {
395 return [];
396 }
397
398 if (originalLine < 1) {
399 throw new Error("Line numbers must be >= 1");
400 }
401
402 if (originalColumn < 0) {
403 throw new Error("Column numbers must be >= 0");
404 }
405
406 const mappings = [];
407
408 this._wasm.withMappingCallback(
409 m => {
410 let lastColumn = m.lastGeneratedColumn;
411 if (this._computedColumnSpans && lastColumn === null) {
412 lastColumn = Infinity;
413 }
414 mappings.push({
415 line: m.generatedLine,
416 column: m.generatedColumn,
417 lastColumn,
418 });
419 },
420 () => {
421 this._wasm.exports.all_generated_locations_for(
422 this._getMappingsPtr(),
423 source,
424 originalLine - 1,
425 "column" in aArgs,
426 originalColumn
427 );
428 }
429 );
430
431 return mappings;
432 }
433
434 destroy() {
435 if (this._mappingsPtr !== 0) {
436 this._wasm.exports.free_mappings(this._mappingsPtr);
437 this._mappingsPtr = 0;
438 }
439 }
440
441 /**
442 * Compute the last column for each generated mapping. The last column is
443 * inclusive.
444 */
445 computeColumnSpans() {
446 if (this._computedColumnSpans) {
447 return;
448 }
449
450 this._wasm.exports.compute_column_spans(this._getMappingsPtr());
451 this._computedColumnSpans = true;
452 }
453
454 /**
455 * Returns the original source, line, and column information for the generated
456 * source's line and column positions provided. The only argument is an object
457 * with the following properties:
458 *
459 * - line: The line number in the generated source. The line number
460 * is 1-based.
461 * - column: The column number in the generated source. The column
462 * number is 0-based.
463 * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
464 * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
465 * closest element that is smaller than or greater than the one we are
466 * searching for, respectively, if the exact element cannot be found.
467 * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.
468 *
469 * and an object is returned with the following properties:
470 *
471 * - source: The original source file, or null.
472 * - line: The line number in the original source, or null. The
473 * line number is 1-based.
474 * - column: The column number in the original source, or null. The
475 * column number is 0-based.
476 * - name: The original identifier, or null.
477 */
478 originalPositionFor(aArgs) {
479 const needle = {
480 generatedLine: util.getArg(aArgs, "line"),
481 generatedColumn: util.getArg(aArgs, "column"),
482 };
483
484 if (needle.generatedLine < 1) {
485 throw new Error("Line numbers must be >= 1");
486 }
487
488 if (needle.generatedColumn < 0) {
489 throw new Error("Column numbers must be >= 0");
490 }
491
492 let bias = util.getArg(
493 aArgs,
494 "bias",
495 SourceMapConsumer.GREATEST_LOWER_BOUND
496 );
497 if (bias == null) {
498 bias = SourceMapConsumer.GREATEST_LOWER_BOUND;
499 }
500
501 let mapping;
502 this._wasm.withMappingCallback(
503 m => (mapping = m),
504 () => {
505 this._wasm.exports.original_location_for(
506 this._getMappingsPtr(),
507 needle.generatedLine - 1,
508 needle.generatedColumn,
509 bias
510 );
511 }
512 );
513
514 if (mapping) {
515 if (mapping.generatedLine === needle.generatedLine) {
516 let source = util.getArg(mapping, "source", null);
517 if (source !== null) {
518 source = this._absoluteSources.at(source);
519 }
520
521 let name = util.getArg(mapping, "name", null);
522 if (name !== null) {
523 name = this._names.at(name);
524 }
525
526 return {
527 source,
528 line: util.getArg(mapping, "originalLine", null),
529 column: util.getArg(mapping, "originalColumn", null),
530 name,
531 };
532 }
533 }
534
535 return {
536 source: null,
537 line: null,
538 column: null,
539 name: null,
540 };
541 }
542
543 /**
544 * Return true if we have the source content for every source in the source
545 * map, false otherwise.
546 */
547 hasContentsOfAllSources() {
548 if (!this.sourcesContent) {
549 return false;
550 }
551 return (
552 this.sourcesContent.length >= this._sources.size() &&
553 !this.sourcesContent.some(function (sc) {
554 return sc == null;
555 })
556 );
557 }
558
559 /**
560 * Returns the original source content. The only argument is the url of the
561 * original source file. Returns null if no original source content is
562 * available.
563 */
564 sourceContentFor(aSource, nullOnMissing) {
565 if (!this.sourcesContent) {
566 return null;
567 }
568
569 const index = this._findSourceIndex(aSource);
570 if (index >= 0) {
571 return this.sourcesContent[index];
572 }
573
574 // This function is used recursively from
575 // IndexedSourceMapConsumer.prototype.sourceContentFor. In that case, we
576 // don't want to throw if we can't find the source - we just want to
577 // return null, so we provide a flag to exit gracefully.
578 if (nullOnMissing) {
579 return null;
580 }
581
582 throw new Error('"' + aSource + '" is not in the SourceMap.');
583 }
584
585 /**
586 * Returns the generated line and column information for the original source,
587 * line, and column positions provided. The only argument is an object with
588 * the following properties:
589 *
590 * - source: The filename of the original source.
591 * - line: The line number in the original source. The line number
592 * is 1-based.
593 * - column: The column number in the original source. The column
594 * number is 0-based.
595 * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
596 * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
597 * closest element that is smaller than or greater than the one we are
598 * searching for, respectively, if the exact element cannot be found.
599 * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.
600 *
601 * and an object is returned with the following properties:
602 *
603 * - line: The line number in the generated source, or null. The
604 * line number is 1-based.
605 * - column: The column number in the generated source, or null.
606 * The column number is 0-based.
607 */
608 generatedPositionFor(aArgs) {
609 let source = util.getArg(aArgs, "source");
610 source = this._findSourceIndex(source);
611 if (source < 0) {
612 return {
613 line: null,
614 column: null,
615 lastColumn: null,
616 };
617 }
618
619 const needle = {
620 source,
621 originalLine: util.getArg(aArgs, "line"),
622 originalColumn: util.getArg(aArgs, "column"),
623 };
624
625 if (needle.originalLine < 1) {
626 throw new Error("Line numbers must be >= 1");
627 }
628
629 if (needle.originalColumn < 0) {
630 throw new Error("Column numbers must be >= 0");
631 }
632
633 let bias = util.getArg(
634 aArgs,
635 "bias",
636 SourceMapConsumer.GREATEST_LOWER_BOUND
637 );
638 if (bias == null) {
639 bias = SourceMapConsumer.GREATEST_LOWER_BOUND;
640 }
641
642 let mapping;
643 this._wasm.withMappingCallback(
644 m => (mapping = m),
645 () => {
646 this._wasm.exports.generated_location_for(
647 this._getMappingsPtr(),
648 needle.source,
649 needle.originalLine - 1,
650 needle.originalColumn,
651 bias
652 );
653 }
654 );
655
656 if (mapping) {
657 if (mapping.source === needle.source) {
658 let lastColumn = mapping.lastGeneratedColumn;
659 if (this._computedColumnSpans && lastColumn === null) {
660 lastColumn = Infinity;
661 }
662 return {
663 line: util.getArg(mapping, "generatedLine", null),
664 column: util.getArg(mapping, "generatedColumn", null),
665 lastColumn,
666 };
667 }
668 }
669
670 return {
671 line: null,
672 column: null,
673 lastColumn: null,
674 };
675 }
676}
677
678BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;
679exports.BasicSourceMapConsumer = BasicSourceMapConsumer;
680
681/**
682 * An IndexedSourceMapConsumer instance represents a parsed source map which
683 * we can query for information. It differs from BasicSourceMapConsumer in
684 * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
685 * input.
686 *
687 * The first parameter is a raw source map (either as a JSON string, or already
688 * parsed to an object). According to the spec for indexed source maps, they
689 * have the following attributes:
690 *
691 * - version: Which version of the source map spec this map is following.
692 * - file: Optional. The generated file this source map is associated with.
693 * - sections: A list of section definitions.
694 *
695 * Each value under the "sections" field has two fields:
696 * - offset: The offset into the original specified at which this section
697 * begins to apply, defined as an object with a "line" and "column"
698 * field.
699 * - map: A source map definition. This source map could also be indexed,
700 * but doesn't have to be.
701 *
702 * Instead of the "map" field, it's also possible to have a "url" field
703 * specifying a URL to retrieve a source map from, but that's currently
704 * unsupported.
705 *
706 * Here's an example source map, taken from the source map spec[0], but
707 * modified to omit a section which uses the "url" field.
708 *
709 * {
710 * version : 3,
711 * file: "app.js",
712 * sections: [{
713 * offset: {line:100, column:10},
714 * map: {
715 * version : 3,
716 * file: "section.js",
717 * sources: ["foo.js", "bar.js"],
718 * names: ["src", "maps", "are", "fun"],
719 * mappings: "AAAA,E;;ABCDE;"
720 * }
721 * }],
722 * }
723 *
724 * The second parameter, if given, is a string whose value is the URL
725 * at which the source map was found. This URL is used to compute the
726 * sources array.
727 *
728 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
729 */
730class IndexedSourceMapConsumer extends SourceMapConsumer {
731 constructor(aSourceMap, aSourceMapURL) {
732 return super(INTERNAL).then(that => {
733 let sourceMap = aSourceMap;
734 if (typeof aSourceMap === "string") {
735 sourceMap = util.parseSourceMapInput(aSourceMap);
736 }
737
738 const version = util.getArg(sourceMap, "version");
739 const sections = util.getArg(sourceMap, "sections");
740
741 if (version != that._version) {
742 throw new Error("Unsupported version: " + version);
743 }
744
745 let lastOffset = {
746 line: -1,
747 column: 0,
748 };
749 return Promise.all(
750 sections.map(s => {
751 if (s.url) {
752 // The url field will require support for asynchronicity.
753 // See https://github.com/mozilla/source-map/issues/16
754 throw new Error(
755 "Support for url field in sections not implemented."
756 );
757 }
758 const offset = util.getArg(s, "offset");
759 const offsetLine = util.getArg(offset, "line");
760 const offsetColumn = util.getArg(offset, "column");
761
762 if (
763 offsetLine < lastOffset.line ||
764 (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)
765 ) {
766 throw new Error(
767 "Section offsets must be ordered and non-overlapping."
768 );
769 }
770 lastOffset = offset;
771
772 const cons = new SourceMapConsumer(
773 util.getArg(s, "map"),
774 aSourceMapURL
775 );
776 return cons.then(consumer => {
777 return {
778 generatedOffset: {
779 // The offset fields are 0-based, but we use 1-based indices when
780 // encoding/decoding from VLQ.
781 generatedLine: offsetLine + 1,
782 generatedColumn: offsetColumn + 1,
783 },
784 consumer,
785 };
786 });
787 })
788 ).then(s => {
789 that._sections = s;
790 return that;
791 });
792 });
793 }
794
795 /**
796 * The list of original sources.
797 */
798 get sources() {
799 const sources = [];
800 for (let i = 0; i < this._sections.length; i++) {
801 for (let j = 0; j < this._sections[i].consumer.sources.length; j++) {
802 sources.push(this._sections[i].consumer.sources[j]);
803 }
804 }
805 return sources;
806 }
807
808 /**
809 * Returns the original source, line, and column information for the generated
810 * source's line and column positions provided. The only argument is an object
811 * with the following properties:
812 *
813 * - line: The line number in the generated source. The line number
814 * is 1-based.
815 * - column: The column number in the generated source. The column
816 * number is 0-based.
817 *
818 * and an object is returned with the following properties:
819 *
820 * - source: The original source file, or null.
821 * - line: The line number in the original source, or null. The
822 * line number is 1-based.
823 * - column: The column number in the original source, or null. The
824 * column number is 0-based.
825 * - name: The original identifier, or null.
826 */
827 originalPositionFor(aArgs) {
828 const needle = {
829 generatedLine: util.getArg(aArgs, "line"),
830 generatedColumn: util.getArg(aArgs, "column"),
831 };
832
833 // Find the section containing the generated position we're trying to map
834 // to an original position.
835 const sectionIndex = binarySearch.search(
836 needle,
837 this._sections,
838 function (aNeedle, section) {
839 const cmp =
840 aNeedle.generatedLine - section.generatedOffset.generatedLine;
841 if (cmp) {
842 return cmp;
843 }
844
845 // The generated column is 0-based, but the section offset column is
846 // stored 1-based.
847 return (
848 aNeedle.generatedColumn -
849 (section.generatedOffset.generatedColumn - 1)
850 );
851 }
852 );
853 const section = this._sections[sectionIndex];
854
855 if (!section) {
856 return {
857 source: null,
858 line: null,
859 column: null,
860 name: null,
861 };
862 }
863
864 return section.consumer.originalPositionFor({
865 line: needle.generatedLine - (section.generatedOffset.generatedLine - 1),
866 column:
867 needle.generatedColumn -
868 (section.generatedOffset.generatedLine === needle.generatedLine
869 ? section.generatedOffset.generatedColumn - 1
870 : 0),
871 bias: aArgs.bias,
872 });
873 }
874
875 /**
876 * Return true if we have the source content for every source in the source
877 * map, false otherwise.
878 */
879 hasContentsOfAllSources() {
880 return this._sections.every(function (s) {
881 return s.consumer.hasContentsOfAllSources();
882 });
883 }
884
885 /**
886 * Returns the original source content. The only argument is the url of the
887 * original source file. Returns null if no original source content is
888 * available.
889 */
890 sourceContentFor(aSource, nullOnMissing) {
891 for (let i = 0; i < this._sections.length; i++) {
892 const section = this._sections[i];
893
894 const content = section.consumer.sourceContentFor(aSource, true);
895 if (content) {
896 return content;
897 }
898 }
899 if (nullOnMissing) {
900 return null;
901 }
902 throw new Error('"' + aSource + '" is not in the SourceMap.');
903 }
904
905 _findSectionIndex(source) {
906 for (let i = 0; i < this._sections.length; i++) {
907 const { consumer } = this._sections[i];
908 if (consumer._findSourceIndex(source) !== -1) {
909 return i;
910 }
911 }
912 return -1;
913 }
914
915 /**
916 * Returns the generated line and column information for the original source,
917 * line, and column positions provided. The only argument is an object with
918 * the following properties:
919 *
920 * - source: The filename of the original source.
921 * - line: The line number in the original source. The line number
922 * is 1-based.
923 * - column: The column number in the original source. The column
924 * number is 0-based.
925 *
926 * and an object is returned with the following properties:
927 *
928 * - line: The line number in the generated source, or null. The
929 * line number is 1-based.
930 * - column: The column number in the generated source, or null.
931 * The column number is 0-based.
932 */
933 generatedPositionFor(aArgs) {
934 const index = this._findSectionIndex(util.getArg(aArgs, "source"));
935 const section = index >= 0 ? this._sections[index] : null;
936 const nextSection =
937 index >= 0 && index + 1 < this._sections.length
938 ? this._sections[index + 1]
939 : null;
940
941 const generatedPosition =
942 section && section.consumer.generatedPositionFor(aArgs);
943 if (generatedPosition && generatedPosition.line !== null) {
944 const lineShift = section.generatedOffset.generatedLine - 1;
945 const columnShift = section.generatedOffset.generatedColumn - 1;
946
947 if (generatedPosition.line === 1) {
948 generatedPosition.column += columnShift;
949 if (typeof generatedPosition.lastColumn === "number") {
950 generatedPosition.lastColumn += columnShift;
951 }
952 }
953
954 if (
955 generatedPosition.lastColumn === Infinity &&
956 nextSection &&
957 generatedPosition.line === nextSection.generatedOffset.generatedLine
958 ) {
959 generatedPosition.lastColumn =
960 nextSection.generatedOffset.generatedColumn - 2;
961 }
962 generatedPosition.line += lineShift;
963
964 return generatedPosition;
965 }
966
967 return {
968 line: null,
969 column: null,
970 lastColumn: null,
971 };
972 }
973
974 allGeneratedPositionsFor(aArgs) {
975 const index = this._findSectionIndex(util.getArg(aArgs, "source"));
976 const section = index >= 0 ? this._sections[index] : null;
977 const nextSection =
978 index >= 0 && index + 1 < this._sections.length
979 ? this._sections[index + 1]
980 : null;
981
982 if (!section) return [];
983
984 return section.consumer
985 .allGeneratedPositionsFor(aArgs)
986 .map(generatedPosition => {
987 const lineShift = section.generatedOffset.generatedLine - 1;
988 const columnShift = section.generatedOffset.generatedColumn - 1;
989
990 if (generatedPosition.line === 1) {
991 generatedPosition.column += columnShift;
992 if (typeof generatedPosition.lastColumn === "number") {
993 generatedPosition.lastColumn += columnShift;
994 }
995 }
996
997 if (
998 generatedPosition.lastColumn === Infinity &&
999 nextSection &&
1000 generatedPosition.line === nextSection.generatedOffset.generatedLine
1001 ) {
1002 generatedPosition.lastColumn =
1003 nextSection.generatedOffset.generatedColumn - 2;
1004 }
1005 generatedPosition.line += lineShift;
1006
1007 return generatedPosition;
1008 });
1009 }
1010
1011 eachMapping(aCallback, aContext, aOrder) {
1012 this._sections.forEach((section, index) => {
1013 const nextSection =
1014 index + 1 < this._sections.length ? this._sections[index + 1] : null;
1015 const { generatedOffset } = section;
1016
1017 const lineShift = generatedOffset.generatedLine - 1;
1018 const columnShift = generatedOffset.generatedColumn - 1;
1019
1020 section.consumer.eachMapping(
1021 function (mapping) {
1022 if (mapping.generatedLine === 1) {
1023 mapping.generatedColumn += columnShift;
1024
1025 if (typeof mapping.lastGeneratedColumn === "number") {
1026 mapping.lastGeneratedColumn += columnShift;
1027 }
1028 }
1029
1030 if (
1031 mapping.lastGeneratedColumn === Infinity &&
1032 nextSection &&
1033 mapping.generatedLine === nextSection.generatedOffset.generatedLine
1034 ) {
1035 mapping.lastGeneratedColumn =
1036 nextSection.generatedOffset.generatedColumn - 2;
1037 }
1038 mapping.generatedLine += lineShift;
1039
1040 aCallback.call(this, mapping);
1041 },
1042 aContext,
1043 aOrder
1044 );
1045 });
1046 }
1047
1048 computeColumnSpans() {
1049 for (let i = 0; i < this._sections.length; i++) {
1050 this._sections[i].consumer.computeColumnSpans();
1051 }
1052 }
1053
1054 destroy() {
1055 for (let i = 0; i < this._sections.length; i++) {
1056 this._sections[i].consumer.destroy();
1057 }
1058 }
1059}
1060exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
1061
1062/*
1063 * Cheat to get around inter-twingled classes. `factory()` can be at the end
1064 * where it has access to non-hoisted classes, but it gets hoisted itself.
1065 */
1066function _factory(aSourceMap, aSourceMapURL) {
1067 let sourceMap = aSourceMap;
1068 if (typeof aSourceMap === "string") {
1069 sourceMap = util.parseSourceMapInput(aSourceMap);
1070 }
1071
1072 const consumer =
1073 sourceMap.sections != null
1074 ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
1075 : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
1076 return Promise.resolve(consumer);
1077}
1078
1079function _factoryBSM(aSourceMap, aSourceMapURL) {
1080 return BasicSourceMapConsumer.fromSourceMap(aSourceMap, aSourceMapURL);
1081}
Note: See TracBrowser for help on using the repository browser.