Support infallible attributes and methods
authorBen Turner <bent.mozilla@gmail.com>
Fri, 10 Feb 2012 13:51:57 -0800
changeset 86748 8b5809ac11749bdd197100953df2eda9623e34d7
parent 86747 0e1782880738bc138575da0f587d47a65745a00a
child 86749 0c6f1e615f6829c0139b69dc88580e1f295e1a21
push id103
push userbturner@mozilla.com
push dateFri, 10 Feb 2012 21:52:04 +0000
milestone13.0a1
Support infallible attributes and methods
dom/bindings/Bindings.conf
dom/bindings/Codegen.py
dom/bindings/Configuration.py
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -13,16 +13,19 @@
 #   * 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).
+#   * infallible - An array of attributes and methods specified in the .webidl
+#                  file that cannot fail and therefore do not require the final
+#                  nsresult& argument (defaults to an empty array).
 #
 # 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
@@ -700,89 +700,98 @@ class PerSignatureCall():
     # XXXbz void methods have a signature with a isVoid() type object
     #       as first element.  Methods with no args have length-0 arg
     #       lists as second element in signaure.
     # XXXbz if isInterface() true on a type, type.inner is the interface object
     # XXXbz is isPrimitive() true on a type, then .tag() will return an
     #       IDLType.Tags value.  So you can compare
     #       type.tag() == IDLType.Tags.int8 or whatever.
     def __init__(self, returnType, arguments, nativeMethodName,
-                 descriptor, idlNode):
+                 descriptor, idlNode, extendedAttributes):
         self.returnType = returnType
         self.arguments = arguments
         self.nativeMethodName = nativeMethodName
         self.descriptor = descriptor
         self.idlNode = idlNode
+        self.extendedAttributes = extendedAttributes
 
     def getArgv(self):
         assert(False) # Override me
     def getArgc(self):
         assert(False) # Override me
     def getErrorReport(self):
         assert(False) # Override me
 
+    def isFallible(self):
+        return not 'infallible' in self.extendedAttributes
+
     def unwrap_arguments(self):
 
         # XXXbholley - This is just proof of concept code for bz to do something
         # useful with.
         for i in range(len(self.arguments)):
             argType = self.arguments[i].type
             if argType.isInterface():
                 argDescriptor = self.descriptor.getDescriptor(str(argType))
                 assert self.descriptor.workers == argDescriptor.workers
 
         args = [ArgumentConverter(self.arguments[i], i, self.getArgv(),
                                   self.getArgc()) for
                 i in range(len(self.arguments))]
         return "".join([str(arg) for arg in args])
 
     def generate_call(self):
-        # XXXbz add provisions for infallible calls here?
         nativeArgs = ["arg" + str(i) for i in range(len(self.arguments))]
         # XXXbz arguments that have to go in outparams go here?
-        nativeArgs.append("&rv")
+        if self.isFallible():
+            nativeArgs.append("rv")
         # XXXbz need to have full config to do this retval business right
         if self.returnType is None or self.returnType.isVoid():
             # Nothing to declare.
             resultDeclaration = ""
         elif self.returnType.isPrimitive() and self.returnType.tag() in builtinNames:
-            resultDeclaration = "%s result = 0;" % builtinNames[self.returnType.tag()]
+            resultDeclaration = "  %s result = 0;\n" % builtinNames[self.returnType.tag()]
         else:
-            resultDeclaration = "// XXX need to declare |result| for type %s." % self.returnType
+            resultDeclaration = "  // XXX need to declare |result| for type %s.\n" % self.returnType
+
+        if self.isFallible():
+            rvDeclaration =  '  nsresult rv = NS_OK;\n'
+            errorCheckClause = '  if (NS_FAILED(rv)) {\n    %s\n  }\n' \
+                               % self.getErrorReport()
+        else:
+            rvDeclaration = ''
+            errorCheckClause = ''
 
         return """
-  nsresult rv = NS_OK;
-  %s
-  // XXXbz need to actually make those methods exist!
+%s%s  // XXXbz need to actually make those methods exist!
 #if 0
   self->%s(%s);
 #endif
-  if (NS_FAILED(rv)) {%s
-  }""" % (resultDeclaration, self.nativeMethodName, ', '.join(nativeArgs), self.getErrorReport())
+%s""" % (rvDeclaration, resultDeclaration, self.nativeMethodName,
+         ', '.join(nativeArgs), errorCheckClause)
 
     def wrap_return_value(self):
         resultTemplateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp'}
         return string.Template(
             getWrapTemplateForType(self.returnType)
             ).substitute(resultTemplateValues)
 
     def getErrorReport(self):
-        return """
-    return xpc_qsThrowMethodFailedWithDetails(cx, rv, "%s", "%s");""" % (self.descriptor.name,
-                                                                         self.idlNode.identifier.name)
+        return 'return xpc_qsThrowMethodFailedWithDetails(cx, rv, "%s", "%s");'\
+               % (self.descriptor.name, self.idlNode.identifier.name)
 
     def __str__(self):
         return (self.unwrap_arguments() + self.generate_call() +
                 self.wrap_return_value())
 
 class PerSignatureMethodCall(PerSignatureCall):
-    def __init__(self, returnType, arguments, nativeMethodName,
-                 descriptor, method):
+    def __init__(self, returnType, arguments, nativeMethodName, descriptor,
+                 method, extendedAttributes):
         PerSignatureCall.__init__(self, returnType, arguments, nativeMethodName,
-                                  descriptor, method)
+                                  descriptor, method, extendedAttributes)
     def getArgv(self):
         return "argv" if len(self.arguments) > 0 else ""
     def getArgc(self):
         return "argc"
     def unwrap_arguments(self):
         requiredArgs = len(self.arguments)
         while requiredArgs and self.arguments[requiredArgs-1].optional:
             requiredArgs -= 1
@@ -791,40 +800,42 @@ class PerSignatureMethodCall(PerSignatur
   // XXXbz is this the right place for this check?  Or should it be more
   // up-front somewhere, not per-signature?
   if (argc < %d) {
     return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
   }
   %s""" % (requiredArgs, argv)) + PerSignatureCall.unwrap_arguments(self)
 
 class GetterSetterCall(PerSignatureCall):
-    def __init__(self, returnType, arguments, nativeMethodName,
-                 descriptor, attr):
+    def __init__(self, returnType, arguments, nativeMethodName, descriptor,
+                 attr, extendedAttributes):
         PerSignatureCall.__init__(self, returnType, arguments, nativeMethodName,
-                                  descriptor, attr)
+                                  descriptor, attr, extendedAttributes)
     def getArgv(self):
         return "vp"
 
 class GetterCall(GetterSetterCall):
-    def __init__(self, returnType, nativeMethodName,
-                 descriptor, attr):
+    def __init__(self, returnType, nativeMethodName, descriptor, attr,
+                 extendedAttributes):
         GetterSetterCall.__init__(self, returnType, [], nativeMethodName,
-                                  descriptor, attr)
+                                  descriptor, attr, extendedAttributes)
     def getArgc(self):
         return "0"
 
 class FakeArgument():
     def __init__(self, type):
         self.type = type
         self.optional = False
 
 class SetterCall(GetterSetterCall):
-    def __init__(self, argType, nativeMethodName, descriptor, attr):
+    def __init__(self, argType, nativeMethodName, descriptor, attr,
+                 extendedAttributes):
         GetterSetterCall.__init__(self, None, [FakeArgument(argType)],
-                                  nativeMethodName, descriptor, attr)
+                                  nativeMethodName, descriptor, attr,
+                                  extendedAttributes)
     def wrap_return_value(self):
         # We have no return value
         return "\n  return true;"
     def getArgc(self):
         return "1"
 
 class CGAbstractBindingMethod(CGAbstractStaticMethod):
     def __init__(self, descriptor, name, returnType, args):
@@ -843,66 +854,78 @@ class CGAbstractBindingMethod(CGAbstract
        "depth::" + self.descriptor.name, returnStr)
 
     def generate_code(self):
         assert(False) # Override me
 
 def MakeNativeName(name):
     return name[0].upper() + name[1:]
 
-class CGNativeMethod(CGAbstractBindingMethod):
+class CGNativeBindingMethod(CGAbstractBindingMethod):
+    """ Class common to all interface methods and attributes. """
+
+    def __init__(self, descriptor, name, returnType, args, baseName):
+        self.extendedAttributes = {}
+        if baseName in descriptor.infallible:
+            self.extendedAttributes['infallible'] = True
+        CGAbstractBindingMethod.__init__(self, descriptor, name, returnType,
+                                         args)
+
+class CGNativeMethod(CGNativeBindingMethod):
     def __init__(self, descriptor, method):
         self.method = method
+        baseName = method.identifier.name
         args = [Argument('JSContext*', 'cx'), Argument('uintN', 'argc'),
                 Argument('JS::Value*', 'vp')]
-        CGAbstractBindingMethod.__init__(self, descriptor,
-                                         method.identifier.name,
-                                         'JSBool', args)
+        CGNativeBindingMethod.__init__(self, descriptor, baseName, 'JSBool',
+                                       args, baseName)
     def unwrap_this(self):
          return """
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;""" + CGAbstractBindingMethod.unwrap_this(self)
 
     def generate_code(self):
         signatures = self.method.signatures()
         nativeName = MakeNativeName(self.method.identifier.name)
-        callGenerators = [PerSignatureMethodCall(s[0], s[1], nativeName, self.descriptor, self.method)
+        callGenerators = [PerSignatureMethodCall(s[0], s[1], nativeName,
+                                                 self.descriptor, self.method,
+                                                 self.extendedAttributes)
                           for s in signatures]
         if len(callGenerators) != 1:
             raise TypeError("Don't know how to handle overloads yet.  Will need to generate code to pick the right overload based on the arguments, then jump to the right generated code")
 
         return str(callGenerators[0]);
 
-class CGNativeGetter(CGAbstractBindingMethod):
+class CGNativeGetter(CGNativeBindingMethod):
     def __init__(self, descriptor, attr):
         self.attr = attr
+        baseName = attr.identifier.name
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
                 Argument('jsid', 'id'), Argument('JS::Value*', 'vp')]
-        CGAbstractBindingMethod.__init__(self, descriptor,
-                                         'get_'+attr.identifier.name,
-                                         'JSBool', args)
+        CGNativeBindingMethod.__init__(self, descriptor, 'get_' + baseName,
+                                       'JSBool', args, baseName)
     def generate_code(self):
         nativeMethodName = "Get" + MakeNativeName(self.attr.identifier.name)
         return str(GetterCall(self.attr.type, nativeMethodName, self.descriptor,
-                              self.attr))
+                              self.attr, self.extendedAttributes))
 
-class CGNativeSetter(CGAbstractBindingMethod):
+class CGNativeSetter(CGNativeBindingMethod):
     def __init__(self, descriptor, attr):
         self.attr = attr
+        baseName = attr.identifier.name
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
                 Argument('jsid', 'id'), Argument('JSBool', 'strict'),
                 Argument('JS::Value*', 'vp')]
-        CGAbstractBindingMethod.__init__(self, descriptor,
-                                         'set_'+attr.identifier.name,
-                                         'JSBool', args)
+        CGNativeBindingMethod.__init__(self, descriptor, 'set_' + baseName,
+                                       'JSBool', args, baseName)
     def generate_code(self):
         nativeMethodName = "Set" + MakeNativeName(self.attr.identifier.name)
         return str(SetterCall(self.attr.type, nativeMethodName, self.descriptor,
-                              self.attr))
+                              self.attr, self.extendedAttributes))
 
 class CGDescriptor(CGThing):
     def __init__(self, descriptor):
         CGThing.__init__(self)
 
         # XXXbholley - Not everything should actually have a jsclass.
         cgThings = [CGNativeMethod(descriptor, m) for m in
                     descriptor.interface.members if m.isMethod()]
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -69,16 +69,22 @@ class Descriptor:
 
         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)
 
+        infallible = desc.get('infallible', [])
+        if not isinstance(infallible, list):
+            assert isinstance(infallible, string)
+            infallible = [infallible]
+        self.infallible = infallible
+
         # Build the prototype chain.
         self.prototypeChain = []
         parent = interface
         while parent:
             self.prototypeChain.insert(0, make_name(parent.identifier.name))
             parent = parent.parent
 
     def getDescriptor(self, interfaceName):