You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							418 lines
						
					
					
						
							10 KiB
						
					
					
				
			
		
		
	
	
							418 lines
						
					
					
						
							10 KiB
						
					
					
				| /* -*- 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
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * This is a helper function for getting values from parameter/options
 | |
|  * objects.
 | |
|  *
 | |
|  * @param args The object we are extracting values from
 | |
|  * @param name The name of the property we are getting.
 | |
|  * @param defaultValue An optional value to return if the property is missing
 | |
|  * from the object. If this is not specified and the property is missing, an
 | |
|  * error will be thrown.
 | |
|  */
 | |
| function getArg(aArgs, aName, aDefaultValue) {
 | |
|   if (aName in aArgs) {
 | |
|     return aArgs[aName];
 | |
|   } else if (arguments.length === 3) {
 | |
|     return aDefaultValue;
 | |
|   } else {
 | |
|     throw new Error('"' + aName + '" is a required argument.');
 | |
|   }
 | |
| }
 | |
| exports.getArg = getArg;
 | |
| 
 | |
| var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
 | |
| var dataUrlRegexp = /^data:.+\,.+$/;
 | |
| 
 | |
| function urlParse(aUrl) {
 | |
|   var match = aUrl.match(urlRegexp);
 | |
|   if (!match) {
 | |
|     return null;
 | |
|   }
 | |
|   return {
 | |
|     scheme: match[1],
 | |
|     auth: match[2],
 | |
|     host: match[3],
 | |
|     port: match[4],
 | |
|     path: match[5]
 | |
|   };
 | |
| }
 | |
| exports.urlParse = urlParse;
 | |
| 
 | |
| function urlGenerate(aParsedUrl) {
 | |
|   var url = '';
 | |
|   if (aParsedUrl.scheme) {
 | |
|     url += aParsedUrl.scheme + ':';
 | |
|   }
 | |
|   url += '//';
 | |
|   if (aParsedUrl.auth) {
 | |
|     url += aParsedUrl.auth + '@';
 | |
|   }
 | |
|   if (aParsedUrl.host) {
 | |
|     url += aParsedUrl.host;
 | |
|   }
 | |
|   if (aParsedUrl.port) {
 | |
|     url += ":" + aParsedUrl.port
 | |
|   }
 | |
|   if (aParsedUrl.path) {
 | |
|     url += aParsedUrl.path;
 | |
|   }
 | |
|   return url;
 | |
| }
 | |
| exports.urlGenerate = urlGenerate;
 | |
| 
 | |
| /**
 | |
|  * Normalizes a path, or the path portion of a URL:
 | |
|  *
 | |
|  * - Replaces consecutive slashes with one slash.
 | |
|  * - Removes unnecessary '.' parts.
 | |
|  * - Removes unnecessary '<dir>/..' parts.
 | |
|  *
 | |
|  * Based on code in the Node.js 'path' core module.
 | |
|  *
 | |
|  * @param aPath The path or url to normalize.
 | |
|  */
 | |
| function normalize(aPath) {
 | |
|   var path = aPath;
 | |
|   var url = urlParse(aPath);
 | |
|   if (url) {
 | |
|     if (!url.path) {
 | |
|       return aPath;
 | |
|     }
 | |
|     path = url.path;
 | |
|   }
 | |
|   var isAbsolute = exports.isAbsolute(path);
 | |
| 
 | |
|   var parts = path.split(/\/+/);
 | |
|   for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
 | |
|     part = parts[i];
 | |
|     if (part === '.') {
 | |
|       parts.splice(i, 1);
 | |
|     } else if (part === '..') {
 | |
|       up++;
 | |
|     } else if (up > 0) {
 | |
|       if (part === '') {
 | |
|         // The first part is blank if the path is absolute. Trying to go
 | |
|         // above the root is a no-op. Therefore we can remove all '..' parts
 | |
|         // directly after the root.
 | |
|         parts.splice(i + 1, up);
 | |
|         up = 0;
 | |
|       } else {
 | |
|         parts.splice(i, 2);
 | |
|         up--;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   path = parts.join('/');
 | |
| 
 | |
|   if (path === '') {
 | |
|     path = isAbsolute ? '/' : '.';
 | |
|   }
 | |
| 
 | |
|   if (url) {
 | |
|     url.path = path;
 | |
|     return urlGenerate(url);
 | |
|   }
 | |
|   return path;
 | |
| }
 | |
| exports.normalize = normalize;
 | |
| 
 | |
| /**
 | |
|  * Joins two paths/URLs.
 | |
|  *
 | |
|  * @param aRoot The root path or URL.
 | |
|  * @param aPath The path or URL to be joined with the root.
 | |
|  *
 | |
|  * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
 | |
|  *   scheme-relative URL: Then the scheme of aRoot, if any, is prepended
 | |
|  *   first.
 | |
|  * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
 | |
|  *   is updated with the result and aRoot is returned. Otherwise the result
 | |
|  *   is returned.
 | |
|  *   - If aPath is absolute, the result is aPath.
 | |
|  *   - Otherwise the two paths are joined with a slash.
 | |
|  * - Joining for example 'http://' and 'www.example.com' is also supported.
 | |
|  */
 | |
| function join(aRoot, aPath) {
 | |
|   if (aRoot === "") {
 | |
|     aRoot = ".";
 | |
|   }
 | |
|   if (aPath === "") {
 | |
|     aPath = ".";
 | |
|   }
 | |
|   var aPathUrl = urlParse(aPath);
 | |
|   var aRootUrl = urlParse(aRoot);
 | |
|   if (aRootUrl) {
 | |
|     aRoot = aRootUrl.path || '/';
 | |
|   }
 | |
| 
 | |
|   // `join(foo, '//www.example.org')`
 | |
|   if (aPathUrl && !aPathUrl.scheme) {
 | |
|     if (aRootUrl) {
 | |
|       aPathUrl.scheme = aRootUrl.scheme;
 | |
|     }
 | |
|     return urlGenerate(aPathUrl);
 | |
|   }
 | |
| 
 | |
|   if (aPathUrl || aPath.match(dataUrlRegexp)) {
 | |
|     return aPath;
 | |
|   }
 | |
| 
 | |
|   // `join('http://', 'www.example.com')`
 | |
|   if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
 | |
|     aRootUrl.host = aPath;
 | |
|     return urlGenerate(aRootUrl);
 | |
|   }
 | |
| 
 | |
|   var joined = aPath.charAt(0) === '/'
 | |
|     ? aPath
 | |
|     : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
 | |
| 
 | |
|   if (aRootUrl) {
 | |
|     aRootUrl.path = joined;
 | |
|     return urlGenerate(aRootUrl);
 | |
|   }
 | |
|   return joined;
 | |
| }
 | |
| exports.join = join;
 | |
| 
 | |
| exports.isAbsolute = function (aPath) {
 | |
|   return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Make a path relative to a URL or another path.
 | |
|  *
 | |
|  * @param aRoot The root path or URL.
 | |
|  * @param aPath The path or URL to be made relative to aRoot.
 | |
|  */
 | |
| function relative(aRoot, aPath) {
 | |
|   if (aRoot === "") {
 | |
|     aRoot = ".";
 | |
|   }
 | |
| 
 | |
|   aRoot = aRoot.replace(/\/$/, '');
 | |
| 
 | |
|   // 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;
 | |
|   }
 | |
| 
 | |
|   // 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;
 | |
| 
 | |
| var supportsNullProto = (function () {
 | |
|   var obj = Object.create(null);
 | |
|   return !('__proto__' in obj);
 | |
| }());
 | |
| 
 | |
| function identity (s) {
 | |
|   return s;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * 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
 | |
|  * https://github.com/mozilla/source-map/issues/30
 | |
|  *
 | |
|  * @param String aStr
 | |
|  */
 | |
| function toSetString(aStr) {
 | |
|   if (isProtoString(aStr)) {
 | |
|     return '$' + aStr;
 | |
|   }
 | |
| 
 | |
|   return aStr;
 | |
| }
 | |
| exports.toSetString = supportsNullProto ? identity : toSetString;
 | |
| 
 | |
| function fromSetString(aStr) {
 | |
|   if (isProtoString(aStr)) {
 | |
|     return aStr.slice(1);
 | |
|   }
 | |
| 
 | |
|   return aStr;
 | |
| }
 | |
| exports.fromSetString = supportsNullProto ? identity : fromSetString;
 | |
| 
 | |
| function isProtoString(s) {
 | |
|   if (!s) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   var length = s.length;
 | |
| 
 | |
|   if (length < 9 /* "__proto__".length */) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (s.charCodeAt(length - 1) !== 95  /* '_' */ ||
 | |
|       s.charCodeAt(length - 2) !== 95  /* '_' */ ||
 | |
|       s.charCodeAt(length - 3) !== 111 /* 'o' */ ||
 | |
|       s.charCodeAt(length - 4) !== 116 /* 't' */ ||
 | |
|       s.charCodeAt(length - 5) !== 111 /* 'o' */ ||
 | |
|       s.charCodeAt(length - 6) !== 114 /* 'r' */ ||
 | |
|       s.charCodeAt(length - 7) !== 112 /* 'p' */ ||
 | |
|       s.charCodeAt(length - 8) !== 95  /* '_' */ ||
 | |
|       s.charCodeAt(length - 9) !== 95  /* '_' */) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   for (var i = length - 10; i >= 0; i--) {
 | |
|     if (s.charCodeAt(i) !== 36 /* '$' */) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Comparator between two mappings where the original positions are compared.
 | |
|  *
 | |
|  * Optionally pass in `true` as `onlyCompareGenerated` to consider two
 | |
|  * mappings with the same original source/line/column, but different generated
 | |
|  * line and column the same. Useful when searching for a mapping with a
 | |
|  * stubbed out mapping.
 | |
|  */
 | |
| function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
 | |
|   var cmp = mappingA.source - mappingB.source;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = mappingA.originalLine - mappingB.originalLine;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = mappingA.originalColumn - mappingB.originalColumn;
 | |
|   if (cmp !== 0 || onlyCompareOriginal) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = mappingA.generatedColumn - mappingB.generatedColumn;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = mappingA.generatedLine - mappingB.generatedLine;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   return mappingA.name - mappingB.name;
 | |
| }
 | |
| exports.compareByOriginalPositions = compareByOriginalPositions;
 | |
| 
 | |
| /**
 | |
|  * Comparator between two mappings with deflated source and name indices where
 | |
|  * the generated positions are compared.
 | |
|  *
 | |
|  * Optionally pass in `true` as `onlyCompareGenerated` to consider two
 | |
|  * mappings with the same generated line and column, but different
 | |
|  * source/name/original line and column the same. Useful when searching for a
 | |
|  * mapping with a stubbed out mapping.
 | |
|  */
 | |
| function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
 | |
|   var cmp = mappingA.generatedLine - mappingB.generatedLine;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = mappingA.generatedColumn - mappingB.generatedColumn;
 | |
|   if (cmp !== 0 || onlyCompareGenerated) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = mappingA.source - mappingB.source;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = mappingA.originalLine - mappingB.originalLine;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = mappingA.originalColumn - mappingB.originalColumn;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   return mappingA.name - mappingB.name;
 | |
| }
 | |
| exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
 | |
| 
 | |
| function strcmp(aStr1, aStr2) {
 | |
|   if (aStr1 === aStr2) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   if (aStr1 > aStr2) {
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Comparator between two mappings with inflated source and name strings where
 | |
|  * the generated positions are compared.
 | |
|  */
 | |
| function compareByGeneratedPositionsInflated(mappingA, mappingB) {
 | |
|   var cmp = mappingA.generatedLine - mappingB.generatedLine;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = mappingA.generatedColumn - mappingB.generatedColumn;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = strcmp(mappingA.source, mappingB.source);
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = mappingA.originalLine - mappingB.originalLine;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   cmp = mappingA.originalColumn - mappingB.originalColumn;
 | |
|   if (cmp !== 0) {
 | |
|     return cmp;
 | |
|   }
 | |
| 
 | |
|   return strcmp(mappingA.name, mappingB.name);
 | |
| }
 | |
| exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
 |