bug 525172: from IPDL specs generate .cpp files with method definitions instead of everything in .h files. r=bsmedberg
authorChris Jones <jones.chris.g@gmail.com>
Thu, 03 Dec 2009 02:16:09 -0600
changeset 36137 a006c48594102ef7eba67b126ed0fa44a3aa1c11
parent 36136 1da06de159400756bcb60047eeaeb4185a7d7ce2
child 36138 5160e99545e97c8263d36774d87a70487cf7cf98
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs525172
milestone1.9.3a1pre
bug 525172: from IPDL specs generate .cpp files with method definitions instead of everything in .h files. r=bsmedberg
ipc/Makefile.in
ipc/glue/IPCMessageUtils.h
ipc/ipdl/Makefile.in
ipc/ipdl/ipdl.py
ipc/ipdl/ipdl/__init__.py
ipc/ipdl/ipdl/builtin.py
ipc/ipdl/ipdl/cxx/ast.py
ipc/ipdl/ipdl/cxx/cgen.py
ipc/ipdl/ipdl/lower.py
ipc/ipdl/test/cxx/IPDLUnitTestTypes.h
ipc/ipdl/test/cxx/Makefile.in
toolkit/library/libxul-config.mk
toolkit/toolkit-tiers.mk
xpcom/build.mk
--- a/ipc/Makefile.in
+++ b/ipc/Makefile.in
@@ -36,12 +36,16 @@
 
 DEPTH = ..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-DIRS += testshell
+# NB: chromium/ and glue/ are in tier_xpcom
+
+# tier_gecko:
+DIRS += ipdl testshell
+
 TOOL_DIRS = app
 
 include $(topsrcdir)/config/rules.mk
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -40,20 +40,16 @@
 #define __IPC_GLUE_IPCMESSAGEUTILS_H__
 
 #include "chrome/common/ipc_message_utils.h"
 
 #include "prtypes.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 
-#include "IPCMessageStart.h"
-
-COMPILE_ASSERT(LastMsgIndex <= 64, need_to_update_IPC_MESSAGE_MACRO);
-
 namespace IPC {
 
 template <>
 struct ParamTraits<nsACString>
 {
   typedef nsACString paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -39,37 +39,68 @@ topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 GARBAGE_DIRS += _ipdlheaders
 GARBAGE += ipdl_lextab.py ipdl_yacctab.py
 
-include $(topsrcdir)/config/rules.mk
+MODULE = ipdlgen
+LIBRARY_NAME = mozipdlgen_s
+FORCE_STATIC_LIB = 1
+LIBXUL_LIBRARY = 1
+EXPORT_LIBRARY = 1
 
+##-----------------------------------------------------------------------------
+## When you add IPDL files to a source directory, list the directory here.
+##
 IPDLDIRS =  \
   dom/plugins  \
   dom/ipc  \
   netwerk/ipc  \
   netwerk/protocol/http/src  \
   ipc/ipdl/test/cxx  \
   ipc/testshell  \
   $(NULL)
+##-----------------------------------------------------------------------------
+
+ifdef MOZ_IPDL_TESTS
+DIRS += test
+endif
 
 vpath %.ipdl $(topsrcdir)
 
 define ADD_IPDLDIR
 include $(topsrcdir)/$(IPDLDIR)/ipdl.mk
 ALL_IPDLSRCS += $$(IPDLSRCS:%=$(IPDLDIR)/%)
+PROTOCOLS += $$(IPDLSRCS)
 endef
 
 ALL_IPDLSRCS :=
+PROTOCOLS :=
 
 $(foreach IPDLDIR,$(IPDLDIRS),$(eval $(ADD_IPDLDIR)))
 
+
+CPPSRCS =					\
+  $(PROTOCOLS:%.ipdl=%Parent.cpp)		\
+  $(PROTOCOLS:%.ipdl=%Child.cpp)		\
+  $(NULL)
+
+LOCAL_INCLUDES += -I$(DEPTH)/ipc/ipdl/_ipdlheaders
+
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+include $(topsrcdir)/config/rules.mk
+
+
+# NB: the IPDL compiler manages .ipdl-->.h/.cpp dependencies itself,
+# which is why we don't have explicit .h/.cpp targets here
 export:: $(ALL_IPDLSRCS)
-	$(PYTHON) $(topsrcdir)/config/pythonpath.py \
-	  -I$(topsrcdir)/other-licenses/ply \
-	  $(srcdir)/ipdl.py \
-	  -d _ipdlheaders \
-	  $(IPDLDIRS:%=-I$(topsrcdir)/%) \
+	$(PYTHON) $(topsrcdir)/config/pythonpath.py	\
+	  -I$(topsrcdir)/other-licenses/ply		\
+	  $(srcdir)/ipdl.py				\
+	  --outheaders-dir=_ipdlheaders			\
+	  --outcpp-dir=.				\
+	  $(IPDLDIRS:%=-I$(topsrcdir)/%)		\
 	  $^
--- a/ipc/ipdl/ipdl.py
+++ b/ipc/ipdl/ipdl.py
@@ -37,35 +37,46 @@ import ipdl
 
 def log(minv, fmt, *args):
     if _verbosity >= minv:
         print >>sys.stderr, fmt % args
 
 # process command line
 
 op = optparse.OptionParser(usage='ipdl.py [options] IPDLfiles...')
-op.add_option('-d', '--output-dir', dest='outputdir', default='.',
-              help='Directory in which to put generated headers')
 op.add_option('-I', '--include', dest='includedirs', default=[ ],
               action='append',
               help='Additional directory to search for included protocol specifications')
 op.add_option('-v', '--verbose', dest='verbosity', default=1, action='count',
               help='Verbose logging (specify -vv or -vvv for very verbose logging)')
 op.add_option('-q', '--quiet', dest='verbosity', action='store_const', const=0,
               help="Suppress logging output")
+op.add_option('-d', '--outheaders-dir', dest='headersdir', default='.',
+              help="""Director into which C++ headers will be generated.
+A protocol Foo in the namespace bar will cause the headers
+  dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h
+to be generated""")
+op.add_option('-o', '--outcpp-dir', dest='cppdir', default='.',
+              help="""Director into which C++ sources will be generated
+A protocol Foo in the namespace bar will cause the sources
+  cppdir/FooParent.cpp, cppdir/FooChild.cpp
+to be generated""")
+
 
 options, files = op.parse_args()
 _verbosity = options.verbosity
-codedir = options.outputdir
+headersdir = options.headersdir
+cppdir = options.cppdir
 includedirs = [ os.path.abspath(incdir) for incdir in options.includedirs ]
 
 if not len(files):
     op.error("No IPDL files specified")
 
-log(1, 'Generated headers will be placed in "%s"', codedir)
+log(1, 'Generated C++ headers will be generated relative to "%s"', headersdir)
+log(1, 'Generated C++ sources will be generated relative to "%s"', cppdir)
 
 allprotocols = []
 
 for f in files:
     log(1, 'Parsing specification %s', f)
     if f == '-':
         fd = sys.stdin
         filename = '<stdin>'
@@ -87,30 +98,37 @@ for f in files:
     if not ipdl.typecheck(ast):
         print >>sys.stderr, 'Specification is not well typed.'
         sys.exit(1)
 
     if _verbosity > 2:
         log(3, '  pretty printed code:')
         ipdl.genipdl(ast, codedir)
 
-    ipdl.gencxx(ast, codedir)
+    ipdl.gencxx(filename, ast, headersdir, cppdir)
 
 allprotocols.sort()
 
 ipcmsgstart = StringIO()
 
 print >>ipcmsgstart, """
 // CODE GENERATED by ipdl.py. Do not edit.
 
+#ifndef IPCMessageStart_h
+#define IPCMessageStart_h
+
 enum IPCMessageStart {
 """
 
 for name in allprotocols:
     print >>ipcmsgstart, "  %s," % name
 
 print >>ipcmsgstart, """
   LastMsgIndex
 };
+
+COMPILE_ASSERT(LastMsgIndex <= 64, need_to_update_IPC_MESSAGE_MACRO);
+
+#endif // ifndef IPCMessageStart_h
 """
 
 ipdl.writeifmodified(ipcmsgstart.getvalue(),
-                     os.path.join(codedir, 'IPCMessageStart.h'))
+                     os.path.join(headersdir, 'IPCMessageStart.h'))
--- a/ipc/ipdl/ipdl/__init__.py
+++ b/ipc/ipdl/ipdl/__init__.py
@@ -37,39 +37,53 @@ from cStringIO import StringIO
 
 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', includedirs=[ ], errout=sys.stderr):
     '''Return an IPDL AST if parsing was successful.  Print errors to |errout|
     if it is not.'''
     return Parser().parse(specstring, os.path.abspath(filename), includedirs, errout)
 
+
 def typecheck(ast, errout=sys.stderr):
     '''Return 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):
-        file = os.path.join(
-            outdir,
-            *([ns.name for ns in ast.protocol.namespaces] + [hdr.name]))
+
+def gencxx(ipdlfilename, ast, outheadersdir, outcppdir):
+    headers, cpps = LowerToCxx().lower(ast)
 
+    def resolveHeader(hdr):
+        return [
+            hdr, 
+            os.path.join(
+                outheadersdir,
+                *([ns.name for ns in ast.protocol.namespaces] + [hdr.name]))
+        ]
+    def resolveCpp(cpp):
+        return [ cpp, os.path.join(outcppdir, cpp.name) ]
+
+    for ast, filename in ([ resolveHeader(hdr) for hdr in headers ]
+                          + [ resolveCpp(cpp) for cpp in cpps ]):
         tempfile = StringIO()
-        CxxCodeGen(tempfile).cgen(hdr)
-        writeifmodified(tempfile.getvalue(), file)
+        CxxCodeGen(tempfile).cgen(ast)
+        writeifmodified(tempfile.getvalue(), filename)
+
 
 def genipdl(ast, outdir):
     return IPDLCodeGen().cgen(ast)
 
+
 def writeifmodified(contents, file):
     dir = os.path.dirname(file)
     os.path.exists(dir) or os.makedirs(dir)
 
     oldcontents = None
     if os.path.exists(file):
         fd = open(file, 'rb')
         oldcontents = fd.read()
--- a/ipc/ipdl/ipdl/builtin.py
+++ b/ipc/ipdl/ipdl/builtin.py
@@ -62,13 +62,14 @@ Types = (
     'nsCString',
 )
 
 
 Includes = (
     'base/basictypes.h',
     'prtime.h',
     'nscore.h',
+    'IPCMessageStart.h',
     'IPC/IPCMessageUtils.h',
     'nsStringGlue.h',
     'nsTArray.h',
     'mozilla/ipc/ProtocolUtils.h',
 )
--- a/ipc/ipdl/ipdl/cxx/ast.py
+++ b/ipc/ipdl/ipdl/cxx/ast.py
@@ -72,16 +72,21 @@ class Visitor:
         tdef.fromtype.accept(self)
 
     def visitForwardDecl(self, fd):
         pass
 
     def visitDecl(self, decl):
         decl.type.accept(self)
 
+    def visitParam(self, param):
+        self.visitDecl(param)
+        if param.default is not None:
+            param.default.accept(self)
+
     def visitClass(self, cls):
         for inherit in cls.inherits:
             inherit.accept(self)
         self.visitBlock(cls)
 
     def visitInherit(self, inh):
         pass
 
@@ -393,16 +398,24 @@ class Decl(Node):
         assert isinstance(name, str)
 
         Node.__init__(self)
         self.type = type
         self.name = name
     def __deepcopy__(self, memo):
         return Decl(copy.deepcopy(self.type, memo), self.name)
 
+class Param(Decl):
+    def __init__(self, type, name, default=None):
+        Decl.__init__(self, type, name)
+        self.default = default
+    def __deepcopy__(self, memo):
+        return Param(copy.deepcopy(self.type, memo), self.name,
+                     copy.deepcopy(self.default, memo))
+
 ##------------------------------
 # class stuff
 class Class(Block):
     def __init__(self, name, inherits=[ ],
                  interface=0, abstract=0, final=0,
                  specializes=None, struct=0):
         assert not (interface and abstract)
         assert not (abstract and final)
@@ -481,16 +494,21 @@ class ConstructorDefn(MethodDefn):
     def __init__(self, decl, memberinits=[ ]):
         MethodDefn.__init__(self, decl)
         self.memberinits = memberinits
 
 class DestructorDecl(MethodDecl):
     def __init__(self, name, virtual=0):
         MethodDecl.__init__(self, name, params=[ ], ret=None,
                             virtual=virtual)
+
+    def __deepcopy__(self, memo):
+        return DestructorDecl(self.name, self.virtual)
+
+        
 class DestructorDefn(MethodDefn):
     def __init__(self, decl):  MethodDefn.__init__(self, decl)
 
 ##------------------------------
 # expressions
 class ExprVar(Node):
     def __init__(self, name):
         assert isinstance(name, str)
--- a/ipc/ipdl/ipdl/cxx/cgen.py
+++ b/ipc/ipdl/ipdl/cxx/cgen.py
@@ -131,16 +131,22 @@ class CxxCodeGen(CodePrinter, Visitor):
         if d.name:
             self.write(' '+ d.name)
 
         if isinstance(d.type, TypeArray):
             self.write('[')
             d.type.nmemb.accept(self)
             self.write(']')
 
+    def visitParam(self, p):
+        self.visitDecl(p)
+        if p.default is not None:
+            self.write(' = ')
+            p.default.accept(self)
+
     def visitClass(self, c):
         if c.specializes is not None:
             self.printdentln('template<>')
         
         if c.struct:
             self.printdent('struct')
         else:
             self.printdent('class')
@@ -253,17 +259,22 @@ class CxxCodeGen(CodePrinter, Visitor):
 
         self.dedent()
         self.printdentln('}')
 
 
     def visitDestructorDecl(self, dd):
         if dd.virtual:
             self.write('virtual ')
-        self.write('~'+ dd.name +'()')
+
+        # hack alert
+        parts = dd.name.split('::')
+        parts[-1] = '~'+ parts[-1]
+
+        self.write('::'.join(parts) +'()')
 
     def visitDestructorDefn(self, dd):
         self.printdent()
         dd.decl.accept(self)
         self.println()
 
         self.printdentln('{')
         self.indent()
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -43,47 +43,54 @@ from ipdl.type import TypeVisitor
 # this code will remain off until the chromium base lib is replaced
 EMIT_LOGGING_CODE = ('win32' == sys.platform)
 
 ##-----------------------------------------------------------------------------
 ## "Public" interface to lowering
 ##
 class LowerToCxx:
     def lower(self, tu):
-        '''returns a list of File representing the lowered form of |tu|'''
+        '''returns |[ header: File ], [ cpp : File ]| representing the
+lowered form of |tu|'''
         # annotate the AST with IPDL/C++ IR-type stuff used later
         tu.accept(_DecorateWithCxxStuff())
 
         pname = tu.protocol.name
 
         pheader = File(pname +'.h')
         _GenerateProtocolHeader().lower(tu, pheader)
 
-        parentheader = File(pname +'Parent.h')
-        _GenerateProtocolParentHeader().lower(tu, pname+'Parent', parentheader)
-
-        childheader = File(pname +'Child.h')
-        _GenerateProtocolChildHeader().lower(tu, pname+'Child', childheader)
-
-        return pheader, parentheader, childheader
+        parentheader, parentcpp = File(pname +'Parent.h'), File(pname +'Parent.cpp')
+        _GenerateProtocolParentCode().lower(
+            tu, pname+'Parent', parentheader, parentcpp)
+
+        childheader, childcpp = File(pname +'Child.h'), File(pname +'Child.cpp')
+        _GenerateProtocolChildCode().lower(
+            tu, pname+'Child', childheader, childcpp)
+
+        return [ pheader, parentheader, childheader ], [ parentcpp, childcpp ]
 
 
 ##-----------------------------------------------------------------------------
 ## Helper code
 ##
 
 _NULL_ACTOR_ID = ExprLiteral.ZERO
 _FREED_ACTOR_ID = ExprLiteral.ONE
 
 class _struct: pass
 
-def _protocolHeaderBaseFilename(p):
+def _protocolHeaderName(p, side=''):
+    if side: side = side.title()
+    base = p.name + side
+
+    
     pfx = '/'.join([ ns.name for ns in p.namespaces ])
-    if pfx: return pfx +'/'+ p.name
-    else:   return p.name
+    if pfx: return pfx +'/'+ base
+    else:   return base
 
 def _includeGuardMacroName(headerfile):
     return re.sub(r'[./]', '_', headerfile.name)
 
 def _includeGuardStart(headerfile):
     guard = _includeGuardMacroName(headerfile)
     return [ CppDirective('ifndef', guard),
              CppDirective('define', guard)  ]
@@ -351,17 +358,19 @@ def _killProcess(pid):
         args=[ pid,
                # XXX this is meaningless on POSIX
                ExprVar('base::PROCESS_END_KILLED_BY_USER'),
                ExprLiteral.FALSE ])
 
 # Results that IPDL-generated code returns back to *Channel code.
 # Users never see these
 class _Result:
-    Type = Type('Result')
+    @staticmethod
+    def Type():
+        return Type('Result')
 
     Processed = ExprVar('MsgProcessed')
     NotKnown = ExprVar('MsgNotKnown')
     NotAllowed = ExprVar('MsgNotAllowed')
     PayloadError = ExprVar('MsgPayloadError')
     RouteError = ExprVar('MsgRouteError')
     ValuError = ExprVar('MsgValueError') # [sic]
 
@@ -2265,121 +2274,192 @@ class _FindFriends(ipdl.ast.Visitor):
         for param in md.inParams:
             for actor in ipdl.type.iteractortypes(param.type):
                 yield actor
         for ret in md.outParams:
             for actor in ipdl.type.iteractortypes(ret.type):
                 yield actor
 
 
-class _GenerateProtocolActorHeader(ipdl.ast.Visitor):
+class _GenerateProtocolActorCode(ipdl.ast.Visitor):
     def __init__(self, myside):
         self.side = myside              # "parent" or "child"
         self.prettyside = myside.title()
         self.clsname = None
         self.protocol = None
-        self.file = None
+        self.hdrfile = None
+        self.cppfile = None
         self.ns = None
         self.cls = None
         self.includedActorTypedefs = [ ]
-
-    def lower(self, tu, clsname, cxxHeaderFile):
+        self.protocolCxxIncludes = [ ]
+
+    def lower(self, tu, clsname, cxxHeaderFile, cxxFile):
         self.clsname = clsname
-        self.file = cxxHeaderFile
+        self.hdrfile = cxxHeaderFile
+        self.cppfile = cxxFile
         tu.accept(self)
 
+    def standardTypedefs(self):
+        return [
+            Typedef(Type('IPC::Message'), 'Message'),
+            Typedef(Type(self.protocol.channelName()), 'Channel'),
+            Typedef(Type(self.protocol.fqListenerName()), 'ChannelListener'),
+            Typedef(Type('base::ProcessHandle'), 'ProcessHandle')
+        ]
+
+
     def visitTranslationUnit(self, tu):
-        f = self.file
-
-        f.addthing(Whitespace('''//
+        self.protocol = tu.protocol
+
+        hf = self.hdrfile
+        cf = self.cppfile
+
+        disclaimer = Whitespace('''//
 // Automatically generated by ipdlc.
 // Edit at your own risk
 //
 
-'''))
-        f.addthings(_includeGuardStart(f))
-        f.addthings([
-            Whitespace.NL,
-            CppDirective(
-                'include',
-                '"'+ _protocolHeaderBaseFilename(tu.protocol) +'.h"') ])
-
-        self.protocol = tu.protocol
+''')
+        # make the C++ header
+        hf.addthings(
+            [ disclaimer ]
+            + _includeGuardStart(hf)
+            +[
+                Whitespace.NL,
+                CppDirective(
+                    'include',
+                    '"'+ _protocolHeaderName(tu.protocol) +'.h"')
+            ])
 
         for pinc in tu.protocolIncludes:
             pinc.accept(self)
 
+        # this generates the actor's full impl in self.cls
         tu.protocol.accept(self)
 
-        f.addthing(Whitespace.NL)
-        f.addthings(_includeGuardEnd(f))
+        clsdecl, clsdefn = _ClassDeclDefn().split(self.cls)
+
+        # XXX damn C++ ... return types in the method defn aren't in
+        # class scope
+        for stmt in clsdefn.stmts:
+            if isinstance(stmt, MethodDefn):
+                if stmt.decl.ret and stmt.decl.ret.name == 'Result':
+                    stmt.decl.ret.name = clsdecl.name +'::'+ stmt.decl.ret.name
+
+        def makeNamespace(p, file):
+            if 0 == len(p.namespaces):
+                return file
+            ns = Namespace(p.namespaces[-1].name)
+            outerns = _putInNamespaces(ns, p.namespaces[:-1])
+            file.addthing(outerns)
+            return ns
+
+        hdrns = makeNamespace(self.protocol, self.hdrfile)
+        hdrns.addstmts([
+            Whitespace.NL,
+            Whitespace.NL,
+            clsdecl,
+            Whitespace.NL,
+            Whitespace.NL
+        ])
+
+        self.hdrfile.addthings(
+            ([
+                Whitespace.NL,
+                CppDirective('if', '0') ])
+            + _GenerateSkeletonImpl(
+                _actorName(self.protocol.name, self.side)[1:],
+                self.protocol.namespaces).fromclass(self.cls)
+            +([
+                CppDirective('endif', '// if 0'),
+                Whitespace.NL ])
+            + _includeGuardEnd(hf))
+
+        # make the .cpp file
+        cf.addthings((
+            [ disclaimer,
+              Whitespace.NL,
+              CppDirective(
+                  'include',
+                  '"'+ _protocolHeaderName(self.protocol, self.side) +'.h"'),
+              Whitespace.NL
+            ]
+            + self.protocolCxxIncludes
+            + [ Whitespace.NL ]
+            + self.standardTypedefs()
+            + self.includedActorTypedefs
+            + tu.protocol.decl.cxxtypedefs
+            + [ Whitespace.NL ]))
+
+        cppns = makeNamespace(self.protocol, cf)
+        cppns.addstmts([
+            Whitespace.NL,
+            Whitespace.NL,
+            clsdefn,
+            Whitespace.NL,
+            Whitespace.NL
+        ])
 
 
     def visitProtocolInclude(self, pi):
         ip = pi.tu.protocol
 
-        if self.protocol.decl.type.isManagerOf(ip.decl.type):
-            self.file.addthing(
-                CppDirective(
-                    'include',
-                    '"%s%s.h"'% (_protocolHeaderBaseFilename(ip),
-                                 self.prettyside)))
+        self.hdrfile.addthings([
+            _makeForwardDecl(ip.decl.type, self.side),
+            Whitespace.NL
+        ])
+        self.protocolCxxIncludes.append(
+            CppDirective(
+                'include',
+                '"%s.h"'% (_protocolHeaderName(ip, self.side))))
 
         if ip.decl.fullname is not None:
             self.includedActorTypedefs.append(Typedef(
                 Type(_actorName(ip.decl.fullname, self.prettyside)),
                 _actorName(ip.decl.shortname, self.prettyside)))
 
 
     def visitProtocol(self, p):
-        self.file.addthings([
+        self.hdrfile.addthings([
             CppDirective('ifdef', 'DEBUG'),
             CppDirective('include', '"prenv.h"'),
             CppDirective('endif', '// DEBUG')
         ])
 
         self.protocol = p
 
         # FIXME: all actors impl Iface for now
         if p.decl.type.isManager() or 1:
-            self.file.addthing(CppDirective('include', '"base/id_map.h"'))
-
-        self.file.addthings([
+            self.hdrfile.addthing(CppDirective('include', '"base/id_map.h"'))
+
+        self.hdrfile.addthings([
             CppDirective('include', '"'+ p.channelHeaderFile() +'"'),
             Whitespace.NL ])
 
         inherits = [ Inherit(Type(p.fqListenerName())) ]
         if p.decl.type.isManager():
             inherits.append(Inherit(p.managerCxxType()))
         self.cls = Class(self.clsname, inherits=inherits, abstract=True)
 
         friends = _FindFriends().findFriends(p.decl.type)
         if p.decl.type.isManaged():
             friends.add(p.decl.type.manager)
 
         for friend in friends:
-            self.file.addthings([
+            self.hdrfile.addthings([
                 Whitespace.NL,
                 _makeForwardDecl(friend, self.prettyside),
                 Whitespace.NL
             ])
             self.cls.addstmts([
                 FriendClassDecl(_actorName(friend.fullname(),
                                            self.prettyside)),
                 Whitespace.NL ])
 
-        # construct the namespace into which we'll stick all our decls
-        if 0 == len(p.namespaces):
-            self.ns = self.file
-        else:
-            self.ns = Namespace(p.namespaces[-1].name)
-            outerns = _putInNamespaces(self.ns, p.namespaces[:-1])
-            self.file.addthing(outerns)
-        self.ns.addstmts([ Whitespace.NL, Whitespace.NL ])
-
         self.cls.addstmt(Label.PROTECTED)
         for typedef in p.cxxTypedefs():
             self.cls.addstmt(typedef)
         for typedef in self.includedActorTypedefs:
             self.cls.addstmt(typedef)
         self.cls.addstmt(Whitespace.NL)
 
         # interface methods that the concrete subclass has to impl
@@ -2415,24 +2495,21 @@ class _GenerateProtocolActorHeader(ipdl.
                     defaultRecv.addstmt(StmtReturn(ExprLiteral.TRUE))
                     self.cls.addstmt(defaultRecv)
                 else:
                     recvDecl.pure = 1
                     self.cls.addstmt(StmtDecl(recvDecl))
 
         self.cls.addstmt(Whitespace.NL)
 
-        self.cls.addstmts([
-            Label.PRIVATE,
-            Typedef(Type('IPC::Message'), 'Message'),
-            Typedef(Type(p.channelName()), 'Channel'),
-            Typedef(Type(p.fqListenerName()), 'ChannelListener'),
-            Typedef(Type('base::ProcessHandle'), 'ProcessHandle'),
-            Whitespace.NL,
-        ])
+        self.cls.addstmts((
+            [ Label.PRIVATE ]
+            + self.standardTypedefs()
+            + [ Whitespace.NL ]
+        ))
 
         self.cls.addstmt(Label.PUBLIC)
         # Actor()
         ctor = ConstructorDefn(ConstructorDecl(self.clsname))
         if p.decl.type.isToplevel():
             ctor.memberinits = [
                 ExprMemberInit(p.channelVar(), [
                     ExprCall(ExprVar('ALLOW_THIS_IN_INITIALIZER_LIST'),
@@ -2462,18 +2539,19 @@ class _GenerateProtocolActorHeader(ipdl.
             aThreadVar = ExprVar('aThread')
             processvar = ExprVar('aOtherProcess')
             openmeth = MethodDefn(
                 MethodDecl(
                     'Open',
                     params=[ Decl(Type('Channel::Transport', ptr=True),
                                       aTransportVar.name),
                              Decl(Type('ProcessHandle'), processvar.name),
-                             Decl(Type('MessageLoop', ptr=True),
-                                      aThreadVar.name +' = 0') ],
+                             Param(Type('MessageLoop', ptr=True),
+                                   aThreadVar.name,
+                                   default=ExprLiteral.NULL) ],
                     ret=Type.BOOL))
 
             openmeth.addstmts([
                 StmtExpr(ExprAssn(p.otherProcessVar(), processvar)),
                 StmtReturn(ExprCall(ExprSelect(p.channelVar(), '.', 'Open'),
                                     [ aTransportVar, aThreadVar ]))
             ])
             self.cls.addstmts([
@@ -2525,17 +2603,17 @@ class _GenerateProtocolActorHeader(ipdl.
 
         def makeHandlerMethod(name, switch, hasReply, dispatches=0):
             params = [ Decl(Type('Message', const=1, ref=1), msgvar.name) ]
             if hasReply:
                 params.append(Decl(Type('Message', ref=1, ptr=1),
                                    replyvar.name))
             
             method = MethodDefn(MethodDecl(name, virtual=True,
-                                           params=params, ret=_Result.Type))
+                                           params=params, ret=_Result.Type()))
             if dispatches:
                 routevar = ExprVar('__route')
                 routedecl = StmtDecl(
                     Decl(_actorIdType(), routevar.name),
                     init=ExprCall(ExprSelect(msgvar, '.', 'routing_id')))
 
                 routeif = StmtIf(ExprBinary(
                     ExprVar('MSG_ROUTING_CONTROL'), '!=', routevar))
@@ -2644,27 +2722,16 @@ class _GenerateProtocolActorHeader(ipdl.
             ])
         if p.usesShmem():
             self.cls.addstmts([
                 StmtDecl(Decl(Type('IDMap', T=_rawShmemType()),
                               p.shmemMapVar().name)),
                 StmtDecl(Decl(_shmemIdType(), p.lastShmemIdVar().name))
             ])
 
-        self.ns.addstmts([ self.cls, Whitespace.NL, Whitespace.NL ])
-
-        # generate skeleton implementation of abstract actor class
-        self.file.addthing(CppDirective('if', '0'))
-
-        genskeleton = _GenerateSkeletonImpl()
-        genskeleton.fromclass(self.cls)
-        self.file.addthings(genskeleton.stuff)
-
-        self.file.addthing(CppDirective('endif', '// if 0'))
-
 
     def implementManagerIface(self):
         p = self.protocol
         routedvar = ExprVar('aRouted')
         idvar = ExprVar('aId')
         listenertype = Type('ChannelListener', ptr=1)
 
         register = MethodDefn(MethodDecl(
@@ -3433,61 +3500,94 @@ class _GenerateProtocolActorHeader(ipdl.
         actorname = _actorName(self.protocol.name, self.side)
         return _ifLogging([
             StmtExpr(ExprCall(
                 ExprSelect(msgptr, '->', 'Log'),
                 args=[ ExprLiteral.String('['+ actorname +'] '+ pfx),
                        ExprVar('stderr') ])) ])
 
 
-class _GenerateProtocolParentHeader(_GenerateProtocolActorHeader):
+class _GenerateProtocolParentCode(_GenerateProtocolActorCode):
     def __init__(self):
-        _GenerateProtocolActorHeader.__init__(self, 'parent')
+        _GenerateProtocolActorCode.__init__(self, 'parent')
 
     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):
+class _GenerateProtocolChildCode(_GenerateProtocolActorCode):
     def __init__(self):
-        _GenerateProtocolActorHeader.__init__(self, 'child')
+        _GenerateProtocolActorCode.__init__(self, 'child')
 
     def sendsMessage(self, md):
         return not md.decl.type.isOut()
 
     def receivesMessage(self, md):
         return md.decl.type.isInout() or md.decl.type.isOut()
 
 
 ##-----------------------------------------------------------------------------
-## Bonus (inessential) passes
+## Utility passes
 ##
 
+class _ClassDeclDefn:
+    def split(self, cls):
+        """Warning: destructively splits |cls|!"""
+        defns = Block()
+
+        for i, stmt in enumerate(cls.stmts):
+            if isinstance(stmt, MethodDefn):
+                decl, defn = self.splitMethodDefn(stmt, cls.name)
+                cls.stmts[i] = StmtDecl(decl)
+                defns.addstmts([ defn, Whitespace.NL ])
+
+        return cls, defns
+
+    def splitMethodDefn(self, md, clsname):
+        saveddecl = deepcopy(md.decl)
+        md.decl.name = (clsname +'::'+ md.decl.name)
+        md.decl.virtual = 0
+        md.decl.static = 0
+        for param in md.decl.params:
+            if isinstance(param, Param):
+                param.default = None
+        return saveddecl, md
+
+
+# XXX this is tantalizingly similar to _SplitDeclDefn, but just
+# different enough that I don't see the need to define
+# _GenerateSkeleton in terms of that
 class _GenerateSkeletonImpl(Visitor):
-    def __init__(self, name='ActorImpl'):
+    def __init__(self, name, namespaces):
         self.name = name
-        self.stuff = [ ]
         self.cls = None
-        self.methodimpls = [ ]
+        self.namespaces = namespaces
+        self.methodimpls = Block()
 
     def fromclass(self, cls):
         cls.accept(self)
-        self.stuff.append(Whitespace('''
+
+        nsclass = _putInNamespaces(self.cls, self.namespaces)
+        nsmethodimpls = _putInNamespaces(self.methodimpls, self.namespaces)
+
+        return [
+            Whitespace('''
 //-----------------------------------------------------------------------------
 // Skeleton implementation of abstract actor class
 
-'''))
-        self.stuff.append(Whitespace('// Header file contents\n'))
-        self.stuff.append(self.cls)
-
-        self.stuff.append(Whitespace.NL)
-        self.stuff.append(Whitespace('\n// C++ file contents\n'))
-        self.stuff.extend(self.methodimpls)
+'''),
+            Whitespace('// Header file contents\n'),
+            nsclass,
+            Whitespace.NL,
+            Whitespace('\n// C++ file contents\n'),
+            nsmethodimpls
+        ]
+
 
     def visitClass(self, cls):
         self.cls = Class(self.name, inherits=[ Inherit(Type(cls.name)) ])
         Visitor.visitClass(self, cls)
 
     def visitMethodDecl(self, md):
         if not md.pure:
             return
@@ -3516,13 +3616,12 @@ class _GenerateSkeletonImpl(Visitor):
             StmtDecl(DestructorDecl(self.name, virtual=1)))
         # FIXME/cjones: hack!
         dtor = DestructorDefn(ConstructorDecl(self.implname('~' +self.name)))
         dtor.addstmt(StmtExpr(ExprCall(ExprVar( 'MOZ_COUNT_DTOR'),
                                                [ ExprVar(self.name) ])))
         self.addmethodimpl(dtor)
 
     def addmethodimpl(self, impl):
-        self.methodimpls.append(impl)
-        self.methodimpls.append(Whitespace.NL)
+        self.methodimpls.addstmts([ impl, Whitespace.NL ])
 
     def implname(self, method):
         return self.name +'::'+ method
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** 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 IPC.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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 ***** */
+
+#ifndef mozilla__ipdltest_IPDLUnitTestTypes_h
+#define mozilla__ipdltest_IPDLUnitTestTypes_h
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+struct DirtyRect
+{
+    int x; int y; int w; int h;
+};
+
+}
+}
+
+namespace IPC {
+template<>
+struct ParamTraits<mozilla::_ipdltest::DirtyRect>
+{
+    typedef mozilla::_ipdltest::DirtyRect paramType;
+    static void Write(Message* aMsg, const paramType& aParam) {
+        WriteParam(aMsg, aParam.x);
+        WriteParam(aMsg, aParam.y);
+        WriteParam(aMsg, aParam.w);
+        WriteParam(aMsg, aParam.h);
+    }
+    static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+    {
+        return (ReadParam(aMsg, aIter, &aResult->x) &&
+                ReadParam(aMsg, aIter, &aResult->y) &&
+                ReadParam(aMsg, aIter, &aResult->w) &&
+                ReadParam(aMsg, aIter, &aResult->h));
+    }
+};
+}
+
+
+#endif // ifndef mozilla__ipdltest_IPDLUnitTestTypes_h
--- a/ipc/ipdl/test/cxx/Makefile.in
+++ b/ipc/ipdl/test/cxx/Makefile.in
@@ -44,16 +44,17 @@ include $(DEPTH)/config/autoconf.mk
 TOOL_DIRS += app
 
 MODULE = ipdlunittest
 
 EXPORTS_NAMESPACES = mozilla/_ipdltest
 EXPORTS_mozilla/_ipdltest =  \
   IPDLUnitTests.h  \
   IPDLUnitTestThreadChild.h  \
+  IPDLUnitTestTypes.h \
   $(NULL)
 
 LIBRARY_NAME = $(MODULE)_s
 LIBXUL_LIBRARY = 1
 FORCE_STATIC_LIB = 1
 EXPORT_LIBRARY = 1
 
 # Please keep these organized in the order "easy"-to-"hard"
@@ -73,17 +74,16 @@ GENTESTER := $(srcdir)/genIPDLUnitTests.
 
 CPPSRCS =  \
   IPDLUnitTests.cpp  \
   IPDLUnitTestSubprocess.cpp  \
   IPDLUnitTestThreadChild.cpp  \
   $(IPDLTESTSRCS)  \
   $(NULL)
 
-
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 
 RUNIPDLTEST := $(RUN_TEST_PROGRAM) $(DEPTH)/dist/bin/ipdlunittest$(BIN_SUFFIX)
 
 
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -91,16 +91,17 @@ LOCAL_INCLUDES += -I$(topsrcdir)/widget/
 endif
 
 # dependent libraries
 ifdef MOZ_IPC
 STATIC_LIBS += \
   domipc_s \
   domplugins_s \
   mozipc_s \
+  mozipdlgen_s \
   chromium_s \
   ipcshell_s \
   gfxipc_s \
   $(NULL)
 
 ifdef MOZ_IPDL_TESTS
 STATIC_LIBS += ipdlunittest_s
 endif
--- a/toolkit/toolkit-tiers.mk
+++ b/toolkit/toolkit-tiers.mk
@@ -169,20 +169,16 @@ tier_gecko_dirs	+= \
 ifdef MOZ_UNIVERSALCHARDET
 tier_gecko_dirs += extensions/universalchardet
 endif
 
 ifdef ACCESSIBILITY
 tier_gecko_dirs    += accessible
 endif
 
-ifdef MOZ_IPDL_TESTS
-tier_gecko_dirs += ipc/ipdl/test
-endif
-
 # 
 # tier "toolkit" - xpfe & toolkit
 #
 # The division of "gecko" and "toolkit" is somewhat arbitrary, and related
 # to history where "gecko" wasn't forked between seamonkey/firefox but
 # "toolkit" was.
 #
 
--- a/xpcom/build.mk
+++ b/xpcom/build.mk
@@ -39,10 +39,10 @@ TIERS += xpcom
 
 ifdef NS_TRACE_MALLOC
 tier_xpcom_dirs = tools/trace-malloc/lib
 endif
 
 tier_xpcom_dirs += xpcom
 
 ifdef MOZ_IPC
-tier_xpcom_dirs += ipc/ipdl ipc/chromium ipc/glue
+tier_xpcom_dirs += ipc/chromium ipc/glue
 endif