Bug 1149115 - Merge the latest version of the source-map library with fx-team.
authorEddy Bruël <ejpbruel@gmail.com>
Fri, 08 May 2015 17:03:39 +0200
changeset 274502 55dd223e022ea3901fc605de7c905eae8cd04e3f
parent 274501 aadbe230abece6a89dd3fca6d86a225f1f3dc19d
child 274503 ff1acf6219d7a0e561b774447a4116b89419016f
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1149115
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1149115 - Merge the latest version of the source-map library with fx-team.
toolkit/devtools/sourcemap/SourceMap.jsm
toolkit/devtools/sourcemap/source-map.js
toolkit/devtools/sourcemap/tests/unit/Utils.jsm
toolkit/devtools/sourcemap/tests/unit/test_source_map_consumer.js
toolkit/devtools/sourcemap/tests/unit/test_util.js
--- a/toolkit/devtools/sourcemap/SourceMap.jsm
+++ b/toolkit/devtools/sourcemap/SourceMap.jsm
@@ -177,39 +177,43 @@ define('source-map/source-map-consumer',
           name: mapping.name
         };
       }).forEach(aCallback, context);
     };
 
   /**
    * Returns all generated line and column information for the original source,
    * line, and column provided. If no column is provided, returns all mappings
-   * corresponding to a single line. Otherwise, returns all mappings
-   * corresponding to a single line and column.
+   * corresponding to a either the line we are searching for or the next
+   * closest line that has any mappings. Otherwise, returns all mappings
+   * corresponding to the given line and either the column we are searching for
+   * or the next closest column that has any offsets.
    *
    * 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.
    *   - column: Optional. the column 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) {
+      var line = util.getArg(aArgs, 'line');
+
       // 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 0, 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'),
+        originalLine: line,
         originalColumn: util.getArg(aArgs, 'column', 0)
       };
 
       if (this.sourceRoot != null) {
         needle.source = util.relative(this.sourceRoot, needle.source);
       }
 
       var mappings = [];
@@ -217,32 +221,51 @@ define('source-map/source-map-consumer',
       var index = this._findMapping(needle,
                                     this._originalMappings,
                                     "originalLine",
                                     "originalColumn",
                                     util.compareByOriginalPositions,
                                     binarySearch.LEAST_UPPER_BOUND);
       if (index >= 0) {
         var mapping = this._originalMappings[index];
-        var originalLine = mapping.originalLine;
-        var originalColumn = mapping.originalColumn;
+
+        if (aArgs.column === undefined) {
+          var originalLine = mapping.originalLine;
+
+          // Iterate until either we run out of mappings, or we run into
+          // a mapping for a different line than the one we found. Since
+          // mappings are sorted, this is guaranteed to find all mappings for
+          // the line we found.
+          while (mapping && mapping.originalLine === originalLine) {
+            mappings.push({
+              line: util.getArg(mapping, 'generatedLine', null),
+              column: util.getArg(mapping, 'generatedColumn', null),
+              lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
+            });
 
-        // Iterate until either we run out of mappings, or we run into
-        // a mapping for a different line. Since mappings are sorted, this is
-        // guaranteed to find all mappings for the line we are searching for.
-        while (mapping && mapping.originalLine === originalLine &&
-               (aArgs.column === undefined ||
-                mapping.originalColumn === originalColumn)) {
-          mappings.push({
-            line: util.getArg(mapping, 'generatedLine', null),
-            column: util.getArg(mapping, 'generatedColumn', null),
-            lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
-          });
+            mapping = this._originalMappings[++index];
+          }
+        } else {
+          var originalColumn = mapping.originalColumn;
 
-          mapping = this._originalMappings[++index];
+          // Iterate until either we run out of mappings, or we run into
+          // a mapping for a different line than the one we were searching for.
+          // Since mappings are sorted, this is guaranteed to find all mappings for
+          // the line we are searching for.
+          while (mapping &&
+                 mapping.originalLine === line &&
+                 mapping.originalColumn == originalColumn) {
+            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;
     };
 
   exports.SourceMapConsumer = SourceMapConsumer;
 
@@ -1165,25 +1188,40 @@ define('source-map/util', ['require', 'e
    */
   function relative(aRoot, aPath) {
     if (aRoot === "") {
       aRoot = ".";
     }
 
     aRoot = aRoot.replace(/\/$/, '');
 
-    // XXX: It is possible to remove this block, and the tests still pass!
-    var url = urlParse(aRoot);
-    if (aPath.charAt(0) == "/" && url && url.path == "/") {
-      return aPath.slice(1);
+    // It is possible for the path to be above the root. In this case, simply
+    // checking whether the root is a prefix of the path won't work. Instead, we
+    // need to remove components from the root one by one, until either we find
+    // a prefix that fits, or we run out of components to remove.
+    var level = 0;
+    while (aPath.indexOf(aRoot + '/') !== 0) {
+      var index = aRoot.lastIndexOf("/");
+      if (index < 0) {
+        return aPath;
+      }
+
+      // If the only part of the root that is left is the scheme (i.e. http://,
+      // file:///, etc.), one or more slashes (/), or simply nothing at all, we
+      // have exhausted all components, so the path is not relative to the root.
+      aRoot = aRoot.slice(0, index);
+      if (aRoot.match(/^([^\/]+:\/)\/*$/)) {
+        return aPath;
+      }
+
+      ++level;
     }
 
-    return aPath.indexOf(aRoot + '/') === 0
-      ? aPath.substr(aRoot.length + 1)
-      : aPath;
+    // Make sure we add a "../" for each component we removed from the root.
+    return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
   }
   exports.relative = relative;
 
   /**
    * Because behavior goes wacky when you set `__proto__` on objects, we
    * have to prefix all the strings in our set with an arbitrary character.
    *
    * See https://github.com/mozilla/source-map/pull/31 and
--- a/toolkit/devtools/sourcemap/source-map.js
+++ b/toolkit/devtools/sourcemap/source-map.js
@@ -916,25 +916,40 @@ define('source-map/util', ['require', 'e
    */
   function relative(aRoot, aPath) {
     if (aRoot === "") {
       aRoot = ".";
     }
 
     aRoot = aRoot.replace(/\/$/, '');
 
-    // XXX: It is possible to remove this block, and the tests still pass!
-    var url = urlParse(aRoot);
-    if (aPath.charAt(0) == "/" && url && url.path == "/") {
-      return aPath.slice(1);
+    // It is possible for the path to be above the root. In this case, simply
+    // checking whether the root is a prefix of the path won't work. Instead, we
+    // need to remove components from the root one by one, until either we find
+    // a prefix that fits, or we run out of components to remove.
+    var level = 0;
+    while (aPath.indexOf(aRoot + '/') !== 0) {
+      var index = aRoot.lastIndexOf("/");
+      if (index < 0) {
+        return aPath;
+      }
+
+      // If the only part of the root that is left is the scheme (i.e. http://,
+      // file:///, etc.), one or more slashes (/), or simply nothing at all, we
+      // have exhausted all components, so the path is not relative to the root.
+      aRoot = aRoot.slice(0, index);
+      if (aRoot.match(/^([^\/]+:\/)\/*$/)) {
+        return aPath;
+      }
+
+      ++level;
     }
 
-    return aPath.indexOf(aRoot + '/') === 0
-      ? aPath.substr(aRoot.length + 1)
-      : aPath;
+    // Make sure we add a "../" for each component we removed from the root.
+    return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
   }
   exports.relative = relative;
 
   /**
    * Because behavior goes wacky when you set `__proto__` on objects, we
    * have to prefix all the strings in our set with an arbitrary character.
    *
    * See https://github.com/mozilla/source-map/pull/31 and
@@ -1376,39 +1391,43 @@ define('source-map/source-map-consumer',
           name: mapping.name
         };
       }).forEach(aCallback, context);
     };
 
   /**
    * Returns all generated line and column information for the original source,
    * line, and column provided. If no column is provided, returns all mappings
-   * corresponding to a single line. Otherwise, returns all mappings
-   * corresponding to a single line and column.
+   * corresponding to a either the line we are searching for or the next
+   * closest line that has any mappings. Otherwise, returns all mappings
+   * corresponding to the given line and either the column we are searching for
+   * or the next closest column that has any offsets.
    *
    * 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.
    *   - column: Optional. the column 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) {
+      var line = util.getArg(aArgs, 'line');
+
       // 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 0, 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'),
+        originalLine: line,
         originalColumn: util.getArg(aArgs, 'column', 0)
       };
 
       if (this.sourceRoot != null) {
         needle.source = util.relative(this.sourceRoot, needle.source);
       }
 
       var mappings = [];
@@ -1416,32 +1435,51 @@ define('source-map/source-map-consumer',
       var index = this._findMapping(needle,
                                     this._originalMappings,
                                     "originalLine",
                                     "originalColumn",
                                     util.compareByOriginalPositions,
                                     binarySearch.LEAST_UPPER_BOUND);
       if (index >= 0) {
         var mapping = this._originalMappings[index];
-        var originalLine = mapping.originalLine;
-        var originalColumn = mapping.originalColumn;
+
+        if (aArgs.column === undefined) {
+          var originalLine = mapping.originalLine;
+
+          // Iterate until either we run out of mappings, or we run into
+          // a mapping for a different line than the one we found. Since
+          // mappings are sorted, this is guaranteed to find all mappings for
+          // the line we found.
+          while (mapping && mapping.originalLine === originalLine) {
+            mappings.push({
+              line: util.getArg(mapping, 'generatedLine', null),
+              column: util.getArg(mapping, 'generatedColumn', null),
+              lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
+            });
 
-        // Iterate until either we run out of mappings, or we run into
-        // a mapping for a different line. Since mappings are sorted, this is
-        // guaranteed to find all mappings for the line we are searching for.
-        while (mapping && mapping.originalLine === originalLine &&
-               (aArgs.column === undefined ||
-                mapping.originalColumn === originalColumn)) {
-          mappings.push({
-            line: util.getArg(mapping, 'generatedLine', null),
-            column: util.getArg(mapping, 'generatedColumn', null),
-            lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
-          });
+            mapping = this._originalMappings[++index];
+          }
+        } else {
+          var originalColumn = mapping.originalColumn;
 
-          mapping = this._originalMappings[++index];
+          // Iterate until either we run out of mappings, or we run into
+          // a mapping for a different line than the one we were searching for.
+          // Since mappings are sorted, this is guaranteed to find all mappings for
+          // the line we are searching for.
+          while (mapping &&
+                 mapping.originalLine === line &&
+                 mapping.originalColumn == originalColumn) {
+            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;
     };
 
   exports.SourceMapConsumer = SourceMapConsumer;
 
--- a/toolkit/devtools/sourcemap/tests/unit/Utils.jsm
+++ b/toolkit/devtools/sourcemap/tests/unit/Utils.jsm
@@ -562,25 +562,40 @@ define('lib/source-map/util', ['require'
    */
   function relative(aRoot, aPath) {
     if (aRoot === "") {
       aRoot = ".";
     }
 
     aRoot = aRoot.replace(/\/$/, '');
 
-    // XXX: It is possible to remove this block, and the tests still pass!
-    var url = urlParse(aRoot);
-    if (aPath.charAt(0) == "/" && url && url.path == "/") {
-      return aPath.slice(1);
+    // It is possible for the path to be above the root. In this case, simply
+    // checking whether the root is a prefix of the path won't work. Instead, we
+    // need to remove components from the root one by one, until either we find
+    // a prefix that fits, or we run out of components to remove.
+    var level = 0;
+    while (aPath.indexOf(aRoot + '/') !== 0) {
+      var index = aRoot.lastIndexOf("/");
+      if (index < 0) {
+        return aPath;
+      }
+
+      // If the only part of the root that is left is the scheme (i.e. http://,
+      // file:///, etc.), one or more slashes (/), or simply nothing at all, we
+      // have exhausted all components, so the path is not relative to the root.
+      aRoot = aRoot.slice(0, index);
+      if (aRoot.match(/^([^\/]+:\/)\/*$/)) {
+        return aPath;
+      }
+
+      ++level;
     }
 
-    return aPath.indexOf(aRoot + '/') === 0
-      ? aPath.substr(aRoot.length + 1)
-      : aPath;
+    // Make sure we add a "../" for each component we removed from the root.
+    return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
   }
   exports.relative = relative;
 
   /**
    * Because behavior goes wacky when you set `__proto__` on objects, we
    * have to prefix all the strings in our set with an arbitrary character.
    *
    * See https://github.com/mozilla/source-map/pull/31 and
--- a/toolkit/devtools/sourcemap/tests/unit/test_source_map_consumer.js
+++ b/toolkit/devtools/sourcemap/tests/unit/test_source_map_consumer.js
@@ -641,16 +641,41 @@ define("test/source-map/test-source-map-
 
     assert.equal(mappings.length, 2);
     assert.equal(mappings[0].line, 1);
     assert.equal(mappings[0].column, 2);
     assert.equal(mappings[1].line, 1);
     assert.equal(mappings[1].column, 3);
   };
 
+  exports['test allGeneratedPositionsFor for column on different line fuzzy'] = function (assert, util) {
+    var map = new SourceMapGenerator({
+      file: 'generated.js'
+    });
+    map.addMapping({
+      original: { line: 2, column: 1 },
+      generated: { line: 2, column: 2 },
+      source: 'foo.coffee'
+    });
+    map.addMapping({
+      original: { line: 2, column: 1 },
+      generated: { line: 2, column: 3 },
+      source: 'foo.coffee'
+    });
+    map = new SourceMapConsumer(map.toString());
+
+    var mappings = map.allGeneratedPositionsFor({
+      line: 1,
+      column: 0,
+      source: 'foo.coffee'
+    });
+
+    assert.equal(mappings.length, 0);
+  };
+
   exports['test computeColumnSpans'] = function (assert, util) {
     var map = new SourceMapGenerator({
       file: 'generated.js'
     });
     map.addMapping({
       original: { line: 1, column: 1 },
       generated: { line: 1, column: 1 },
       source: 'foo.coffee'
--- a/toolkit/devtools/sourcemap/tests/unit/test_util.js
+++ b/toolkit/devtools/sourcemap/tests/unit/test_util.js
@@ -202,17 +202,17 @@ define("test/source-map/test-util", ["re
 
     assert.equal(libUtil.join('http://www.example.com', '//foo.org/bar'), 'http://foo.org/bar');
     assert.equal(libUtil.join('//www.example.com', '//foo.org/bar'), '//foo.org/bar');
   };
 
   // TODO Issue #128: Define and test this function properly.
   exports['test relative()'] = function (assert, util) {
     assert.equal(libUtil.relative('/the/root', '/the/root/one.js'), 'one.js');
-    assert.equal(libUtil.relative('/the/root', '/the/rootone.js'), '/the/rootone.js');
+    assert.equal(libUtil.relative('/the/root', '/the/rootone.js'), '../rootone.js');
 
     assert.equal(libUtil.relative('', '/the/root/one.js'), '/the/root/one.js');
     assert.equal(libUtil.relative('.', '/the/root/one.js'), '/the/root/one.js');
     assert.equal(libUtil.relative('', 'the/root/one.js'), 'the/root/one.js');
     assert.equal(libUtil.relative('.', 'the/root/one.js'), 'the/root/one.js');
 
     assert.equal(libUtil.relative('/', '/the/root/one.js'), 'the/root/one.js');
     assert.equal(libUtil.relative('/', 'the/root/one.js'), 'the/root/one.js');