Bug 1314861: Add defineLazyGetter global to SDK loader modules. r?ochameau draft
authorKris Maglione <maglione.k@gmail.com>
Fri, 07 Apr 2017 17:12:49 -0700
changeset 558810 69c218163a366ccf491bcf10fd5e1a8df3e1f2ad
parent 558554 2c72b26390decc016f150c9e00bd91dc7cb5f98b
child 558811 aad8a872168fb97a73cb98358e449a888c07a4d0
push id52953
push usermaglione.k@gmail.com
push dateSat, 08 Apr 2017 01:32:07 +0000
reviewersochameau
bugs1314861
milestone55.0a1
Bug 1314861: Add defineLazyGetter global to SDK loader modules. r?ochameau MozReview-Commit-ID: L1NShFpnb2S
addon-sdk/source/lib/toolkit/loader.js
--- 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,