Bug 1260653 - Shrink NativeProperties. r=bz.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 31 Mar 2016 09:57:20 +1100
changeset 291139 5ae0a75858a8169197d9fc40b94504d7e45bfc72
parent 291138 a388e3337aff87b521bd7b98a8dc6f972a82b8fa
child 291140 2528365bac075c3bb74d093df41773a35119f0f8
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
bugs1260653
milestone48.0a1
Bug 1260653 - Shrink NativeProperties. r=bz. This patch makes NativeProperties variable-length and reduces static data by 110,336 bytes on 64-bit, and half that on 32-bit. MozReview-Commit-ID: 2etZ5AnEhgO
dom/bindings/BindingUtils.cpp
dom/bindings/Codegen.py
dom/bindings/DOMJSClass.h
xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -688,46 +688,47 @@ CreateInterfaceObject(JSContext* cx, JS:
     }
 
     if (!JS_DefineProperty(cx, constructor, "name", nameStr, JSPROP_READONLY)) {
       return nullptr;
     }
   }
 
   if (properties) {
-    if (properties->staticMethods &&
-        !DefinePrefable(cx, constructor, properties->staticMethods)) {
+    if (properties->HasStaticMethods() &&
+        !DefinePrefable(cx, constructor, properties->StaticMethods())) {
       return nullptr;
     }
 
-    if (properties->staticAttributes &&
-        !DefinePrefable(cx, constructor, properties->staticAttributes)) {
+    if (properties->HasStaticAttributes() &&
+        !DefinePrefable(cx, constructor, properties->StaticAttributes())) {
       return nullptr;
     }
 
-    if (properties->constants &&
-        !DefinePrefable(cx, constructor, properties->constants)) {
+    if (properties->HasConstants() &&
+        !DefinePrefable(cx, constructor, properties->Constants())) {
       return nullptr;
     }
   }
 
   if (chromeOnlyProperties) {
-    if (chromeOnlyProperties->staticMethods &&
-        !DefinePrefable(cx, constructor, chromeOnlyProperties->staticMethods)) {
+    if (chromeOnlyProperties->HasStaticMethods() &&
+        !DefinePrefable(cx, constructor,
+                        chromeOnlyProperties->StaticMethods())) {
       return nullptr;
     }
 
-    if (chromeOnlyProperties->staticAttributes &&
+    if (chromeOnlyProperties->HasStaticAttributes() &&
         !DefinePrefable(cx, constructor,
-                        chromeOnlyProperties->staticAttributes)) {
+                        chromeOnlyProperties->StaticAttributes())) {
       return nullptr;
     }
 
-    if (chromeOnlyProperties->constants &&
-        !DefinePrefable(cx, constructor, chromeOnlyProperties->constants)) {
+    if (chromeOnlyProperties->HasConstants() &&
+        !DefinePrefable(cx, constructor, chromeOnlyProperties->Constants())) {
       return nullptr;
     }
   }
 
   if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
     return nullptr;
   }
 
@@ -802,45 +803,45 @@ CreateInterfacePrototypeObject(JSContext
 }
 
 bool
 DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
                  const NativeProperties* properties,
                  const NativeProperties* chromeOnlyProperties)
 {
   if (properties) {
-    if (properties->methods &&
-        !DefinePrefable(cx, obj, properties->methods)) {
+    if (properties->HasMethods() &&
+        !DefinePrefable(cx, obj, properties->Methods())) {
       return false;
     }
 
-    if (properties->attributes &&
-        !DefinePrefable(cx, obj, properties->attributes)) {
+    if (properties->HasAttributes() &&
+        !DefinePrefable(cx, obj, properties->Attributes())) {
       return false;
     }
 
-    if (properties->constants &&
-        !DefinePrefable(cx, obj, properties->constants)) {
+    if (properties->HasConstants() &&
+        !DefinePrefable(cx, obj, properties->Constants())) {
       return false;
     }
   }
 
   if (chromeOnlyProperties) {
-    if (chromeOnlyProperties->methods &&
-        !DefinePrefable(cx, obj, chromeOnlyProperties->methods)) {
+    if (chromeOnlyProperties->HasMethods() &&
+        !DefinePrefable(cx, obj, chromeOnlyProperties->Methods())) {
       return false;
     }
 
-    if (chromeOnlyProperties->attributes &&
-        !DefinePrefable(cx, obj, chromeOnlyProperties->attributes)) {
+    if (chromeOnlyProperties->HasAttributes() &&
+        !DefinePrefable(cx, obj, chromeOnlyProperties->Attributes())) {
       return false;
     }
 
-    if (chromeOnlyProperties->constants &&
-        !DefinePrefable(cx, obj, chromeOnlyProperties->constants)) {
+    if (chromeOnlyProperties->HasConstants() &&
+        !DefinePrefable(cx, obj, chromeOnlyProperties->Constants())) {
       return false;
     }
   }
 
   return true;
 }
 
 void
@@ -854,26 +855,27 @@ CreateInterfaceObjects(JSContext* cx, JS
                        const NativeProperties* properties,
                        const NativeProperties* chromeOnlyProperties,
                        const char* name, bool defineOnGlobal,
                        const char* const* unscopableNames)
 {
   MOZ_ASSERT(protoClass || constructorClass || constructor,
              "Need at least one class or a constructor!");
   MOZ_ASSERT(!((properties &&
-                (properties->methods || properties->attributes)) ||
+                (properties->HasMethods() || properties->HasAttributes())) ||
                (chromeOnlyProperties &&
-                (chromeOnlyProperties->methods ||
-                 chromeOnlyProperties->attributes))) || protoClass,
+                (chromeOnlyProperties->HasMethods() ||
+                 chromeOnlyProperties->HasAttributes()))) || protoClass,
              "Methods or properties but no protoClass!");
   MOZ_ASSERT(!((properties &&
-                (properties->staticMethods || properties->staticAttributes)) ||
+                (properties->HasStaticMethods() ||
+                 properties->HasStaticAttributes())) ||
                (chromeOnlyProperties &&
-                (chromeOnlyProperties->staticMethods ||
-                 chromeOnlyProperties->staticAttributes))) ||
+                (chromeOnlyProperties->HasStaticMethods() ||
+                 chromeOnlyProperties->HasStaticAttributes()))) ||
              constructorClass || constructor,
              "Static methods but no constructorClass or constructor!");
   MOZ_ASSERT(bool(name) == bool(constructorClass || constructor),
              "Must have name precisely when we have an interface object");
   MOZ_ASSERT(!constructorClass || !constructor);
   MOZ_ASSERT(!protoClass == !protoCache,
              "If, and only if, there is an interface prototype object we need "
              "to cache it");
@@ -1185,17 +1187,18 @@ XrayCreateFunction(JSContext* cx, JS::Ha
                                 JS::ObjectValue(*obj));
 #endif
   return obj;
 }
 
 static bool
 XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
                      JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
-                     const Prefable<const JSPropertySpec>* attributes, jsid* attributeIds,
+                     const Prefable<const JSPropertySpec>* attributes,
+                     const jsid* attributeIds,
                      const JSPropertySpec* attributeSpecs,
                      JS::MutableHandle<JS::PropertyDescriptor> desc,
                      bool& cacheOnHolder)
 {
   for (; attributes->specs; ++attributes) {
     if (attributes->isEnabled(cx, obj)) {
       // Set i to be the index into our full list of ids/specs that we're
       // looking at now.
@@ -1235,17 +1238,17 @@ XrayResolveAttribute(JSContext* cx, JS::
   }
   return true;
 }
 
 static bool
 XrayResolveMethod(JSContext* cx, JS::Handle<JSObject*> wrapper,
                   JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                   const Prefable<const JSFunctionSpec>* methods,
-                  jsid* methodIds,
+                  const jsid* methodIds,
                   const JSFunctionSpec* methodSpecs,
                   JS::MutableHandle<JS::PropertyDescriptor> desc,
                   bool& cacheOnHolder)
 {
   const Prefable<const JSFunctionSpec>* method;
   for (method = methods; method->specs; ++method) {
     if (method->isEnabled(cx, obj)) {
       // Set i to be the index into our full list of ids/specs that we're
@@ -1296,35 +1299,35 @@ XrayResolveUnforgeableProperty(JSContext
                                JS::MutableHandle<JS::PropertyDescriptor> desc,
                                bool& cacheOnHolder,
                                const NativeProperties* nativeProperties)
 {
   if (!nativeProperties) {
     return true;
   }
 
-  if (nativeProperties->unforgeableAttributes) {
+  if (nativeProperties->HasUnforgeableAttributes()) {
     if (!XrayResolveAttribute(cx, wrapper, obj, id,
-                              nativeProperties->unforgeableAttributes,
-                              nativeProperties->unforgeableAttributeIds,
-                              nativeProperties->unforgeableAttributeSpecs,
+                              nativeProperties->UnforgeableAttributes(),
+                              nativeProperties->UnforgeableAttributeIds(),
+                              nativeProperties->UnforgeableAttributeSpecs(),
                               desc, cacheOnHolder)) {
       return false;
     }
 
     if (desc.object()) {
       return true;
     }
   }
 
-  if (nativeProperties->unforgeableMethods) {
+  if (nativeProperties->HasUnforgeableMethods()) {
     if (!XrayResolveMethod(cx, wrapper, obj, id,
-                           nativeProperties->unforgeableMethods,
-                           nativeProperties->unforgeableMethodIds,
-                           nativeProperties->unforgeableMethodSpecs,
+                           nativeProperties->UnforgeableMethods(),
+                           nativeProperties->UnforgeableMethodIds(),
+                           nativeProperties->UnforgeableMethodSpecs(),
                            desc, cacheOnHolder)) {
       return false;
     }
 
     if (desc.object()) {
       return true;
     }
   }
@@ -1334,89 +1337,95 @@ XrayResolveUnforgeableProperty(JSContext
 
 static bool
 XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                     JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                     JS::MutableHandle<JS::PropertyDescriptor> desc,
                     bool& cacheOnHolder, DOMObjectType type,
                     const NativeProperties* nativeProperties)
 {
-  const Prefable<const JSFunctionSpec>* methods;
-  jsid* methodIds;
-  const JSFunctionSpec* methodSpecs;
+  bool hasMethods = false;
   if (type == eInterface) {
-    methods = nativeProperties->staticMethods;
-    methodIds = nativeProperties->staticMethodIds;
-    methodSpecs = nativeProperties->staticMethodSpecs;
+    hasMethods = nativeProperties->HasStaticMethods();
   } else {
-    methods = nativeProperties->methods;
-    methodIds = nativeProperties->methodIds;
-    methodSpecs = nativeProperties->methodSpecs;
+    hasMethods = nativeProperties->HasMethods();
   }
-  if (methods) {
+  if (hasMethods) {
+    const Prefable<const JSFunctionSpec>* methods;
+    const jsid* methodIds;
+    const JSFunctionSpec* methodSpecs;
+    if (type == eInterface) {
+      methods = nativeProperties->StaticMethods();
+      methodIds = nativeProperties->StaticMethodIds();
+      methodSpecs = nativeProperties->StaticMethodSpecs();
+    } else {
+      methods = nativeProperties->Methods();
+      methodIds = nativeProperties->MethodIds();
+      methodSpecs = nativeProperties->MethodSpecs();
+    }
     JS::Rooted<jsid> methodId(cx);
     if (nativeProperties->iteratorAliasMethodIndex != -1 &&
         id == SYMBOL_TO_JSID(
                 JS::GetWellKnownSymbol(cx, JS::SymbolCode::iterator))) {
       methodId =
-        nativeProperties->methodIds[nativeProperties->iteratorAliasMethodIndex];
+        nativeProperties->MethodIds()[nativeProperties->iteratorAliasMethodIndex];
     } else {
       methodId = id;
     }
     if (!XrayResolveMethod(cx, wrapper, obj, methodId, methods, methodIds,
                            methodSpecs, desc, cacheOnHolder)) {
       return false;
     }
     if (desc.object()) {
       return true;
     }
   }
 
   if (type == eInterface) {
-    if (nativeProperties->staticAttributes) {
+    if (nativeProperties->HasStaticAttributes()) {
       if (!XrayResolveAttribute(cx, wrapper, obj, id,
-                                nativeProperties->staticAttributes,
-                                nativeProperties->staticAttributeIds,
-                                nativeProperties->staticAttributeSpecs, desc,
-                                cacheOnHolder)) {
+                                nativeProperties->StaticAttributes(),
+                                nativeProperties->StaticAttributeIds(),
+                                nativeProperties->StaticAttributeSpecs(),
+                                desc, cacheOnHolder)) {
         return false;
       }
       if (desc.object()) {
         return true;
       }
     }
   } else {
-    if (nativeProperties->attributes) {
+    if (nativeProperties->HasAttributes()) {
       if (!XrayResolveAttribute(cx, wrapper, obj, id,
-                                nativeProperties->attributes,
-                                nativeProperties->attributeIds,
-                                nativeProperties->attributeSpecs, desc,
-                                cacheOnHolder)) {
+                                nativeProperties->Attributes(),
+                                nativeProperties->AttributeIds(),
+                                nativeProperties->AttributeSpecs(),
+                                desc, cacheOnHolder)) {
         return false;
       }
       if (desc.object()) {
         return true;
       }
     }
   }
 
-  if (nativeProperties->constants) {
+  if (nativeProperties->HasConstants()) {
     const Prefable<const ConstantSpec>* constant;
-    for (constant = nativeProperties->constants; constant->specs; ++constant) {
+    for (constant = nativeProperties->Constants(); constant->specs; ++constant) {
       if (constant->isEnabled(cx, obj)) {
         // Set i to be the index into our full list of ids/specs that we're
         // looking at now.
-        size_t i = constant->specs - nativeProperties->constantSpecs;
-        for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) {
-          if (id == nativeProperties->constantIds[i]) {
+        size_t i = constant->specs - nativeProperties->ConstantSpecs();
+        for ( ; nativeProperties->ConstantIds()[i] != JSID_VOID; ++i) {
+          if (id == nativeProperties->ConstantIds()[i]) {
             cacheOnHolder = true;
 
             desc.setAttributes(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
             desc.object().set(wrapper);
-            desc.value().set(nativeProperties->constantSpecs[i].value);
+            desc.value().set(nativeProperties->ConstantSpecs()[i].value);
             return true;
           }
         }
       }
     }
   }
 
   return true;
@@ -1630,17 +1639,17 @@ XrayDefineProperty(JSContext* cx, JS::Ha
   return handler->defineProperty(cx, wrapper, id, desc, result, defined);
 }
 
 template<typename SpecType>
 bool
 XrayAttributeOrMethodKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
                           JS::Handle<JSObject*> obj,
                           const Prefable<const SpecType>* list,
-                          jsid* ids, const SpecType* specList,
+                          const jsid* ids, const SpecType* specList,
                           unsigned flags, JS::AutoIdVector& props)
 {
   for (; list->specs; ++list) {
     if (list->isEnabled(cx, obj)) {
       // Set i to be the index into our full list of ids/specs that we're
       // looking at now.
       size_t i = list->specs - specList;
       for ( ; ids[i] != JSID_VOID; ++i) {
@@ -1653,62 +1662,62 @@ XrayAttributeOrMethodKeys(JSContext* cx,
           return false;
         }
       }
     }
   }
   return true;
 }
 
-#define ADD_KEYS_IF_DEFINED(fieldName) {                                      \
-  if (nativeProperties->fieldName##s &&                                       \
+#define ADD_KEYS_IF_DEFINED(FieldName) {                                      \
+  if (nativeProperties->Has##FieldName##s() &&                                \
       !XrayAttributeOrMethodKeys(cx, wrapper, obj,                            \
-                                 nativeProperties->fieldName##s,              \
-                                 nativeProperties->fieldName##Ids,            \
-                                 nativeProperties->fieldName##Specs,          \
+                                 nativeProperties->FieldName##s(),            \
+                                 nativeProperties->FieldName##Ids(),          \
+                                 nativeProperties->FieldName##Specs(),        \
                                  flags, props)) {                             \
     return false;                                                             \
   }                                                                           \
 }
 
 
 bool
 XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
                     JS::Handle<JSObject*> obj,
                     unsigned flags, JS::AutoIdVector& props,
                     DOMObjectType type,
                     const NativeProperties* nativeProperties)
 {
   MOZ_ASSERT(type != eNamedPropertiesObject);
 
   if (IsInstance(type)) {
-    ADD_KEYS_IF_DEFINED(unforgeableMethod);
-    ADD_KEYS_IF_DEFINED(unforgeableAttribute);
+    ADD_KEYS_IF_DEFINED(UnforgeableMethod);
+    ADD_KEYS_IF_DEFINED(UnforgeableAttribute);
     if (type == eGlobalInstance) {
-      ADD_KEYS_IF_DEFINED(method);
-      ADD_KEYS_IF_DEFINED(attribute);
+      ADD_KEYS_IF_DEFINED(Method);
+      ADD_KEYS_IF_DEFINED(Attribute);
     }
   } else if (type == eInterface) {
-    ADD_KEYS_IF_DEFINED(staticMethod);
-    ADD_KEYS_IF_DEFINED(staticAttribute);
+    ADD_KEYS_IF_DEFINED(StaticMethod);
+    ADD_KEYS_IF_DEFINED(StaticAttribute);
   } else if (type != eGlobalInterfacePrototype) {
     MOZ_ASSERT(IsInterfacePrototype(type));
-    ADD_KEYS_IF_DEFINED(method);
-    ADD_KEYS_IF_DEFINED(attribute);
+    ADD_KEYS_IF_DEFINED(Method);
+    ADD_KEYS_IF_DEFINED(Attribute);
   }
 
-  if (nativeProperties->constants) {
+  if (nativeProperties->HasConstants()) {
     const Prefable<const ConstantSpec>* constant;
-    for (constant = nativeProperties->constants; constant->specs; ++constant) {
+    for (constant = nativeProperties->Constants(); constant->specs; ++constant) {
       if (constant->isEnabled(cx, obj)) {
         // Set i to be the index into our full list of ids/specs that we're
         // looking at now.
-        size_t i = constant->specs - nativeProperties->constantSpecs;
-        for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) {
-          if (!props.append(nativeProperties->constantIds[i])) {
+        size_t i = constant->specs - nativeProperties->ConstantSpecs();
+        for ( ; nativeProperties->ConstantIds()[i] != JSID_VOID; ++i) {
+          if (!props.append(nativeProperties->ConstantIds()[i])) {
             return false;
           }
         }
       }
     }
   }
 
   return true;
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -327,21 +327,21 @@ class CGNativePropertyHooks(CGThing):
             enumerateOwnProperties = "EnumerateOwnProperties"
         elif self.descriptor.needsXrayResolveHooks():
             resolveOwnProperty = "ResolveOwnPropertyViaResolve"
             enumerateOwnProperties = "EnumerateOwnPropertiesViaGetOwnPropertyNames"
         else:
             resolveOwnProperty = "nullptr"
             enumerateOwnProperties = "nullptr"
         if self.properties.hasNonChromeOnly():
-            regular = "&sNativeProperties"
+            regular = "sNativeProperties.Upcast()"
         else:
             regular = "nullptr"
         if self.properties.hasChromeOnly():
-            chrome = "&sChromeOnlyNativeProperties"
+            chrome = "sChromeOnlyNativeProperties.Upcast()"
         else:
             chrome = "nullptr"
         constructorID = "constructors::id::"
         if self.descriptor.interface.hasInterfaceObject():
             constructorID += self.descriptor.name
         else:
             constructorID += "_ID_Count"
         prototypeID = "prototypes::id::"
@@ -2648,38 +2648,54 @@ class PropertyArrays():
 
 
 class CGNativeProperties(CGList):
     def __init__(self, descriptor, properties):
         def generateNativeProperties(name, chrome):
             def check(p):
                 return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
 
-            nativeProps = []
-            for array in properties.arrayNames():
-                propertyArray = getattr(properties, array)
-                if check(propertyArray):
-                    if propertyArray.usedForXrays():
-                        ids = "%(name)s_ids"
-                    else:
-                        ids = "nullptr"
-                    props = "%(name)s, " + ids + ", %(name)s_specs"
-                    props = (props % {'name': propertyArray.variableName(chrome)})
-                else:
-                    props = "nullptr, nullptr, nullptr"
-                nativeProps.append(CGGeneric(props))
+            nativePropsInts = []
+            nativePropsTrios = []
+
             iteratorAliasIndex = -1
             for index, item in enumerate(properties.methods.regular):
                 if item.get("hasIteratorAlias"):
                     iteratorAliasIndex = index
                     break
-            nativeProps.append(CGGeneric(str(iteratorAliasIndex)))
+            nativePropsInts.append(CGGeneric(str(iteratorAliasIndex)))
+
+            offset = 0
+            for array in properties.arrayNames():
+                propertyArray = getattr(properties, array)
+                if check(propertyArray):
+                    varName = propertyArray.variableName(chrome)
+                    bitfields = "true,  %d /* %s */" % (offset, varName)
+                    offset += 1
+                    nativePropsInts.append(CGGeneric(bitfields))
+
+                    if propertyArray.usedForXrays():
+                        ids = "%(name)s_ids"
+                    else:
+                        ids = "nullptr"
+                    trio = "{ %(name)s, " + ids + ", %(name)s_specs }"
+                    trio = trio % {'name': varName}
+                    nativePropsTrios.append(CGGeneric(trio))
+                else:
+                    bitfields = "false, 0"
+                    nativePropsInts.append(CGGeneric(bitfields))
+
+            nativePropsTrios = \
+                [CGWrapper(CGIndenter(CGList(nativePropsTrios, ",\n")),
+                           pre='{\n', post='\n}')]
+            nativeProps = nativePropsInts + nativePropsTrios
+            pre = ("static const NativePropertiesN<%d> %s = {\n" %
+                   (offset, name))
             return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")),
-                             pre="static const NativeProperties %s = {\n" % name,
-                             post="\n};\n")
+                             pre=pre, post="\n};\n")
 
         nativeProperties = []
         if properties.hasNonChromeOnly():
             nativeProperties.append(
                 generateNativeProperties("sNativeProperties", False))
         if properties.hasChromeOnly():
             nativeProperties.append(
                 generateNativeProperties("sChromeOnlyNativeProperties", True))
@@ -2856,21 +2872,21 @@ class CGCreateInterfaceObjectsMethod(CGA
         else:
             # We don't have slots to store the named constructors.
             assert len(self.descriptor.interface.namedConstructors) == 0
             interfaceClass = "nullptr"
             interfaceCache = "nullptr"
 
         isGlobal = self.descriptor.isGlobal() is not None
         if not isGlobal and self.properties.hasNonChromeOnly():
-            properties = "&sNativeProperties"
+            properties = "sNativeProperties.Upcast()"
         else:
             properties = "nullptr"
         if not isGlobal and self.properties.hasChromeOnly():
-            chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? &sChromeOnlyNativeProperties : nullptr"
+            chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr"
         else:
             chromeProperties = "nullptr"
 
         call = fill(
             """
             JS::Heap<JSObject*>* protoCache = ${protoCache};
             JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
             dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
@@ -3737,21 +3753,21 @@ class CGWrapGlobalMethod(CGAbstractMetho
                 Argument('bool', 'aInitStandardClasses'),
                 Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
         CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
         self.descriptor = descriptor
         self.properties = properties
 
     def definition_body(self):
         if self.properties.hasNonChromeOnly():
-            properties = "&sNativeProperties"
+            properties = "sNativeProperties.Upcast()"
         else:
             properties = "nullptr"
         if self.properties.hasChromeOnly():
-            chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? &sChromeOnlyNativeProperties : nullptr"
+            chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr"
         else:
             chromeProperties = "nullptr"
 
         if self.descriptor.workers:
             fireOnNewGlobal = """// XXXkhuey can't do this yet until workers can lazy resolve.
 // JS_FireOnNewGlobalObject(aCx, aReflector);
 """
         else:
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -153,49 +153,110 @@ struct Prefable {
   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* const specs;
 };
 
-struct NativeProperties
-{
-  const Prefable<const JSFunctionSpec>* staticMethods;
-  jsid* staticMethodIds;
-  const JSFunctionSpec* staticMethodSpecs;
+// Conceptually, NativeProperties has seven (Prefable<T>*, jsid*, T*) trios
+// (where T is one of JSFunctionSpec, JSPropertySpec, or ConstantSpec), one for
+// each of: static methods and attributes, methods and attributes, unforgeable
+// methods and attributes, and constants.
+//
+// That's 21 pointers, but in most instances most of the trios are all null,
+// and there are many instances. To save space we use a variable-length type,
+// NativePropertiesN<N>, to hold the data and getters to access it. It has N
+// actual trios (stored in trios[]), plus four bits for each of the 7 possible
+// trios: 1 bit that states if that trio is present, and 3 that state that
+// trio's offset (if present) in trios[].
+//
+// All trio accesses should be done via the getters, which contain assertions
+// that check we don't overrun the end of the struct. (The trio data members are
+// public only so they can be statically initialized.) These assertions should
+// never fail so long as (a) accesses to the variable-length part are guarded by
+// appropriate Has*() calls, and (b) all instances are well-formed, i.e. the
+// value of N matches the number of mHas* members that are true.
+//
+// Finally, we define a typedef of NativePropertiesN<7>, NativeProperties, which
+// we use as a "base" type used to refer to all instances of NativePropertiesN.
+// (7 is used because that's the maximum valid parameter, though any other
+// value 1..6 could also be used.) This is reasonable because of the
+// aforementioned assertions in the getters. Upcast() is used to convert
+// specific instances to this "base" type.
+//
+template <int N>
+struct NativePropertiesN {
+  // Trio structs are stored in the trios[] array, and each element in the
+  // array could require a different T. Therefore, we can't use the correct
+  // type for mPrefables and mSpecs. Instead we use void* and cast to the
+  // correct type in the getters.
+  struct Trio {
+    const /*Prefable<const T>*/ void* const mPrefables;
+    const jsid* const mIds;
+    const /*T*/ void* const mSpecs;
+  };
 
-  const Prefable<const JSPropertySpec>* staticAttributes;
-  jsid* staticAttributeIds;
-  const JSPropertySpec* staticAttributeSpecs;
+  const int32_t iteratorAliasMethodIndex;
 
-  const Prefable<const JSFunctionSpec>* methods;
-  jsid* methodIds;
-  const JSFunctionSpec* methodSpecs;
+  constexpr const NativePropertiesN<7>* Upcast() const {
+    return reinterpret_cast<const NativePropertiesN<7>*>(this);
+  }
 
-  const Prefable<const JSPropertySpec>* attributes;
-  jsid* attributeIds;
-  const JSPropertySpec* attributeSpecs;
-
-  const Prefable<const JSFunctionSpec>* unforgeableMethods;
-  jsid* unforgeableMethodIds;
-  const JSFunctionSpec* unforgeableMethodSpecs;
+#define DO(SpecT, FieldName) \
+public: \
+  /* The bitfields indicating the trio's presence and (if present) offset. */ \
+  const uint32_t mHas##FieldName##s:1; \
+  const uint32_t m##FieldName##sOffset:3; \
+private: \
+  const Trio* FieldName##sTrio() const { \
+    MOZ_ASSERT(Has##FieldName##s()); \
+    return &trios[m##FieldName##sOffset]; \
+  } \
+public: \
+  bool Has##FieldName##s() const { \
+    return mHas##FieldName##s; \
+  } \
+  const Prefable<const SpecT>* FieldName##s() const { \
+    return static_cast<const Prefable<const SpecT>*> \
+                      (FieldName##sTrio()->mPrefables); \
+  } \
+  const jsid* FieldName##Ids() const { \
+    return FieldName##sTrio()->mIds; \
+  } \
+  const SpecT* FieldName##Specs() const { \
+    return static_cast<const SpecT*>(FieldName##sTrio()->mSpecs); \
+  }
 
-  const Prefable<const JSPropertySpec>* unforgeableAttributes;
-  jsid* unforgeableAttributeIds;
-  const JSPropertySpec* unforgeableAttributeSpecs;
+  DO(JSFunctionSpec, StaticMethod)
+  DO(JSPropertySpec, StaticAttribute)
+  DO(JSFunctionSpec, Method)
+  DO(JSPropertySpec, Attribute)
+  DO(JSFunctionSpec, UnforgeableMethod)
+  DO(JSPropertySpec, UnforgeableAttribute)
+  DO(ConstantSpec,   Constant)
+
+#undef DO
+
+  const Trio trios[N];
+};
 
-  const Prefable<const ConstantSpec>* constants;
-  jsid* constantIds;
-  const ConstantSpec* constantSpecs;
+// Ensure the struct has the expected size. The 8 is for the
+// iteratorAliasMethodIndex plus the bitfields; the rest is for trios[].
+static_assert(sizeof(NativePropertiesN<1>) == 8 +  3*sizeof(void*), "1 size");
+static_assert(sizeof(NativePropertiesN<2>) == 8 +  6*sizeof(void*), "2 size");
+static_assert(sizeof(NativePropertiesN<3>) == 8 +  9*sizeof(void*), "3 size");
+static_assert(sizeof(NativePropertiesN<4>) == 8 + 12*sizeof(void*), "4 size");
+static_assert(sizeof(NativePropertiesN<5>) == 8 + 15*sizeof(void*), "5 size");
+static_assert(sizeof(NativePropertiesN<6>) == 8 + 18*sizeof(void*), "6 size");
+static_assert(sizeof(NativePropertiesN<7>) == 8 + 21*sizeof(void*), "7 size");
 
-  // Index into methods for the entry that is [Alias="@@iterator"], -1 if none
-  int32_t iteratorAliasMethodIndex;
-};
+// The "base" type.
+typedef NativePropertiesN<7> NativeProperties;
 
 struct NativePropertiesHolder
 {
   const NativeProperties* regular;
   const NativeProperties* chromeOnly;
 };
 
 // Helper structure for Xrays for DOM binding objects. The same instance is used
--- a/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp
+++ b/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp
@@ -555,18 +555,18 @@ ShimInterfaceInfo::GetConstantCount(uint
     const mozilla::dom::NativePropertyHooks* propHooks = mNativePropHooks;
     do {
         const mozilla::dom::NativeProperties* props[] = {
             propHooks->mNativeProperties.regular,
             propHooks->mNativeProperties.chromeOnly
         };
         for (size_t i = 0; i < ArrayLength(props); ++i) {
             auto prop = props[i];
-            if (prop && prop->constants) {
-                for (auto cs = prop->constants->specs; cs->name; ++cs) {
+            if (prop && prop->HasConstants()) {
+                for (auto cs = prop->Constants()->specs; cs->name; ++cs) {
                     // We have found one constant here.  We explicitly do not
                     // bother calling isEnabled() here because it's OK to define
                     // potentially extra constants on these shim interfaces.
                     ++count;
                 }
             }
         }
     } while ((propHooks = propHooks->mProtoHooks));
@@ -601,18 +601,18 @@ ShimInterfaceInfo::GetConstant(uint16_t 
     const mozilla::dom::NativePropertyHooks* propHooks = mNativePropHooks;
     do {
         const mozilla::dom::NativeProperties* props[] = {
             propHooks->mNativeProperties.regular,
             propHooks->mNativeProperties.chromeOnly
         };
         for (size_t i = 0; i < ArrayLength(props); ++i) {
             auto prop = props[i];
-            if (prop && prop->constants) {
-                for (auto cs = prop->constants->specs; cs->name; ++cs) {
+            if (prop && prop->HasConstants()) {
+                for (auto cs = prop->Constants()->specs; cs->name; ++cs) {
                     // We have found one constant here.  We explicitly do not
                     // bother calling isEnabled() here because it's OK to define
                     // potentially extra constants on these shim interfaces.
                     if (index == aIndex) {
                         aConstant.set(cs->value);
                         *aName = ToNewCString(nsDependentCString(cs->name));
                         return NS_OK;
                     }