Bug 1388221: Optimize defineLazyModuleGetter for already-loaded modules. r?florian draft
authorKris Maglione <maglione.k@gmail.com>
Tue, 08 Aug 2017 10:09:07 -0700
changeset 642834 03cef6fe258cebe4c0c2c4887511ff5bbb3bbdfe
parent 642833 92318a417ec18e031710826e0d532555ae8a7e6c
child 725112 b4f90faa16530653da2986472cf2ad421bcdc383
push id72879
push usermaglione.k@gmail.com
push dateTue, 08 Aug 2017 20:03:21 +0000
reviewersflorian
bugs1388221
milestone57.0a1
Bug 1388221: Optimize defineLazyModuleGetter for already-loaded modules. r?florian Creating and populating temporary export objects is fairly expensive. Defining and then redefining lazy getters is sometimes even more expensive. Caching the export objects from module imports, and immediately defining properties from already-imported modules appears to save a considerable amount of overhead at startup. MozReview-Commit-ID: 2jR1i0WpIcw
js/xpconnect/loader/XPCOMUtils.jsm
--- a/js/xpconnect/loader/XPCOMUtils.jsm
+++ b/js/xpconnect/loader/XPCOMUtils.jsm
@@ -90,16 +90,18 @@
 
 this.EXPORTED_SYMBOLS = [ "XPCOMUtils" ];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 
+const modules = {};
+
 this.XPCOMUtils = {
   /**
    * Generate a QueryInterface implementation. The returned function must be
    * assigned to the 'QueryInterface' property of a JS object. When invoked on
    * that object, it checks if the given iid is listed in the |interfaces|
    * param, and if it is, returns |this| (the object it was called on).
    * If the JS object has a classInfo property it'll be returned for the
    * nsIClassInfo IID, generateCI can be used to generate the classInfo
@@ -298,30 +300,43 @@ this.XPCOMUtils = {
                                    aPreLambda, aPostLambda, aProxy)
   {
     let proxy = aProxy || {};
 
     if (typeof(aPreLambda) === "function") {
       aPreLambda.apply(proxy);
     }
 
-    this.defineLazyGetter(aObject, aName, function XPCU_moduleLambda() {
-      var temp = {};
+    function XPCU_moduleLambda() {
       try {
-        Cu.import(aResource, temp);
+        let temp;
+        if (aResource in modules) {
+          temp = modules[aResource];
+        } else {
+          temp = {};
+          Cu.import(aResource, temp);
+          modules[aResource] = temp;
+        }
 
         if (typeof(aPostLambda) === "function") {
           aPostLambda.apply(proxy);
         }
+
+        return temp[aSymbol || aName];
       } catch (ex) {
         Cu.reportError("Failed to load module " + aResource + ".");
         throw ex;
       }
-      return temp[aSymbol || aName];
-    });
+    }
+
+    if (!aPostLambda && (aResource in modules || Cu.isModuleLoaded(aResource))) {
+      aObject[aSymbol || aName] = XPCU_moduleLambda();
+    } else {
+      this.defineLazyGetter(aObject, aName, XPCU_moduleLambda);
+    }
   },
 
   /**
    * Defines a getter on a specified object for preference value. The
    * preference is read the first time that the property is accessed,
    * and is thereafter kept up-to-date using a preference observer.
    *
    * @param aObject