Better finalizer/trace hooks with config options
authorBen Turner <bent.mozilla@gmail.com>
Tue, 07 Feb 2012 22:29:00 -0800
changeset 86459 64089ddcb92dbbb546086e52f23fa5e8fbf5baad
parent 86458 fcf6c993a8a568640df3a452d02675b32413c59b
child 86460 16141e260c6120a000bf9ebddf3fb4a3da6b03ae
push id96
push userbturner@mozilla.com
push dateWed, 08 Feb 2012 06:28:55 +0000
milestone13.0a1
Better finalizer/trace hooks with config options
dom/bindings/Bindings.conf
dom/bindings/Codegen.py
dom/bindings/Configuration.py
dom/bindings/DOMJSClass.h
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -9,16 +9,20 @@
 # |implements| variety, for example, are non-concrete).
 #
 # Valid fields for all descriptors:
 #   * concrete - Indicates whether this is a concrete descriptor (required)
 #   * workers - Indicates whether the descriptor is intended to be used for
 #               worker threads (defaults to false)
 #   * headerFile - The file in which the nativeClass or nativeInterface is
 #                  declared (defaults to an educated guess).
+#   * customTrace - The native class will use a custom trace hook (defaults to
+#                   true for workers, false otherwise).
+#   * customFinalize - The native class will use a custom finalize hook
+#                      (defaults to true for workers, false otherwise).
 #
 # Valid fields for concrete descriptors:
 #   * nativeClass - The concrete class that instances of this interface will
 #                   unwrap to (required)
 #
 # Valid fields for non-concrete descriptors:
 #   * nativeInterface - The native type that instances of this interface will
 #                       unwrap to.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -4,16 +4,19 @@
 
 # Common codegen classes.
 
 import os
 import string
 
 from WebIDL import *
 
+FINALIZE_HOOK_NAME = 'Finalize'
+TRACE_HOOK_NAME = 'Trace'
+
 class CGThing():
     """
     Abstract base case for things that spit out code.
     """
     def __init__(self):
         pass # Nothing for now
     def declare(self):
         """Produce code for a header file."""
@@ -24,42 +27,44 @@ class CGThing():
 
 class CGDOMJSClass(CGThing):
     def __init__(self, descriptor):
         CGThing.__init__(self)
         self.descriptor = descriptor
     def declare(self):
         return "  extern DOMJSClass Class;\n"
     def define(self):
+        traceHook = TRACE_HOOK_NAME if self.descriptor.customTrace else 'NULL'
         prototypeChainString = ', '.join(['id::' + proto \
                                           for proto in self.descriptor.prototypeChain])
         return """
 DOMJSClass Class = {
   { "%s",
     JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1),
     JS_PropertyStub,       /* addProperty */
     JS_PropertyStub,       /* delProperty */
     JS_PropertyStub,       /* getProperty */
     JS_StrictPropertyStub, /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
-    _finalize,
+    %s,
     NULL,                  /* reserved0 */
     NULL,                  /* checkAccess */
     NULL,                  /* call */
     NULL,                  /* construct */
     NULL,                  /* xdrObject */
     NULL,                  /* hasInstance */
-    _trace,
+    %s,
     NULL                   /* reserved1 */
   },
   { %s }, -1, %s
 };
-""" % (self.descriptor.interface.identifier.name, prototypeChainString,
+""" % (self.descriptor.interface.identifier.name, FINALIZE_HOOK_NAME,
+       traceHook, prototypeChainString,
        str(self.descriptor.nativeIsISupports).lower())
 
 class CGPrototypeJSClass(CGThing):
     def __init__(self, descriptor):
         CGThing.__init__(self)
         self.descriptor = descriptor
     def declare(self):
         # We're purely for internal consumption
@@ -263,16 +268,69 @@ class CGAbstractStaticMethod(CGAbstractM
     """
     def __init__(self, descriptor, name, returnType, args):
         CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
                                   inline=False, static=True)
     def declare(self):
         # We only have implementation
         return ""
 
+class CGAbstractClassHook(CGAbstractStaticMethod):
+    """
+    Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
+    'this' unwrapping as it assumes that the unwrapped type is always known.
+    """
+
+    def __init__(self, descriptor, name, returnType, args):
+        CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
+                                        args)
+
+    def definition_body_prologue(self):
+        return """
+  MOZ_ASSERT(js::GetObjectJSClass(obj) == Class.ToJSClass());
+  %s *self = UnwrapDOMObject<%s>(obj);
+""" % (self.descriptor.nativeClass, self.descriptor.nativeClass)
+
+    def definition_body(self):
+        return self.definition_body_prologue() + self.generate_code()
+
+    def generate_code(self):
+        # Override me
+        assert(False)
+
+class CGClassFinalizeHook(CGAbstractClassHook):
+    def __init__(self, descriptor):
+        args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj')]
+        CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
+                                         'void', args)
+
+    def generate_code(self):
+        if self.descriptor.customFinalize:
+            return """
+  if (self) {
+#if 0
+    self->%s(%s);
+#endif
+  }""" % (self.name, ', '.join([str(a.name) for a in self.args]))
+        return "\n  self->Release();"
+
+class CGClassTraceHook(CGAbstractClassHook):
+    def __init__(self, descriptor):
+        args = [Argument('JSTracer*', 'trc'), Argument('JSObject*', 'obj')]
+        CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void',
+                                     args)
+
+    def generate_code(self):
+        return """
+  if (self) {
+#if 0
+    self->%s(%s);
+#endif
+  }""" % (self.name, ', '.join([str(a.name) for a in self.args]))
+
 class MethodDefiner:
     def __init__(self, descriptor):
         self.descriptor = descriptor
         self.methods = [m for m in self.descriptor.interface.members if
                         m.isMethod()]
     def hasMethods(self):
         return len(self.methods) != 0
     def __str__(self):
@@ -733,48 +791,16 @@ class CGAbstractBindingMethod(CGAbstract
   if (!UnwrapThis(cx, obj, %s, %s, &self))
     return%s;
 """ % (self.descriptor.nativeClass, "id::" + self.descriptor.name,
        "depth::" + self.descriptor.name, returnStr)
 
     def generate_code(self):
         assert(False) # Override me
 
-class CGClassFinalizeMethod(CGAbstractBindingMethod):
-    def __init__(self, descriptor):
-        args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj')]
-        CGAbstractBindingMethod.__init__(self, descriptor, '_finalize', 'void',
-                                         args)
-
-    def generate_code(self):
-        return """
-#if 0
-  self->%s(%s);
-#endif""" % (self.name, ', '.join([str(a.name) for a in self.args]))
-
-class CGClassTraceMethod(CGAbstractBindingMethod):
-    def __init__(self, descriptor):
-        args = [Argument('JSTracer*', 'trc'), Argument('JSObject*', 'obj')]
-        CGAbstractBindingMethod.__init__(self, descriptor, '_trace', 'void',
-                                         args)
-
-    def unwrap_this(self):
-        return """
-  %s *self;
-  if (!UnwrapThis(trc->context, obj, %s, %s, &self))
-    return;
-""" % (self.descriptor.nativeClass, "id::" + self.descriptor.name,
-       "depth::" + self.descriptor.name)
-
-    def generate_code(self):
-        return """
-#if 0
-  self->%s(%s);
-#endif""" % (self.name, ', '.join([str(a.name) for a in self.args]))
-
 def MakeNativeName(name):
     return name[0].upper() + name[1:]
 
 class CGNativeMethod(CGAbstractBindingMethod):
     def __init__(self, descriptor, method):
         self.method = method
         args = [Argument('JSContext*', 'cx'), Argument('uintN', 'argc'),
                 Argument('JS::Value*', 'vp')]
@@ -833,18 +859,24 @@ class CGDescriptor(CGThing):
         # XXXbholley - Not everything should actually have a jsclass.
         cgThings = [CGNativeMethod(descriptor, m) for m in
                     descriptor.interface.members if m.isMethod()]
         cgThings.extend([CGNativeGetter(descriptor, a) for a in
                          descriptor.interface.members if a.isAttr()])
         cgThings.extend([CGNativeSetter(descriptor, a) for a in
                          descriptor.interface.members if
                          a.isAttr() and not a.readonly])
-        cgThings.extend([CGClassFinalizeMethod(descriptor)])
-        cgThings.extend([CGClassTraceMethod(descriptor)])
+
+        # Always have a finalize hook, regardless of whether the class wants a
+        # custom hook.
+        cgThings.append(CGClassFinalizeHook(descriptor))
+
+        # Only generate a trace hook if the class wants a custom hook.
+        if (descriptor.customTrace):
+            cgThings.append(CGClassTraceHook(descriptor))
 
         # XXXbz this should check for [NoInterfaceObject]
         if True:
             cgThings.append(CGConstructorJSClass(descriptor))
         cgThings.extend([CGDOMJSClass(descriptor),
                          CGPrototypeJSClass(descriptor),
                          CGCreateProtoObjectMethod(descriptor),
                          CGGetProtoObjectMethod(descriptor)])
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -58,16 +58,19 @@ class Descriptor:
             self.nativeClass = desc['nativeClass']
         else:
             self.nativeInterface = desc.get('nativeInterface', 'XXXFillMeInbz!')
 
         headerDefault = self.nativeClass if self.concrete else self.nativeInterface
         headerDefault = headerDefault.split("::")[-1] + ".h"
         self.headerFile = desc.get('headerFile', headerDefault)
 
+        self.customTrace = desc.get('customTrace', self.workers)
+        self.customFinalize = desc.get('customFinalize', self.workers)
+
         def make_name(name):
             return name + "_workers" if self.workers else name
         self.name = make_name(interface.identifier.name)
 
         # Build the prototype chain.
         self.prototypeChain = []
         parent = interface
         while parent:
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -52,16 +52,16 @@ struct DOMJSClass
 
   static DOMJSClass* FromJSClass(js::Class *base) {
     return FromJSClass(Jsvalify(base));
   }
   static const DOMJSClass* FromJSClass(const js::Class *base) {
     return FromJSClass(Jsvalify(base));
   }
 
-  operator JSClass&() { return mBase; }
+  JSClass* ToJSClass() const { return const_cast<JSClass*>(&mBase); }
 };
 
 } // namespace bindings
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_bindings_DOMJSClass_h */