Bug 1444286. Common up the Get*ObjectHandle methods in bindings. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 09 Mar 2018 16:04:11 -0500
changeset 765599 5b750710adaa157b6fb593a5744bae758379f616
parent 765598 e2a6bd47f69707533d8cb6eda535e9a011bfa8bd
child 765600 ac6bc78dadb9d7fb66d8c8b67d65948e1ed64b9a
push id102114
push userbmo:jgilbert@mozilla.com
push dateFri, 09 Mar 2018 22:13:16 +0000
reviewerspeterv
bugs1444286
milestone60.0a1
Bug 1444286. Common up the Get*ObjectHandle methods in bindings. r=peterv This reduces codesize, at the cost of a bit more includes in the binding headers. MozReview-Commit-ID: 40dLELF36Oh
dom/bindings/BindingDeclarations.h
dom/bindings/BindingUtils.cpp
dom/bindings/Codegen.py
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -538,12 +538,23 @@ enum class CallerType : uint32_t {
 // A class that can be passed (by value or const reference) to indicate that the
 // caller is always a system caller.  This can be used as the type of an
 // argument to force only system callers to call a function.
 class SystemCallerGuarantee {
 public:
   operator CallerType() const { return CallerType::System; }
 };
 
+class ProtoAndIfaceCache;
+typedef void (*CreateInterfaceObjectsMethod)(JSContext* aCx,
+                                             JS::Handle<JSObject*> aGlobal,
+                                             ProtoAndIfaceCache& aCache,
+                                             bool aDefineOnGlobal);
+JS::Handle<JSObject*> GetPerInterfaceObjectHandle(
+  JSContext* aCx,
+  size_t aSlotId,
+  CreateInterfaceObjectsMethod aCreator,
+  bool aDefineOnGlobal);
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_BindingDeclarations_h__
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3800,10 +3800,46 @@ UnprivilegedJunkScopeOrWorkerGlobal()
   if (NS_IsMainThread()) {
     return xpc::UnprivilegedJunkScope();
   }
 
   return GetCurrentThreadWorkerGlobal();
 }
 } // namespace binding_detail
 
+JS::Handle<JSObject*>
+GetPerInterfaceObjectHandle(JSContext* aCx,
+                            size_t aSlotId,
+                            CreateInterfaceObjectsMethod aCreator,
+                            bool aDefineOnGlobal)
+{
+  /* Make sure our global is sane.  Hopefully we can remove this sometime */
+  JSObject* global = JS::CurrentGlobalOrNull(aCx);
+  if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
+    return nullptr;
+  }
+
+  /* Check to see whether the interface objects are already installed */
+  ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
+  if (!protoAndIfaceCache.HasEntryInSlot(aSlotId)) {
+    JS::Rooted<JSObject*> rootedGlobal(aCx, global);
+    aCreator(aCx, rootedGlobal, protoAndIfaceCache, aDefineOnGlobal);
+  }
+
+  /*
+   * The object might _still_ be null, but that's OK.
+   *
+   * Calling fromMarkedLocation() is safe because protoAndIfaceCache is
+   * traced by TraceProtoAndIfaceCache() and its contents are never
+   * changed after they have been set.
+   *
+   * Calling address() avoids the read barrier that does gray unmarking, but
+   * it's not possible for the object to be gray here.
+   */
+
+  const JS::Heap<JSObject*>& entrySlot =
+    protoAndIfaceCache.EntrySlotMustExist(aSlotId);
+  MOZ_ASSERT(JS::ObjectIsNotGray(entrySlot));
+  return JS::Handle<JSObject*>::fromMarkedLocation(entrySlot.address());
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3395,107 +3395,76 @@ class CGCreateInterfaceObjectsMethod(CGA
 
         return CGList(
             [getParentProto, getConstructorProto, initIds,
              prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup,
              makeProtoPrototypeImmutable],
             "\n").define()
 
 
-class CGGetPerInterfaceObject(CGAbstractMethod):
-    """
-    A method for getting a per-interface object (a prototype object or interface
-    constructor object).
-    """
-    def __init__(self, descriptor, name, idPrefix="", extraArgs=[]):
-        args = [Argument('JSContext*', 'aCx')] + extraArgs
-        CGAbstractMethod.__init__(self, descriptor, name,
-                                  'JS::Handle<JSObject*>', args)
-        self.id = idPrefix + "id::" + self.descriptor.name
-
-    def definition_body(self):
-        return fill(
-            """
-            /* Make sure our global is sane.  Hopefully we can remove this sometime */
-            JSObject* global = JS::CurrentGlobalOrNull(aCx);
-            if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
-              return nullptr;
-            }
-
-            /* Check to see whether the interface objects are already installed */
-            ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
-            if (!protoAndIfaceCache.HasEntryInSlot(${id})) {
-              JS::Rooted<JSObject*> rootedGlobal(aCx, global);
-              CreateInterfaceObjects(aCx, rootedGlobal, protoAndIfaceCache, aDefineOnGlobal);
-            }
-
-            /*
-             * The object might _still_ be null, but that's OK.
-             *
-             * Calling fromMarkedLocation() is safe because protoAndIfaceCache is
-             * traced by TraceProtoAndIfaceCache() and its contents are never
-             * changed after they have been set.
-             *
-             * Calling address() avoids the read read barrier that does gray
-             * unmarking, but it's not possible for the object to be gray here.
-             */
-
-            const JS::Heap<JSObject*>& entrySlot = protoAndIfaceCache.EntrySlotMustExist(${id});
-            MOZ_ASSERT(JS::ObjectIsNotGray(entrySlot));
-            return JS::Handle<JSObject*>::fromMarkedLocation(entrySlot.address());
-            """,
-            id=self.id)
-
-
-class CGGetProtoObjectHandleMethod(CGGetPerInterfaceObject):
+class CGGetProtoObjectHandleMethod(CGAbstractMethod):
     """
     A method for getting the interface prototype object.
     """
     def __init__(self, descriptor):
-        CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObjectHandle",
-                                         "prototypes::")
+        CGAbstractMethod.__init__(
+            self, descriptor, "GetProtoObjectHandle",
+            'JS::Handle<JSObject*>',
+            [Argument('JSContext*', 'aCx')],
+            inline=True)
 
     def definition_body(self):
-        return dedent("""
+        return fill(
+            """
             /* Get the interface prototype object for this class.  This will create the
                object as needed. */
-            bool aDefineOnGlobal = true;
-
-            """) + CGGetPerInterfaceObject.definition_body(self)
+            return GetPerInterfaceObjectHandle(aCx, prototypes::id::${name},
+                                               &CreateInterfaceObjects,
+                                               /* aDefineOnGlobal = */ true);
+
+            """,
+            name=self.descriptor.name)
 
 
 class CGGetProtoObjectMethod(CGAbstractMethod):
     """
     A method for getting the interface prototype object.
     """
     def __init__(self, descriptor):
         CGAbstractMethod.__init__(
             self, descriptor, "GetProtoObject", "JSObject*",
             [Argument('JSContext*', 'aCx')])
 
     def definition_body(self):
         return "return GetProtoObjectHandle(aCx);\n"
 
 
-class CGGetConstructorObjectHandleMethod(CGGetPerInterfaceObject):
+class CGGetConstructorObjectHandleMethod(CGAbstractMethod):
     """
     A method for getting the interface constructor object.
     """
     def __init__(self, descriptor):
-        CGGetPerInterfaceObject.__init__(
+        CGAbstractMethod.__init__(
             self, descriptor, "GetConstructorObjectHandle",
-            "constructors::",
-            extraArgs=[Argument("bool", "aDefineOnGlobal", "true")])
+            'JS::Handle<JSObject*>',
+            [Argument('JSContext*', 'aCx'),
+             Argument('bool', 'aDefineOnGlobal', 'true')],
+            inline=True)
 
     def definition_body(self):
-        return dedent("""
+        return fill(
+            """
             /* Get the interface object for this class.  This will create the object as
                needed. */
 
-            """) + CGGetPerInterfaceObject.definition_body(self)
+            return GetPerInterfaceObjectHandle(aCx, constructors::id::${name},
+                                               &CreateInterfaceObjects,
+                                               aDefineOnGlobal);
+            """,
+            name=self.descriptor.name)
 
 
 class CGGetConstructorObjectMethod(CGAbstractMethod):
     """
     A method for getting the interface constructor object.
     """
     def __init__(self, descriptor):
         CGAbstractMethod.__init__(
@@ -14381,16 +14350,17 @@ class CGBindingRoot(CGThing):
         hasNonEmptyDictionaries = any(
             len(dict.members) > 0 for dict in dictionaries)
         callbacks = config.getCallbacks(webIDLFile)
         callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
                                                     isCallback=True)
         jsImplemented = config.getDescriptors(webIDLFile=webIDLFile,
                                               isJSImplemented=True)
         bindingDeclareHeaders["nsWeakReference.h"] = jsImplemented
+        bindingDeclareHeaders["mozilla/dom/PrototypeList.h"] = descriptors
         bindingHeaders["nsIGlobalObject.h"] = jsImplemented
         bindingHeaders["AtomList.h"] = hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
 
         def descriptorClearsPropsInSlots(descriptor):
             if not descriptor.wrapperCache:
                 return False
             return any(m.isAttr() and m.getExtendedAttribute("StoreInSlot")
                        for m in descriptor.interface.members)
@@ -17327,16 +17297,17 @@ class GlobalGenRoots():
                                       "Too many prototypes!"))
 
         # Wrap all of that in our namespaces.
         idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'],
                                    CGWrapper(idEnum, pre='\n'))
         idEnum = CGWrapper(idEnum, post='\n')
 
         curr = CGList([CGGeneric(define="#include <stdint.h>\n\n"),
+                       CGGeneric(declare='#include "jsfriendapi.h"\n\n'),
                        idEnum])
 
         # Let things know the maximum length of the prototype chain.
         maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH"
         maxMacro = CGGeneric(declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength))
         curr.append(CGWrapper(maxMacro, post='\n\n'))
         curr.append(fieldSizeAssert(maxMacroName, "depth",
                                     "Some inheritance chain is too long!"))