Bug 1011826 - Split Prefable into two pieces. r=bz.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 24 Mar 2016 06:09:24 +1100
changeset 290222 531508a2e75575e007d3eb578c77fd3ecf5fee1e
parent 290221 c75b2b195f28a5ed556a7dfc12e9b45bc56c971a
child 290223 ae01415f56b22fc573dac4cef502d808afb3c62f
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1011826
milestone48.0a1
Bug 1011826 - Split Prefable into two pieces. r=bz. The fields in Prefable relating to disabling are usually all zero. This patch moves them into a new struct PrefableDisablers. This reduces static data size by 92 KB, which applies to every process. It might also make isEnabled() faster because the common case only involves one test instead of two.
dom/bindings/Codegen.py
dom/bindings/DOMJSClass.h
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1943,16 +1943,24 @@ class MemberCondition:
                 self.available == other.available and
                 self.checkAnyPermissions == other.checkAnyPermissions and
                 self.checkAllPermissions == other.checkAllPermissions and
                 self.nonExposedGlobals == other.nonExposedGlobals)
 
     def __ne__(self, other):
         return not self.__eq__(other)
 
+    def hasDisablers(self):
+        return (self.pref is not None or
+                self.func != "nullptr" or
+                self.available != "nullptr" or
+                self.checkAnyPermissions != "nullptr" or
+                self.checkAllPermissions != "nullptr" or
+                self.nonExposedGlobals != "0")
+
 
 class PropertyDefiner:
     """
     A common superclass for defining things on prototype objects.
 
     Subclasses should implement generateArray to generate the actual arrays of
     things we're defining.  They should also set self.chrome to the list of
     things only exposed to chrome and self.regular to the list of things exposed
@@ -2063,68 +2071,85 @@ class PropertyDefiner:
         # inserted at every point where the pref name controlling the member
         # changes.  That will make sure the order of the properties as exposed
         # on the interface and interface prototype objects does not change when
         # pref control is added to members while still allowing us to define all
         # the members in the smallest number of JSAPI calls.
         assert len(array) != 0
         # So we won't put a specTerminator at the very front of the list:
         lastCondition = getCondition(array[0], self.descriptor)
+
         specs = []
+        disablers = []
         prefableSpecs = []
 
-        prefableTemplate = '  { true, %s, %s, %s, %s, %s, &%s[%d] }'
-        prefCacheTemplate = '&%s[%d].enabled'
+        disablersTemplate = dedent(
+            """
+            static PrefableDisablers %s_disablers%d = {
+              true, %s, %s, %s, %s, %s
+            };
+            """)
+        prefableWithDisablersTemplate = '  { &%s_disablers%d, &%s_specs[%d] }'
+        prefableWithoutDisablersTemplate = '  { nullptr, &%s_specs[%d] }'
+        prefCacheTemplate = '&%s[%d].disablers->enabled'
 
         def switchToCondition(props, condition):
             # Remember the info about where our pref-controlled
             # booleans live.
             if condition.pref is not None:
                 props.prefCacheData.append(
                     (condition.pref,
                      prefCacheTemplate % (name, len(prefableSpecs))))
             # Set up pointers to the new sets of specs inside prefableSpecs
-            prefableSpecs.append(prefableTemplate %
-                                 (condition.nonExposedGlobals,
+            if condition.hasDisablers():
+                prefableSpecs.append(prefableWithDisablersTemplate %
+                                     (name, len(specs), name, len(specs)))
+                disablers.append(disablersTemplate %
+                                 (name, len(specs),
+                                  condition.nonExposedGlobals,
                                   condition.func,
                                   condition.available,
                                   condition.checkAnyPermissions,
-                                  condition.checkAllPermissions,
-                                  name + "_specs", len(specs)))
+                                  condition.checkAllPermissions))
+            else:
+                prefableSpecs.append(prefableWithoutDisablersTemplate %
+                                     (name, len(specs)))
 
         switchToCondition(self, lastCondition)
 
         for member in array:
             curCondition = getCondition(member, self.descriptor)
             if lastCondition != curCondition:
                 # Terminate previous list
                 specs.append(specTerminator)
-                # And switch to our new pref
+                # And switch to our new condition
                 switchToCondition(self, curCondition)
                 lastCondition = curCondition
             # And the actual spec
             specs.append(specFormatter(getDataTuple(member)))
         specs.append(specTerminator)
-        prefableSpecs.append("  { false, 0, nullptr, nullptr, nullptr, nullptr, nullptr }")
+        prefableSpecs.append("  { nullptr, nullptr }")
 
         specType = "const " + specType
         arrays = fill(
             """
             static ${specType} ${name}_specs[] = {
             ${specs}
             };
 
+            ${disablers}
             // Can't be const because the pref-enabled boolean needs to be writable
             static Prefable<${specType}> ${name}[] = {
             ${prefableSpecs}
             };
 
             """,
             specType=specType,
             name=name,
+            disablers='\n'.join(disablers),
             specs=',\n'.join(specs),
             prefableSpecs=',\n'.join(prefableSpecs))
         if doIdArrays:
             arrays += "static jsid %s_ids[%i];\n\n" % (name, len(specs))
         return arrays
 
 
 # The length of a method is the minimum of the lengths of the
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_DOMJSClass_h
 #define mozilla_dom_DOMJSClass_h
 
 #include "jsfriendapi.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Likely.h"
 
 #include "mozilla/dom/PrototypeList.h" // auto-generated
 
 #include "mozilla/dom/JSSlots.h"
 
 class nsCycleCollectionParticipant;
 
 // All DOM globals must have a slot at DOM_PROTOTYPE_SLOT.
@@ -72,18 +73,17 @@ namespace GlobalNames {
 static const uint32_t Window = 1u << 0;
 static const uint32_t BackstagePass = 1u << 1;
 static const uint32_t DedicatedWorkerGlobalScope = 1u << 2;
 static const uint32_t SharedWorkerGlobalScope = 1u << 3;
 static const uint32_t ServiceWorkerGlobalScope = 1u << 4;
 static const uint32_t WorkerDebuggerGlobalScope = 1u << 5;
 } // namespace GlobalNames
 
-template<typename T>
-struct Prefable {
+struct PrefableDisablers {
   inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const {
     // Reading "enabled" on a worker thread is technically undefined behavior,
     // because it's written only on main threads, with no barriers of any sort.
     // So we want to avoid doing that.  But we don't particularly want to make
     // expensive NS_IsMainThread calls here.
     //
     // The good news is that "enabled" is only written for things that have a
     // Pref annotation, and such things can never be exposed on non-Window
@@ -92,19 +92,16 @@ struct Prefable {
     if (nonExposedGlobals &&
         IsNonExposedGlobal(cx, js::GetGlobalForObjectCrossCompartment(obj),
                            nonExposedGlobals)) {
       return false;
     }
     if (!enabled) {
       return false;
     }
-    if (!enabledFunc && !availableFunc && !checkAnyPermissions && !checkAllPermissions) {
-      return true;
-    }
     if (enabledFunc &&
         !enabledFunc(cx, js::GetGlobalForObjectCrossCompartment(obj))) {
       return false;
     }
     if (availableFunc &&
         !availableFunc(cx, js::GetGlobalForObjectCrossCompartment(obj))) {
       return false;
     }
@@ -116,35 +113,54 @@ struct Prefable {
     if (checkAllPermissions &&
         !CheckAllPermissions(cx, js::GetGlobalForObjectCrossCompartment(obj),
                              checkAllPermissions)) {
       return false;
     }
     return true;
   }
 
-  // A boolean indicating whether this set of specs is enabled
+  // A boolean indicating whether this set of specs is enabled. Not const
+  // because it will change at runtime if the corresponding pref is changed.
   bool enabled;
+
   // Bitmask of global names that we should not be exposed in.
-  uint32_t nonExposedGlobals;
+  const uint16_t nonExposedGlobals;
+
   // A function pointer to a function that can say the property is disabled
   // even if "enabled" is set to true.  If the pointer is null the value of
   // "enabled" is used as-is unless availableFunc overrides.
-  PropertyEnabled enabledFunc;
+  const PropertyEnabled enabledFunc;
+
   // A function pointer to a function that can be used to disable a
   // property even if "enabled" is true and enabledFunc allowed.  This
   // is basically a hack to avoid having to codegen PropertyEnabled
   // implementations in case when we need to do two separate checks.
-  PropertyEnabled availableFunc;
-  const char* const* checkAnyPermissions;
-  const char* const* checkAllPermissions;
+  const PropertyEnabled availableFunc;
+  const char* const* const checkAnyPermissions;
+  const char* const* const checkAllPermissions;
+};
+
+template<typename T>
+struct Prefable {
+  inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const {
+    if (MOZ_LIKELY(!disablers)) {
+      return true;
+    }
+    return disablers->isEnabled(cx, obj);
+  }
+
+  // Things that can disable this set of specs. |nullptr| means "cannot be
+  // disabled".
+  PrefableDisablers* const disablers;
+
   // Array of specs, terminated in whatever way is customary for T.
   // Null to indicate a end-of-array for Prefable, when such an
   // indicator is needed.
-  const T* specs;
+  const T* const specs;
 };
 
 struct NativeProperties
 {
   const Prefable<const JSFunctionSpec>* staticMethods;
   jsid* staticMethodIds;
   const JSFunctionSpec* staticMethodSpecs;