Rejigger codegen to make it easier to handle overloads
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 02 Feb 2012 16:00:17 -0500
changeset 86006 8a06ecf9b6fdba19b1004e63c567403e9fae5e07
parent 86005 98112293e26376b44af89c19f570b8b3140fdca1
child 86007 2f87237e2de8036d5a84035392943e321b0ae152
push id87
push userbzbarsky@mozilla.com
push dateThu, 02 Feb 2012 21:12:35 +0000
milestone13.0a1
Rejigger codegen to make it easier to handle overloads
dom/bindings/Codegen.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1,15 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # Common codegen classes.
 
-import re
 import os
 
 class CGThing():
     """
     Abstract base case for things that spit out code.
     """
     def __init__(self):
         pass # Nothing for now
@@ -382,80 +381,183 @@ class CGGetProtoObjectMethod(CGAbstractM
   JSObject *ourProto = protoArray[id::%s];
   if (!ourProto) {
     ourProto = protoArray[id::%s] = CreateProtoObject(aCx, aGlobal);
   }
 
   /* ourProto might _still_ be null, but that's OK */
   return ourProto;""" % (self.descriptor.name, self.descriptor.name)
 
+class PerSignatureCall():
+    """
+    This class handles the guts of generating code for a particular
+    call signature.  A call signature consists of three things:
+
+    1) A return type, which can be None to indicate that there is no
+       actual return value (e.g. this is an attribute setter) or an
+       IDLType if there's an IDL type involved (including |void|).
+    2) An argument list, which is allowed to be empty.
+    3) A name of a native method to call.
+
+    We also need to know whether this is a method or a getter/setter
+    to do error reporting correctly.
+    """
+    # XXXbz For now each entry in the argument list is an IDLType, but
+    # longer-term we may want to have ways of flagging things like
+    # JSContext* or optional_argc in there.
+    
+    # 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):
+        self.returnType = returnType
+        self.arguments = arguments
+        self.nativeMethodName = nativeMethodName
+
+    def unwrap_arguments(self):
+        return ""
+
+    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")
+        # XXXbz need to have full config to do this retval business right
+        nativeRetval = "" if self.returnType is None else ""
+        return """
+  nsresult rv = NS_OK;
+  // XXXbz need to actually make those methods exist!
+#if 0
+  self->%s(%s);
+#endif
+  if (NS_FAILED(rv)) {%s
+  }""" % (self.nativeMethodName, ', '.join(nativeArgs), self.getErrorReport())
+
+    def wrap_return_value(self):
+        if self.returnType.isVoid():
+            return """
+  *vp = JSVAL_VOID;
+  return true;"""
+        if self.returnType.isInterface():
+            # Wrap the object
+            return """
+  // XXXbz need to learn to wrap objects
+  return false;"""
+
+        return """
+  // XXXbz need to learn to wrap other things
+  return false;"""
+
+    def __str__(self):
+        return (self.unwrap_arguments() + self.generate_call() +
+                self.wrap_return_value())
+
+class PerSignatureMethodCall(PerSignatureCall):
+    def __init__(self, returnType, arguments, nativeMethodName):
+        PerSignatureCall.__init__(self, returnType, arguments, nativeMethodName)
+    def getErrorReport(self):
+        return """
+    return xpc_qsThrowMethodFailed(cx, rv, vp);"""
+
+class GetterSetterCall(PerSignatureCall):
+    def __init__(self, returnType, arguments, nativeMethodName):
+        PerSignatureCall.__init__(self, returnType, arguments, nativeMethodName)
+    def getErrorReport(self):
+        return """
+    if (js::IsWrapper(obj)) {
+      // Unwrap the security wrappers, because that's what
+      // xpc_qsThrowGetterSetterFailed expects to see.
+      obj = XPCWrapper::Unwrap(cx, obj, false);
+    }
+    return xpc_qsThrowGetterSetterFailed(cx, rv, obj, id);"""
+
+class GetterCall(GetterSetterCall):
+    def __init__(self, returnType, nativeMethodName):
+        GetterSetterCall.__init__(self, returnType, [], nativeMethodName)
+
+class SetterCall(GetterSetterCall):
+    def __init__(self, argType, nativeMethodName):
+        GetterSetterCall.__init__(self, None, [argType], nativeMethodName)
+    def wrap_return_value(self):
+        # We have no return value
+        return "\n return true;"
+
 class CGAbstractBindingMethod(CGAbstractStaticMethod):
     def __init__(self, descriptor, name, returnType, args):
         CGAbstractStaticMethod.__init__(self, descriptor, name,
                                         returnType, args)
     def definition_body(self):
-        return (self.unwrap_this() + self.unwrap_args() +
-                self.generate_call() + self.wrap_return_value())
+        return (self.unwrap_this() + self.generate_code())
 
     def unwrap_this(self):
         return """
   %s *self;
   if (!UnwrapThis(cx, obj, %s, %s, &self))
     return false;
 """ % (self.descriptor.nativeClass, "id::" + self.descriptor.name,
        "depth::" + self.descriptor.name)
 
-    def unwrap_args(self):
-        # NEED TO ADD SOME IMPL HERE
-        return """// XXXbz ARGUMENT UNWRAPPING CODE HERE.  Maybe this should be in subclasses of CGAbstractBindingMethod!
-"""
+    def generate_code(self):
+        assert(False) # Override me
 
-    def generate_call(self):
-        # NEED TO ADD SOME IMPL HERE
-        return """// XXXbz CALL GENERATION HERE
-"""
-
-    def wrap_return_value(self):
-        # NEED TO ADD SOME IMPL HERE
-        return """// XXXbz RETURN VALUE WRAPPING, IF ANY, HERE
-  return false;"""
-
+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')]
         CGAbstractBindingMethod.__init__(self, descriptor,
                                          method.identifier.name,
                                          'JSBool', args)
     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)
+                          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):
     def __init__(self, descriptor, attr):
         self.attr = attr
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
                 Argument('jsid', 'id'), Argument('JS::Value*', 'vp')]
         CGAbstractBindingMethod.__init__(self, descriptor,
                                          'get_'+attr.identifier.name,
                                          'JSBool', args)
+    def generate_code(self):
+        nativeMethodName = "Get" + MakeNativeName(self.attr.identifier.name)
+        return str(GetterCall(self.attr.type, nativeMethodName))
 
 class CGNativeSetter(CGAbstractBindingMethod):
     def __init__(self, descriptor, attr):
         self.attr = attr
         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)
+    def generate_code(self):
+        nativeMethodName = "Set" + MakeNativeName(self.attr.identifier.name)
+        return str(SetterCall(self.attr.type, nativeMethodName))
 
 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()]