Skeleton codegen for getters, setters, methods
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 27 Jan 2012 17:25:11 +0100
changeset 85173 40b5d985fb50d3770e8688efbb562e0286854876
parent 85172 eb779fe7de0adfeb6f01b05cd32ab459bc3d9017
child 85174 80331d6bca1d03d5c718869cdcc85b9256fd8e58
push id72
push userbzbarsky@mozilla.com
push dateFri, 27 Jan 2012 16:25:18 +0000
milestone12.0a1
Skeleton codegen for getters, setters, methods
dom/bindings/Codegen.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -7,20 +7,20 @@
 class CodegenThing():
     """
     Abstract base case for things that spit out code within a class.
     """
     def __init__(self, implementation):
         self.implementation = implementation
     def declare(self):
         """Produce code for a header file."""
-        return "" # Override me!
+        assert(False)  # Override me!
     def define(self):
         """Produce code for a cpp file."""
-        return "" # Override me!
+        assert(False) # Override me!
 
 class DOMJSClass(CodegenThing):
     def __init__(self, implementation):
         CodegenThing.__init__(self, implementation)
     def declare(self):
         return "extern DOMJSClass Class;\n\n"
     def define(self):
         prototypeChainString = ', '.join(['id::' + proto \
@@ -65,41 +65,109 @@ class AbstractMethod(CodegenThing):
     def define(self):
         return "" if self.inline else self._define()
     def definition_prologue(self):
         maybeNewline = " " if self.inline else "\n"
         return "%s%s%s(%s)\n{" % (self.returnType, maybeNewline, self.name, self._argstring())
     def definition_epilogue(self):
         return "\n}\n"
     def definition_body(self):
-        return "" # Override me!
+        assert(False) # Override me!
+
+class MethodDefiner:
+    def __init__(self, implementation):
+        self.implementation = implementation
+    def __str__(self):
+        methods = [m for m in self.implementation.domClass.interface.members if m.isMethod()]
+        if len(methods) == 0:
+            return ""
+
+        # The length of a method is the maximum of the lengths of the
+        # argument lists of all its overloads.
+        def methodLength(method):
+            signatures = method.signatures()
+            return max([len(s[1]) for s in signatures])
+
+        funcdecls = ['    JS_FN("%s", %s, %s, JSPROP_ENUMERATE)' %
+                     (m.identifier.name, m.identifier.name,
+                      methodLength(m)) for m in methods]
+        # And add our JS_FS_END
+        funcdecls.append('    JS_FS_END')
+
+        return ("  static JSFunctionSpec methods[] = {\n" +
+                ',\n'.join(funcdecls) + "\n" +
+                "  };\n" +
+                "  if (!JS_DefineFunctions(aCx, ourProto, methods)) {\n" +
+                "    return NULL;\n" +
+                "  }\n")
+
+class AttrDefiner:
+    def __init__(self, implementation):
+        self.implementation = implementation
+    def __str__(self):
+        attrs = [m for m in self.implementation.domClass.interface.members if m.isAttr()]
+        if len(attrs) == 0:
+            return ""
+
+        def flags(attr):
+            flags = "JSPROP_NATIVE_ACCESSORS | JSPROP_SHARED | JSPROP_ENUMERATE"
+            if attr.readonly:
+                return "JSPROP_READONLY | " + flags
+            return flags
+
+        def getter(attr):
+            return "get_" + attr.identifier.name
+
+        def setter(attr):
+            if attr.readonly:
+                return "NULL"
+            return "set_" + attr.identifier.name
+
+        attrdecls = ['    { "%s", 0, %s, %s, %s }' %
+                     (attr.identifier.name, flags(attr), getter(attr),
+                      setter(attr)) for attr in attrs]
+        attrdecls.append('    { 0, 0, 0, 0, 0 }')
+
+        return ("  static JSPropertySpec props[] = {\n" +
+                ',\n'.join(attrdecls) + "\n" +
+                "  };\n" +
+                "  if (!JS_DefineProperties(aCx, ourProto, props)) {\n" +
+                "    return NULL;\n" +
+                "  }\n")
 
 class CreateProtoObjectMethod(AbstractMethod):
     def __init__(self, implementation):
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')]
         AbstractMethod.__init__(self, implementation, 'CreateProtoObject', 'JSObject*', args)
     def definition_body(self):
         protoChain = self.implementation.prototypeChain
         if len(protoChain) == 1:
             getParentProto = "GetCanonicalObjectProto(aCx, aGlobal)"
         else:
             parentProtoName = self.implementation.prototypeChain[-2]
             getParentProto = "%s::GetProtoObject(aCx, aGlobal)" % (parentProtoName)
+
+        defineMethods = MethodDefiner(self.implementation)
+        defineAttributes = AttrDefiner(self.implementation);
+
         return """
   JSObject* parentProto = %s;
   if (!parentProto) {
     return NULL;
   }
 
   JSObject* ourProto = JS_NewObject(aCx, NULL, parentProto, aGlobal);
   if (!ourProto) {
     return NULL;
   }
-  // XXXbz actually set up methods/properties here
-  return ourProto;""" % (getParentProto)
+
+%s
+%s
+
+  return ourProto;""" % (getParentProto, defineMethods, defineAttributes)
 
 class GetProtoObjectMethod(AbstractMethod):
     def __init__(self, implementation):
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')]
         AbstractMethod.__init__(self, implementation, 'GetProtoObject',
                                 'JSObject*', args, inline=True)
     def definition_body(self):
         return """
@@ -116,16 +184,61 @@ class GetProtoObjectMethod(AbstractMetho
   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.implementation.name, self.implementation.name)
 
+class NativeMethod(AbstractMethod):
+    def __init__(self, implementation, method):
+        self.method = method
+        args = [Argument('JSContext*', 'cx'), Argument('uintN', 'argc'),
+                Argument('js::Value*', 'vp')]
+        AbstractMethod.__init__(self, implementation, method.identifier.name,
+                                'JSBool', args)
+    def declare(self):
+        # We only have an implementation
+        return ""
+    def definition_body(self):
+        # NEED TO ADD SOME IMPL HERE
+        return """
+  return false;"""
+
+class NativeGetter(AbstractMethod):
+    def __init__(self, implementation, attr):
+        self.attr = attr
+        args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
+                Argument('jsid', 'id'), Argument('js::Value*', 'vp')]
+        AbstractMethod.__init__(self, implementation,
+                                'get_'+attr.identifier.name, 'JSBool', args)
+    def declare(self):
+        # We only have an implementation
+        return ""
+    def definition_body(self):
+        # NEED TO ADD SOME IMPL HERE
+        return """
+  return false;"""
+
+class NativeSetter(AbstractMethod):
+    def __init__(self, implementation, attr):
+        self.attr = attr
+        args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
+                Argument('jsid', 'id'), Argument('JSBool', 'strict'),
+                Argument('js::Value*', 'vp')]
+        AbstractMethod.__init__(self, implementation,
+                                'set_'+attr.identifier.name, 'JSBool', args)
+    def declare(self):
+        # We only have an implementation
+        return ""
+    def definition_body(self):
+        # NEED TO ADD SOME IMPL HERE
+        return """
+  return false;"""
 
 class DOMClassImplementation():
     def __init__(self, domClass, implConf):
         self.domClass = domClass
         self.nativeClass = implConf['nativeClass']
         self.workers = implConf.get('workers', False)
         def make_name(name):
             return name + "_workers" if self.workers else name
@@ -135,18 +248,26 @@ class DOMClassImplementation():
         # Build the prototype chain.
         self.prototypeChain = []
         parent = domClass.interface
         while parent:
             self.prototypeChain.insert(0, make_name(parent.identifier.name))
             parent = parent.parent
 
         # XXXbholley - Not everything should actually have a jsclass.
-        self.codegenThings = [DOMJSClass(self), CreateProtoObjectMethod(self),
-                              GetProtoObjectMethod(self)]
+        self.codegenThings = [NativeMethod(self, m) for m in
+                              domClass.interface.members if m.isMethod()]
+        self.codegenThings.extend([NativeGetter(self, a) for a in
+                                   domClass.interface.members if a.isAttr()])
+        self.codegenThings.extend([NativeSetter(self, a) for a in
+                                   domClass.interface.members if
+                                   a.isAttr() and not a.readonly])
+        self.codegenThings.extend([DOMJSClass(self),
+                                   CreateProtoObjectMethod(self),
+                                   GetProtoObjectMethod(self)])
 
 class DOMClass():
     def __init__(self, classConf, interface):
         self.name = classConf['name']
         self.interface = interface
         self.implementations = [DOMClassImplementation(self, implConf) for implConf in classConf['implementations']]
 
 class Configuration: