Bug 1456261. Add cycle collection bits for WebIDL dictionaries. r=smaug
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 24 Apr 2018 11:57:40 -0400
changeset 468921 0f0c781e4886f4d1ee667033b38f653405569759
parent 468920 26e4719fcec6ae8b764c1236544ffbef93a13c78
child 468922 a6aad40b3d02f3e71074b8fbcf7705af243cf7f4
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1456261
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1456261. Add cycle collection bits for WebIDL dictionaries. r=smaug
dom/bindings/BindingDeclarations.h
dom/bindings/Codegen.py
xpcom/base/OwningNonNull.h
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -61,16 +61,33 @@ private:
 
 public:
   bool IsAnyMemberPresent() const
   {
     return mIsAnyMemberPresent;
   }
 };
 
+template<typename T>
+inline typename EnableIf<IsBaseOf<DictionaryBase, T>::value, void>::Type
+ImplCycleCollectionUnlink(T& aDictionary)
+{
+  aDictionary.UnlinkForCC();
+}
+
+template<typename T>
+inline typename EnableIf<IsBaseOf<DictionaryBase, T>::value, void>::Type
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+                            T& aDictionary,
+                            const char* aName,
+                            uint32_t aFlags = 0)
+{
+  aDictionary.TraverseForCC(aCallback, aFlags);
+}
+
 // Struct that serves as a base class for all typed arrays and array buffers and
 // array buffer views.  Particularly useful so we can use IsBaseOf to detect
 // typed array/buffer/view template arguments.
 struct AllTypedArraysBase {
 };
 
 // Struct that serves as a base class for all owning unions.
 // Particularly useful so we can use IsBaseOf to detect owning union
@@ -377,16 +394,37 @@ public:
 private:
   // Forbid copy-construction and assignment
   Optional(const Optional& other) = delete;
   const Optional &operator=(const Optional &other) = delete;
 
   const nsAString* mStr;
 };
 
+template<typename T>
+inline void
+ImplCycleCollectionUnlink(Optional<T>& aField)
+{
+  if (aField.WasPassed()) {
+    ImplCycleCollectionUnlink(aField.Value());
+  }
+}
+
+template<typename T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+                            Optional<T>& aField,
+                            const char* aName,
+                            uint32_t aFlags = 0)
+{
+  if (aField.WasPassed()) {
+    ImplCycleCollectionTraverse(aCallback, aField.Value(), aName, aFlags);
+  }
+}
+
 template<class T>
 class NonNull
 {
 public:
   NonNull()
 #ifdef DEBUG
     : inited(false)
 #endif
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -68,39 +68,43 @@ def isTypeCopyConstructible(type):
             (type.isDictionary() and
              CGDictionary.isDictionaryCopyConstructible(type.inner)) or
             # Interface types are only copy-constructible if they're Gecko
             # interfaces.  SpiderMonkey interfaces are not copy-constructible
             # because of rooting issues.
             (type.isInterface() and type.isGeckoInterface()))
 
 
+class CycleCollectionUnsupported(TypeError):
+    def __init__(self, message):
+        TypeError.__init__(self, message)
+
+
 def idlTypeNeedsCycleCollection(type):
     type = type.unroll()  # Takes care of sequences and nullables
     if ((type.isPrimitive() and type.tag() in builtinNames) or
         type.isEnum() or
         type.isString() or
         type.isAny() or
         type.isObject() or
         type.isSpiderMonkeyInterface()):
         return False
     elif type.isCallback() or type.isPromise() or type.isGeckoInterface():
         return True
     elif type.isUnion():
         return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
     elif type.isRecord():
         if idlTypeNeedsCycleCollection(type.inner):
-            raise TypeError("Cycle collection for type %s is not supported" % type)
+            raise CycleCollectionUnsupported("Cycle collection for type %s is not supported" %
+                                             type)
         return False
     elif type.isDictionary():
-        if any(idlTypeNeedsCycleCollection(m.type) for m in type.inner.members):
-            raise TypeError("Cycle collection for type %s is not supported" % type)
-        return False
+        return CGDictionary.dictionaryNeedsCycleCollection(type.inner)
     else:
-        raise TypeError("Don't know whether to cycle-collect type %s" % type)
+        raise CycleCollectionUnsupported("Don't know whether to cycle-collect type %s" % type)
 
 
 def wantsAddProperty(desc):
     return (desc.concrete and desc.wrapperCache and not desc.isGlobal())
 
 
 # We'll want to insert the indent at the beginnings of lines, but we
 # don't want to indent empty lines.  So only indent lines that have a
@@ -12823,16 +12827,66 @@ class CGDictionary(CGThing):
 
         if memberTraces:
             body += "\n".join(memberTraces)
 
         return ClassMethod("TraceDictionary", "void", [
             Argument("JSTracer*", "trc"),
         ], body=body)
 
+    @staticmethod
+    def dictionaryNeedsCycleCollection(dictionary):
+        return (any(idlTypeNeedsCycleCollection(m.type) for m in dictionary.members) or
+                (dictionary.parent and
+                 CGDictionary.dictionaryNeedsCycleCollection(dictionary.parent)))
+
+    def traverseForCCMethod(self):
+        body = ""
+        if (self.dictionary.parent and
+            self.dictionaryNeedsCycleCollection(self.dictionary.parent)):
+            cls = self.makeClassName(self.dictionary.parent)
+            body += "%s::TraverseForCC(aCallback, aFlags);\n" % cls
+
+        for m, _ in self.memberInfo:
+            if idlTypeNeedsCycleCollection(m.type):
+                memberName = self.makeMemberName(m.identifier.name);
+                body += ('ImplCycleCollectionTraverse(aCallback, %s, "%s", aFlags);\n' %
+                         (memberName, memberName))
+
+        return ClassMethod(
+            "TraverseForCC", "void",
+            [
+                Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
+                Argument("uint32_t", "aFlags"),
+            ],
+            body=body,
+            # Inline so we don't pay a codesize hit unless someone actually uses
+            # this traverse method.
+            inline=True,
+            bodyInHeader=True)
+
+    def unlinkForCCMethod(self):
+        body = ""
+        if (self.dictionary.parent and
+            self.dictionaryNeedsCycleCollection(self.dictionary.parent)):
+            cls = self.makeClassName(self.dictionary.parent)
+            body += "%s::UnlinkForCC();\n" % cls
+
+        for m, _ in self.memberInfo:
+            if idlTypeNeedsCycleCollection(m.type):
+                memberName = self.makeMemberName(m.identifier.name);
+                body += ('ImplCycleCollectionUnlink(%s);\n' % memberName)
+
+        return ClassMethod(
+            "UnlinkForCC", "void", [], body=body,
+            # Inline so we don't pay a codesize hit unless someone actually uses
+            # this unlink method.
+            inline=True,
+            bodyInHeader=True)
+
     def assignmentOperator(self):
         body = CGList([])
         if self.dictionary.parent:
             body.append(CGGeneric(
                 "%s::operator=(aOther);\n" %
                 self.makeClassName(self.dictionary.parent)))
         for m, _ in self.memberInfo:
             memberName = self.makeMemberName(m.identifier.name)
@@ -12907,16 +12961,26 @@ class CGDictionary(CGThing):
         except MethodNotNewObjectError:
             # If we can't have a ToObjectInternal() because one of our members
             # can only be returned from [NewObject] methods, then just skip
             # generating ToObjectInternal() and ToJSON (since the latter depens
             # on the former).
             pass
         methods.append(self.traceDictionaryMethod())
 
+        try:
+            if self.dictionaryNeedsCycleCollection(d):
+                methods.append(self.traverseForCCMethod())
+                methods.append(self.unlinkForCCMethod())
+        except CycleCollectionUnsupported:
+            # We have some member that we don't know how to CC.  Don't output
+            # our cycle collection overloads, so attempts to CC us will fail to
+            # compile instead of misbehaving.
+            pass
+
         if CGDictionary.isDictionaryCopyConstructible(d):
             disallowCopyConstruction = False
             # Note: no base constructors because our operator= will
             # deal with that.
             ctors.append(ClassConstructor([Argument("const %s&" % selfName,
                                                     "aOther")],
                                           bodyInHeader=True,
                                           visibility="public",
--- a/xpcom/base/OwningNonNull.h
+++ b/xpcom/base/OwningNonNull.h
@@ -152,16 +152,24 @@ protected:
   }
 
   RefPtr<T> mPtr;
 #ifdef DEBUG
   bool mInited = false;
 #endif
 };
 
+template<typename T>
+inline void
+ImplCycleCollectionUnlink(OwningNonNull<T>& aField)
+{
+  RefPtr<T> releaser(aField.forget());
+  // Now just let releaser go out of scope.
+}
+
 template <typename T>
 inline void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
                             OwningNonNull<T>& aField,
                             const char* aName,
                             uint32_t aFlags = 0)
 {
   CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags);