dom/bindings/BindingGen.py
author Boris Zbarsky <bzbarsky@mit.edu>
Thu, 26 Jan 2012 17:23:31 +0100
changeset 85149 4119ff5bd1104150455174319f6f1edba3d41c8f
parent 85148 fdee219a75502a61e28f8d2990ec4d91385e4dbd
child 85150 cc2e300dab5b4a3beea5b95d2e9b32337cb032a0
permissions -rw-r--r--
Implement prototype setup infrastructure.

# 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 Codegen import Configuration, DOMClass, DOMClassImplementation

def generate_binding_header(config, outputprefix):
    """
    protoList is the list of prototypes defined by this binding
    outputprefix is a prefix to use for the header guards and filename
    """

    filename = outputprefix + ".h"
    print "Generating binding header: %s" % (filename)
    f = open(filename, 'w')

    prologue = """
/* THIS FILE IS AUTO-GENERATED - DO NOT EDIT */

#ifndef %s_h
#define %s_h

#include "mozilla/dom/bindings/DOMJSClass.h"
#include "mozilla/dom/bindings/Utils.h"

namespace mozilla {
namespace dom {
namespace bindings {
namespace prototypes {
""" % (outputprefix, outputprefix)

    chunk = """
namespace %s {
  extern DOMJSClass Class;

  JSObject* CreateProtoObject(JSContext* aCx, JSObject* aGlobal);

  /* Get the prototype object for this class.  This will create the prototype
     as needed. */
  JSObject* GetProtoObject(JSContext* aCx, JSObject* aGlobal) {
    /* Make sure our global is sane.  Hopefully we can remove this sometime */
    if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
      return NULL;
    }
    /* Check to see whether the prototype is already installed */
    JSObject **protoArray =
      static_cast<JSObject**>(js::GetReservedSlot(aGlobal, DOM_PROTOTYPE_SLOT).toPrivate());
    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;
  }
}
"""

    epilogue = """
} // namespace prototypes
} // namespace bindings
} // namespace dom
} // namespace mozilla

#endif // %s_h
""" % (outputprefix)

    # Write from templates.
    f.write(prologue)
    for domClass in config.dom_classes.values():
        for implementation in domClass.implementations:
            f.write(chunk % (implementation.name, implementation.name,
                             implementation.name))
    f.write(epilogue)
    f.close()

def generate_binding_cpp(config, outputprefix):
    """
    protoStructure is a list containing information about the prototypes
    this binding file defines.  Each entry is a dict with the following entries:

      protoName: the name of the prototype in our prototype ID list
      canonicalName: the JS-visible name of the prototype
      interfaceChain: a list of the prototypes that this prototype inherits
                      from, in order from least-derived to most-derived.
      nativeIsISupports: a boolean indicating whether the native this class
                         wraps is a subclass of nsISupports.
    """

    filename = outputprefix + ".cpp"
    print "Generating binding implementation: %s" % (filename)
    f = open(filename, 'w')

    parentIncludeTemplate = """#include "%sBinding.h"
"""

    includes = ""
    for domClass in config.dom_classes.values():
        parentInterface = domClass.interface.parent
        if parentInterface:
            parentFileName = os.path.split(parentInterface.filename())[1].replace(".webidl", "");
            includes += parentIncludeTemplate % parentFileName

    prologue = """/* THIS FILE IS AUTO-GENERATED - DO NOT EDIT */

%s
#include "%s.h"

namespace mozilla {
namespace dom {
namespace bindings {
namespace prototypes {
""" % (includes, outputprefix)

    chunk = """
namespace %s {

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
  },
    {%s}, -1, %s
};

JSObject*
CreateProtoObject(JSContext* aCx, JSObject* aGlobal)
{
%s
}

} // namespace %s
"""

    createProtoTemplate = """  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;
"""

    epilogue = """
} // namespace prototypes
} // namespace bindings
} // namespace dom
} // namespace mozilla
"""

    # Write from templates.
    f.write(prologue)
    for domClass in config.dom_classes.values():
        for implementation in domClass.implementations:
            interfaceChain = domClass.interfaceChain
            interfaceChainString = ', '.join(['id::' + iface for iface in interfaceChain])
            if (len(interfaceChain) == 1):
                getParentProto = "GetCanonicalObjectProto(aCx, aGlobal)"
            else:
                # The last entry of interfaceChain is ourselves; we want
                # the entry before that one.
                parentProtoName = interfaceChain[-2]
                getParentProto = ("%s::GetProtoObject(aCx, aGlobal)" %
                                  parentProtoName)

            createProto = createProtoTemplate % (getParentProto)
                
            f.write(chunk % (implementation.name, domClass.name, interfaceChainString,
                             str(implementation.nativeIsISupports).lower(),
                             createProto, implementation.name))
    f.write(epilogue)
    f.close()

def main():

    # Parse arguments.
    from optparse import OptionParser
    usagestring = "usage: %prog configFile outputPrefix webIDLFile"
    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) != 3:
        o.error(usagestring)
    configFile = args[0]
    outputPrefix = args[1]
    webIDLFile = args[2]

    # Load the parsing results
    f = open('ParserResults.pkl', 'rb')
    parserData = cPickle.load(f)
    f.close()

    # Filter the parsing results to this file.
    filteredData = dict()
    for r in parserData:
        if r.filename() == webIDLFile:
            filteredData[r.identifier.name] = r

    # Mix in the configuration data.
    config = Configuration(configFile, filteredData)

    # Generate the prototype classes.
    generate_binding_header(config, outputPrefix);
    generate_binding_cpp(config, outputPrefix);

if __name__ == '__main__':
    main()