--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -36,17 +36,19 @@ const { join: pathJoin, normalize, dirna
XPCOMUtils.defineLazyServiceGetter(this, "resProto",
"@mozilla.org/network/protocol;1?name=resource",
"nsIResProtocolHandler");
XPCOMUtils.defineLazyServiceGetter(this, "zipCache",
"@mozilla.org/libjar/zip-reader-cache;1",
"nsIZipReaderCache");
-XPCOMUtils.defineLazyGetter(this, "XulApp", () => {
+const { defineLazyGetter } = XPCOMUtils;
+
+defineLazyGetter(this, "XulApp", () => {
let xulappURI = module.uri.replace("toolkit/loader.js",
"sdk/system/xul-app.jsm");
return Cu.import(xulappURI, {});
});
// Define some shortcuts.
const bind = Function.call.bind(Function.bind);
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
@@ -471,16 +473,24 @@ const load = iced(function load(loader,
// Create a new object in this sandbox, that will be used as
// the scope object for this particular module
sandbox = new loader.sharedGlobalSandbox.Object();
// Inject all expected globals in the scope object
getOwnIdentifiers(globals).forEach(function(name) {
descriptors[name] = getOwnPropertyDescriptor(globals, name)
descriptors[name].configurable = true;
});
+ descriptors.lazyRequire = {
+ configurable: true,
+ value: lazyRequire.bind(sandbox),
+ };
+ descriptors.lazyRequireModule = {
+ configurable: true,
+ value: lazyRequireModule.bind(sandbox),
+ };
Object.defineProperties(sandbox, descriptors);
}
else {
sandbox = Sandbox({
name: module.uri,
prototype: Object.create(globals, descriptors),
wantXrays: false,
wantGlobalProperties: module.id == "sdk/indexed-db" ? ["indexedDB"] : [],
@@ -725,16 +735,66 @@ const resolveURI = iced(function resolve
if(stripped === "" || id === stripped || id.startsWith(stripped + "/")) {
return normalizeExt(id.replace(path, uri));
}
}
return null;
});
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.
+ *
+ * @param {object} obj
+ * The target object on which to define the lazy getters.
+ * @param {string} moduleId
+ * The ID of the module to require, as passed to require().
+ * @param {Array<string | object>} args
+ * Any number of properties to import from the module. A string
+ * will cause the property to be defined which resolves to the
+ * same property in the module's exports. An object will define a
+ * lazy getter for every value in the object which corresponds to
+ * the given key in the module's exports, as in an ordinary
+ * destructuring assignment.
+ */
+function lazyRequire(obj, moduleId, ...args) {
+ let module;
+ let getModule = () => {
+ if (!module)
+ module = this.require(moduleId);
+ return module;
+ };
+
+ for (let props of args) {
+ if (typeof props !== "object")
+ props = {[props]: props};
+
+ for (let [fromName, toName] of Object.entries(props))
+ defineLazyGetter(obj, toName, () => getModule()[fromName]);
+ }
+}
+
+/**
+ * Defines a lazy getter on the given object which causes a module to be
+ * lazily imported the first time it is accessed.
+ *
+ * @param {object} obj
+ * The target object on which to define the lazy getter.
+ * @param {string} moduleId
+ * The ID of the module to require, as passed to require().
+ * @param {string} [prop = moduleId]
+ * The name of the lazy getter property to define.
+ */
+function lazyRequireModule(obj, moduleId, prop = moduleId) {
+ defineLazyGetter(obj, prop, () => this.require(moduleId));
+}
+
+
// Creates version of `require` that will be exposed to the given `module`
// in the context of the given `loader`. Each module gets own limited copy
// of `require` that is allowed to load only a modules that are associated
// with it during link time.
const Require = iced(function Require(loader, requirer) {
let {
modules, mapping, resolve: loaderResolve, load,
manifest, rootURI, isNative, requireMap,