Bug 1314861: Pre-compute path mapping function to save on runtime lookups. r?ochameau
MozReview-Commit-ID: Lc4ju1XOfgR
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -704,39 +704,57 @@ const nodeResolve = iced(function nodeRe
});
Loader.nodeResolve = nodeResolve;
function addTrailingSlash(path) {
return path.replace(/\/*$/, "/");
}
-const resolveURI = iced(function resolveURI(id, mapping) {
- // Do not resolve if already a resource URI
- if (isAbsoluteURI(id))
- return normalizeExt(id);
+function compileMapping(mapping) {
+ let patterns = [];
+ let paths = {};
+
+ let escapeMeta = str => str.replace(/([.\\?+*(){}[\]^$])/g, '\\$1')
for (let [path, uri] of mapping) {
// Strip off any trailing slashes to make comparisons simpler
- let stripped = path.replace(/\/+$/, "");
+ if (path.endsWith("/")) {
+ path = path.slice(0, -1);
+ uri = uri.replace(/\/+$/, "");
+ }
+
+ paths[path] = uri;
// We only want to match path segments explicitly. Examples:
// * "foo/bar" matches for "foo/bar"
// * "foo/bar" matches for "foo/bar/baz"
// * "foo/bar" does not match for "foo/bar-1"
// * "foo/bar/" does not match for "foo/bar"
// * "foo/bar/" matches for "foo/bar/baz"
//
// Check for an empty path, an exact match, or a substring match
// with the next character being a forward slash.
- if(stripped === "" || id === stripped || id.startsWith(stripped + "/")) {
- return normalizeExt(id.replace(path, uri));
- }
+ if (path == "")
+ patterns.push("");
+ else
+ patterns.push(`${escapeMeta(path)}(?=$|/)`);
}
- return null;
+
+ let pattern = new RegExp(`^(${patterns.join('|')})`);
+
+ return id => id.replace(pattern, (m0, m1) => paths[m1]);
+}
+
+const resolveURI = iced(function resolveURI(id, mapping) {
+ // Do not resolve if already a resource URI
+ if (isAbsoluteURI(id))
+ return normalizeExt(id);
+
+ return normalizeExt(mapping(id))
});
Loader.resolveURI = resolveURI;
/**
* Defines lazy getters on the given object, which lazily require the
* given module the first time they are accessed, and then resolve that
* module's exported properties.
*
@@ -1079,16 +1097,18 @@ function Loader(options) {
// observer notifications.
let destructor = freeze(Object.create(null));
// Make mapping array that is sorted from longest path to shortest path.
let mapping = Object.keys(paths)
.sort((a, b) => b.length - a.length)
.map(path => [path, paths[path]]);
+ mapping = compileMapping(mapping);
+
// Define pseudo modules.
modules = override({
'@loader/unload': destructor,
'@loader/options': options,
'chrome': { Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm,
CC: bind(CC, Components), components: Components,
// `ChromeWorker` has to be inject in loader global scope.
// It is done by bootstrap.js:loadSandbox for the SDK.