Finish up prototype setup, modulo [NoInterfaceObject]
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 31 Jan 2012 22:53:52 -0500
changeset 86003 771825d1497dfaf98368c8d872c1952c9a134a95
parent 86002 7e8dc4f7e14f45f1e9313fce886a495f39891dad
child 86004 6b8a7a4d4655cf3212637624efd24fc4ad2d194f
push id84
push userbzbarsky@mozilla.com
push dateWed, 01 Feb 2012 04:32:29 +0000
milestone13.0a1
Finish up prototype setup, modulo [NoInterfaceObject]
dom/bindings/Codegen.py
dom/bindings/Makefile.in
dom/bindings/Utils.cpp
dom/bindings/Utils.h
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -24,28 +24,73 @@ class CGDOMJSClass(CGThing):
     def __init__(self, descriptor):
         CGThing.__init__(self)
         self.descriptor = descriptor
     def declare(self):
         return "  extern DOMJSClass Class;\n"
     def define(self):
         prototypeChainString = ', '.join(['id::' + proto \
                                           for proto in self.descriptor.prototypeChain])
-        return """DOMJSClass Class = {
+        return """
+DOMJSClass Class = {
   { "%s",
     JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1),
-    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+    JS_PropertyStub,       /* addProperty */
+    JS_PropertyStub,       /* delProperty */
+    JS_PropertyStub,       /* getProperty */
+    JS_StrictPropertyStub, /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    NULL,                  /* finalize */
+    NULL,                  /* reserved0 */
+    NULL,                  /* checkAccess */
+    NULL,                  /* call */
+    NULL,                  /* construct */
+    NULL,                  /* xdrObject */
+    NULL,                  /* hasInstance */
+    NULL,                  /* trace */
+    NULL                   /* reserved1 */
   },
   { %s }, -1, %s
 };
 """ % (self.descriptor.interface.identifier.name, prototypeChainString,
        str(self.descriptor.nativeIsISupports).lower())
 
+class CGConstructorJSClass(CGThing):
+    def __init__(self, descriptor):
+        CGThing.__init__(self)
+        self.descriptor = descriptor
+    def declare(self):
+        # We're purely for internal consumption
+        return ""
+    def define(self):
+        return """
+static JSClass ConstructorClass = {
+  "Function", 0,
+  JS_PropertyStub,       /* addProperty */
+  JS_PropertyStub,       /* delProperty */
+  JS_PropertyStub,       /* getProperty */
+  JS_StrictPropertyStub, /* setProperty */
+  JS_EnumerateStub,
+  JS_ResolveStub,
+  JS_ConvertStub,
+  NULL,                  /* finalize */
+  NULL,                  /* reserved0 */
+  NULL,                  /* checkAccess */
+  NULL,                  /* call */
+  NULL,                  /* construct */
+  NULL,                  /* xdrObject */
+  // XXXbz This needs a useful hasInstance hook
+  NULL,                  /* hasInstance */
+  NULL,                  /* trace */
+  NULL                   /* reserved1 */
+};
+"""
+
 class CGList(CGThing):
     def __init__(self, children):
         CGThing.__init__(self)
         self.children = children
     def append(self, child):
         self.children.append(child)
     def prepend(self, child):
         self.children.insert(0, child)
@@ -188,46 +233,49 @@ class CGAbstractStaticMethod(CGAbstractM
                                   inline=False, static=True)
     def declare(self):
         # We only have implementation
         return ""
 
 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):
-        methods = [m for m in self.descriptor.interface.members if m.isMethod()]
-        if len(methods) == 0:
+        if not self.hasMethods():
             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]
+                      methodLength(m)) for m in self.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")
+                "  };\n")
 
 class AttrDefiner:
     def __init__(self, descriptor):
         self.descriptor = descriptor
+        self.attrs = [m for m in self.descriptor.interface.members if
+                      m.isAttr()]
+    def hasAttrs(self):
+        return len(self.attrs) != 0
     def __str__(self):
-        attrs = [m for m in self.descriptor.interface.members if m.isAttr()]
-        if len(attrs) == 0:
+        if not self.hasAttrs():
             return ""
 
         def flags(attr):
             flags = "JSPROP_NATIVE_ACCESSORS | JSPROP_SHARED | JSPROP_ENUMERATE"
             if attr.readonly:
                 return "JSPROP_READONLY | " + flags
             return flags
 
@@ -236,56 +284,54 @@ class AttrDefiner:
 
         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]
+                      setter(attr)) for attr in self.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")
+                "  };\n")
 
 class CGCreateProtoObjectMethod(CGAbstractMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')]
         CGAbstractMethod.__init__(self, descriptor, 'CreateProtoObject', 'JSObject*', args)
     def definition_body(self):
         protoChain = self.descriptor.prototypeChain
         if len(protoChain) == 1:
-            getParentProto = "GetCanonicalObjectProto(aCx, aGlobal)"
+            getParentProto = "GetCanonicalProto(aCx, aGlobal, JSProto_Object)"
         else:
             parentProtoName = self.descriptor.prototypeChain[-2]
             getParentProto = "%s::GetProtoObject(aCx, aGlobal)" % (parentProtoName)
 
         defineMethods = MethodDefiner(self.descriptor)
         defineAttributes = AttrDefiner(self.descriptor);
 
         return """
   JSObject* parentProto = %s;
   if (!parentProto) {
     return NULL;
   }
 
-  JSObject* ourProto = JS_NewObject(aCx, NULL, parentProto, aGlobal);
-  if (!ourProto) {
-    return NULL;
-  }
-
 %s
 %s
 
-  return ourProto;""" % (getParentProto, defineMethods, defineAttributes)
+  return FinishProtoCreation(aCx, parentProto, &ConstructorClass, %s,
+                             %s, aGlobal, "%s");""" % (
+            getParentProto, defineMethods, defineAttributes,
+            "methods" if defineMethods.hasMethods() else "NULL",
+            "props" if defineAttributes.hasAttrs() else "NULL",
+            # XXXbz this should be "NULL" if we're [NoInterfaceObject]
+            self.descriptor.interface.identifier.name)
 
 class CGGetProtoObjectMethod(CGAbstractMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')]
         CGAbstractMethod.__init__(self, descriptor, 'GetProtoObject',
                                   'JSObject*', args, inline=True)
     def definition_body(self):
         return """
@@ -379,16 +425,17 @@ class CGDescriptor(CGThing):
         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([CGDOMJSClass(descriptor),
+                         CGConstructorJSClass(descriptor),
                          CGCreateProtoObjectMethod(descriptor),
                          CGGetProtoObjectMethod(descriptor)])
 
         allCGThings = CGList(cgThings)
         allCGThings = CGWrapper(allCGThings, post="\n")
         self.cgRoot = CGWrapper(CGNamespace(descriptor.name, allCGThings), post="\n")
     def declare(self):
         return self.cgRoot.declare()
--- a/dom/bindings/Makefile.in
+++ b/dom/bindings/Makefile.in
@@ -17,16 +17,17 @@ include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/dom/webidl/WebIDL.mk
 
 binding_include_path := mozilla/dom/bindings
 binding_header_files := $(subst .webidl,Binding.h,$(webidl_files))
 binding_cpp_files := $(subst .webidl,Binding.cpp,$(webidl_files))
 
 CPPSRCS = \
   $(binding_cpp_files) \
+  Utils.cpp \
   $(NULL)
 
 EXPORTS_NAMESPACES = $(binding_include_path)
 
 EXPORTS_$(binding_include_path) = \
   DOMJSClass.h \
   PrototypeList.h \
   Utils.h \
new file mode 100644
--- /dev/null
+++ b/dom/bindings/Utils.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* vim: set ts=2 sw=2 et tw=79: */
+/* 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/. */
+
+#include "Utils.h"
+
+namespace mozilla {
+namespace dom {
+namespace bindings {
+
+JSObject*
+FinishProtoCreation(JSContext *cx, JSObject *parentProto,
+		    JSClass *constructorClass,
+		    JSFunctionSpec *methods,
+		    JSPropertySpec *properties,
+		    JSObject *global, const char* name)
+{
+  JSObject* ourProto = JS_NewObject(cx, NULL, parentProto, global);
+  if (!ourProto) {
+    return NULL;
+  }
+
+  JSObject* functionProto = GetCanonicalProto(cx, global, JSProto_Function);
+  if (!functionProto) {
+    return NULL;
+  }
+
+  JSObject* constructor =
+    JS_NewObject(cx, constructorClass, functionProto, global);
+  if (!constructor) {
+    return NULL;
+  }
+
+  if (!JS_LinkConstructorAndPrototype(cx, constructor, ourProto)) {
+    return NULL;
+  }
+
+  if (methods && !JS_DefineFunctions(cx, ourProto, methods)) {
+    return NULL;
+  }
+
+  if (properties && !JS_DefineProperties(cx, ourProto, properties)) {
+    return NULL;
+  }
+
+  if (name && !JS_DefineProperty(cx, global, name, OBJECT_TO_JSVAL(constructor),
+				 NULL, NULL, 0)) {
+    return NULL;
+  }
+
+  return ourProto;
+}
+
+} // namespace bindings
+} // namespace dom
+} // namespace mozilla
+
--- a/dom/bindings/Utils.h
+++ b/dom/bindings/Utils.h
@@ -149,25 +149,42 @@ AllocateProtoCache(JSObject *obj)
 inline void
 DestroyProtoCache(JSObject *obj)
 {
   JSObject **protoArray = GetProtoArray(obj);
   delete [] protoArray;
 }
 
 inline JSObject*
-GetCanonicalObjectProto(JSContext *cx, JSObject *global)
+GetCanonicalProto(JSContext *cx, JSObject *global, JSProtoKey protoKey)
 {
   JSObject* proto;
-  if (!js_GetClassPrototype(cx, global, JSProto_Object, &proto)) {
+  if (!js_GetClassPrototype(cx, global, protoKey, &proto)) {
     return NULL;
   }
   return proto;
 }
 
+/*
+ * Complete creation of a proto object.  parentProto is the prototype
+ * our new object should use.  constructorClass is the class to use
+ * for the corresponding constructor.  methods and properties are to
+ * be defined on the prototype; those arguments are allowed to be
+ * null.  The resulting constructor object will be defined on the
+ * given global with property name |name| unless |name| is null.
+ *
+ * The return value is the newly-created prototype object.
+ */
+JSObject*
+FinishProtoCreation(JSContext *cx, JSObject *parentProto,
+                    JSClass *constructorClass,
+                    JSFunctionSpec *methods,
+                    JSPropertySpec *properties,
+                    JSObject *global, const char* name);
+
 template<class T>
 inline bool
 WrapNewBindingObject(JSContext *cx, JSObject *scope, T *value, jsval *vp)
 {
   JSObject *obj = value->GetWrapper();
   if (obj)
     return obj;