dom/bindings/Codegen.py
author Bobby Holley <bobbyholley@gmail.com>
Fri, 27 Jan 2012 10:57:26 +0100
changeset 85152 be14d1424179f715ee3665c3e8010c3555e74149
parent 85150 cc2e300dab5b4a3beea5b95d2e9b32337cb032a0
child 85157 b010910c1b7afb91c429ecd4499dc3c7f63255d6
permissions -rw-r--r--
Fix some generated code formatting issues.

# 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.

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!
    def define(self):
        """Produce code for a cpp file."""
        return "" # 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):
        interfaceChainString = ', '.join(['id::' + iface \
                                          for iface in self.implementation.domClass.interfaceChain])
        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
  },
    {%s}, -1, %s
};
""" % (self.implementation.domClass.name, interfaceChainString,
       str(self.implementation.nativeIsISupports).lower())




class Argument():
    def __init__(self, argType, name):
        self.argType = argType
        self.name = name
    def __str__(self):
        return self.argType + ' ' + self.name

class AbstractMethod(CodegenThing):
    def __init__(self, implementation, name, returnType, args, inline=False):
        CodegenThing.__init__(self, implementation)
        self.name = name
        self.returnType = returnType
        self.args = args
        self.inline = inline
    def _argstring(self):
        return ', '.join([str(a) for a in self.args])
    def declare(self):
        if self.inline:
            return self._define()
        return "%s %s(%s);\n" % (self.returnType, self.name, self._argstring())
    def _define(self):
        return self.definition_prologue() + self.definition_body() + self.definition_epilogue()
    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!

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):
        interfaceChain = self.implementation.domClass.interfaceChain
        if len(interfaceChain) == 1:
            getParentProto = "GetCanonicalObjectProto(aCx, aGlobal)"
        else:
            parentProtoName = self.implementation.domClass.interfaceChain[-2]
            getParentProto = "%s::GetProtoObject(aCx, aGlobal)" % (parentProtoName)
        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)

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 """
  /* Get the prototype object for this class.  This will create the prototype
     as needed. */

  /* 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;""" % (self.implementation.domClass.name, self.implementation.domClass.name)


class DOMClassImplementation():
    def __init__(self, domClass, implConf):
        self.domClass = domClass
        self.nativeClass = implConf['nativeClass']
        self.workers = implConf.get('workers', False)
        if self.workers:
            self.name = domClass.name + '_workers'
        else:
            self.name = domClass.name
        self.nativeIsISupports = not self.workers

        # XXXbholley - Not everything should actually have a jsclass.
        self.codegenThings = [DOMJSClass(self), CreateProtoObjectMethod(self),
                              GetProtoObjectMethod(self)]

class DOMClass():
    def __init__(self, classConf, interface):
        self.name = classConf['name']
        self.implementations = [DOMClassImplementation(self, implConf) for implConf in classConf['implementations']]

        # Build the interface chain.
        self.interfaceChain = [self.name]
        parent = interface.parent
        while parent:
            self.interfaceChain.insert(0, parent.identifier.name)
            parent = parent.parent
        self.interface = interface

class Configuration:
    def __init__(self, filename, parseData):
        self.configFile = {}
        execfile(filename, self.configFile)

        # We need dom_classes.
        if 'dom_classes' not in self.configFile:
            raise UserError(filename + ": `dom_classes` was not defined.")

        # Build data structures for classes where we have parse data. We've
        # already filtered the parse data so that this does what we want.
        self.dom_classes = dict()
        for classConf in self.configFile['dom_classes']:
            name = classConf['name']
            if name not in parseData:
                continue
            self.dom_classes[name] = DOMClass(classConf, parseData[name])