Import IPDL from cjones' working repo, revision 282b4211d881. New IPDL work will take place in electrolysis.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Tue, 30 Jun 2009 15:38:59 -0400
changeset 35742 042ca6009da4e1e8b55390544a9a38aa3e1dc846
parent 35741 f69d9e5b551f4038c6561c72ddacc753e16244a2
child 35743 2514fa68b1784bd1d9c94b47663c787237afda0e
push id10694
push userbsmedberg@mozilla.com
push dateMon, 14 Dec 2009 15:23:10 +0000
treeherdermozilla-central@683dfdc4adf0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.2a1pre
Import IPDL from cjones' working repo, revision 282b4211d881. New IPDL work will take place in electrolysis.
ipc/ipdl/ipdl/Makefile
ipc/ipdl/ipdl/__init__.py
ipc/ipdl/ipdl/ast.py
ipc/ipdl/ipdl/builtin.py
ipc/ipdl/ipdl/cgen.py
ipc/ipdl/ipdl/cxx/__init__.py
ipc/ipdl/ipdl/cxx/ast.py
ipc/ipdl/ipdl/cxx/cgen.py
ipc/ipdl/ipdl/lower.py
ipc/ipdl/ipdl/parser.py
ipc/ipdl/ipdl/type.py
ipc/ipdl/ipdlc
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/Makefile
@@ -0,0 +1,7 @@
+.PHONY: clean
+
+clean:
+	rm -f cxx/*~ *~ *.pyc cxx/*.pyc parser.out ipdl_lextab.py ipdl_yacctab.py
+
+check:
+	@echo "Implement me!  With tests/!"
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/__init__.py
@@ -0,0 +1,58 @@
+ # ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck' ]
+
+import os, sys
+
+from ipdl.cgen import IPDLCodeGen
+from ipdl.lower import LowerToCxx
+from ipdl.parser import Parser
+from ipdl.type import TypeCheck
+
+from ipdl.cxx.cgen import CxxCodeGen
+
+def parse(specstring, filename='<stdin>'):
+    return Parser().parse(specstring, filename)
+
+def typecheck(ast, errout=sys.stderr):
+    '''Returns True iff |ast| is well typed.  Print errors to |errout| if
+    it is not.'''
+    return TypeCheck().check(ast, errout)
+
+def gencxx(ast, outdir):
+    for hdr in LowerToCxx().lower(ast):
+        path = os.path.join(outdir, hdr.filename)
+        CxxCodeGen(outf=open(path, 'w')).cgen(hdr)
+
+def genipdl(ast, outdir):
+    return IPDLCodeGen().cgen(ast)
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/ast.py
@@ -0,0 +1,280 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import sys
+
+class Visitor:
+    def defaultVisit(self, node):
+        raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
+            node.__class__.__name__)
+
+    def visitTranslationUnit(self, tu):
+        for cxxInc in tu.cxxIncludes:
+            cxxInc.accept(self)
+        for protoInc in tu.protocolIncludes:
+            protoInc.accept(self)
+        for using in tu.using:
+            using.accept(self)
+        tu.protocol.accept(self)
+
+    def visitCxxInclude(self, inc):
+        pass
+
+    def visitProtocolInclude(self, inc):
+        # Note: we don't visit the child AST here, because that needs delicate
+        # and pass-specific handling
+        pass
+
+    def visitUsingStmt(self, using):
+        pass
+
+    def visitProtocol(self, p):
+        for namespace in p.namespaces:
+            namespace.accept(self)
+        if p.manager is not None:
+            p.manager.accept(self)
+        for managed in p.managesStmts:
+            managed.accept(self)
+        for msgDecl in p.messageDecls:
+            msgDecl.accept(self)
+        for transitionStmt in p.transitionStmts:
+            transitionStmt.accept(self)
+
+    def visitNamespace(self, ns):
+        pass
+
+    def visitManagerStmt(self, mgr):
+        pass
+
+    def visitManagesStmt(self, mgs):
+        pass
+
+    def visitMessageDecl(self, md):
+        for inParam in md.inParams:
+            inParam.accept(self)
+        for outParam in md.outParams:
+            outParam.accept(self)
+
+    def visitParam(self, decl):
+        pass
+
+    def visitTypeSpec(self, ts):
+        pass
+
+    def visitDecl(self, d):
+        pass
+
+class Loc:
+    def __init__(self, filename='<??>', lineno=0):
+        assert filename
+        self.filename = filename
+        self.lineno = lineno
+    def __repr__(self):
+        return '%r:%r'% (self.filename, self.lineno)
+    def __str__(self):
+        return '%s:%s'% (self.filename, self.lineno)
+
+Loc.NONE = Loc(filename='<??>', lineno=0)
+
+class _struct():
+    pass
+
+class Node:
+    def __init__(self, loc=Loc.NONE):
+        self.loc = loc
+
+    def accept(self, visitor):
+        visit = getattr(visitor, 'visit'+ self.__class__.__name__, None)
+        if visit is None:
+            return getattr(visitor, 'defaultVisit')(self)
+        return visit(self)
+
+    def addAttrs(self, attrsName):
+        if not hasattr(self, attrsName):
+            setattr(self, attrsName, _struct())
+
+class TranslationUnit(Node):
+    def __init__(self):
+        Node.__init__(self)
+        self.filename = None
+        self.cxxIncludes = [ ]
+        self.protocolIncludes = [ ]
+        self.using = [ ]
+        self.protocol = None
+
+    def addCxxInclude(self, cxxInclude): self.cxxIncludes.append(cxxInclude)
+    def addProtocolInclude(self, pInc): self.protocolIncludes.append(pInc)
+    def addUsingStmt(self, using): self.using.append(using)
+
+    def setProtocol(self, protocol): self.protocol = protocol
+
+class CxxInclude(Node):
+    def __init__(self, loc, cxxFile):
+        Node.__init__(self, loc)
+        self.file = cxxFile
+
+class ProtocolInclude(Node):
+    def __init__(self, loc, protocolFile):
+        Node.__init__(self, loc)
+        self.file = protocolFile
+
+class UsingStmt(Node):
+    def __init__(self, loc, cxxTypeSpec):
+        Node.__init__(self, loc)
+        self.type = cxxTypeSpec
+        
+# "singletons"
+class ASYNC:
+    pretty = 'Async'
+class RPC:
+    pretty = 'Rpc'
+class SYNC:
+    pretty = 'Sync'
+
+class INOUT:
+    pretty = 'InOut'
+class IN:
+    @staticmethod
+    def pretty(ss): return _prettyTable['In'][ss.pretty]
+class OUT:
+    @staticmethod
+    def pretty(ss): return _prettyTable['Out'][ss.pretty]
+
+_prettyTable = {
+    'In'  : { 'Async': 'AsyncRecv',
+             'Sync': 'SyncRecv',
+             'Rpc': 'RpcAnswer' },
+    'Out' : { 'Async': 'AsyncSend',
+              'Sync': 'SyncSend',
+              'Rpc': 'RpcCall' }
+    # inout doesn't make sense here
+}
+
+
+class Protocol(Node):
+    def __init__(self, loc):
+        Node.__init__(self, loc)
+        self.name = None
+        self.namespaces = [ ]
+        self.sendSemantics = ASYNC
+        self.managesStmts = [ ]
+        self.messageDecls = [ ]
+        self.transitionStmts = [ ]
+
+    def addOuterNamespace(self, namespace):
+        self.namespaces.insert(0, namespace)
+
+    def addManagesStmts(self, managesStmts):
+        self.managesStmts += managesStmts
+
+    def addMessageDecls(self, messageDecls):
+        self.messageDecls += messageDecls
+
+    def addTransitionStmts(self, transStmts):
+        self.transitionStmts += transStmts
+
+class Namespace(Node):
+    def __init__(self, loc, namespace):
+        Node.__init__(self, loc)
+        self.namespace = namespace
+
+class ManagerStmt(Node):
+    def __init__(self, loc, managerName):
+        Node.__init__(self, loc)
+        self.name = managerName
+
+class ManagesStmt(Node):
+    def __init__(self, loc, managedName):
+        Node.__init__(self, loc)
+        self.name = managedName
+
+class MessageDecl(Node):
+    def __init__(self, loc):
+        Node.__init__(self, loc)
+        self.name = None
+        self.sendSemantics = ASYNC
+        self.direction = None
+        self.inParams = [ ]
+        self.outParams = [ ]
+
+    def addInParams(self, inParamsList):
+        self.inParams += inParamsList
+
+    def addOutParams(self, outParamsList):
+        self.outParams += outParamsList
+
+    def hasReply(self):
+        return self.sendSemantics is SYNC or self.sendSemantics is RPC
+
+class Param(Node):
+    def __init__(self, loc, typespec, name):
+        Node.__init__(self, loc)
+        self.name = name
+        self.typespec = typespec
+
+class TypeSpec(Node):
+    def __init__(self, loc, spec):
+        Node.__init__(self, loc)
+        self.spec = spec
+
+    def basename(self):
+        return self.spec.baseid
+
+    def __str__(self):  return str(self.spec)
+
+class QualifiedId:              # FIXME inherit from node?
+    def __init__(self, loc, baseid, quals=[ ]):
+        self.loc = loc
+        self.baseid = baseid
+        self.quals = quals
+
+    def qualify(self, id):
+        self.quals.append(self.baseid)
+        self.baseid = id
+
+    def __str__(self):
+        if 0 == len(self.quals):
+            return self.baseid
+        return '::'.join(self.quals) +'::'+ self.baseid
+
+# added by type checking passes
+class Decl(Node):
+    def __init__(self, loc):
+        Node.__init__(self, loc)
+        self.progname = None    # what the programmer typed, if relevant
+        self.shortname = None   # shortest way to refer to this decl
+        self.fullname = None    # full way to refer to this decl
+        self.loc = loc
+        self.type = None
+        self.scope = None
+
+
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/builtin.py
@@ -0,0 +1,68 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+# WARNING: the syntax of the builtin types is not checked, so please
+# don't add something syntactically invalid.  It will not be fun to
+# track down the bug.
+
+Types = (
+    # C types
+    'char',
+    'short',
+    'int',
+    'long',
+    'float',
+    'double',
+
+    # stdint types
+    'int8_t',
+    'uint8_t',
+    'int16_t',
+    'uint16_t',
+    'int32_t',
+    'uint32_t',
+    'int64_t',
+    'uint64_t',
+    'intptr_t',
+    'uintptr_t',
+
+    # Mozilla types: "less" standard things we know how serialize/deserialize
+    'mozilla::ipc::String',
+    'mozilla::ipc::StringArray',
+)
+
+
+Includes = (
+    'nscore.h',
+    'IPC/IPCMessageUtils.h',
+    'mozilla/ipc/MessageTypes.h',
+)
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/cgen.py
@@ -0,0 +1,129 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import os, sys
+
+from ipdl.ast import Visitor
+from ipdl.ast import IN, OUT, INOUT, ASYNC, SYNC, RPC
+
+class CodePrinter:
+    def __init__(self, outf=sys.stdout, indentCols=4):
+        self.outf = outf
+        self.col = 0
+        self.indentCols = indentCols
+
+    def write(self, str):
+        self.outf.write(str)
+
+    def printdent(self, str=''):
+        self.write((' '* self.col) + str)
+
+    def println(self, str=''):
+        self.write(str +'\n')
+
+    def printdentln(self, str):
+        self.write((' '* self.col) + str +'\n')
+
+    def indent(self):  self.col += self.indentCols
+    def dedent(self):  self.col -= self.indentCols
+
+
+##-----------------------------------------------------------------------------
+class IPDLCodeGen(CodePrinter, Visitor):
+    '''Spits back out equivalent IPDL to the code that generated this.
+Also known as pretty-printing.'''
+
+    def __init__(self, outf=sys.stdout, indentCols=4, printed=set()):
+        CodePrinter.__init__(self, outf, indentCols)
+        self.printed = printed
+
+    def visitTranslationUnit(self, tu):
+        self.printed.add(tu.filename)
+        self.println('//\n// Automatically generated by ipdlc\n//')
+        CodeGen.visitTranslationUnit(self, tu)
+
+    def visitCxxInclude(self, inc):
+        self.println('include "'+ inc.file +'";')
+
+    def visitProtocolInclude(self, inc):
+        self.println('include protocol "'+ inc.file +'";')
+        if inc.tu.filename not in self.printed:
+            self.println('/* Included file:')
+            IPDLCodeGen(outf=self.outf, indentCols=self.indentCols,
+                        printed=self.printed).visitTranslationUnit(inc.tu)
+        
+            self.println('*/')
+
+    def visitProtocol(self, p):
+        self.println()
+        for namespace in p.namespaces:  namespace.accept(self)
+
+        self.println('%s protocol %s\n{'% (p.sendSemantics[0], p.name))
+        self.indent()
+
+        for mgs in p.managesStmts:
+            mgs.accept(self)
+        if len(p.managesStmts):  self.println()
+
+        for msgDecl in p.messageDecls:  msgDecl.accept(self)
+        self.println()
+
+        for transStmt in p.transitionStmts:  transStmt.accept(self)
+
+        self.dedent()
+        self.println('}')
+        self.write('}\n'* len(p.namespaces))
+
+    def visitManagerStmt(self, mgr):
+        self.printdentln('manager '+ mgr.name +';')
+
+    def visitManagesStmt(self, mgs):
+        self.printdentln('manages '+ mgs.name +';')
+
+    def visitMessageDecl(self, msg):
+        self.printdent('%s %s %s('% (msg.sendSemantics[0], msg.direction[0], msg.name))
+        for i, inp in enumerate(msg.inParams):
+            inp.accept(self)
+            if i != (len(msg.inParams) - 1):  self.write(', ')
+        self.write(')')
+        if 0 == len(msg.outParams):
+            self.println(';')
+            return
+
+        self.println()
+        self.indent()
+        self.printdent('returns (')
+        for i, outp in enumerate(msg.outParams):
+            outp.accept(self)
+            if i != (len(msg.outParams) - 1):  self.write(', ')
+        self.println(');')
+        self.dedent()
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/cxx/__init__.py
@@ -0,0 +1,34 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import ipdl.cxx.ast
+import ipdl.cxx.cgen
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/cxx/ast.py
@@ -0,0 +1,426 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import copy, sys
+
+class Visitor:
+    def defaultVisit(self, node):
+        raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
+            node.__class__.__name__)
+
+    def visitWhitespace(self, ws):
+        pass
+
+    def visitFile(self, f):
+        for thing in f.stuff:
+            thing.accept(self)
+
+    def visitCppDirective(self, ppd):
+        pass
+
+    def visitBlock(self, block):
+        for stmt in block.stmts:
+            stmt.accept(self)
+
+    def visitNamespace(self, ns):
+        self.visitBlock(ns)
+
+    def visitType(self, type):
+        pass
+
+    def visitTypeEnum(self, enum):
+        pass
+
+    def visitTypedef(self, tdef):
+        tdef.fromtype.accept(self)
+        tdef.tottype.accept(self)
+
+    def visitDecl(self, decl):
+        decl.type.accept(self)
+
+    def visitClass(self, cls):
+        for viz, parent in cls.inherits:
+            parent.accept(self)
+        self.visitBlock(cls)
+
+    def visitInherit(self, inh):
+        pass
+
+    def visitMethodDecl(self, meth):
+        for param in meth.params:
+            param.accept(self)
+        if meth.ret is not None:
+            meth.ret.accept(self)
+
+    def visitMethodDefn(self, meth):
+        meth.decl.accept(self)
+        self.visitBlock(meth)
+
+    def visitConstructorDecl(self, ctor):
+        self.visitMethodDecl(ctor)
+
+    def visitConstructorDefn(self, cd):
+        cd.decl.accept(self)
+        for init in cd.memberinits:
+            init.accept(self)
+        self.visitBlock(cd)
+
+    def visitDestructorDecl(self, dtor):
+        self.visitMethodDecl(dtor)
+
+    def visitDestructorDefn(self, dd):
+        dd.decl.accept(self)
+        self.visitBlock(dd)
+
+    def visitExprVar(self, v):
+        pass
+
+    def visitExprPrefixUnop(self, e):
+        e.expr.accept(self)
+
+    def visitExprAddrOf(self, eao):
+        self.visitExprPrefixUnop(eao)
+
+    def visitExprDeref(self, ed):
+        self.visitExprPrefixUnop(ed)
+
+    def visitExprSelect(self, es):
+        es.obj.accept(self)
+
+    def visitExprAssn(self, ea):
+        ea.lhs.accept(self)
+        ea.rhs.accept(self)
+
+    def visitExprCall(self, ec):
+        ec.func.accept(self)
+        for arg in ec.args:
+            arg.accept(self)
+
+    def visitExprNew(self, en):
+        self.visitExprCall(en)
+
+    def visitExprDelete(self, ed):
+        ed.obj.accept(self)
+
+    def visitExprMemberInit(self, minit):
+        self.visitExprCall(minit)
+
+    def visitStmtBlock(self, sb):
+        self.visitBlock(sb)
+
+    def visitStmtDecl(self, sd):
+        sd.decl.accept(self)
+
+    def visitLabel(self, label):
+        pass
+
+    def visitCaseLabel(self, case):
+        pass
+
+    def visitDefaultLabel(self, dl):
+        pass
+
+    def visitStmtSwitch(self, ss):
+        ss.expr.accept(self)
+        self.visitBlock(ss)
+
+    def visitStmtExpr(self, se):
+        se.expr.accept(self)
+
+    def visitStmtReturn(self, sr):
+        if sr.expr is not None:
+            sr.expr.accept(self)
+
+##------------------------------
+class Node:
+    def __init__(self):
+        pass
+
+    def accept(self, visitor):
+        visit = getattr(visitor, 'visit'+ self.__class__.__name__, None)
+        if visit is None:
+            return getattr(visitor, 'defaultVisit')(self)
+        return visit(self)
+
+class Whitespace(Node):
+    # yes, this is silly.  but we need to stick comments in the
+    # generated code without resorting to more serious hacks
+    def __init__(self, ws):
+        Node.__init__(self)
+        self.ws = ws
+Whitespace.NL = Whitespace('\n')
+
+class File(Node):
+    def __init__(self, filename):
+        Node.__init__(self)
+        self.filename = filename
+        # array of stuff in the file --- stmts and preprocessor thingies
+        self.stuff = [ ]
+
+    def addthing(self, thing):
+        self.stuff.append(thing)
+
+    # "look like" a Block so code doesn't have to care whether they're
+    # in global scope or not
+    def addstmt(self, stmt):
+        self.stuff.append(stmt)
+
+class CppDirective(Node):
+    '''represents |#[directive] [rest]|, where |rest| is any string'''
+    def __init__(self, directive, rest):
+        Node.__init__(self)
+        self.directive = directive
+        self.rest = rest
+
+class Block(Node):
+    def __init__(self):
+        Node.__init__(self)
+        self.stmts = [ ]
+
+    def addstmt(self, stmt):
+        self.stmts.append(stmt)
+
+##------------------------------
+# type and decl thingies
+class Namespace(Block):
+    def __init__(self, name):
+        Block.__init__(self)
+        self.name = name
+
+class Type(Node):
+    def __init__(self, name, const=False, ptr=False, ref=False):
+        Node.__init__(self)
+        self.name = name
+        self.const = const
+        self.ptr = ptr
+        self.ref = ref
+        # XXX could get serious here with recursive types, but shouldn't 
+        # need that for this codegen
+    def __deepcopy__(self, memo):
+        return Type(self.name, self.const, self.ptr, self.ref)
+
+class TypeEnum(Node):
+    def __init__(self, name=None):
+        '''name can be None'''
+        Node.__init__(self)
+        self.name = name
+        self.idnums = [ ]    # pairs of ('Foo', [num]) or ('Foo', None)
+
+    def addId(self, id, num=None):
+        self.idnums.append((id, num))
+
+class Typedef(Node):
+    def __init__(self, fromtype, totype):
+        Node.__init__(self)
+        self.fromtype = fromtype
+        self.totype = totype
+
+class Decl(Node):
+    '''represents |Foo bar|, e.g. in a function signature'''
+    def __init__(self, type, name):
+        Node.__init__(self)
+        self.type = type
+        self.name = name
+    def __deepcopy__(self, memo):
+        return Decl(copy.deepcopy(self.type, memo), self.name)
+
+##------------------------------
+# class stuff
+class Class(Block):
+    def __init__(self, name, inherits=[ ],
+                 interface=False, final=False):
+        assert not (interface and final)
+
+        Block.__init__(self)
+        self.name = name
+        self.inherits = inherits # array of (viz, Type) pairs
+        self.interface = interface
+        self.final = final
+
+class Inherit(Node):
+    def __init__(self, name, viz='public'):
+        Node.__init__(self)
+        self.name = name
+        self.viz = viz
+
+class MethodDecl(Node):
+    def __init__(self, name, params=[ ], ret=Type('void'),
+                 virtual=False, const=False, pure=False, static=False):
+        assert not (virtual and static)
+        assert not pure or virtual # pure => virtual
+
+        Node.__init__(self)
+        self.name = name
+        self.params = params
+        self.ret = ret
+        self.virtual = virtual
+        self.const = const
+        self.pure = pure
+        self.static = static
+    def __deepcopy__(self, memo):
+        return MethodDecl(self.name,
+                          copy.deepcopy(self.params, memo),
+                          copy.deepcopy(self.ret, memo),
+                          self.virtual,
+                          self.const,
+                          self.pure)
+
+class MethodDefn(Block):
+    def __init__(self, decl):
+        Block.__init__(self)
+        self.decl = decl
+
+class ConstructorDecl(MethodDecl):
+    def __init__(self, name, params=[ ]):
+        MethodDecl.__init__(self, name, params=params, ret=None)
+
+class ConstructorDefn(MethodDefn):
+    def __init__(self, decl, memberinits=[ ]):
+        MethodDefn.__init__(self, decl)
+        self.memberinits = memberinits
+
+class DestructorDecl(MethodDecl):
+    def __init__(self, name, virtual=False):
+        MethodDecl.__init__(self, name, params=[ ], ret=None,
+                            virtual=virtual)
+class DestructorDefn(MethodDefn):
+    def __init__(self, decl):  MethodDefn.__init__(self, decl)
+
+##------------------------------
+# expressions
+class ExprVar(Node):
+    def __init__(self, name):
+        Node.__init__(self)
+        self.name = name
+
+class ExprPrefixUnop(Node):
+    def __init__(self, expr, op):
+        self.expr = expr
+        self.op = op
+
+class ExprAddrOf(ExprPrefixUnop):
+    def __init__(self, expr):
+        ExprPrefixUnop.__init__(self, expr, '&')
+
+class ExprDeref(ExprPrefixUnop):
+    def __init__(self, expr):
+        ExprPrefixUnop.__init__(self, expr, '*')
+
+class ExprSelect(Node):
+    def __init__(self, obj, op, field):
+        Node.__init__(self)
+        self.obj = obj
+        self.op = op
+        self.field = field
+
+class ExprAssn(Node):
+    def __init__(self, lhs, rhs):
+        Node.__init__(self)
+        self.lhs = lhs
+        self.rhs = rhs
+
+class ExprCall(Node):
+    def __init__(self, func, args=[ ]):
+        Node.__init__(self)
+        self.func = func
+        self.args = args
+
+class ExprNew(ExprCall):
+    # XXX taking some poetic license ...
+    def __init__(self, type, args=[ ]):
+        ExprCall.__init__(self, ExprVar(type.name), args)
+
+class ExprDelete(Node):
+    def __init__(self, obj):
+        Node.__init__(self)
+        self.obj = obj
+
+class ExprMemberInit(ExprCall):
+    def __init__(self, member, args=[ ]):
+        ExprCall.__init__(self, member, args)
+
+##------------------------------
+# statements etc.
+class StmtBlock(Block):
+    def __init__(self):
+        Block.__init__(self)
+
+class StmtDecl(Node):
+    def __init__(self, decl):
+        Node.__init__(self)
+        self.decl = decl
+
+class Label(Node):
+    def __init__(self, name):
+        Node.__init__(self)
+        self.name = name
+
+class CaseLabel(Node):
+    def __init__(self, name):
+        Node.__init__(self)
+        self.name = name
+
+class DefaultLabel(Node):
+    def __init__(self):
+        Node.__init__(self)
+
+class StmtIf(Node):
+    def __init__(self, cond):
+        Node.__init__(self)
+        self.cond = cond
+        self.ifb = Block()
+        self.elseb = None
+    def addifstmt(self, stmt):
+        self.ifb.addstmt(stmt)
+    def addelsestmt(self, stmt):
+        if self.elseb is None: self.elseb = Block()
+        self.elseb.addstmt(stmt)
+
+class StmtSwitch(Block):
+    def __init__(self, expr):
+        Block.__init__(self)
+        self.expr = expr
+
+    def addcase(self, case, block):
+        '''NOTE: |case| is not checked for uniqueness'''
+        self.addstmt(case)
+        self.addstmt(block)
+
+class StmtExpr(Node):
+    def __init__(self, expr):
+        Node.__init__(self)
+        self.expr = expr
+
+class StmtReturn(Node):
+    def __init__(self, expr=None):
+        Node.__init__(self)
+        self.expr = expr
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/cxx/cgen.py
@@ -0,0 +1,337 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import sys
+
+from ipdl.cgen import CodePrinter
+from ipdl.cxx.ast import Visitor
+
+class CxxCodeGen(CodePrinter, Visitor):
+    def __init__(self, outf=sys.stdout, indentCols=4):
+        CodePrinter.__init__(self, outf, indentCols)
+
+    def cgen(self, cxxfile):
+        cxxfile.accept(self)
+
+    def visitWhitespace(self, ws):
+        self.write(ws.ws)
+
+    def visitCppDirective(self, cd):
+        self.println('#%s %s'% (cd.directive, cd.rest))
+
+    def visitNamespace(self, ns):
+        self.println('namespace '+ ns.name +' {')
+        self.visitBlock(ns)
+        self.println('} // namespace '+ ns.name)
+
+    def visitType(self, t):
+        ts = ''
+        if t.const:  ts += 'const '
+        ts += t.name
+        if t.ptr:  ts += '*'
+        if t.ref:  ts += '&'
+        self.write(ts)
+
+    def visitTypeEnum(self, te):
+        self.write('enum')
+        if te.name:
+            self.write(' '+ te.name)
+        self.println(' {')
+
+        self.indent()
+        nids = len(te.idnums)
+        for i, (id, num) in enumerate(te.idnums):
+            self.printdent(id)
+            if num:
+                self.write(' = '+ str(num))
+            if i != (nids-1):
+                self.write(',')
+            self.println()
+        self.dedent()
+        self.printdent('}')
+            
+
+    def visitTypedef(self, td):
+        self.printdent('typedef ')
+        td.fromtype.accept(self)
+        self.write(' ')
+        td.totype.accept(self)
+        self.println(';')
+
+    def visitDecl(self, d):
+        d.type.accept(self)
+        if d.name:
+            self.write(' '+ d.name)
+
+    def visitClass(self, c):
+        self.printdent('class')
+        if c.interface:
+            # FIXME/cjones: turn this "on" when we get the analysis
+            self.write(' /*NS_INTERFACE_CLASS*/')
+        if c.final:
+            self.write(' /*NS_FINAL_CLASS*/')
+        self.write(' '+ c.name)
+
+        ninh = len(c.inherits)
+        if 0 < ninh:
+            self.println(' :')
+            self.indent()
+            for i, inherit in enumerate(c.inherits):
+                self.printdent()
+                inherit.accept(self)
+                if i != (ninh - 1):
+                    self.println(',')
+            self.dedent()
+        self.println()
+
+        self.printdentln('{')
+        self.indent()
+
+        self.visitBlock(c)
+
+        self.dedent()
+        self.printdentln('};')
+
+    def visitInherit(self, inh):
+        self.write(inh.viz +' '+ inh.name)
+
+
+    def visitMethodDecl(self, md):
+        assert not (md.static and md.virtual)
+        if md.static:
+            self.write('static ')
+        if md.virtual:
+            self.write('virtual ')
+        if md.ret:
+            md.ret.accept(self)
+            self.write(' ')
+        self.write(md.name +'(')
+        self.writeDeclList(md.params)
+        self.write(')')
+        if md.const:
+            self.write(' const')
+        if md.pure:
+            self.write(' = 0')
+
+    def visitMethodDefn(self, md):
+        self.printdent()
+        md.decl.accept(self)
+        self.println()
+
+        self.printdentln('{')
+        self.indent()
+        self.visitBlock(md)
+        self.dedent()
+        self.printdentln('}')
+
+
+    def visitConstructorDecl(self, cd):
+        # FIXME/cjones: explicit when possible
+        self.visitMethodDecl(cd)
+
+    def visitConstructorDefn(self, cd):
+        self.printdent()
+        cd.decl.accept(self)
+        if len(cd.memberinits):
+            self.println(' :')
+            self.indent()
+            ninits = len(cd.memberinits)
+            for i, init in enumerate(cd.memberinits):
+                self.printdent()
+                init.accept(self)
+                if i != (ninits-1):
+                    self.println(',')
+            self.dedent()
+        self.println()
+
+        self.printdentln('{')
+        self.indent()
+
+        self.visitBlock(cd)
+
+        self.dedent()
+        self.printdentln('}')
+
+
+    def visitDestructorDecl(self, dd):
+        if dd.virtual:
+            self.write('virtual ')
+        self.write('~'+ dd.name +'()')
+
+    def visitDestructorDefn(self, dd):
+        self.printdent()
+        dd.decl.accept(self)
+        self.println()
+
+        self.printdentln('{')
+        self.indent()
+
+        self.visitBlock(dd)
+
+        self.dedent()
+        self.printdentln('}')
+
+
+    def visitExprVar(self, ev):
+        self.write(ev.name)
+
+    def visitExprPrefixUnop(self, e):
+        self.write(e.op)
+        self.write('(')
+        e.expr.accept(self)
+        self.write(')')
+
+    def visitExprSelect(self, es):
+        es.obj.accept(self)
+        self.write(es.op + es.field)
+
+    def visitExprAssn(self, ea):
+        ea.lhs.accept(self)
+        self.write(' = ')
+        ea.rhs.accept(self)
+
+    def visitExprCall(self, ec):
+        ec.func.accept(self)
+        self.write('(')
+        self.writeExprList(ec.args)
+        self.write(')')
+
+    def visitExprNew(self, en):
+        self.write('new ')
+        self.visitExprCall(en)
+
+    def visitExprDelete(self, ed):
+        self.write('delete ')
+        ed.accept(self)
+
+
+    def visitStmtBlock(self, b):
+        self.printdentln('{')
+        self.indent()
+        self.visitBlock(b)
+        self.dedent()
+        self.printdentln('}')
+
+    def visitLabel(self, label):
+        self.dedent()           # better not be at global scope ...
+        self.printdentln(label.name +':')
+        self.indent()
+
+    def visitCaseLabel(self, cl):
+        self.dedent()
+        self.printdentln('case '+ cl.name +':')
+        self.indent()
+
+    def visitDefaultLabel(self, dl):
+        self.dedent()
+        self.printdentln('default:')
+        self.indent()
+
+
+    def visitStmtIf(self, si):
+        self.printdent('if (')
+        si.cond.accept(self)
+        self.println(') {')
+        self.indent()
+        si.ifb.accept(self)
+        self.dedent()
+        self.printdentln('}')
+
+        if si.elseb is not None:
+            self.printdentln('else {')
+            self.indent()
+            si.elseb.accept(self)
+            self.dedent()
+            self.println('}')
+
+
+    def visitStmtSwitch(self, sw):
+        self.printdent('switch (')
+        sw.expr.accept(self)
+        self.println(') {')
+        self.indent()
+        self.visitBlock(sw)
+        self.dedent()
+        self.printdentln('}')
+
+
+    def visitStmtDecl(self, sd):
+        self.printdent()
+        sd.decl.accept(self)
+        self.println(';')
+
+
+    def visitStmtExpr(self, se):
+        self.printdent()
+        se.expr.accept(self)
+        self.println(';')
+
+
+    def visitStmtReturn(self, sr):
+        self.printdent('return')
+        if sr.expr:
+            self.write (' ')
+            sr.expr.accept(self)
+        self.println(';')
+
+
+    def writeDeclList(self, decls):
+        # FIXME/cjones: try to do nice formatting of these guys
+
+        ndecls = len(decls)
+        if 0 == ndecls:
+            return
+        elif 1 == ndecls:
+            decls[0].accept(self)
+            return
+
+        self.indent()
+        self.indent()
+        self.indent()
+        for i, decl in enumerate(decls):
+            self.println()
+            self.printdent()
+            decl.accept(self)
+            if i != (ndecls-1):
+                self.write(',')
+        self.dedent()
+        self.dedent()
+        self.dedent()
+
+    def writeExprList(self, exprs):
+        # FIXME/cjones: try to do nice formatting and share code with
+        # writeDeclList()
+        nexprs = len(exprs)
+        for i, expr in enumerate(exprs):
+            expr.accept(self)
+            if i != (nexprs-1):
+                self.write(', ')
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/lower.py
@@ -0,0 +1,729 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import os
+from copy import deepcopy
+
+from ipdl.ast import Visitor, ASYNC, SYNC, RPC, IN, OUT, INOUT
+import ipdl.cxx.ast as cxx
+
+
+def _joinProtocolNamespacesName(sep, p, pname):
+    return sep.join([ ns.namespace for ns in p.namespaces ] + [ pname ])
+
+def _protocolIncludeGuard(p, pname):
+    return _joinProtocolNamespacesName('_', p, pname) +'_h'
+
+def _protocolHeaderFilename(p, pname):
+    return _joinProtocolNamespacesName(os.path.sep, p, pname)+ '.h'
+
+def _protocolHeaderName(pname):
+    return pname +'Protocol'
+
+
+class _struct: pass
+
+class LowerToCxx:
+    def lower(self, tu):
+        '''returns a list of cxx.File representing the lowered form of |tu|'''
+        pname = _protocolHeaderName(tu.protocol.name)
+        pheader = cxx.File(pname +'.h')
+        GenerateProtocolHeader().lower(tu, pname, pheader)
+
+        parentname = pname +'Parent'
+        parentheader = cxx.File(parentname +'.h')
+        GenerateProtocolParentHeader().lower(
+            tu, pname, parentname, parentheader)
+
+        childname = pname +'Child'
+        childheader = cxx.File(childname +'.h')
+        GenerateProtocolChildHeader().lower(
+            tu, pname, childname, childheader)
+
+        return pheader, parentheader, childheader
+
+
+##-----------------------------------------------------------------------------
+class GenerateProtocolHeader(Visitor):
+    '''creates a "generic" protocol header from an IPDL AST'''
+    def __init__(self):
+        self.pname = None
+        self.file = None
+        self.ns = None          # the namespace we toss all this stuff into
+        # list of typedefs for the protocol namespace.  these are 
+        # produced from various places in the AST and collected here
+        self.typedefs = [ ]
+
+    def lower(self, tu, protocol, outcxxfile):
+        self.pname = protocol
+        self.file = outcxxfile
+        tu.accept(self)
+
+
+    def typedef(self, t, name):
+        self.typedefs.append(cxx.Typedef(cxx.Type(t), cxx.Type(name)))
+
+    def injectTypedefs(self, scope):
+        for tdef in self.typedefs:
+            scope.addstmt(tdef)
+
+
+    def visitTranslationUnit(self, tu):
+        f = self.file
+
+        f.addthing(cxx.Whitespace('''//
+// Automatically generated by ipdlc.
+// Edit at your own risk
+//
+
+'''))
+        includeguard = _protocolIncludeGuard(tu.protocol, self.pname)
+        f.addthing(cxx.CppDirective('ifndef', includeguard))
+        f.addthing(cxx.CppDirective('define', includeguard))
+        f.addthing(cxx.Whitespace.NL)
+
+        Visitor.visitTranslationUnit(self, tu)
+
+        f.addthing(cxx.Whitespace.NL)
+        f.addthing(cxx.CppDirective('endif', '// ifndef '+ includeguard))
+
+
+    def visitCxxInclude(self, inc):
+        self.file.addthing(cxx.CppDirective('include', '"'+ inc.file +'"'))
+
+
+    def visitProtocolInclude(self, inc):
+        p = inc.tu.protocol
+        self.file.addthing(
+            cxx.CppDirective(
+                'include',
+                '"'+ _protocolHeaderFilename(p, _protocolHeaderName(p.name)) +'"'))
+        # FIXME/cjones: not clear what types we need from other header
+#         if p.decl.fullname is not None:
+#             self.typedef(p.decl.fullname, p.decl.shortname)
+
+
+    def visitUsingStmt(self, using):
+        if using.decl.fullname is not None:
+            self.typedef(using.decl.fullname, using.decl.shortname)
+
+
+    def visitProtocol(self, p):
+        self.file.addthing(cxx.Whitespace.NL)
+
+        # construct the namespace into which we'll stick all our decls
+        if 0 == len(p.namespaces):
+            self.ns = self.file
+        else:
+            innernamespaces = p.namespaces[1:]
+            self.ns = cxx.Namespace(p.namespaces[0].namespace)
+            self.file.addthing(self.ns)
+
+            for ns in innernamespaces:
+                innerns = cxx.Namespace(ns.namespace)
+                self.ns.addstmt(innerns)
+                self.ns = innerns
+
+        ns = cxx.Namespace(self.pname)
+        self.ns.addstmt(ns)
+        self.ns = ns
+        ns.addstmt(cxx.Whitespace.NL)
+        ns.addstmt(cxx.Whitespace.NL)
+
+        # previsit the messages and stash away some common info used
+        # several times later
+        for md in p.messageDecls:
+            md.accept(self)
+
+        # generate parent and child interfaces
+        iparent = cxx.Class('IParent', interface=True)
+        iparent.addstmt(cxx.Label('protected'))
+        self.injectTypedefs(iparent)
+        iparent.addstmt(cxx.Whitespace.NL)
+
+        p.accept(GenerateParentInterface(iparent))
+        ns.addstmt(iparent)
+        ns.addstmt(cxx.Whitespace.NL)
+
+        ns.addstmt(cxx.Whitespace.NL)
+        ichild = cxx.Class('IChild', interface=True)
+        ichild.addstmt(cxx.Label('protected'))
+        self.injectTypedefs(ichild)
+        ichild.addstmt(cxx.Whitespace.NL)
+
+        p.accept(GenerateChildInterface(ichild))
+        ns.addstmt(ichild)
+        ns.addstmt(cxx.Whitespace.NL)
+
+        # TODO
+        for ts in p.transitionStmts:
+            ts.accept(self)
+        ns.addstmt(cxx.StmtDecl(cxx.Decl(cxx.TypeEnum('State'), '')))
+        ns.addstmt(cxx.Whitespace.NL)
+
+        # spit out message type enum and classes
+        msgstart = self.pname +'MsgStart << 12'
+        msgenum = cxx.TypeEnum(self.pname +'MsgType')
+        msgenum.addId(self.pname +'Start', msgstart)
+        msgenum.addId(self.pname +'PreStart', '('+ msgstart +') - 1')
+
+        for md in p.messageDecls:
+            msgenum.addId(md._cxx.id +'__ID')
+            if md.decl.type.hasReply():
+                msgenum.addId(md._cxx.replyid +'__ID')            
+
+        msgenum.addId(self.pname +'End')
+        ns.addstmt(cxx.StmtDecl(cxx.Decl(msgenum, '')))
+
+        for md in p.messageDecls:
+            ns.addstmt(generateMessageClass(md, self.injectTypedefs))
+            if md.decl.type.hasReply():
+                ns.addstmt(generateReplyClass(md, self.injectTypedefs))
+
+        ns.addstmt(cxx.Whitespace.NL)
+        ns.addstmt(cxx.Whitespace.NL)
+
+
+    def visitMessageDecl(self, md):
+        # where we squirrel away some common information
+        setattr(md, '_cxx', _struct())
+
+        md._cxx.params = [ ]
+        for param in md.inParams:
+            md._cxx.params.append(cxx.Decl(cxx.Type(param.type.name()),
+                                           param.progname))
+        md._cxx.returns = [ ]
+        for param in md.outParams:
+            md._cxx.returns.append(cxx.Decl(cxx.Type(param.type.name()),
+                                            param.progname))
+
+        # generate C++ interface to message sending/handling
+        method = cxx.MethodDecl(
+            name=md.decl.progname,
+            params=[ ],
+            ret=cxx.Type('nsresult'),
+            virtual=True)
+        for param in md._cxx.params:
+            pcopy = deepcopy(param)
+            pcopy.type.const = True
+            pcopy.type.ref = True
+            method.params.append(pcopy)
+        for ret in md._cxx.returns:
+            rcopy = deepcopy(ret)
+            rcopy.type.ptr = True
+            method.params.append(rcopy)
+        md._cxx.method = method
+
+        # the ID is used by the IPC layer only
+        md._cxx.id = 'Msg_%s'% (md.decl.progname)
+        md._cxx.nsid = '%s::%s'% (self.pname, md._cxx.id)
+        if md.decl.type.hasReply():
+            md._cxx.replyid = 'Reply_%s'% (md.decl.progname)
+            md._cxx.nsreplyid = '%s::%s'% (self.pname, md._cxx.replyid)
+
+
+class GenerateInterface(Visitor):
+    def __init__(self, iface, name):
+        self.iface = iface
+        self.name = name
+
+    def visitProtocol(self, p):
+        ifc = self.iface
+        n = self.name
+
+        ifc.addstmt(cxx.Label('public'))
+        nmsgs = len(p.messageDecls)
+        for i, msgdecl in enumerate(p.messageDecls):
+            msgdecl.accept(self)
+
+        ifc.addstmt(cxx.Whitespace.NL)
+        ifc.addstmt(cxx.Label('protected'))
+        ifc.addstmt(cxx.ConstructorDefn(
+                cxx.ConstructorDecl(n)))
+        ifc.addstmt(cxx.DestructorDefn(
+                cxx.DestructorDecl(n, virtual=True)))
+
+        # disable unwanted ctors/operators
+        ifc.addstmt(cxx.Whitespace.NL)
+        ifc.addstmt(cxx.Label('private'))
+        ref = cxx.Type(n, ref=True)
+        constref = cxx.Type(n, const=True, ref=True)
+        ifc.addstmt(cxx.StmtDecl(
+                cxx.ConstructorDecl(n,
+                                    params=[ cxx.Decl(constref, '')])))
+        ifc.addstmt(cxx.StmtDecl(
+                cxx.MethodDecl('operator=',
+                               params= [ cxx.Decl(constref, '') ],
+                               ret=ref)))
+
+    def visitMessageDecl(self, md):
+        if self.msgComesIn(md.decl.type):
+            method = deepcopy(md._cxx.method)
+            method.pure = True
+            self.iface.addstmt(cxx.StmtDecl(method))
+            self.iface.addstmt(cxx.Whitespace.NL)
+
+
+class GenerateParentInterface(GenerateInterface):
+    def __init__(self, iparent):
+        GenerateInterface.__init__(self, iparent, iparent.name)
+    def msgComesIn(self, mtype):  return mtype.isIn() or mtype.isInout()
+
+class GenerateChildInterface(GenerateInterface):
+    def __init__(self, iparent):
+        GenerateInterface.__init__(self, iparent, iparent.name)
+    def msgComesIn(self, mtype):  return mtype.isOut() or mtype.isInout()
+
+
+def generateMsgClass(clsname, params, typedefInjector):
+        cls = cxx.Class(name=clsname,
+                        inherits=[ cxx.Inherit('IPC::Message') ])
+        cls.addstmt(cxx.Label('private'))
+        typedefInjector(cls)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        cls.addstmt(cxx.Label('public'))
+
+        idenum = cxx.TypeEnum()
+        idenum.addId('ID', clsname +'__ID')
+        cls.addstmt(cxx.StmtDecl(cxx.Decl(idenum, '')))
+
+        # FIXME/cjones: need to handle "managed" messages
+
+        constparams = deepcopy(params)
+        writestmts = [ ]
+        for cparam in constparams:
+            cparam.type.const = True
+            cparam.type.ref = True
+            writestmts.append(
+                cxx.StmtExpr(cxx.ExprCall(cxx.ExprVar('IPC::WriteParam'),
+                                          [ cxx.ExprVar('this'),
+                                            cxx.ExprVar(cparam.name) ])))
+
+        # make the message constructor (serializer)
+        ctordecl = cxx.ConstructorDecl(clsname, params=constparams)
+
+        superinit = cxx.ExprMemberInit(
+            cxx.ExprVar('IPC::Message'),
+            [ cxx.ExprVar('MSG_ROUTING_CONTROL'),
+              cxx.ExprVar('ID'),
+              cxx.ExprVar('PRIORITY_NORMAL') ])
+
+        ctor = cxx.ConstructorDefn(ctordecl, [ superinit ])
+        for writestmt in writestmts:
+            ctor.addstmt(writestmt)
+        cls.addstmt(ctor)
+
+        cls.addstmt(cxx.Whitespace.NL)
+
+        # make the message deserializer
+        outparams = deepcopy(params)
+        for oparam in outparams:
+            oparam.type.ptr = True
+
+        reader = cxx.MethodDefn(
+            cxx.MethodDecl(
+                'Read',
+                params=([ cxx.Decl(cxx.Type('Message', ptr=True, const=True),
+                                   'msg') ]
+                        + outparams),
+                ret=cxx.Type('bool'),
+                static=True))
+
+        # avoid generating an unused variable when we don't deserialize
+        # anything.  why generate the method anyway?  it keeps other code
+        # consistent, and we might do some checking in here eventually
+        if len(outparams):
+            # hack
+            reader.addstmt(
+                cxx.StmtDecl(cxx.Decl(cxx.Type('void', ptr=True), 'iter = 0')))
+            reader.addstmt(cxx.Whitespace.NL)
+
+        for oparam in outparams:
+            cond = cxx.ExprPrefixUnop(
+                cxx.ExprCall(cxx.ExprVar('IPC::ReadParam'),
+                             [ cxx.ExprVar('msg'),
+                               cxx.ExprAddrOf(cxx.ExprVar('iter')),
+                               cxx.ExprVar(oparam.name) ]),
+                '!')
+            ifstmt = cxx.StmtIf(cond)
+            # false isn't a var
+            ifstmt.addifstmt(cxx.StmtReturn(cxx.ExprVar('false')))
+            reader.addstmt(ifstmt)
+            reader.addstmt(cxx.Whitespace.NL)
+
+        # false isn't a var
+        reader.addstmt(cxx.StmtReturn(cxx.ExprVar('true')))
+
+        cls.addstmt(reader)
+
+        return cls
+
+def generateMessageClass(md, typedefInjector):
+    return generateMsgClass(md._cxx.id, md._cxx.params, typedefInjector)
+
+def generateReplyClass(md, typedefInjector):
+    return generateMsgClass(md._cxx.replyid, md._cxx.returns, typedefInjector)
+
+
+##-----------------------------------------------------------------------------
+_channelTable = {
+    'Async': [ 'mozilla', 'ipc', 'AsyncChannel' ],
+    'Sync': [ 'mozilla', 'ipc', 'SyncChannel' ],
+    'Rpc': [ 'mozilla', 'ipc', 'RPCChannel' ]
+}
+
+
+class GenerateProtocolActorHeader(Visitor):
+    def __init__(self, thisiface, thatiface):
+        self.thisiface = thisiface
+        self.thatiface = thatiface
+        self.clsname = None
+        self.pname = None
+        self.file = None
+        self.ns = None
+
+    def lower(self, tu, pname, clsname, cxxHeaderFile):
+        self.pname = pname
+        self.clsname = clsname
+        self.file = cxxHeaderFile
+        tu.accept(self)
+
+    def visitTranslationUnit(self, tu):
+        f = self.file
+
+        f.addthing(cxx.Whitespace('''//
+// Automatically generated by ipdlc.
+// Edit at your own risk
+//
+
+'''))
+        includeguard = _protocolIncludeGuard(tu.protocol, self.clsname)
+        f.addthing(cxx.CppDirective('ifndef', includeguard))
+        f.addthing(cxx.CppDirective('define', includeguard))
+        f.addthing(cxx.Whitespace.NL)
+
+        mainheader = _protocolHeaderFilename(tu.protocol, self.pname)
+        f.addthing(cxx.CppDirective('include', '"'+ mainheader +'"'))
+
+        tu.protocol.accept(self)
+
+        f.addthing(cxx.Whitespace.NL)
+        f.addthing(cxx.CppDirective('endif', '// ifndef '+ includeguard))
+
+
+    def visitProtocol(self, p):
+        channel = _channelTable[p.decl.type.sendSemantics.pretty]
+        channelname = '::'.join(channel)
+        channelfile = '/'.join(channel) +'.h'
+        if p.decl.type.isToplevel():
+            self.channelsel = '.'
+        else:
+            self.channelsel = '->'
+
+        self.file.addthing(cxx.CppDirective('include', '"'+ channelfile +'"'))
+        self.file.addthing(cxx.Whitespace.NL)
+
+        # construct the namespace into which we'll stick all our decls
+        if 0 == len(p.namespaces):
+            self.ns = self.file
+        else:
+            innernamespaces = p.namespaces[1:]
+            self.ns = cxx.Namespace(p.namespaces[0].namespace)
+            self.file.addthing(self.ns)
+
+            for ns in innernamespaces:
+                innerns = cxx.Namespace(ns.namespace)
+                self.ns.addstmt(innerns)
+                self.ns = innerns
+
+        self.ns.addstmt(cxx.Whitespace.NL)
+        self.ns.addstmt(cxx.Whitespace.NL)
+
+        iface = p.decl.fullname +'Protocol::'+ self.thatiface
+        cls = cxx.Class(self.clsname,
+                        inherits=[ cxx.Inherit(iface),
+                                   cxx.Inherit(channelname +'::Listener') ],
+                        final=True)
+
+        cls.addstmt(cxx.Label('private'))
+        impliface = p.decl.fullname +'Protocol::'+ self.thisiface
+        cls.addstmt(cxx.Typedef(cxx.Type('IPC::Message'),
+                                cxx.Type('Message')))
+        cls.addstmt(cxx.Typedef(cxx.Type(channelname),
+                                cxx.Type('Channel')))
+        cls.addstmt(cxx.Typedef(cxx.Type(impliface),
+                                cxx.Type(self.thisiface)))
+        cls.addstmt(cxx.Whitespace.NL)
+        
+        # TODO manager param to constructor, when protocol is managed
+
+        cls.addstmt(cxx.Label('public'))
+        ctor = cxx.ConstructorDefn(
+            cxx.ConstructorDecl(
+                self.clsname,
+                [ cxx.Decl(cxx.Type(self.thisiface, ptr=True), 'aImpl') ]),
+            [ cxx.ExprMemberInit(cxx.ExprVar('mImpl'),
+                                 [ cxx.ExprVar('aImpl') ]) ])
+        if p.decl.type.isToplevel():
+            ctor.memberinits.append(
+                cxx.ExprMemberInit(cxx.ExprVar('mChannel'),
+                                   [ cxx.ExprVar('this') ]))
+        cls.addstmt(ctor)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        dtor = cxx.DestructorDefn(
+            cxx.DestructorDecl(self.clsname, virtual=True))
+        cls.addstmt(dtor)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        if p.decl.type.isToplevel():
+            # open
+            openmeth = cxx.MethodDefn(
+                cxx.MethodDecl(
+                    'Open',
+                    params=[ cxx.Decl(cxx.Type('IPC::Channel', ptr=True),
+                                      'aChannel'),
+                             cxx.Decl(cxx.Type('MessageLoop', ptr=True),
+                                      'aThread = 0') ],
+                    ret=cxx.Type('bool')))
+            openmeth.addstmt(cxx.StmtReturn(
+                    cxx.ExprCall(
+                        cxx.ExprSelect(cxx.ExprVar('mChannel'), '.', 'Open'),
+                        [ cxx.ExprVar('aChannel'), cxx.ExprVar('aThread') ])))
+            cls.addstmt(openmeth)
+            cls.addstmt(cxx.Whitespace.NL)
+
+            # close
+            closemeth = cxx.MethodDefn(cxx.MethodDecl('Close'))
+            closemeth.addstmt(cxx.StmtExpr(
+                    cxx.ExprCall(
+                        cxx.ExprSelect(cxx.ExprVar('mChannel'), '.', 'Close'))))
+            cls.addstmt(closemeth)
+            cls.addstmt(cxx.Whitespace.NL)
+
+        # incoming message dispatchers
+        self.asyncswitch = cxx.StmtSwitch(
+            cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('msg'), '.', 'type'), [ ]))
+        if p.decl.type.talksSync():
+            self.syncswitch = deepcopy(self.asyncswitch)
+            if p.decl.type.talksRpc():
+                self.rpcswitch = deepcopy(self.syncswitch)
+
+        # implement child iface and add handlers to message switches
+        self.cls = cls
+        for md in p.messageDecls:
+            self.visitMessageDecl(md)
+
+        # add default cases
+        default = cxx.StmtBlock()
+        default.addstmt(cxx.StmtReturn(cxx.ExprVar('MsgNotKnown')))
+        
+        self.asyncswitch.addcase(cxx.DefaultLabel(), default)
+        if p.decl.type.talksSync():
+            self.syncswitch.addcase(cxx.DefaultLabel(), default)
+            if p.decl.type.talksRpc():
+                self.rpcswitch.addcase(cxx.DefaultLabel(), default)
+
+        asynchandler = cxx.MethodDefn(
+            cxx.MethodDecl(
+                'OnMessageReceived', virtual=True,
+                params=[ cxx.Decl(cxx.Type('Message', const=1, ref=1),'msg') ],
+                ret=cxx.Type('Result')))
+
+        if p.decl.type.talksSync():
+            synchandler = deepcopy(asynchandler)
+            synchandler.decl.params.append(cxx.Decl(
+                    cxx.Type('Message', ref=1, ptr=1), 'reply'))
+
+            if p.decl.type.talksRpc():
+                rpchandler = deepcopy(synchandler)
+                rpchandler.decl.name = 'OnCallReceived'
+
+        asynchandler.addstmt(self.asyncswitch)
+        cls.addstmt(asynchandler)
+        cls.addstmt(cxx.Whitespace.NL)
+
+        if p.decl.type.talksSync():
+            synchandler.addstmt(self.syncswitch)
+            cls.addstmt(synchandler)
+            cls.addstmt(cxx.Whitespace.NL)
+
+            if p.decl.type.talksRpc():
+                rpchandler.addstmt(self.rpcswitch)
+                cls.addstmt(rpchandler)
+                cls.addstmt(cxx.Whitespace.NL)
+
+        # private members and methods
+
+        # TODO handle manager stuff: lookups, routing
+
+        cls.addstmt(cxx.Label('private'))
+        cls.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type(self.thisiface, ptr=True),
+                                          'mImpl')))
+        channeltype = cxx.Type('Channel')
+        if p.decl.type.isManaged():
+            channeltype.ptr = True # subprotocols inherit this
+        cls.addstmt(cxx.StmtDecl(cxx.Decl(channeltype, 'mChannel')))
+
+        self.ns.addstmt(cls)
+        self.ns.addstmt(cxx.Whitespace.NL)
+        self.ns.addstmt(cxx.Whitespace.NL)
+
+
+    def visitMessageDecl(self, md):
+        # TODO special handling of constructor messages
+
+        # create method for "that" interface
+        if self.sendsMessage(md):
+            impl = cxx.MethodDefn(md._cxx.method)
+
+            impl.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type('nsresult'), '__rv')))
+            rv = cxx.ExprVar('__rv')
+            failif = cxx.StmtIf(rv)
+            failif.ifb.addstmt(cxx.StmtReturn(rv))
+
+            hasreply = md.decl.type.hasReply()
+            if hasreply:
+                impl.addstmt(cxx.StmtDecl(
+                        cxx.Decl(cxx.Type('Message'), 'reply')))
+                reply = cxx.ExprVar('reply')
+            impl.addstmt(cxx.Whitespace.NL)
+
+            sendcall = cxx.ExprCall(
+                cxx.ExprSelect(
+                    cxx.ExprVar('mChannel'), self.channelsel, 'Call'),
+                [ cxx.ExprNew(cxx.Type(md._cxx.nsid),
+                              [ cxx.ExprVar(p.name)
+                                for p in md._cxx.params ]) ])
+            if hasreply:
+                sendcall.args.append(cxx.ExprAddrOf(reply))
+
+            # TODO special handling of actor handles
+
+            impl.addstmt(cxx.StmtExpr(cxx.ExprAssn(rv, sendcall)))
+            if not hasreply:
+                impl.addstmt(cxx.StmtReturn(rv))
+                self.cls.addstmt(impl)
+                self.cls.addstmt(cxx.Whitespace.NL)
+            else:
+                impl.addstmt(failif)
+
+                unpack = cxx.ExprCall(cxx.ExprVar(md._cxx.nsreplyid +'::Read'),
+                                      [ cxx.ExprAddrOf(cxx.ExprVar('reply')) ]
+                                      + [ cxx.ExprVar(r.name)
+                                          for r in md._cxx.returns ])
+                errhandle = cxx.StmtIf(cxx.ExprPrefixUnop(unpack, '!'))
+                errhandle.ifb.addstmt(cxx.StmtReturn(
+                        cxx.ExprVar('MsgPayloadError')))
+                impl.addstmt(errhandle)
+
+                # TODO special handling of actor handles
+
+                impl.addstmt(cxx.StmtReturn(cxx.ExprVar('NS_OK')))
+
+            self.cls.addstmt(impl)
+            self.cls.addstmt(cxx.Whitespace.NL)
+
+
+        # create case for this message in the big handler switch statement
+        if self.receivesMessage(md):
+            case = cxx.CaseLabel(md._cxx.nsid +'__ID')
+            block = cxx.StmtBlock()
+
+            rv = cxx.ExprVar('__rv')
+            for param in md._cxx.params:
+                block.addstmt(cxx.StmtDecl(param))
+            for ret in md._cxx.returns:
+                block.addstmt(cxx.StmtDecl(ret))
+            block.addstmt(cxx.Whitespace.NL)
+
+            unpack = cxx.ExprCall(cxx.ExprVar(md._cxx.nsid +'::Read'),
+                                  [ cxx.ExprAddrOf(cxx.ExprVar('msg')) ]
+                                  + [ cxx.ExprAddrOf(cxx.ExprVar(p.name))
+                                      for p in md._cxx.params ])
+            errhandle = cxx.StmtIf(cxx.ExprPrefixUnop(unpack, '!'))
+            errhandle.ifb.addstmt(cxx.StmtReturn(
+                    cxx.ExprVar('MsgPayloadError')))
+            block.addstmt(errhandle)
+
+            # TODO special handling of actor handles
+
+            callimpl = cxx.ExprCall(
+                cxx.ExprSelect(cxx.ExprVar('mImpl'), '->',
+                               md.decl.progname), [ ])
+            callimpl.args += [ cxx.ExprVar(p.name) for p in md._cxx.params ]
+            callimpl.args += [ cxx.ExprAddrOf(cxx.ExprVar(r.name))
+                               for r in md._cxx.returns ]
+            errhandle = cxx.StmtIf(callimpl)
+            errhandle.ifb.addstmt(cxx.StmtReturn(
+                    cxx.ExprVar('MsgValueError')))
+            block.addstmt(errhandle)
+
+            # TODO special handling of actor handles
+
+            if md.decl.type.hasReply():
+                replymsg = cxx.ExprNew(
+                    cxx.Type(md._cxx.nsreplyid),
+                    [ cxx.ExprVar(r.name) for r in md._cxx.returns ])
+                block.addstmt(cxx.StmtExpr(cxx.ExprAssn(cxx.ExprVar('reply'),
+                                                        replymsg)))
+
+            block.addstmt(cxx.StmtReturn(cxx.ExprVar('MsgProcessed')))
+
+            if md.decl.type.isAsync():
+                self.asyncswitch.addcase(case, block)
+            elif md.decl.type.isSync():
+                self.syncswitch.addcase(case, block)
+            else:
+                self.rpcswitch.addcase(case, block)
+
+
+class GenerateProtocolParentHeader(GenerateProtocolActorHeader):
+    def __init__(self):
+        GenerateProtocolActorHeader.__init__(self, 'IParent', 'IChild')
+
+    def sendsMessage(self, md):
+        return not md.decl.type.isIn()
+
+    def receivesMessage(self, md):
+        return md.decl.type.isInout() or md.decl.type.isIn()
+
+class GenerateProtocolChildHeader(GenerateProtocolActorHeader):
+    def __init__(self):
+        GenerateProtocolActorHeader.__init__(self, 'IChild', 'IParent')
+
+    def sendsMessage(self, md):
+        return not md.decl.type.isOut()
+
+    def receivesMessage(self, md):
+        return md.decl.type.isInout() or md.decl.type.isOut()
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/parser.py
@@ -0,0 +1,381 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import os, sys
+from ply import lex, yacc
+
+from ipdl.ast import *
+
+def _getcallerpath():
+    '''Return the absolute path of the file containing the code that
+**CALLED** this function.'''
+    return os.path.abspath(sys._getframe(1).f_code.co_filename)
+
+# we want PLY to generate its output in the module directory, not wherever
+# the user chooses to run ipdlc from
+_thisdir, _ = os.path.split(_getcallerpath())
+
+##-----------------------------------------------------------------------------
+
+class Parser:
+    # when we reach an |include protocol "foo.ipdl";| statement, we need to
+    # save the current parser state and create a new one.  this "stack" is
+    # where that state is saved
+    #
+    # there is one Parser per file
+    current = None
+    parseStack = [ ]
+    parsed = { }
+
+    def __init__(self, debug=0):
+        self.debug = debug
+        self.filename = None
+        self.loc = None         # not always up to date
+        self.lexer = None
+        self.parser = None
+        self.tu = TranslationUnit()
+
+    def parse(self, input, filename):
+        realpath = os.path.abspath(filename)
+        if realpath in Parser.parsed:
+            return Parser.parsed[realpath].tu
+
+        self.lexer = lex.lex(debug=self.debug,
+                             optimize=not self.debug,
+                             lextab="ipdl_lextab",
+                             outputdir=_thisdir)
+        self.parser = yacc.yacc(debug=self.debug,
+                                optimize=not self.debug,
+                                tabmodule="ipdl_yacctab",
+                                outputdir=_thisdir)
+        self.filename = filename
+        self.tu.filename = realpath
+
+        Parser.parsed[realpath] = self
+        Parser.parseStack.append(Parser.current)
+        Parser.current = self
+
+        ast = self.parser.parse(input=input, lexer=self.lexer,
+                                 debug=self.debug)
+
+        Parser.current = Parser.parseStack.pop()
+        return ast
+
+    # returns a GCC-style string representation of the include stack.
+    # e.g.,
+    #   in file included from 'foo.ipdl', line 120:
+    #   in file included from 'bar.ipd', line 12:
+    # which can be printed above a proper error message or warning
+    @staticmethod
+    def includeStackString():
+        s = ''
+        for parse in Parser.parseStack[1:]:
+            s += "  in file included from `%s', line %d:\n"% (
+                parse.loc.filename, parse.loc.lineno)
+        return s
+
+def locFromTok(p, num):
+    return Loc(Parser.current.filename, p.lineno(num))
+
+
+##-----------------------------------------------------------------------------
+
+reserved = set((
+        'answer',
+        'async',
+        'call',
+        'goto',
+        'in',
+        'include',
+        'inout',
+        'manager',
+        'manages',
+        'namespace',
+        'out',
+        'protocol',
+        'recv',
+        'returns',
+        'rpc',
+        'send',
+        'share',
+        'sync',
+        'transfer',
+        'using'))
+tokens = [
+    'COLONCOLON', 'ID', 'STRING'
+] + [ r.upper() for r in reserved ]
+
+t_COLONCOLON = '::'
+
+literals = '(){}[];,~'
+t_ignore = ' \f\t\v'
+
+def t_linecomment(t):
+    r'//[^\n]*'
+
+def t_multilinecomment(t):
+    r'/\*(\n|.)*?\*/'
+    t.lexer.lineno += t.value.count('\n')
+
+def t_NL(t):
+    r'\n+'
+    t.lexer.lineno += len(t.value)
+
+def t_ID(t):
+    r'[a-zA-Z_][a-zA-Z0-9_]*'
+    if t.value in reserved:
+        t.type = t.value.upper()
+    return t
+
+def t_STRING(t):
+    r'"[^"\n]*"'
+    t.value = t.value[1:-1]
+    return t
+
+def t_error(t):
+    includeStackStr = Parser.includeStackString()
+    raise Exception, '%s%s: lexically invalid characters %s'% (
+        includeStackStr, Loc(Parser.current.filename, t.lineno), str(t))
+
+##-----------------------------------------------------------------------------
+
+def p_TranslationUnit(p):
+    """TranslationUnit : Preamble NamespacedProtocolDefn"""
+    tu = Parser.current.tu
+    for stmt in p[1]:
+        if isinstance(stmt, CxxInclude):  tu.addCxxInclude(stmt)
+        elif isinstance(stmt, ProtocolInclude): tu.addProtocolInclude(stmt)
+        elif isinstance(stmt, UsingStmt): tu.addUsingStmt(stmt)
+        else:
+            assert 0
+    tu.protocol = p[2]
+    p[0] = tu
+
+##--------------------
+## Preamble
+def p_Preamble(p):
+    """Preamble : Preamble PreambleStmt ';'
+                |"""
+    if 1 == len(p):
+        p[0] = [ ]
+    else:
+        p[1].append(p[2])
+        p[0] = p[1]
+
+def p_PreambleStmt(p):
+    """PreambleStmt : CxxIncludeStmt
+                    | ProtocolIncludeStmt
+                    | UsingStmt"""
+    p[0] = p[1]
+
+def p_CxxIncludeStmt(p):
+    """CxxIncludeStmt : INCLUDE STRING"""
+    p[0] = CxxInclude(locFromTok(p, 1), p[2])
+
+def p_ProtocolIncludeStmt(p):
+    """ProtocolIncludeStmt : INCLUDE PROTOCOL STRING"""
+    loc = locFromTok(p, 1)
+    Parser.current.loc = loc
+
+    inc = ProtocolInclude(loc, p[3])
+    inc.tu = Parser().parse(open(inc.file).read(), inc.file)
+    p[0] = inc
+
+def p_UsingStmt(p):
+    """UsingStmt : USING CxxType"""
+    p[0] = UsingStmt(locFromTok(p, 1), p[2])
+
+##--------------------
+## Protocol definition
+def p_NamespacedProtocolDefn(p):
+    """NamespacedProtocolDefn : NAMESPACE ID '{' NamespacedProtocolDefn '}'
+                              | ProtocolDefn"""
+    if 2 == len(p):
+        p[0] = p[1]
+    else:
+        protocol = p[4]
+        protocol.addOuterNamespace(Namespace(locFromTok(p, 1), p[2]))
+        p[0] = protocol
+
+def p_ProtocolDefn(p):
+    """ProtocolDefn : SendSemanticsQual PROTOCOL ID '{' ManagerStmtOpt ManagesStmts MessageDecls TransitionStmts '}' ';'"""
+    protocol = Protocol(locFromTok(p, 2))
+    protocol.name = p[3]
+    protocol.sendSemantics = p[1]
+    protocol.manager = p[5]
+    protocol.addManagesStmts(p[6])
+    protocol.addMessageDecls(p[7])
+    protocol.addTransitionStmts(p[8])
+    p[0] = protocol
+
+def p_ManagesStmts(p):
+    """ManagesStmts : ManagesStmts ManagesStmt
+                    | """
+    if 1 == len(p):
+        p[0] = [ ]
+    else:
+        p[1].append(p[2])
+        p[0] = p[1]
+
+def p_ManagerStmtOpt(p):
+    """ManagerStmtOpt : MANAGER ID ';' 
+                      | """
+    if 1 == len(p):
+        p[0] = None
+    else:
+        p[0] = ManagerStmt(locFromTok(p, 1), p[2])
+
+def p_ManagesStmt(p):
+    """ManagesStmt : MANAGES ID ';'"""
+    p[0] = ManagesStmt(locFromTok(p, 1), p[2])
+
+def p_MessageDecls(p):
+    """MessageDecls : MessageDecls MessageDecl ';'
+                    | MessageDecl ';'"""
+    if 3 == len(p):
+        p[0] = [ p[1] ]
+    else:
+        p[1].append(p[2])
+        p[0] = p[1]
+
+def p_MessageDecl(p):
+    """MessageDecl : SendSemanticsQual DirectionQual MessageBody"""
+    msg = p[3]
+    msg.sendSemantics = p[1]
+    msg.direction = p[2]
+    p[0] = msg
+
+def p_MessageBody(p):
+    """MessageBody : MessageId MessageInParams MessageOutParams"""
+    # FIXME/cjones: need better loc info: use one of the quals
+    msg = MessageDecl(locFromTok(p, 1))
+    msg.name = p[1]
+    msg.addInParams(p[2])
+    msg.addOutParams(p[3])
+    p[0] = msg
+
+def p_MessageId(p):
+    """MessageId : ID
+                 | '~' ID"""
+    if 3 == len(p):
+        p[1] += p[2] # munge dtor name to "~Foo". handled later
+    p[0] = p[1]
+
+def p_MessageInParams(p):
+    """MessageInParams : '(' ParamList ')'"""
+    p[0] = p[2]
+
+def p_MessageOutParams(p):
+    """MessageOutParams : RETURNS '(' ParamList ')'
+                        | """
+    if 1 == len(p):
+        p[0] = [ ]
+    else:
+        p[0] = p[3]
+
+def p_TransitionStmts(p):
+    """TransitionStmts : """
+    # FIXME/cjones: impl
+    p[0] = [ ]
+
+##--------------------
+## Minor stuff
+def p_SendSemanticsQual(p):
+    """SendSemanticsQual : ASYNC
+                         | RPC
+                         | SYNC
+                         | """
+    if 1 == len(p):
+        p[0] = ASYNC
+        return
+
+    s = p[1]
+    if 'async' == s: p[0] = ASYNC
+    elif 'rpc' == s: p[0] = RPC
+    elif 'sync'== s: p[0] = SYNC
+    else:
+        assert 0
+
+def p_DirectionQual(p):
+    """DirectionQual : IN
+                     | INOUT
+                     | OUT"""
+    s = p[1]
+    if 'in' == s:  p[0] = IN
+    elif 'inout' == s:  p[0] = INOUT
+    elif 'out' == s:  p[0] = OUT
+    else:
+        assert 0
+
+def p_ParamList(p):
+    """ParamList : ParamList ',' Param
+                 | Param
+                 | """
+    if 1 == len(p):
+        p[0] = [ ]
+    elif 2 == len(p):
+        p[0] = [ p[1] ]
+    else:
+        p[1].append(p[3])
+        p[0] = p[1]
+
+def p_Param(p):
+    """Param : ID ID"""
+    loc = locFromTok(p, 1)
+    p[0] = Param(loc,
+                 TypeSpec(loc, QualifiedId(loc, p[1])),
+                 p[2])
+
+##--------------------
+## C++ stuff
+def p_CxxType(p):
+    """CxxType : QualifiedID
+               | ID"""
+    if isinstance(p[0], QualifiedId):
+        p[0] = TypeSpec(p[1].loc, p[1])
+    else:
+        loc = locFromTok(p, 1)
+        p[0] = TypeSpec(loc, QualifiedId(loc, p[1]))
+
+def p_QualifiedID(p):
+    """QualifiedID : QualifiedID COLONCOLON ID
+                   | ID COLONCOLON ID"""
+    if isinstance(p[1], QualifiedId):
+        p[1].qualify(p[3])
+        p[0] = p[1]
+    else:
+        p[0] = QualifiedId(locFromTok(p, 1), p[3])
+
+def p_error(t):
+    includeStackStr = Parser.includeStackString()
+    raise Exception, '%s%s: syntax error near "%s"'% (
+        includeStackStr, Loc(Parser.current.filename, t.lineno), t.value)
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/type.py
@@ -0,0 +1,679 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import sys
+
+from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, TypeSpec, UsingStmt, Visitor, ASYNC, SYNC, RPC, IN, OUT, INOUT
+import ipdl.builtin as builtin
+
+class Type:
+    # Is this a C++ type?
+    def isCxx():
+        return False
+    # Is this an IPDL type?
+    def isIPDL():
+        return False
+    # Can this type appear in IPDL programs?
+    def isVisible():
+        return False
+    def isVoid(self):
+        return False
+    def typename(self):
+        return self.__class__.__name__
+
+    def name(self): raise Exception, 'NYI'
+    def fullname(self): raise Exception, 'NYI'
+
+class VoidType(Type):
+    # the following are not a type-o's (hah): void is both a Cxx and IPDL type
+    def isCxx():
+        return True
+    def isIPDL():
+        return True
+    def isVisible(self):
+        return True
+    def isVoid(self):
+        return True
+
+    def name(self): return 'void'
+    def fullname(self): return 'void'
+
+VOID = VoidType()
+
+##--------------------
+class CxxType(Type):
+    def isCxx(self):
+        return True
+    def isBuiltin(self):
+        return False
+    def isImported(self):
+        return False
+    def isGenerated(self):
+        return False
+    def isVisible(self):
+        return True
+
+class BuiltinCxxType(CxxType):
+    def __init__(self, qname):
+        assert isinstance(qname, QualifiedId)
+        self.loc = qname.loc
+        self.qname = qname
+    def isBuiltin(self):  return True
+
+    def name(self):
+        return self.qname.baseid
+    def fullname(self):
+        return str(self.qname)
+
+class ImportedCxxType(CxxType):
+    def __init__(self, qname):
+        assert isinstance(qname, QualifiedId)
+        self.loc = qname.loc
+        self.qname = qname
+    def isImported(self): return True
+
+    def name(self):
+        return self.qname.baseid
+    def fullname(self):
+        return str(self.qname)
+
+class GeneratedCxxType(CxxType):
+    def isGenerated(self): return True
+    def isVisible(self):   return False
+
+##--------------------
+class IPDLType(Type):
+    def isIPDL(self):  return True
+    def isVisible(self): return True
+
+    def isAsync(self): return self.sendSemantics is ASYNC
+    def isSync(self): return self.sendSemantics is SYNC
+    def isRpc(self): return self.sendSemantics is RPC
+
+    def talksAsync(self): return True
+    def talksSync(self): return self.isSync() or self.isRpc()
+    def talksRpc(self): return self.isRpc()
+
+    def hasReply(self):  return self.isSync() or self.isRpc()
+
+    def needsMoreJuiceThan(self, o):
+        return (o.isAsync() and not self.isAsync()
+                or o.isSync() and self.isRpc())
+
+class MessageType(IPDLType):
+    def __init__(self, sendSemantics, direction,
+                 ctor=False, dtor=False, cdtype=None):
+        assert not (ctor and dtor)
+        assert not (ctor or dtor) or type is not None
+
+        self.sendSemantics = sendSemantics
+        self.direction = direction
+        self.params = [ ]
+        self.returns = [ ]
+        self.ctor = ctor
+        self.dtor = dtor
+        self.cdtype = cdtype
+    def isMessage(self): return True
+
+    def isCtor(self): return self.ctor
+    def isDtor(self): return self.dtor
+    def constructedType(self):  return self.cdtype
+
+    def isIn(self): return self.direction is IN
+    def isOut(self): return self.direction is OUT
+    def isInout(self): return self.direction is INOUT
+
+class ProtocolType(IPDLType):
+    def __init__(self, qname, sendSemantics):
+        self.qname = qname
+        self.sendSemantics = sendSemantics
+        self.manager = None
+        self.manages = [ ]
+    def isProtocol(self): return True
+
+    def name(self):
+        return self.qname.baseid
+    def fullname(self):
+        return str(self.qname)
+
+    def managedBy(self, mgr):
+        self.manager = mgr
+
+    def isManager(self, pt):
+        for managed in self.manages:
+            if pt is managed:
+                return True
+        return False
+
+    def isManaged(self):
+        return self.manager is not None
+    def isToplevel(self):
+        return not self.isManaged()
+
+##--------------------
+_builtinloc = Loc('<builtin>', 0)
+def makeBuiltinUsing(tname):
+    quals = tname.split('::')
+    base = quals.pop()
+    quals = quals[0:]
+    return UsingStmt(_builtinloc,
+                     TypeSpec(_builtinloc,
+                              QualifiedId(_builtinloc, base, quals)))
+
+builtinUsing = [ makeBuiltinUsing(t) for t in builtin.Types ]
+builtinIncludes = [ CxxInclude(_builtinloc, f) for f in builtin.Includes ]
+
+##--------------------
+def errormsg(loc, fmt, *args):
+    while not isinstance(loc, Loc):
+        if loc is None:  loc = Loc.NONE
+        else:            loc = loc.loc
+    return '%s: error: %s'% (str(loc), fmt % args)
+
+
+class SymbolTable:
+    def __init__(self, errors):
+        self.errors = errors
+        self.scopes = [ { } ]   # stack({})
+        self.globalScope = self.scopes[0]
+        self.currentScope = self.globalScope
+    
+    def enterScope(self, node):
+        assert (isinstance(self.scopes[0], dict)
+                and self.globalScope is self.scopes[0])
+        assert (isinstance(self.currentScope, dict))
+
+        if not hasattr(node, 'symtab'):
+            node.symtab = { }
+
+        self.scopes.append(node.symtab)
+        self.currentScope = self.scopes[-1]
+
+    def exitScope(self, node):
+        symtab = self.scopes.pop()
+        assert self.currentScope is symtab
+
+        self.currentScope = self.scopes[-1]
+
+        assert (isinstance(self.scopes[0], dict)
+                and self.globalScope is self.scopes[0])
+        assert isinstance(self.currentScope, dict)
+
+    def lookup(self, sym):
+        # NB: since IPDL doesn't allow any aliased names of different types,
+        # it doesn't matter in which order we walk the scope chain to resolve
+        # |sym|
+        for scope in self.scopes:
+            decl = scope.get(sym, None)
+            if decl is not None:  return decl
+        return None
+
+    def declare(self, decl):
+        assert decl.progname or decl.shortname or decl.fullname
+        assert decl.loc
+        assert decl.type
+
+        def tryadd(name):
+            olddecl = self.lookup(name)
+            if olddecl is not None:
+                self.errors.append(errormsg(
+                        decl.loc,
+                        "redeclaration of symbol `%s', first declared at %s",
+                        name, olddecl.loc))
+                return
+            self.currentScope[name] = decl
+            decl.scope = self.currentScope
+
+        if decl.progname:  tryadd(decl.progname)
+        if decl.shortname: tryadd(decl.shortname)
+        if decl.fullname:  tryadd(decl.fullname)
+
+
+class TypeCheck(Visitor):
+    '''This pass sets the .type attribute of every AST node.  For some
+nodes, the type is meaningless and it is set to "VOID."  This pass
+also sets the .decl attribute of AST nodes for which that is relevant;
+a decl says where, with what type, and under what name(s) a node was
+declared.
+
+With this information, it finally type checks the AST.'''
+
+    def __init__(self):
+        # NB: no IPDL compile will EVER print a warning.  A program has
+        # one of two attributes: it is either well typed, or not well typed.
+        self.errors = [ ]       # [ string ]
+        self.symtab = SymbolTable(self.errors)
+
+    def check(self, tu, errout=sys.stderr):
+        tu.cxxIncludes = builtinIncludes + tu.cxxIncludes
+
+        # tag each relevant node with "decl" information, giving type, name,
+        # and location of declaration
+        tu.accept(GatherDecls(builtinUsing, self.symtab, self.errors))
+        if len(self.errors):
+            # no point in checking types if we couldn't even resolve symbols
+            self.reportErrors(errout)
+            return False
+
+        # now that the nodes have decls, type checking is much easier.
+        tu.accept(CheckTypes(self.symtab, self.errors))
+        if (len(self.errors)):
+            # no point in later passes if type checking fails
+            self.reportErrors(errout)
+            return False
+
+        return True
+
+    def reportErrors(self, errout):
+        for error in self.errors:
+            print >>errout, error
+
+
+class GatherDecls(Visitor):
+    def __init__(self, builtinUsing, symtab, errors):
+        self.builtinUsing = builtinUsing
+        self.symtab = symtab
+        self.errors = errors
+        self.visited = set()    # set(filename)
+        self.depth = 0
+
+    def visitTranslationUnit(self, tu):
+        # all TranslationUnits declare symbols in global scope
+        if tu.filename in self.visited:
+            return
+        self.visited.add(tu.filename)
+        self.depth += 1
+
+        # bit of a hack here --- we want the builtin |using|
+        # statements to be added to the symbol table before anything
+        # else, but we also want them in the top-level translation
+        # unit's list of using stmts so that we can use them later
+        # down the pipe.  so we add them to the symbol table before
+        # anything else, and prepend them to the top-level TU after
+        # it's visited all its |using| decls
+        if 1 == self.depth:
+            for using in self.builtinUsing:
+                udecl = Decl(using.loc)
+                udecl.shortname = using.type.basename()
+                fullname = str(using.type)
+                if udecl.shortname != fullname:
+                    udecl.fullname = fullname
+                udecl.type = BuiltinCxxType(using.type.spec)
+                self.symtab.declare(udecl)
+                using.decl = udecl
+
+        p = tu.protocol
+
+        # FIXME/cjones: it's a little weird and counterintuitive to put
+        # both the namespace and non-namespaced name in the global scope.
+        # try to figure out something better; maybe a type-neutral |using|
+        # that works for C++ and protocol types?
+        pdecl = Decl(p.loc)
+        pdecl.shortname = p.name
+
+        fullname = QualifiedId(p.loc, p.name,
+                               [ ns.namespace for ns in p.namespaces ])
+        if len(fullname.quals):
+            pdecl.fullname = str(fullname)
+
+        pdecl.type = ProtocolType(fullname, p.sendSemantics)
+        pdecl.body = p
+        self.symtab.declare(pdecl)
+
+        p.decl = pdecl
+        p.type = pdecl.type
+
+        # make sure we have decls for all dependent protocols
+        for pinc in tu.protocolIncludes:
+            pinc.accept(self)
+
+        # each protocol has its own scope for types brought in through |using|
+        self.symtab.enterScope(tu)
+
+        # and for all imported C++ types
+        for using in tu.using:
+            using.accept(self)
+
+        # (see long comment above)
+        if 1 == self.depth:
+            tu.using = self.builtinUsing + tu.using
+
+        # grab symbols in the protocol itself
+        p.accept(self)
+
+        self.symtab.exitScope(tu)
+
+        tu.type = VOID
+        self.depth -= 1
+
+    def visitProtocolInclude(self, pi):
+        pi.tu.accept(self)
+
+    def visitUsingStmt(self, using):
+        decl = Decl(using.loc)
+        decl.shortname = using.type.basename()
+        fullname = str(using.type)
+        if decl.shortname != fullname:
+            decl.fullname = fullname
+        decl.type = ImportedCxxType(using.type.spec)
+        self.symtab.declare(decl)
+        using.decl = decl
+
+
+    def visitProtocol(self, p):
+        # protocol scope
+        self.symtab.enterScope(p)
+
+        if p.manager is not None:
+            p.manager.of = p
+            p.manager.accept(self)
+
+        for managed in p.managesStmts:
+            managed.manager = p
+            managed.accept(self)
+
+        setattr(self, 'currentProtocolDecl', p.decl)
+        for msg in p.messageDecls:
+            msg.accept(self)
+        del self.currentProtocolDecl
+
+        for trans in p.transitionStmts:
+            trans.accept(self)
+
+        # declare all the little C++ thingies that will be generated.
+
+        # they're not relevant to IPDL itself, but those ("invisible")
+        # symbols can clash with others in the IPDL spec, and we'd like
+        # to catch those before C++ compilers are allowed to obfuscate
+        # the error
+
+        self.symtab.exitScope(p)
+
+
+    def visitManagerStmt(self, mgr):
+        mgrdecl = self.symtab.lookup(mgr.name)
+        pdecl = mgr.of.decl
+        assert pdecl
+
+        pname, mgrname = pdecl.shortname, mgr.name
+        loc = mgr.loc
+
+        if mgrdecl is None:
+            self.errors.append(
+                errmsg(
+                    loc,
+                    "protocol `%s' referenced as |manager| of `%s' has not been declared",
+                    mgrname, pname))
+        elif not isinstance(mgrdecl.type, ProtocolType):
+            self.errors.append(
+                errmsg(
+                    loc,
+                    "entity `%s' referenced as |manager| of `%s' is not of `protocol' type; instead it is of type `%s'",
+                    mgrname, pname, mgrdecl.type.typename()))
+        else:
+            assert pdecl.type.manager is None
+            mgr.decl = mgrdecl
+            pdecl.type.manager = mgrdecl.type
+
+
+    def visitManagesStmt(self, mgs):
+        mgsdecl = self.symtab.lookup(mgs.name)
+        pdecl = mgs.manager.decl
+        assert pdecl
+
+        pname, mgsname = pdecl.shortname, mgs.name
+        loc = mgs.loc
+
+        if mgsdecl is None:
+            self.errors.append(
+                errormsg(
+                    loc,
+                    "protocol `%s', managed by `%s', has not been declared",
+                    mgsname, pdeclname))
+        elif not isinstance(mgsdecl.type, ProtocolType):
+            self.errors.append(
+                errormsg(
+                    loc,
+                    "%s declares itself managing a non-`protocol' entity `%s' of type `%s'",
+                    pdeclname, mgsname, mgsdecl.type.typename()))
+        else:
+            mgs.decl = mgsdecl
+            pdecl.type.manages.append(mgsdecl.type)
+
+
+    def visitMessageDecl(self, md):
+        msgname = md.name
+        loc = md.loc
+
+        isctor = False
+        isdtor = False
+        cdtype = None
+
+        if '~' == msgname[0]:
+            # it's a destructor.  look up the constructed type
+            msgname = msgname[1:]
+
+            decl = self.symtab.lookup(msgname)
+            if decl is None:
+                self.errors.append(
+                    errormsg(
+                        loc,
+                        "type `%s' has not been declared",
+                        msgname))
+            elif not decl.type.isProtocol():
+                self.errors.append(
+                    errormsg(
+                        loc,
+                        "destructor for non-protocol type `%s'",
+                        msgname))
+            else:
+                msgname += 'Destructor'
+                isdtor = True
+                cdtype = decl.type
+
+        decl = self.symtab.lookup(msgname)
+
+        if decl is not None and decl.type.isProtocol():
+            # probably a ctor.  we'll check validity later.
+            msgname += 'Constructor'
+            isctor = True
+            cdtype = decl.type
+        elif decl is not None:
+            self.errors.append(
+                errormsg(
+                    loc,
+                    "message name `%s' already declared as `%s'",
+                    msgname, decl.type.typename()))
+            # if we error here, no big deal; move on to find more
+        decl = None
+
+        # enter message scope
+        self.symtab.enterScope(md)
+
+        msgtype = MessageType(md.sendSemantics, md.direction,
+                              ctor=isctor, dtor=isdtor, cdtype=cdtype)
+
+        # replace inparam Param nodes with proper Decls
+        for i, inparam in enumerate(md.inParams):
+            inptname = inparam.typespec.basename()
+            inploc = inparam.typespec.loc
+
+            inptdecl = self.symtab.lookup(inptname)
+
+            if inptdecl is None:
+                self.errors.append(
+                    errormsg(
+                        inploc,
+                        "inparam typename `%s' of message `%s' has not been declared",
+                        inptname, msgname))
+            else:
+                inpdecl = Decl(inploc)
+                inpdecl.progname = inparam.name
+                inpdecl.type = inptdecl.type
+
+                self.symtab.declare(inpdecl)
+
+                msgtype.params.append(inpdecl.type)
+                md.inParams[i] = inpdecl
+
+        # replace outparam Param with proper Decls
+        for i, outparam in enumerate(md.outParams):
+            outptname = outparam.typespec.basename()
+            outploc = outparam.typespec.loc
+
+            outptdecl = self.symtab.lookup(outptname)
+
+            if outptdecl is None:
+                self.errors.append(
+                    errormsg(
+                        outploc,
+                        "outparam typename `%s' of message `%s' has not been declared",
+                        outptname, msgname))
+            else:
+                outpdecl = Decl(outploc)
+                outpdecl.progname = outparam.name
+                outpdecl.type = outptdecl.type
+
+                self.symtab.declare(outpdecl)
+
+                msgtype.returns.append(outpdecl.type)
+                md.outParams[i] = outpdecl
+
+        self.symtab.exitScope(md)
+
+        decl = Decl(loc)
+        decl.progname = msgname
+        decl.type = msgtype
+
+        self.symtab.declare(decl)
+        md.decl = decl
+        md.protocolDecl = self.currentProtocolDecl
+
+
+class CheckTypes(Visitor):
+    def __init__(self, symtab, errors):
+        self.symtab = symtab
+        self.errors = errors
+        self.visited = set()
+
+    def visitProtocolInclude(self, inc):
+        if inc.tu.filename in self.visited:
+            return
+        self.visited.add(inc.tu.filename)
+        inc.tu.protocol.accept(self)
+
+
+    def visitProtocol(self, p):
+        # check that we require no more "power" than our manager protocol
+        ptype, pname = p.decl.type, p.decl.shortname
+        mgrtype = ptype.manager
+        if mgrtype is not None and ptype.needsMoreJuiceThan(mgrtype):
+            mgrname = p.manager.decl.shortname
+            self.errors.append(errormsg(
+                    p.decl.loc,
+                    "protocol `%s' requires more powerful send semantics than its manager `%s' provides",
+                    pname,
+                    mgrname))
+
+        return Visitor.visitProtocol(self, p)
+        
+
+    def visitManagesStmt(self, mgs):
+        pdecl = mgs.manager.decl
+        ptype, pname = pdecl.type, pdecl.shortname
+
+        mgsdecl = mgs.decl
+        mgstype, mgsname = mgsdecl.type, mgsdecl.shortname
+
+        loc = mgs.loc
+
+        # we added this information; sanity check it
+        for managed in ptype.manages:
+            if managed is mgstype:
+                break
+        else:
+            assert False
+
+        # check that the "managed" protocol agrees
+        if mgstype.manager is not ptype:
+            self.errors.append(errormsg(
+                    loc,
+                    "|manages| declaration in protocol `%s' does not match any |manager| declaration in protocol `%s'",
+                    pname, mgsname))
+
+
+    def visitManagerStmt(self, mgr):
+        pdecl = mgr.of.decl
+        ptype, pname = pdecl.type, pdecl.shortname
+
+        mgrdecl = mgr.decl
+        mgrtype, mgrname = mgrdecl.type, mgrdecl.shortname
+
+        # we added this information; sanity check it
+        assert ptype.manager is mgrtype
+
+        loc = mgr.loc
+
+        # check that the "manager" protocol agrees
+        if not mgrtype.isManager(ptype):
+            self.errors.append(errormsg(
+                    loc,
+                    "|manager| declaration in protocol `%s' does not match any |manages| declaration in protocol `%s'",
+                    pname, mgrname))
+
+
+    def visitMessageDecl(self, md):
+        mtype, mname = md.decl.type, md.decl.progname
+        ptype, pname = md.protocolDecl.type, md.protocolDecl.shortname
+
+        loc = md.decl.loc
+
+        if mtype.needsMoreJuiceThan(ptype):
+            self.errors.append(errormsg(
+                    loc,
+                    "message `%s' requires more powerful send semantics than its protocol `%s' provides",
+                    mname,
+                    pname))
+
+        if mtype.isAsync() and len(mtype.returns):
+            # XXX/cjones could modify grammar to disallow this ...
+            self.errors.append(errormsg(
+                    loc,
+                    "asynchronous message `%s' requests returned values",
+                    mname))
+
+        if (mtype.isCtor() or mtype.isDtor()) and not ptype.isManager(mtype.constructedType()):
+            self.errors.append(errormsg(
+                    loc,
+                    "ctor/dtor for protocol `%s', which is not managed by protocol `%s'", 
+                    mname[:-len('constructor')],
+                    pname))
new file mode 100755
--- /dev/null
+++ b/ipc/ipdl/ipdlc
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import getopt, os, re, sys
+
+_verbosity = 1
+def log(minv, fmt, *args):
+    if _verbosity >= minv:
+        print >>sys.stderr, fmt % args
+
+def getcallerpath():
+    '''Return the absolute path of the file containing the code that
+**CALLED** this function.'''
+    return os.path.abspath(sys._getframe(1).f_code.co_filename)
+
+# auto-add the compiler modules to the pythonpath
+installdir, _ = os.path.split(getcallerpath())
+sys.path.append(installdir)
+
+import ipdl
+
+# process command line
+
+def usage(err):
+    if err:
+        out, rv = (sys.stderr, 1)
+    else:
+        out, rv = (sys.stdout, 0)
+    print >>out, '''
+Usage:  ipdlc [OPTIONS...] SPECIFICATION
+
+where OPTIONS are zero or more of
+  -h, --help
+          : print this message and exit
+  -d DIR, --output-dir=DIR
+          : directory in which to put generated code.
+              Created if it doesn't exist.
+            default: use current working directory.
+  -v N, --verbosity=N
+          : be as verbose as N.  set to 0 for no output, and higher than 1
+              for more output.
+            default: -v 1
+
+and SPECIFICATION is a single IPDL specification file.  '-' = read from stdin.
+  default : read specification from stdin
+'''
+    sys.exit(rv)
+
+try:
+    opts, args = getopt.gnu_getopt(
+        sys.argv[1:],
+        'd:hv:',
+        ['help',
+         'output-dir=',
+         'verbosity='])
+except getopt.GetoptError, err:
+    print >>sys.stderr, str(err), '\n'
+    usage(err=True)
+
+codedir = os.getcwd()
+spec = '-'
+
+for o, a in opts:
+    if o in ('-h', '--help'):
+        usage(err=False)
+    elif o in ('-d', '--output-dir'):
+        codedir = a
+    elif o in ('-v', '--verbosity'):
+        _verbosity = int(a)
+
+if 1 < len(args):
+   usage(err=True)
+if 1 == len(args):
+    spec = args[0]
+
+specstring = None
+specfilename = None
+if spec != '-':
+    log (1, 'using specification %s', spec)
+    specstring = open(spec, 'r').read()
+    specfilename = spec
+else:
+    log(1, 'reading specification from stdin')
+    specstring = sys.stdin.read()
+    specfilename = '<stdin>'
+
+log(3, '  specification:\n%s', specstring)
+
+log(1, 'parsing specification')
+ast = ipdl.parse(specstring, specfilename)
+
+log(1, 'checking types')
+if not ipdl.typecheck(ast):
+    print >>sys.stderr, 'Specification is not well typed.'
+    sys.exit(1)
+
+if _verbosity >= 3:
+    log(3, '  pretty printed code:')
+    ipdl.genipdl(ast, codedir)
+
+log(1, 'generating code to "%s"', codedir)
+ipdl.gencxx(ast, codedir)
+
+log(1, '''\nIMPORTANT: remember to add the new enum value
+
+  %sMsgStart,
+
+to the |IPCMessageStart| enum in "ipc/glue/IPCMessageUtils.h".
+Your code will not compile until this value is added.
+'''% (ast.protocol.name))