Bug 796983 part 2. Add a way to generate an example class declaration for a given WebIDL interface. r=jst
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 17 Oct 2012 17:01:55 -0400
changeset 110723 50d6355b80c44f79bd42b12af7e00b8614d0cb59
parent 110722 d8798b6331be496aadbc691fee4cd4f4280f54cf
child 110724 4f40efa904b00ea9250e97c1ad781b96f2e21477
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersjst
bugs796983
milestone19.0a1
Bug 796983 part 2. Add a way to generate an example class declaration for a given WebIDL interface. r=jst We mark constructors as static in the parser because they are. This allows us to just use the isStatic() for the IDLMember to mark our declarations static. To generate an example interface implementation, just "make interfacename-example" in $objdir/dom/bindings. This will place files called interfacename-example.h and interfacename-example.cpp in that directory. For example, "make XMLHttpRequest-example" will get you $objdir/dom/bindings/XMLHttpRequest-example.h and $objdir/dom/bindings/XMLHttpRequest-example.cpp. Attribute getters currently default to const methods, while setters and operations default to non-const methods.
dom/bindings/Codegen.py
dom/bindings/ExampleGen.py
dom/bindings/Makefile.in
dom/bindings/parser/WebIDL.py
dom/bindings/parser/tests/test_constructor.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1706,16 +1706,17 @@ def typeIsSequenceOrHasSequenceMember(ty
         return typeIsSequenceOrHasSequenceMember(elementType)
     if type.isDictionary():
         return dictionaryHasSequenceMember(type.inner)
     if type.isUnion():
         return any(typeIsSequenceOrHasSequenceMember(m.type) for m in
                    type.flatMemberTypes)
     return False
 
+# If this function is modified, modify CGExampleMember.getArg accordingly
 def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
                                     isDefinitelyObject=False,
                                     isMember=False,
                                     isOptional=False,
                                     invalidEnumValueFatal=True,
                                     defaultValue=None,
                                     treatNullAs="Default",
                                     treatUndefinedAs="Default",
@@ -3008,16 +3009,19 @@ def typeNeedsCx(type, retVal=False):
         return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
     if retVal and type.isSpiderMonkeyInterface():
         return True
     return type.isCallback() or type.isAny() or type.isObject()
 
 # Returns a tuple consisting of a CGThing containing the type of the return
 # value, or None if there is no need for a return value, and a boolean signaling
 # whether the return value is passed in an out parameter.
+#
+# Whenever this is modified, please update CGExampleMember.getReturnType as
+# needed
 def getRetvalDeclarationForType(returnType, descriptorProvider,
                                 resultAlreadyAddRefed):
     if returnType is None or returnType.isVoid():
         # Nothing to declare
         return None, False
     if returnType.isPrimitive() and returnType.tag() in builtinNames:
         result = CGGeneric(builtinNames[returnType.tag()])
         if returnType.nullable():
@@ -3060,16 +3064,21 @@ def getRetvalDeclarationForType(returnTy
         return result, True
     raise TypeError("Don't know how to declare return value for %s" %
                     returnType)
 
 def isResultAlreadyAddRefed(descriptor, extendedAttributes):
     # Default to already_AddRefed on the main thread, raw pointer in workers
     return not descriptor.workers and not 'resultNotAddRefed' in extendedAttributes
 
+def needCx(returnType, arguments, extendedAttributes):
+    return (typeNeedsCx(returnType, True) or
+            any(typeNeedsCx(a.type) for (a, _) in arguments) or
+            'implicitJSContext' in extendedAttributes)
+
 class CGCallGenerator(CGThing):
     """
     A class to generate an actual call to a C++ object.  Assumes that the C++
     object is stored in a variable whose name is given by the |object| argument.
 
     errorReport should be a CGThing for an error report or None if no
     error reporting is needed.
     """
@@ -3096,19 +3105,17 @@ class CGCallGenerator(CGThing):
             args.append(CGGeneric(name))
 
         # Return values that go in outparams go here
         if resultOutParam:
             args.append(CGGeneric("result"))
         if isFallible:
             args.append(CGGeneric("rv"))
 
-        needsCx = (typeNeedsCx(returnType, True) or
-                   any(typeNeedsCx(a.type) for (a, _) in arguments) or
-                   'implicitJSContext' in extendedAttributes)
+        needsCx = needCx(returnType, arguments, extendedAttributes)
 
         if not "cx" in argsPre and needsCx:
             args.prepend(CGGeneric("cx"))
 
         # Build up our actual call
         self.cgRoot = CGList([], "\n")
 
         call = CGGeneric(nativeMethodName)
@@ -3535,16 +3542,20 @@ class FakeArgument():
         self.type = type
         self.optional = False
         self.variadic = False
         self.defaultValue = None
         self.treatNullAs = interfaceMember.treatNullAs
         self.treatUndefinedAs = interfaceMember.treatUndefinedAs
         self.enforceRange = False
         self.clamp = False
+        class FakeIdentifier():
+            def __init__(self):
+                self.name = "arg"
+        self.identifier = FakeIdentifier()
 
 class CGSetterCall(CGPerSignatureCall):
     """
     A class to generate a native object setter call for a particular IDL
     setter.
     """
     def __init__(self, argType, nativeMethodName, descriptor, attr):
         CGPerSignatureCall.__init__(self, None, [],
@@ -3635,21 +3646,26 @@ class CGSpecializedMethod(CGAbstractStat
         self.method = method
         name = method.identifier.name
         args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
                 Argument('%s*' % descriptor.nativeType, 'self'),
                 Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
         CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
 
     def definition_body(self):
-        name = self.method.identifier.name
-        nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+        nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
+                                                        self.method)
         return CGMethodCall([], nativeName, self.method.isStatic(),
                             self.descriptor, self.method).define()
 
+    @staticmethod
+    def makeNativeName(descriptor, method):
+        name = method.identifier.name
+        return MakeNativeName(descriptor.binaryNames.get(name, name))
+
 class CGGenericGetter(CGAbstractBindingMethod):
     """
     A class for generating the C++ code for an IDL attribute getter.
     """
     def __init__(self, descriptor, lenientThis=False):
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
                 Argument('JS::Value*', 'vp')]
         if lenientThis:
@@ -3680,29 +3696,33 @@ class CGSpecializedGetter(CGAbstractStat
         name = 'get_' + attr.identifier.name
         args = [ Argument('JSContext*', 'cx'),
                  Argument('JSHandleObject', 'obj'),
                  Argument('%s*' % descriptor.nativeType, 'self'),
                  Argument('JS::Value*', 'vp') ]
         CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
 
     def definition_body(self):
-        name = self.attr.identifier.name
-        nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+        nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
+                                                        self.attr)
+        return CGIndenter(CGGetterCall(self.attr.type, nativeName,
+                                       self.descriptor, self.attr)).define()
+
+    @staticmethod
+    def makeNativeName(descriptor, attr):
+        name = attr.identifier.name
+        nativeName = MakeNativeName(descriptor.binaryNames.get(name, name))
         # resultOutParam does not depend on whether resultAlreadyAddRefed is set
-        (_, resultOutParam) = getRetvalDeclarationForType(self.attr.type,
-                                                          self.descriptor,
+        (_, resultOutParam) = getRetvalDeclarationForType(attr.type, descriptor,
                                                           False)
         infallible = ('infallible' in
-                      self.descriptor.getExtendedAttributes(self.attr,
-                                                            getter=True))
-        if resultOutParam or self.attr.type.nullable() or not infallible:
+                      descriptor.getExtendedAttributes(attr, getter=True))
+        if resultOutParam or attr.type.nullable() or not infallible:
             nativeName = "Get" + nativeName
-        return CGIndenter(CGGetterCall(self.attr.type, nativeName,
-                                       self.descriptor, self.attr)).define()
+        return nativeName
 
 class CGGenericSetter(CGAbstractBindingMethod):
     """
     A class for generating the C++ code for an IDL attribute setter.
     """
     def __init__(self, descriptor, lenientThis=False):
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
                 Argument('JS::Value*', 'vp')]
@@ -3742,21 +3762,26 @@ class CGSpecializedSetter(CGAbstractStat
         name = 'set_' + attr.identifier.name
         args = [ Argument('JSContext*', 'cx'),
                  Argument('JSHandleObject', 'obj'),
                  Argument('%s*' % descriptor.nativeType, 'self'),
                  Argument('JS::Value*', 'argv')]
         CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
 
     def definition_body(self):
-        name = self.attr.identifier.name
-        nativeName = "Set" + MakeNativeName(self.descriptor.binaryNames.get(name, name))
+        nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+                                                        self.attr)
         return CGIndenter(CGSetterCall(self.attr.type, nativeName,
                                        self.descriptor, self.attr)).define()
 
+    @staticmethod
+    def makeNativeName(descriptor, attr):
+        name = attr.identifier.name
+        return "Set" + MakeNativeName(descriptor.binaryNames.get(name, name))
+
 def memberIsCreator(member):
     return member.getExtendedAttribute("Creator") is not None
 
 class CGMemberJITInfo(CGThing):
     """
     A class for generating the JITInfo for a property that points to
     our specialized getter and setter.
     """
@@ -5778,16 +5803,408 @@ class CGBindingRoot(CGThing):
         # Store the final result.
         self.root = curr
 
     def declare(self):
         return stripTrailingWhitespace(self.root.declare())
     def define(self):
         return stripTrailingWhitespace(self.root.define())
 
+class CGExampleMember(CGThing):
+    def __init__(self, descriptor, member, name, signatures, extendedAttrs):
+        self.descriptor = descriptor
+        self.member = member
+        self.name = name
+        self.signatures = signatures
+        self.extendedAttrs = extendedAttrs
+        self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.descriptor,
+                                                             self.extendedAttrs)
+
+    def define(self):
+        static = "static " if self.member.isStatic() else ""
+        # Mark our getters, which are attrs that have a non-void return type,
+        # as const.
+        if self.member.isAttr() and not self.signatures[0][0].isVoid():
+            const = " const"
+        else:
+            const = ""
+        return "\n".join("%s%s %s(%s)%s;" %
+                         (static,
+                          self.getReturnType(s[0], False),
+                          self.name,
+                          self.getArgs(s[0], s[1]),
+                          const) for s in self.signatures)
+
+    def getReturnType(self, type, isMember):
+        if type.isVoid():
+            return "void"
+        if type.isPrimitive() and type.tag() in builtinNames:
+            result = CGGeneric(builtinNames[type.tag()])
+            if type.nullable():
+                result = CGWrapper(result, pre="Nullable<", post=">")
+            return result.define()
+        if type.isString():
+            if isMember:
+                return "nsString"
+            # Outparam
+            return "void"
+        if type.isEnum():
+            if type.nullable():
+                raise TypeError("We don't support nullable enum return values")
+            return type.inner.identifier.name
+        if type.isGeckoInterface():
+            nativeType = self.descriptor.getDescriptor(
+                type.unroll().inner.identifier.name).nativeType
+            # Now trim off unnecessary namespaces
+            nativeType = nativeType.split("::")
+            if nativeType[0] == "mozilla":
+                nativeType.pop(0)
+                if nativeType[0] == "dom":
+                    nativeType.pop(0)
+            result = CGGeneric("::".join(nativeType))
+            if self.resultAlreadyAddRefed:
+                if isMember:
+                    holder = "nsRefPtr"
+                else:
+                    holder = "already_AddRefed"
+                if memberIsCreator(self.member):
+                    warning = ""
+                else:
+                    warning = "// Mark this as resultNotAddRefed to return raw pointers\n"
+                result = CGWrapper(result,
+                                   pre=("%s%s<" % (warning, holder)),
+                                   post=">")
+            else:
+                result = CGWrapper(result, post="*")
+            return result.define()
+        if type.isCallback():
+            # XXXbz we're going to assume that callback types are always
+            # nullable for now.
+            return "JSObject*"
+        if type.isAny():
+            return "JS::Value"
+        if type.isObject() or type.isSpiderMonkeyInterface():
+            return "JSObject*"
+        if type.isSequence():
+            assert not isMember
+            # Outparam
+            return "void"
+        raise TypeError("Don't know how to declare return value for %s" %
+                        type)
+
+    def getArgs(self, returnType, argList):
+        args = [self.getArg(arg) for arg in argList]
+        # Now the outparams
+        if returnType.isString():
+            args.append("nsString& retval")
+        elif returnType.isSequence():
+            nullable = returnType.nullable()
+            if nullable:
+                returnType = returnType.inner
+            # And now the actual underlying type
+            elementDecl = self.getReturnType(returnType.inner, True)
+            type = CGWrapper(CGGeneric(elementDecl), pre="nsTArray< ", post=" >")
+            if nullable:
+                type = CGWrapper(type, pre="Nullable< ", post=" >")
+            args.append("%s& retval" % type.define())
+        # And the ErrorResult
+        if not 'infallible' in self.extendedAttrs:
+            args.append("ErrorResult& rv")
+        # And if we're static, a global
+        if self.member.isStatic():
+            args.insert(0, "nsISupports* global")
+        # And jscontext bits.  needCx expects a list of tuples, in each of which
+        # the first element is the actual argument
+        if needCx(returnType, ((a, "") for a in argList), self.extendedAttrs):
+            args.insert(0, "JSContext* cx")
+        return ", ".join(args)
+
+    def doGetArgType(self, type, optional, isMember):
+        """
+        The main work of getArgType.  Returns a string type decl, whether this
+        is a const ref, as well as whether the type should be wrapped in
+        Nullable as needed.
+        """
+        if type.isArray():
+            raise TypeError("Can't handle array arguments yet")
+
+        if type.isSequence():
+            nullable = type.nullable()
+            if nullable:
+                type = type.inner
+            elementType = type.inner
+            decl = CGWrapper(self.getArgType(elementType, False, True)[0],
+                             pre="Sequence< ", post=" >")
+            return decl.define(), True, True
+
+        if type.isUnion():
+            if type.nullable():
+                type = type.inner
+            return str(type), True, True
+
+        if type.isGeckoInterface():
+            iface = type.unroll().inner
+            argIsPointer = type.nullable() or iface.isExternal()
+            forceOwningType = iface.isCallback() or isMember
+            if argIsPointer:
+                if (optional or isMember) and forceOwningType:
+                    typeDecl = "nsRefPtr<%s>"
+                else:
+                    typeDecl = "%s*"
+            else:
+                if optional or isMember:
+                    if forceOwningType:
+                        typeDecl = "OwningNonNull<%s>"
+                    else:
+                        typeDecl = "NonNull<%s>"
+                else:
+                    typeDecl = "%s&"
+            return (typeDecl % iface.identifier.name), False, False
+
+        if type.isSpiderMonkeyInterface():
+            assert not isMember
+            if type.nullable():
+                typeDecl = "%s*"
+            else:
+                typeDecl = "%s&"
+            return (typeDecl % type.name), False, False
+
+        if type.isString():
+            if isMember:
+                declType = "nsString"
+            else:
+                declType = "nsAString"
+            return declType, True, False
+
+        if type.isEnum():
+            return type.inner.identifier.name, False, True
+
+        if type.isCallback():
+            return "JSObject*", False, False
+
+        if type.isAny():
+            return "JS::Value", False, False
+
+        if type.isObject():
+            if type.nullable():
+                declType = "%s*"
+            else:
+                if optional:
+                    declType = "NonNull<%s>"
+                else:
+                    declType = "%s&"
+            return (declType % "JSObject"), False, False
+
+        if type.isDictionary():
+            return type.inner.identifier.name, True, True
+
+        assert type.isPrimitive()
+
+        return builtinNames[type.tag()], False, True
+
+    def getArgType(self, type, optional, isMember):
+        """
+        Get the type of an argument declaration.  Returns the type CGThing, and
+        whether this should be a const ref.
+        """
+        (decl, ref, handleNullable) = self.doGetArgType(type, optional, isMember)
+        decl = CGGeneric(decl)
+        if handleNullable and type.nullable():
+            decl = CGWrapper(decl, pre="Nullable< ", post=" >")
+            ref = True
+        if optional:
+            decl = CGWrapper(decl, pre="Optional< ", post=" >")
+            ref = True
+        return (decl, ref)
+
+    def getArg(self, arg):
+        """
+        Get the full argument declaration for an argument
+        """
+        (decl, ref) = self.getArgType(arg.type,
+                                      arg.optional and not arg.defaultValue,
+                                      False)
+        if ref:
+            decl = CGWrapper(decl, pre="const ", post="&")
+
+        return "%s %s" % (decl.define(), arg.identifier.name)
+
+
+class CGExampleMethod(CGExampleMember):
+    def __init__(self, descriptor, method):
+        CGExampleMember.__init__(self, descriptor, method,
+                                 CGSpecializedMethod.makeNativeName(descriptor,
+                                                                    method),
+                                 method.signatures(),
+                                 descriptor.getExtendedAttributes(method))
+
+class CGExampleGetter(CGExampleMember):
+    def __init__(self, descriptor, attr):
+        CGExampleMember.__init__(self, descriptor, attr,
+                                 CGSpecializedGetter.makeNativeName(descriptor,
+                                                                    attr),
+                                 [(attr.type, [])],
+                                 descriptor.getExtendedAttributes(attr,
+                                                                  getter=True))
+
+class CGExampleSetter(CGExampleMember):
+    def __init__(self, descriptor, attr):
+        CGExampleMember.__init__(self, descriptor, attr,
+                                 CGSpecializedSetter.makeNativeName(descriptor,
+                                                                    attr),
+                                 [(BuiltinTypes[IDLBuiltinType.Types.void],
+                                   [FakeArgument(attr.type, attr)])],
+                                 descriptor.getExtendedAttributes(attr,
+                                                                  setter=True))
+
+class CGExampleClass(CGThing):
+    """
+    Codegen for the actual example class implemenation for this descriptor
+    """
+    def __init__(self, descriptor):
+        self.descriptor = descriptor
+
+        iface = descriptor.interface
+
+        methodDecls = []
+        if iface.ctor():
+            methodDecls.append(CGExampleMethod(descriptor, iface.ctor()))
+        for m in iface.members:
+            if m.isMethod():
+                methodDecls.append(CGExampleMethod(descriptor, m))
+            elif m.isAttr():
+                methodDecls.append(CGExampleGetter(descriptor, m))
+                if not m.readonly:
+                    methodDecls.append(CGExampleSetter(descriptor, m))
+
+        self.decl = CGIndenter(CGList(methodDecls, "\n\n"))
+
+        if descriptor.wrapperCache:
+            wrapFunc = ("  virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,\n"
+                        "                               bool* aTriedToWrap);\n")
+        else:
+            wrapFunc = "  virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope);\n"
+
+        classDecl = CGWrapper(
+            CGGeneric("public nsISupports,\n"
+                      "public nsWrapperCache"),
+            pre=("class %s MOZ_FINAL : " % descriptor.name),
+            post=(string.Template(
+                    "\n"
+                    "{\n"
+                    "public:\n"
+                    "  ${ifaceName}();\n"
+                    "  ~${ifaceName}();\n"
+                    "\n"
+                    "  NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
+                    "  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(${ifaceName})\n"
+                    "\n"
+                    "  void* GetParentObject() const\n"
+                    "  {\n"
+                    "    // TODO: return something sensible here, and change the return type\n"
+                    "    return somethingSensible;\n"
+                    "  }\n"
+                    "\n" +
+                    wrapFunc +
+                    "\n").substitute({ "ifaceName": descriptor.name })),
+            reindent=True)
+
+        self.decl = CGWrapper(self.decl,
+                              pre=("\n" + classDecl.define()),
+                              post="\n};\n\n")
+
+    def declare(self):
+        return self.decl.define()
+
+    def define(self):
+        classImpl = """
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${ifaceName})
+NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})
+NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+${ifaceName}::${ifaceName}()
+{
+  SetIsDOMBinding();
+}
+
+${ifaceName}::~${ifaceName}()
+{
+}
+"""
+        if self.descriptor.wrapperCache:
+            classImpl += """
+JSObject*
+${ifaceName}::WrapObject(JSContext* aCx, JSObject* aScope, bool* aTriedToWrap)
+{
+  return ${ifaceName}Binding::Wrap(aCx, aScope, this, aTriedToWrap);
+}
+
+"""
+        else:
+            classImpl += """
+JSObject*
+${ifaceName}::WrapObject(JSContext* aCx, JSObject* aScope)
+{
+  return ${ifaceName}Binding::Wrap(aCx, aScope, this);
+}
+
+"""
+        return string.Template(classImpl).substitute(
+            { "ifaceName": self.descriptor.name }
+            )
+
+
+class CGExampleRoot(CGThing):
+    """
+    Root codegen class for example implementation generation.  Instantiate the
+    class and call declare or define to generate header or cpp code,
+    respectively.
+    """
+    def __init__(self, config, interfaceName):
+        # Let's assume we're not doing workers stuff
+        descriptor = config.getDescriptor(interfaceName, False)
+
+        self.root = CGExampleClass(descriptor)
+
+        self.root = CGNamespace.build(["mozilla", "dom"], self.root);
+
+        self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True),
+                            self.root], "\n")
+
+        # Throw in our #includes
+        self.root = CGHeaders([], [],
+                              [ "nsWrapperCache.h",
+                                "nsCycleCollectionParticipant.h",
+                                "mozilla/Attributes.h" ],
+                              [ "%s.h" % interfaceName,
+                                "mozilla/dom/%sBinding.h" % interfaceName,
+                                "nsContentUtils.h" ], self.root);
+
+        # In the header, #pragma once before everything
+        self.root = CGWrapper(self.root, declarePre="#pragma once\n\n")
+
+        # And our license block comes before everything else
+        self.root = CGWrapper(self.root, pre="""/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+""")
+
+    def declare(self):
+        return self.root.declare()
+
+    def define(self):
+        return self.root.define()
+
 
 class GlobalGenRoots():
     """
     Roots for global codegen.
 
     To generate code, call the method associated with the target, and then
     call the appropriate define/declare method.
     """
copy from dom/bindings/BindingGen.py
copy to dom/bindings/ExampleGen.py
--- a/dom/bindings/BindingGen.py
+++ b/dom/bindings/ExampleGen.py
@@ -1,69 +1,52 @@
 # 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/.
 
 import os
 import cPickle
 import WebIDL
 from Configuration import *
-from Codegen import CGBindingRoot, replaceFileIfChanged
+from Codegen import CGExampleRoot, replaceFileIfChanged
 # import Codegen in general, so we can set a variable on it
 import Codegen
 
-def generate_binding_header(config, outputprefix, webidlfile):
+def generate_interface_example(config, interfaceName):
     """
     |config| Is the configuration object.
-    |outputprefix| is a prefix to use for the header guards and filename.
+    |interfaceName| is the name of the interface we're generating an example for.
     """
 
-    filename = outputprefix + ".h"
-    root = CGBindingRoot(config, outputprefix, webidlfile)
-    if replaceFileIfChanged(filename, root.declare()):
-        print "Generating binding header: %s" % (filename)
-
-def generate_binding_cpp(config, outputprefix, webidlfile):
-    """
-    |config| Is the configuration object.
-    |outputprefix| is a prefix to use for the header guards and filename.
-    """
-
-    filename = outputprefix + ".cpp"
-    root = CGBindingRoot(config, outputprefix, webidlfile)
-    if replaceFileIfChanged(filename, root.define()):
-        print "Generating binding implementation: %s" % (filename)
+    root = CGExampleRoot(config, interfaceName)
+    exampleHeader = interfaceName + "-example.h"
+    exampleImpl = interfaceName + "-example.cpp"
+    replaceFileIfChanged(exampleHeader, root.declare())
+    replaceFileIfChanged(exampleImpl, root.define())
 
 def main():
 
     # Parse arguments.
     from optparse import OptionParser
-    usagestring = "usage: %prog [header|cpp] configFile outputPrefix webIDLFile"
+    usagestring = "usage: %prog configFile interfaceName"
     o = OptionParser(usage=usagestring)
     o.add_option("--verbose-errors", action='store_true', default=False,
                  help="When an error happens, display the Python traceback.")
     (options, args) = o.parse_args()
 
-    if len(args) != 4 or (args[0] != "header" and args[0] != "cpp"):
+    if len(args) != 2:
         o.error(usagestring)
-    buildTarget = args[0]
-    configFile = os.path.normpath(args[1])
-    outputPrefix = args[2]
-    webIDLFile = os.path.normpath(args[3])
+    configFile = os.path.normpath(args[0])
+    interfaceName = args[1]
 
     # Load the parsing results
     f = open('ParserResults.pkl', 'rb')
     parserData = cPickle.load(f)
     f.close()
 
     # Create the configuration data.
     config = Configuration(configFile, parserData)
 
-    # Generate the prototype classes.
-    if buildTarget == "header":
-        generate_binding_header(config, outputPrefix, webIDLFile);
-    elif buildTarget == "cpp":
-        generate_binding_cpp(config, outputPrefix, webIDLFile);
-    else:
-        assert False # not reached
+    # Generate the example class.
+    generate_interface_example(config, interfaceName)
 
 if __name__ == '__main__':
     main()
--- a/dom/bindings/Makefile.in
+++ b/dom/bindings/Makefile.in
@@ -123,16 +123,24 @@ CSS2Properties.webidl: $(topsrcdir)/layo
 	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
 	  $(PLY_INCLUDE) -I$(srcdir)/parser \
 	  $(srcdir)/BindingGen.py cpp \
 	  $(srcdir)/Bindings.conf $*Binding \
 	  $*.webidl
 
 $(globalgen_targets): ParserResults.pkl
 
+%-example: $(bindinggen_dependencies) \
+           $(all_webidl_files) \
+           $(NULL)
+	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
+	  $(PLY_INCLUDE) -I$(srcdir)/parser \
+	  $(srcdir)/ExampleGen.py \
+	  $(srcdir)/Bindings.conf $*
+
 CACHE_DIR = _cache
 
 globalgen_dependencies := \
   GlobalGen.py \
   Bindings.conf \
   Configuration.py \
   Codegen.py \
   parser/WebIDL.py \
@@ -155,15 +163,17 @@ ParserResults.pkl: $(globalgen_dependenc
 GARBAGE += \
   $(binding_header_files) \
   $(binding_cpp_files) \
   $(all_webidl_files) \
   $(globalgen_targets) \
   ParserResults.pkl \
   webidlyacc.py \
   parser.out \
+  $(wildcard *-example.h) \
+  $(wildcard *-example.cpp) \
   $(NULL)
 
 # Make sure all binding header files are created during the export stage, so we
 # don't have issues with .cpp files being compiled before we've generated the
 # headers they depend on.  This is really only needed for the test files, since
 # the non-test headers are all exported above anyway.
 export:: $(binding_header_files)
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -675,17 +675,18 @@ class IDLInterface(IDLObjectWithScope):
 
                 args = attr.args() if attr.hasArgs() else []
 
                 retType = IDLWrapperType(self.location, self)
                 
                 identifier = IDLUnresolvedIdentifier(self.location, "constructor",
                                                      allowForbidden=True)
 
-                method = IDLMethod(self.location, identifier, retType, args)
+                method = IDLMethod(self.location, identifier, retType, args,
+                                   static=True)
                 # Constructors are always Creators and are always
                 # assumed to be able to throw (since there's no way to
                 # indicate otherwise) and never have any other
                 # extended attributes.
                 method.addExtendedAttributes(
                     [IDLExtendedAttribute(self.location, ("Creator",)),
                      IDLExtendedAttribute(self.location, ("Throws",))])
                 method.resolve(self)
--- a/dom/bindings/parser/tests/test_constructor.py
+++ b/dom/bindings/parser/tests/test_constructor.py
@@ -6,17 +6,17 @@ def WebIDLTest(parser, harness):
                    "Should be an IDLArgument")
         harness.check(argument.identifier.QName(), QName, "Argument has the right QName")
         harness.check(argument.identifier.name, name, "Argument has the right name")
         harness.check(str(argument.type), type, "Argument has the right return type")
         harness.check(argument.optional, optional, "Argument has the right optional value")
         harness.check(argument.variadic, variadic, "Argument has the right variadic value")
 
     def checkMethod(method, QName, name, signatures,
-                    static=False, getter=False, setter=False, creator=False,
+                    static=True, getter=False, setter=False, creator=False,
                     deleter=False, legacycaller=False, stringifier=False):
         harness.ok(isinstance(method, WebIDL.IDLMethod),
                    "Should be an IDLMethod")
         harness.ok(method.isMethod(), "Method is a method")
         harness.ok(not method.isAttr(), "Method is not an attr")
         harness.ok(not method.isConst(), "Method is not a const")
         harness.check(method.identifier.QName(), QName, "Method has the right QName")
         harness.check(method.identifier.name, name, "Method has the right name")