# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import os, re, sys
from copy import deepcopy
from collections import OrderedDict

import ipdl.ast
import ipdl.builtin
from ipdl.cxx.ast import *
from ipdl.type import ActorType, UnionType, TypeVisitor, builtinHeaderIncludes

##-----------------------------------------------------------------------------
## "Public" interface to lowering
##
class LowerToCxx:
    def lower(self, tu, segmentcapacitydict):
        '''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())

        # Any modifications to the filename scheme here need corresponding
        # modifications in the ipdl.py driver script.
        name = tu.name
        pheader, pcpp = File(name +'.h'), File(name +'.cpp')

        _GenerateProtocolCode().lower(tu, pheader, pcpp, segmentcapacitydict)
        headers = [ pheader ]
        cpps = [ pcpp ]

        if tu.protocol:
            pname = tu.protocol.name

            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)

            headers += [ parentheader, childheader ]
            cpps += [ parentcpp, childcpp ]

        return headers, cpps


##-----------------------------------------------------------------------------
## Helper code
##

def hashfunc(value):
    h = hash(value) % 2**32
    if h < 0: h += 2**32
    return h

_NULL_ACTOR_ID = ExprLiteral.ZERO
_FREED_ACTOR_ID = ExprLiteral.ONE

_DISCLAIMER = Whitespace('''//
// Automatically generated by ipdlc.
// Edit at your own risk
//

''')


class _struct: pass

def _namespacedHeaderName(name, namespaces):
    pfx = '/'.join([ ns.name for ns in namespaces ])
    if pfx:
        return pfx +'/'+ name
    else:
        return name

def _ipdlhHeaderName(tu):
    assert tu.filetype == 'header'
    return _namespacedHeaderName(tu.name, tu.namespaces)

def _protocolHeaderName(p, side=''):
    if side: side = side.title()
    base = p.name + side
    return _namespacedHeaderName(base, p.namespaces)

def _includeGuardMacroName(headerfile):
    return re.sub(r'[./]', '_', headerfile.name)

def _includeGuardStart(headerfile):
    guard = _includeGuardMacroName(headerfile)
    return [ CppDirective('ifndef', guard),
             CppDirective('define', guard)  ]

def _includeGuardEnd(headerfile):
    guard = _includeGuardMacroName(headerfile)
    return [ CppDirective('endif', '// ifndef '+ guard) ]

def _messageStartName(ptype):
    return ptype.name() +'MsgStart'

def _protocolId(ptype):
    return ExprVar(_messageStartName(ptype))

def _protocolIdType():
    return Type.INT32

def _actorName(pname, side):
    """|pname| is the protocol name. |side| is 'Parent' or 'Child'."""
    tag = side
    if not tag[0].isupper():  tag = side.title()
    return pname + tag

def _actorIdType():
    return Type.INT32

def _actorTypeTagType():
    return Type.INT32

def _actorId(actor=None):
    if actor is not None:
        return ExprCall(ExprSelect(actor, '->', 'Id'))
    return ExprCall(ExprVar('Id'))

def _actorHId(actorhandle):
    return ExprSelect(actorhandle, '.', 'mId')

def _actorManager(actor):
    return ExprCall(ExprSelect(actor, '->', 'Manager'), args=[])

def _actorState(actor):
    return ExprSelect(actor, '->', 'mState')

def _backstagePass():
    return ExprCall(ExprVar('mozilla::ipc::PrivateIPDLInterface'))

def _iterType(ptr):
    return Type('PickleIterator', ptr=ptr)

def _deleteId():
    return ExprVar('Msg___delete____ID')

def _deleteReplyId():
    return ExprVar('Reply___delete____ID')

def _lookupListener(idexpr):
    return ExprCall(ExprVar('Lookup'), args=[ idexpr ])

def _makeForwardDeclForQClass(clsname, quals, cls=1, struct=0):
    fd = ForwardDecl(clsname, cls=cls, struct=struct)
    if 0 == len(quals):
        return fd

    outerns = Namespace(quals[0])
    innerns = outerns
    for ns in quals[1:]:
        tmpns = Namespace(ns)
        innerns.addstmt(tmpns)
        innerns = tmpns

    innerns.addstmt(fd)
    return outerns

def _makeForwardDeclForActor(ptype, side):
    return _makeForwardDeclForQClass(_actorName(ptype.qname.baseid, side),
                                     ptype.qname.quals)

def _makeForwardDecl(type):
    return _makeForwardDeclForQClass(type.name(), type.qname.quals)


def _putInNamespaces(cxxthing, namespaces):
    """|namespaces| is in order [ outer, ..., inner ]"""
    if 0 == len(namespaces):  return cxxthing

    outerns = Namespace(namespaces[0].name)
    innerns = outerns
    for ns in namespaces[1:]:
        newns = Namespace(ns.name)
        innerns.addstmt(newns)
        innerns = newns
    innerns.addstmt(cxxthing)
    return outerns

def _sendPrefix(msgtype):
    """Prefix of the name of the C++ method that sends |msgtype|."""
    if msgtype.isInterrupt():
        return 'Call'
    return 'Send'

def _recvPrefix(msgtype):
    """Prefix of the name of the C++ method that handles |msgtype|."""
    if msgtype.isInterrupt():
        return 'Answer'
    return 'Recv'

def _flatTypeName(ipdltype):
    """Return a 'flattened' IPDL type name that can be used as an
identifier.
E.g., |Foo[]| --> |ArrayOfFoo|."""
    # NB: this logic depends heavily on what IPDL types are allowed to
    # be constructed; e.g., Foo[][] is disallowed.  needs to be kept in
    # sync with grammar.
    if ipdltype.isIPDL() and ipdltype.isArray():
        return 'ArrayOf'+ ipdltype.basetype.name()
    return ipdltype.name()


def _hasVisibleActor(ipdltype):
    """Return true iff a C++ decl of |ipdltype| would have an Actor* type.
For example: |Actor[]| would turn into |Array<ActorParent*>|, so this
function would return true for |Actor[]|."""
    return (ipdltype.isIPDL()
            and (ipdltype.isActor()
                 or (ipdltype.isArray()
                     and _hasVisibleActor(ipdltype.basetype))))

def _abortIfFalse(cond, msg):
    return StmtExpr(ExprCall(
        ExprVar('MOZ_RELEASE_ASSERT'),
        [ cond, ExprLiteral.String(msg) ]))

def _refptr(T, ptr=0, ref=0):
    return Type('RefPtr', T=T, ptr=ptr, ref=ref)

def _refptrGet(expr):
    return ExprCall(ExprSelect(expr, '.', 'get'))

def _refptrForget(expr):
    return ExprCall(ExprSelect(expr, '.', 'forget'))

def _refptrTake(expr):
    return ExprCall(ExprSelect(expr, '.', 'take'))

def _uniqueptr(T):
    return Type('UniquePtr', T=T)

def _uniqueptrGet(expr):
    return ExprCall(ExprSelect(expr, '.', 'get'))

def _tuple(types, const=0, ref=0):
    return Type('Tuple', T=types, const=const, ref=ref)

def _promise(resolvetype, rejecttype, tail, resolver=False):
    inner = Type('Private') if resolver else None
    return Type('MozPromise', T=[resolvetype, rejecttype, tail], inner=inner)

def _makePromise(returns, side, resolver=False):
    if len(returns) > 1:
        resolvetype = _tuple([d.bareType(side) for d in returns])
    else:
        resolvetype = returns[0].bareType(side)

    needmove = not all(d.isCopyable() for d in returns)

    return _promise(resolvetype,
                    _ResponseRejectReason.Type(),
                    ExprLiteral.TRUE if needmove else ExprLiteral.FALSE,
                    resolver=resolver)

def _makeResolver(returns, side):
    if len(returns) > 1:
        resolvetype = _tuple([d.moveType(side) for d in returns])
    else:
        resolvetype = returns[0].moveType(side)
    return TypeFunction([Decl(resolvetype, '')])

def _cxxArrayType(basetype, const=0, ref=0):
    return Type('nsTArray', T=basetype, const=const, ref=ref, hasimplicitcopyctor=False)

def _cxxManagedContainerType(basetype, const=0, ref=0):
    return Type('ManagedContainer', T=basetype,
                const=const, ref=ref, hasimplicitcopyctor=False)

def _callCxxArrayLength(arr):
    return ExprCall(ExprSelect(arr, '.', 'Length'))

def _callCxxArraySetLength(arr, lenexpr, sel='.'):
    return ExprCall(ExprSelect(arr, sel, 'SetLength'),
                    args=[ lenexpr ])

def _callCxxSwapArrayElements(arr1, arr2, sel='.'):
    return ExprCall(ExprSelect(arr1, sel, 'SwapElements'),
                    args=[ arr2 ])

def _callInsertManagedActor(managees, actor):
    return ExprCall(ExprSelect(managees, '.', 'PutEntry'),
                    args=[ actor ])

def _callRemoveManagedActor(managees, actor):
    return ExprCall(ExprSelect(managees, '.', 'RemoveEntry'),
                    args=[ actor ])

def _callClearManagedActors(managees):
    return ExprCall(ExprSelect(managees, '.', 'Clear'))

def _callHasManagedActor(managees, actor):
    return ExprCall(ExprSelect(managees, '.', 'Contains'), args=[ actor ])

def _otherSide(side):
    if side == 'child':  return 'parent'
    if side == 'parent':  return 'child'
    assert 0

def _ifLogging(topLevelProtocol, stmts):
    iflogging = StmtIf(ExprCall(ExprVar('mozilla::ipc::LoggingEnabledFor'),
                                args=[ topLevelProtocol ]))
    iflogging.addifstmts(stmts)
    return iflogging

# XXX we need to remove these and install proper error handling
def _printErrorMessage(msg):
    if isinstance(msg, str):
        msg = ExprLiteral.String(msg)
    return StmtExpr(
        ExprCall(ExprVar('NS_ERROR'), args=[ msg ]))

def _protocolErrorBreakpoint(msg):
    if isinstance(msg, str):
        msg = ExprLiteral.String(msg)
    return StmtExpr(ExprCall(ExprVar('mozilla::ipc::ProtocolErrorBreakpoint'),
                             args=[ msg ]))

def _printWarningMessage(msg):
    if isinstance(msg, str):
        msg = ExprLiteral.String(msg)
    return StmtExpr(
        ExprCall(ExprVar('NS_WARNING'), args=[ msg ]))

def _fatalError(msg):
    return StmtExpr(
        ExprCall(ExprVar('FatalError'), args=[ ExprLiteral.String(msg) ]))

def _logicError(msg):
    return StmtExpr(
        ExprCall(ExprVar('mozilla::ipc::LogicError'), args=[ ExprLiteral.String(msg) ]))

def _arrayLengthReadError(elementname):
    return StmtExpr(
        ExprCall(ExprVar('mozilla::ipc::ArrayLengthReadError'),
                 args=[ ExprLiteral.String(elementname) ]))

def _unionTypeReadError(unionname):
    return StmtExpr(
        ExprCall(ExprVar('mozilla::ipc::UnionTypeReadError'),
                 args=[ ExprLiteral.String(unionname) ]))

def _sentinelReadError(classname):
    return StmtExpr(
        ExprCall(ExprVar('mozilla::ipc::SentinelReadError'),
                 args=[ ExprLiteral.String(classname) ]))

def _killProcess(pid):
    return ExprCall(
        ExprVar('base::KillProcess'),
        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:
    @staticmethod
    def Type():
        return Type('Result')

    Processed = ExprVar('MsgProcessed')
    NotKnown = ExprVar('MsgNotKnown')
    NotAllowed = ExprVar('MsgNotAllowed')
    PayloadError = ExprVar('MsgPayloadError')
    ProcessingError = ExprVar('MsgProcessingError')
    RouteError = ExprVar('MsgRouteError')
    ValuError = ExprVar('MsgValueError') # [sic]

# these |errfn*| are functions that generate code to be executed on an
# error, such as "bad actor ID".  each is given a Python string
# containing a description of the error

# used in user-facing Send*() methods
def errfnSend(msg, errcode=ExprLiteral.FALSE):
    return [
        _fatalError(msg),
        StmtReturn(errcode)
    ]

def errfnSendCtor(msg):  return errfnSend(msg, errcode=ExprLiteral.NULL)

# TODO should this error handling be strengthened for dtors?
def errfnSendDtor(msg):
    return [
        _printErrorMessage(msg),
        StmtReturn.FALSE
    ]

# used in |OnMessage*()| handlers that hand in-messages off to Recv*()
# interface methods
def errfnRecv(msg, errcode=_Result.ValuError):
    return [
        _fatalError(msg),
        StmtReturn(errcode)
    ]

def errfnSentinel(rvalue=ExprLiteral.FALSE):
    def inner(msg):
        return [ _sentinelReadError(msg), StmtReturn(rvalue) ]
    return inner

def _destroyMethod():
    return ExprVar('ActorDestroy')

def errfnUnreachable(msg):
    return [
        _logicError(msg)
    ]

class _DestroyReason:
    @staticmethod
    def Type():  return Type('ActorDestroyReason')

    Deletion = ExprVar('Deletion')
    AncestorDeletion = ExprVar('AncestorDeletion')
    NormalShutdown = ExprVar('NormalShutdown')
    AbnormalShutdown = ExprVar('AbnormalShutdown')
    FailedConstructor = ExprVar('FailedConstructor')

class _ResponseRejectReason:
    @staticmethod
    def Type():
        return Type('ResponseRejectReason')

    SendError = ExprVar('ResponseRejectReason::SendError')
    ChannelClosed = ExprVar('ResponseRejectReason::ChannelClosed')
    HandlerRejected = ExprVar('ResponseRejectReason::HandlerRejected')
    ActorDestroyed = ExprVar('ResponseRejectReason::ActorDestroyed')


##-----------------------------------------------------------------------------
## Intermediate representation (IR) nodes used during lowering

class _ConvertToCxxType(TypeVisitor):
    def __init__(self, side, fq):
        self.side = side
        self.fq = fq

    def typename(self, thing):
        if self.fq:
            return thing.fullname()
        return thing.name()

    def visitImportedCxxType(self, t):
        cxxtype = Type(self.typename(t))
        if t.isRefcounted():
            cxxtype = _refptr(cxxtype)
        return cxxtype

    def visitActorType(self, a):
        return Type(_actorName(self.typename(a.protocol), self.side), ptr=1)

    def visitStructType(self, s):
        return Type(self.typename(s))

    def visitUnionType(self, u):
        return Type(self.typename(u))

    def visitArrayType(self, a):
        basecxxtype = a.basetype.accept(self)
        return _cxxArrayType(basecxxtype)

    def visitShmemType(self, s):
        return Type(self.typename(s))

    def visitByteBufType(self, s):
        return Type(self.typename(s))

    def visitFDType(self, s):
        return Type(self.typename(s))

    def visitEndpointType(self, s):
        return Type(self.typename(s))

    def visitProtocolType(self, p): assert 0
    def visitMessageType(self, m): assert 0
    def visitVoidType(self, v): assert 0
    def visitStateType(self, st): assert 0

def _cxxBareType(ipdltype, side, fq=0):
    return ipdltype.accept(_ConvertToCxxType(side, fq))

def _cxxRefType(ipdltype, side):
    t = _cxxBareType(ipdltype, side)
    t.ref = 1
    return t

def _cxxConstRefType(ipdltype, side):
    t = _cxxBareType(ipdltype, side)
    if ipdltype.isIPDL() and ipdltype.isActor():
        return t
    if ipdltype.isIPDL() and ipdltype.isShmem():
        t.ref = 1
        return t
    if ipdltype.isIPDL() and ipdltype.isByteBuf():
        t.ref = 1
        return t
    if ipdltype.isIPDL() and ipdltype.isArray():
        # Keep same constness as inner type.
        t.const = _cxxConstRefType(ipdltype.basetype, side).const
        t.ref = 1
        return t
    if ipdltype.isCxx() and ipdltype.isRefcounted():
        # Use T* instead of const RefPtr<T>&
        t = t.T
        t.ptr = 1
        return t
    t.const = 1
    t.ref = 1
    return t

def _cxxTypeNeedsMove(ipdltype):
    return ipdltype.isIPDL() and (ipdltype.isArray() or
                                  ipdltype.isShmem() or
                                  ipdltype.isByteBuf() or
                                  ipdltype.isEndpoint())

def _cxxTypeCanMove(ipdltype):
    return not (ipdltype.isIPDL() and ipdltype.isActor())

def _cxxMoveRefType(ipdltype, side):
    t = _cxxBareType(ipdltype, side)
    if _cxxTypeNeedsMove(ipdltype):
        t.ref = 2
        return t
    return _cxxConstRefType(ipdltype, side)

def _cxxForceMoveRefType(ipdltype, side):
    assert _cxxTypeCanMove(ipdltype)
    t = _cxxBareType(ipdltype, side)
    t.ref = 2
    return t

def _cxxPtrToType(ipdltype, side):
    t = _cxxBareType(ipdltype, side)
    if ipdltype.isIPDL() and ipdltype.isActor():
        t.ptr = 0
        t.ptrptr = 1
        return t
    t.ptr = 1
    return t

def _cxxConstPtrToType(ipdltype, side):
    t = _cxxBareType(ipdltype, side)
    if ipdltype.isIPDL() and ipdltype.isActor():
        t.ptr = 0
        t.ptrconstptr = 1
        return t
    t.const = 1
    t.ptr = 1
    return t

def _allocMethod(ptype, side):
    return ExprVar('Alloc' + ptype.name() + side.title())

def _deallocMethod(ptype, side):
    return ExprVar('Dealloc' + ptype.name() + side.title())

##
## A _HybridDecl straddles IPDL and C++ decls.  It knows which C++
## types correspond to which IPDL types, and it also knows how
## serialize and deserialize "special" IPDL C++ types.
##
class _HybridDecl:
    """A hybrid decl stores both an IPDL type and all the C++ type
info needed by later passes, along with a basic name for the decl."""
    def __init__(self, ipdltype, name):
        self.ipdltype = ipdltype
        self.name = name
        self.idnum = 0

    def isCopyable(self):
        return not _cxxTypeNeedsMove(self.ipdltype)

    def var(self):
        return ExprVar(self.name)

    def mayMoveExpr(self):
        if self.isCopyable():
            return self.var()
        return ExprMove(self.var())

    def bareType(self, side, fq=0):
        """Return this decl's unqualified C++ type."""
        return _cxxBareType(self.ipdltype, side, fq=fq)

    def refType(self, side):
        """Return this decl's C++ type as a 'reference' type, which is not
necessarily a C++ reference."""
        return _cxxRefType(self.ipdltype, side)

    def constRefType(self, side):
        """Return this decl's C++ type as a const, 'reference' type."""
        return _cxxConstRefType(self.ipdltype, side)

    def rvalueRefType(self, side):
        """Return this decl's C++ type as an r-value 'reference' type."""
        return _cxxMoveRefType(self.ipdltype, side)

    def ptrToType(self, side):
        return _cxxPtrToType(self.ipdltype, side)

    def constPtrToType(self, side):
        return _cxxConstPtrToType(self.ipdltype, side)

    def inType(self, side):
        """Return this decl's C++ Type with inparam semantics."""
        if self.ipdltype.isIPDL() and self.ipdltype.isActor():
            return self.bareType(side)
        return self.constRefType(side)

    def moveType(self, side):
        """Return this decl's C++ Type with move semantics."""
        if self.ipdltype.isIPDL() and self.ipdltype.isActor():
            return self.bareType(side)
        return self.rvalueRefType(side);

    def outType(self, side):
        """Return this decl's C++ Type with outparam semantics."""
        t = self.bareType(side)
        if self.ipdltype.isIPDL() and self.ipdltype.isActor():
            t.ptr = 0;  t.ptrptr = 1
            return t
        t.ptr = 1
        return t

    def forceMoveType(self, side):
        """Return this decl's C++ Type with forced move semantics."""
        assert _cxxTypeCanMove(self.ipdltype)
        return _cxxForceMoveRefType(self.ipdltype, side)

##--------------------------------------------------

class HasFQName:
    def fqClassName(self):
        return self.decl.type.fullname()

class _CompoundTypeComponent(_HybridDecl):
    def __init__(self, ipdltype, name, side, ct):
        _HybridDecl.__init__(self, ipdltype, name)
        self.side = side
        self.special = _hasVisibleActor(ipdltype)

    # @override the following methods to pass |self.side| instead of
    # forcing the caller to remember which side we're declared to
    # represent.
    def bareType(self, side=None, fq=0):
        return _HybridDecl.bareType(self, self.side, fq=fq)
    def refType(self, side=None):
        return _HybridDecl.refType(self, self.side)
    def constRefType(self, side=None):
        return _HybridDecl.constRefType(self, self.side)
    def ptrToType(self, side=None):
        return _HybridDecl.ptrToType(self, self.side)
    def constPtrToType(self, side=None):
        return _HybridDecl.constPtrToType(self, self.side)
    def inType(self, side=None):
        return _HybridDecl.inType(self, self.side)
    def forceMoveType(self, side=None):
        return _HybridDecl.forceMoveType(self, self.side)


class StructDecl(ipdl.ast.StructDecl, HasFQName):
    @staticmethod
    def upgrade(structDecl):
        assert isinstance(structDecl, ipdl.ast.StructDecl)
        structDecl.__class__ = StructDecl

class _StructField(_CompoundTypeComponent):
    def __init__(self, ipdltype, name, sd, side=None):
        self.basename = name
        fname = name
        special = _hasVisibleActor(ipdltype)
        if special:
            fname += side.title()

        _CompoundTypeComponent.__init__(self, ipdltype, fname, side, sd)

    def getMethod(self, thisexpr=None, sel='.'):
        meth = self.var()
        if thisexpr is not None:
            return ExprSelect(thisexpr, sel, meth.name)
        return meth

    def initExpr(self, thisexpr):
        expr = ExprCall(self.getMethod(thisexpr=thisexpr))
        if self.ipdltype.isIPDL() and self.ipdltype.isActor():
            expr = ExprCast(expr, self.bareType(), const=1)
        return expr

    def refExpr(self, thisexpr=None):
        ref = self.memberVar()
        if thisexpr is not None:
            ref = ExprSelect(thisexpr, '.', ref.name)
        return ref

    def constRefExpr(self, thisexpr=None):
        # sigh, gross hack
        refexpr = self.refExpr(thisexpr)
        if 'Shmem' == self.ipdltype.name():
            refexpr = ExprCast(refexpr, Type('Shmem', ref=1), const=1)
        if 'ByteBuf' == self.ipdltype.name():
            refexpr = ExprCast(refexpr, Type('ByteBuf', ref=1), const=1)
        if 'FileDescriptor' == self.ipdltype.name():
            refexpr = ExprCast(refexpr, Type('FileDescriptor', ref=1), const=1)
        return refexpr

    def argVar(self):
        return ExprVar('_'+ self.name)

    def memberVar(self):
        return ExprVar(self.name + '_')


class UnionDecl(ipdl.ast.UnionDecl, HasFQName):
    def callType(self, var=None):
        func = ExprVar('type')
        if var is not None:
            func = ExprSelect(var, '.', func.name)
        return ExprCall(func)

    @staticmethod
    def upgrade(unionDecl):
        assert isinstance(unionDecl, ipdl.ast.UnionDecl)
        unionDecl.__class__ = UnionDecl


class _UnionMember(_CompoundTypeComponent):
    """Not in the AFL sense, but rather a member (e.g. |int;|) of an
IPDL union type."""
    def __init__(self, ipdltype, ud, side=None, other=None):
        flatname = _flatTypeName(ipdltype)
        special = _hasVisibleActor(ipdltype)
        if special:
            flatname += side.title()

        _CompoundTypeComponent.__init__(self, ipdltype, 'V'+ flatname, side, ud)
        self.flattypename = flatname
        if special:
            if other is not None:
                self.other = other
            else:
                self.other = _UnionMember(ipdltype, ud, _otherSide(side), self)

        # To create a finite object with a mutually recursive type, a union must
        # be present somewhere in the recursive loop. Because of that we only
        # need to care about introducing indirections inside unions.
        self.recursive = ud.decl.type.mutuallyRecursiveWith(ipdltype)

    def enum(self):
        return 'T' + self.flattypename

    def pqEnum(self):
        return self.ud.name +'::'+ self.enum()

    def enumvar(self):
        return ExprVar(self.enum())

    def internalType(self):
        if self.recursive:
            return self.ptrToType()
        else:
            return self.bareType()

    def unionType(self):
        """Type used for storage in generated C union decl."""
        if self.recursive:
            return self.ptrToType()
        else:
            return Type('mozilla::AlignedStorage2', T=self.internalType())

    def unionValue(self):
        # NB: knows that Union's storage C union is named |mValue|
        return ExprSelect(ExprVar('mValue'), '.', self.name)

    def typedef(self):
        return self.flattypename +'__tdef'

    def callGetConstPtr(self):
        """Return an expression of type self.constptrToSelfType()"""
        return ExprCall(ExprVar(self.getConstPtrName()))

    def callGetPtr(self):
        """Return an expression of type self.ptrToSelfType()"""
        return ExprCall(ExprVar(self.getPtrName()))

    def callOperatorEq(self, rhs):
        if self.ipdltype.isIPDL() and self.ipdltype.isActor():
            rhs = ExprCast(rhs, self.bareType(), const=1)
        return ExprAssn(ExprDeref(self.callGetPtr()), rhs)

    def callCtor(self, expr=None):
        assert not isinstance(expr, list)

        if expr is None:
            args = None
        elif self.ipdltype.isIPDL() and self.ipdltype.isActor():
            args = [ ExprCast(expr, self.bareType(), const=1) ]
        else:
            args = [ expr ]

        if self.recursive:
            return ExprAssn(self.callGetPtr(),
                            ExprNew(self.bareType(self.side),
                                    args=args))
        else:
            return ExprNew(self.bareType(self.side),
                           args=args,
                           newargs=[ ExprVar('mozilla::KnownNotNull'), self.callGetPtr() ])

    def callDtor(self):
        if self.recursive:
            return ExprDelete(self.callGetPtr())
        else:
            return ExprCall(
                ExprSelect(self.callGetPtr(), '->', '~'+ self.typedef()))

    def getTypeName(self): return 'get_'+ self.flattypename
    def getConstTypeName(self): return 'get_'+ self.flattypename

    def getOtherTypeName(self): return 'get_'+ self.otherflattypename

    def getPtrName(self): return 'ptr_'+ self.flattypename
    def getConstPtrName(self): return 'constptr_'+ self.flattypename

    def ptrToSelfExpr(self):
        """|*ptrToSelfExpr()| has type |self.bareType()|"""
        v = self.unionValue()
        if self.recursive:
            return v
        else:
            return ExprCall(ExprSelect(v, '.', 'addr'))

    def constptrToSelfExpr(self):
        """|*constptrToSelfExpr()| has type |self.constType()|"""
        v = self.unionValue()
        if self.recursive:
            return v
        return ExprCall(ExprSelect(v, '.', 'addr'))

    def ptrToInternalType(self):
        t = self.ptrToType()
        if self.recursive:
            t.ref = 1
        return t

    def defaultValue(self, fq=0):
        # Use the default constructor for any class that does not have an
        # implicit copy constructor.
        if not self.bareType().hasimplicitcopyctor:
            return None

        if self.ipdltype.isIPDL() and self.ipdltype.isActor():
            return ExprLiteral.NULL
        # XXX sneaky here, maybe need ExprCtor()?
        return ExprCall(self.bareType(fq=fq))

    def getConstValue(self):
        v = ExprDeref(self.callGetConstPtr())
        # sigh
        if 'ByteBuf' == self.ipdltype.name():
            v = ExprCast(v, Type('ByteBuf', ref=1), const=1)
        if 'Shmem' == self.ipdltype.name():
            v = ExprCast(v, Type('Shmem', ref=1), const=1)
        if 'FileDescriptor' == self.ipdltype.name():
            v = ExprCast(v, Type('FileDescriptor', ref=1), const=1)
        return v

##--------------------------------------------------

class MessageDecl(ipdl.ast.MessageDecl):
    def baseName(self):
        return self.name

    def recvMethod(self):
        name = _recvPrefix(self.decl.type) + self.baseName()
        if self.decl.type.isCtor():
            name += 'Constructor'
        return ExprVar(name)

    def sendMethod(self):
        name = _sendPrefix(self.decl.type) + self.baseName()
        if self.decl.type.isCtor():
            name += 'Constructor'
        return ExprVar(name)

    def hasReply(self):
        return (self.decl.type.hasReply()
                or self.decl.type.isCtor()
                or self.decl.type.isDtor())

    def hasAsyncReturns(self):
        return (self.decl.type.isAsync() and
                self.returns)

    def msgCtorFunc(self):
        return 'Msg_%s'% (self.decl.progname)

    def prettyMsgName(self, pfx=''):
        return pfx + self.msgCtorFunc()

    def pqMsgCtorFunc(self):
        return '%s::%s'% (self.namespace, self.msgCtorFunc())

    def msgId(self):  return self.msgCtorFunc()+ '__ID'
    def pqMsgId(self):
        return '%s::%s'% (self.namespace, self.msgId())

    def replyCtorFunc(self):
        return 'Reply_%s'% (self.decl.progname)

    def pqReplyCtorFunc(self):
        return '%s::%s'% (self.namespace, self.replyCtorFunc())

    def replyId(self):  return self.replyCtorFunc()+ '__ID'
    def pqReplyId(self):
        return '%s::%s'% (self.namespace, self.replyId())

    def prettyReplyName(self, pfx=''):
        return pfx + self.replyCtorFunc()

    def promiseName(self):
        name = self.baseName()
        if self.decl.type.isCtor():
            name += 'Constructor'
        name += 'Promise'
        return name

    def resolverName(self):
        return self.baseName() + 'Resolver'

    def actorDecl(self):
        return self.params[0]

    def makeCxxParams(self, paramsems='in', returnsems='out',
                      side=None, implicit=1):
        """Return a list of C++ decls per the spec'd configuration.
|params| and |returns| is the C++ semantics of those: 'in', 'out', or None."""

        def makeDecl(d, sems):
            if sems is 'in':
                return Decl(d.inType(side), d.name)
            elif sems is 'move':
                return Decl(d.moveType(side), d.name)
            elif sems is 'out':
                return Decl(d.outType(side), d.name)
            else: assert 0

        def makeResolverDecl(returns):
            return Decl(Type(self.resolverName(), ref=2), 'aResolve')

        def makeCallbackResolveDecl(returns):
            if len(returns) > 1:
                resolvetype = _tuple([d.bareType(side) for d in returns])
            else:
                resolvetype = returns[0].bareType(side)

            return Decl(Type("mozilla::ipc::ResolveCallback", T=resolvetype, ref=2),
                        'aResolve')

        def makeCallbackRejectDecl(returns):
            return Decl(Type("mozilla::ipc::RejectCallback", ref=2), 'aReject')

        cxxparams = [ ]
        if paramsems is not None:
            cxxparams.extend([ makeDecl(d, paramsems) for d in self.params ])

        if returnsems is 'promise' and self.returns:
            pass
        elif returnsems is 'callback' and self.returns:
            cxxparams.extend([ makeCallbackResolveDecl(self.returns),
                               makeCallbackRejectDecl(self.returns) ])
        elif returnsems is 'resolver' and self.returns:
            cxxparams.extend([ makeResolverDecl(self.returns) ])
        elif returnsems is not None:
            cxxparams.extend([ makeDecl(r, returnsems) for r in self.returns ])

        if not implicit and self.decl.type.hasImplicitActorParam():
            cxxparams = cxxparams[1:]

        return cxxparams

    def makeCxxArgs(self, paramsems='in', retsems='out', retcallsems='out',
                    implicit=1):
        assert not retcallsems or retsems # retcallsems => returnsems
        cxxargs = [ ]

        if paramsems is 'move':
            cxxargs.extend([ p.mayMoveExpr() for p in self.params ])
        elif paramsems is 'in':
            cxxargs.extend([ p.var() for p in self.params ])
        else:
            assert False

        for ret in self.returns:
            if retsems is 'in':
                if retcallsems is 'in':
                    cxxargs.append(ret.var())
                elif retcallsems is 'out':
                    cxxargs.append(ExprAddrOf(ret.var()))
                else: assert 0
            elif retsems is 'out':
                if retcallsems is 'in':
                    cxxargs.append(ExprDeref(ret.var()))
                elif retcallsems is 'out':
                    cxxargs.append(ret.var())
                else: assert 0
            elif retsems is 'resolver':
                pass
        if retsems is 'resolver':
            cxxargs.append(ExprMove(ExprVar('resolver')))

        if not implicit:
            assert self.decl.type.hasImplicitActorParam()
            cxxargs = cxxargs[1:]

        return cxxargs


    @staticmethod
    def upgrade(messageDecl):
        assert isinstance(messageDecl, ipdl.ast.MessageDecl)
        if messageDecl.decl.type.hasImplicitActorParam():
            messageDecl.params.insert(
                0,
                _HybridDecl(
                    ipdl.type.ActorType(
                        messageDecl.decl.type.constructedType()),
                    'actor'))
        messageDecl.__class__ = MessageDecl


##--------------------------------------------------
def _usesShmem(p):
    for md in p.messageDecls:
        for param in md.inParams:
            if ipdl.type.hasshmem(param.type):
                return True
        for ret in md.outParams:
            if ipdl.type.hasshmem(ret.type):
                return True
    return False

def _subtreeUsesShmem(p):
    if _usesShmem(p):
        return True

    ptype = p.decl.type
    for mgd in ptype.manages:
        if ptype is not mgd:
            if _subtreeUsesShmem(mgd._ast):
                return True
    return False

def _stateType(hasReentrantDelete):
    if hasReentrantDelete:
        return Type('mozilla::ipc::ReEntrantDeleteState')
    else:
        return Type('mozilla::ipc::State')

def _startState(hasReentrantDelete):
    pfx = _stateType(hasReentrantDelete).name + '::'
    return ExprVar(pfx + 'Start')


class Protocol(ipdl.ast.Protocol):
    def cxxTypedefs(self):
        return self.decl.cxxtypedefs

    def channelSel(self):
        if self.decl.type.isToplevel():  return '.'
        return '->'

    def channelType(self):
        return Type('MessageChannel', ptr=not self.decl.type.isToplevel())

    def managerInterfaceType(self, ptr=0):
        return Type('mozilla::ipc::IProtocol', ptr=ptr)

    def openedProtocolInterfaceType(self, ptr=0):
        return Type('mozilla::ipc::IToplevelProtocol',
                    ptr=ptr)

    def _ipdlmgrtype(self):
        assert 1 == len(self.decl.type.managers)
        for mgr in self.decl.type.managers:  return mgr

    def managerActorType(self, side, ptr=0):
        return Type(_actorName(self._ipdlmgrtype().name(), side),
                    ptr=ptr)

    def registerMethod(self):
        return ExprVar('Register')

    def registerIDMethod(self):
        return ExprVar('RegisterID')

    def lookupIDMethod(self):
        return ExprVar('Lookup')

    def unregisterMethod(self, actorThis=None):
        if actorThis is not None:
            return ExprSelect(actorThis, '->', 'Unregister')
        return ExprVar('Unregister')

    def removeManageeMethod(self):
        return ExprVar('RemoveManagee')

    def createSharedMemory(self):
        return ExprVar('CreateSharedMemory')

    def lookupSharedMemory(self):
        return ExprVar('LookupSharedMemory')

    def isTrackingSharedMemory(self):
        return ExprVar('IsTrackingSharedMemory')

    def destroySharedMemory(self):
        return ExprVar('DestroySharedMemory')

    def otherPidMethod(self):
        return ExprVar('OtherPid')

    def callOtherPid(self, actorThis=None):
        fn = self.otherPidMethod()
        if actorThis is not None:
            fn = ExprSelect(actorThis, '->', fn.name)
        return ExprCall(fn)

    def getChannelMethod(self):
        return ExprVar('GetIPCChannel')

    def callGetChannel(self, actorThis=None):
        fn = self.getChannelMethod()
        if actorThis is not None:
            fn = ExprSelect(actorThis, '->', fn.name)
        return ExprCall(fn)

    def processingErrorVar(self):
        assert self.decl.type.isToplevel()
        return ExprVar('ProcessingError')

    def shouldContinueFromTimeoutVar(self):
        assert self.decl.type.isToplevel()
        return ExprVar('ShouldContinueFromReplyTimeout')

    def enteredCxxStackVar(self):
        assert self.decl.type.isToplevel()
        return ExprVar('EnteredCxxStack')

    def exitedCxxStackVar(self):
        assert self.decl.type.isToplevel()
        return ExprVar('ExitedCxxStack')

    def enteredCallVar(self):
        assert self.decl.type.isToplevel()
        return ExprVar('EnteredCall')

    def exitedCallVar(self):
        assert self.decl.type.isToplevel()
        return ExprVar('ExitedCall')

    def onCxxStackVar(self):
        assert self.decl.type.isToplevel()
        return ExprVar('IsOnCxxStack')

    # an actor's C++ private variables
    def channelVar(self, actorThis=None):
        if actorThis is not None:
            return ExprSelect(actorThis, '->', 'mChannel')
        return ExprVar('mChannel')

    def routingId(self, actorThis=None):
        if self.decl.type.isToplevel():
            return ExprVar('MSG_ROUTING_CONTROL')
        if actorThis is not None:
            return ExprCall(ExprSelect(actorThis, '->', 'Id'))
        return ExprCall(ExprVar('Id'))

    def stateVar(self, actorThis=None):
        if actorThis is not None:
            return ExprSelect(actorThis, '->', 'mState')
        return ExprVar('mState')

    def fqStateType(self):
        return _stateType(self.decl.type.hasReentrantDelete)

    def startState(self):
        return _startState(self.decl.type.hasReentrantDelete)

    def nullState(self):
        pfx = self.fqStateType().name + '::'
        return ExprVar(pfx + 'Null')

    def deadState(self):
        pfx = self.fqStateType().name + '::'
        return ExprVar(pfx + 'Dead')

    def dyingState(self):
        assert self.decl.type.hasReentrantDelete
        pfx = self.fqStateType().name + '::'
        return ExprVar(pfx + 'Dying')

    def managerVar(self, thisexpr=None):
        assert thisexpr is not None or not self.decl.type.isToplevel()
        mvar = ExprCall(ExprVar('Manager'), args=[])
        if thisexpr is not None:
            mvar = ExprCall(ExprSelect(thisexpr, '->', 'Manager'), args=[])
        return mvar

    def managedCxxType(self, actortype, side):
        assert self.decl.type.isManagerOf(actortype)
        return Type(_actorName(actortype.name(), side), ptr=1)

    def managedMethod(self, actortype, side):
        assert self.decl.type.isManagerOf(actortype)
        return ExprVar('Managed'+  _actorName(actortype.name(), side))

    def managedVar(self, actortype, side):
        assert self.decl.type.isManagerOf(actortype)
        return ExprVar('mManaged'+ _actorName(actortype.name(), side))

    def managedVarType(self, actortype, side, const=0, ref=0):
        assert self.decl.type.isManagerOf(actortype)
        return _cxxManagedContainerType(Type(_actorName(actortype.name(), side)),
                                        const=const, ref=ref)

    # XXX this is sucky, fix
    def usesShmem(self):
        return _usesShmem(self)

    def subtreeUsesShmem(self):
        return _subtreeUsesShmem(self)

    @staticmethod
    def upgrade(protocol):
        assert isinstance(protocol, ipdl.ast.Protocol)
        protocol.__class__ = Protocol


class TranslationUnit(ipdl.ast.TranslationUnit):
    @staticmethod
    def upgrade(tu):
        assert isinstance(tu, ipdl.ast.TranslationUnit)
        tu.__class__ = TranslationUnit


##-----------------------------------------------------------------------------

class _DecorateWithCxxStuff(ipdl.ast.Visitor):
    """Phase 1 of lowering: decorate the IPDL AST with information
relevant to C++ code generation.

This pass results in an AST that is a poor man's "IR"; in reality, a
"hybrid" AST mainly consisting of IPDL nodes with new C++ info along
with some new IPDL/C++ nodes that are tuned for C++ codegen."""

    def __init__(self):
        self.visitedTus = set()
        # the set of typedefs that allow generated classes to
        # reference known C++ types by their "short name" rather than
        # fully-qualified name. e.g. |Foo| rather than |a::b::Foo|.
        self.typedefs = [ ]
        self.typedefSet = set([ Typedef(Type('mozilla::ipc::ActorHandle'),
                                        'ActorHandle'),
                                Typedef(Type('base::ProcessId'),
                                        'ProcessId'),
                                Typedef(Type('mozilla::ipc::ProtocolId'),
                                        'ProtocolId'),
                                Typedef(Type('mozilla::ipc::Transport'),
                                        'Transport'),
                                Typedef(Type('mozilla::ipc::Endpoint'),
                                        'Endpoint', ['FooSide']),
                                Typedef(Type('mozilla::ipc::TransportDescriptor'),
                                        'TransportDescriptor'),
                                Typedef(Type('mozilla::ipc::ResponseRejectReason'),
                                        'ResponseRejectReason') ])
        self.protocolName = None

    def visitTranslationUnit(self, tu):
        if tu not in self.visitedTus:
            self.visitedTus.add(tu)
            ipdl.ast.Visitor.visitTranslationUnit(self, tu)
            if not isinstance(tu, TranslationUnit):
                TranslationUnit.upgrade(tu)
            self.typedefs[:] = sorted(list(self.typedefSet))

    def visitInclude(self, inc):
        if inc.tu.filetype == 'header':
            inc.tu.accept(self)

    def visitProtocol(self, pro):
        self.protocolName = pro.name
        pro.decl.cxxtypedefs = self.typedefs
        Protocol.upgrade(pro)
        return ipdl.ast.Visitor.visitProtocol(self, pro)


    def visitUsingStmt(self, using):
        if using.decl.fullname is not None:
            self.typedefSet.add(Typedef(Type(using.decl.fullname),
                                        using.decl.shortname))

    def visitStructDecl(self, sd):
        if not isinstance(sd, StructDecl):
            sd.decl.special = 0
            newfields = [ ]
            for f in sd.fields:
                ftype = f.decl.type
                if _hasVisibleActor(ftype):
                    sd.decl.special = 1
                    # if ftype has a visible actor, we need both
                    # |ActorParent| and |ActorChild| fields
                    newfields.append(_StructField(ftype, f.name, sd,
                                                  side='parent'))
                    newfields.append(_StructField(ftype, f.name, sd,
                                                  side='child'))
                else:
                    newfields.append(_StructField(ftype, f.name, sd))
            sd.fields = newfields
            StructDecl.upgrade(sd)

        if sd.decl.fullname is not None:
            self.typedefSet.add(Typedef(Type(sd.fqClassName()), sd.name))


    def visitUnionDecl(self, ud):
        ud.decl.special = 0
        newcomponents = [ ]
        for ctype in ud.decl.type.components:
            if _hasVisibleActor(ctype):
                ud.decl.special = 1
                # if ctype has a visible actor, we need both
                # |ActorParent| and |ActorChild| union members
                newcomponents.append(_UnionMember(ctype, ud, side='parent'))
                newcomponents.append(_UnionMember(ctype, ud, side='child'))
            else:
                newcomponents.append(_UnionMember(ctype, ud))
        ud.components = newcomponents
        UnionDecl.upgrade(ud)

        if ud.decl.fullname is not None:
            self.typedefSet.add(Typedef(Type(ud.fqClassName()), ud.name))


    def visitDecl(self, decl):
        return _HybridDecl(decl.type, decl.progname)

    def visitMessageDecl(self, md):
        md.namespace = self.protocolName
        md.params = [ param.accept(self) for param in md.inParams ]
        md.returns = [ ret.accept(self) for ret in md.outParams ]
        MessageDecl.upgrade(md)


##-----------------------------------------------------------------------------

def msgenums(protocol, pretty=False):
    msgenum = TypeEnum('MessageType')
    msgstart = _messageStartName(protocol.decl.type) +' << 16'
    msgenum.addId(protocol.name + 'Start', msgstart)

    for md in protocol.messageDecls:
        msgenum.addId(md.prettyMsgName() if pretty else  md.msgId())
        if md.hasReply():
            msgenum.addId(md.prettyReplyName() if pretty else md.replyId())

    msgenum.addId(protocol.name +'End')
    return msgenum

class _GenerateProtocolCode(ipdl.ast.Visitor):
    '''Creates code common to both the parent and child actors.'''
    def __init__(self):
        self.protocol = None     # protocol we're generating a class for
        self.hdrfile = None      # what will become Protocol.h
        self.cppfile = None      # what will become Protocol.cpp
        self.cppIncludeHeaders = []
        self.structUnionDefns = []
        self.funcDefns = []

    def lower(self, tu, cxxHeaderFile, cxxFile, segmentcapacitydict):
        self.protocol = tu.protocol
        self.hdrfile = cxxHeaderFile
        self.cppfile = cxxFile
        self.segmentcapacitydict = segmentcapacitydict
        tu.accept(self)

    def visitTranslationUnit(self, tu):
        hf = self.hdrfile

        hf.addthing(_DISCLAIMER)
        hf.addthings(_includeGuardStart(hf))
        hf.addthing(Whitespace.NL)

        for inc in builtinHeaderIncludes:
            self.visitBuiltinCxxInclude(inc)

        # Compute the set of includes we need for declared structure/union
        # classes for this protocol.
        typesToIncludes = {}
        for using in tu.using:
            typestr = str(using.type.spec)
            if typestr not in typesToIncludes:
                typesToIncludes[typestr] = using.header
            else:
                assert typesToIncludes[typestr] == using.header

        aggregateTypeIncludes = set()
        for su in tu.structsAndUnions:
            typedeps = _ComputeTypeDeps(su.decl.type, True)
            if isinstance(su, ipdl.ast.StructDecl):
                for f in su.fields:
                    f.ipdltype.accept(typedeps)
            elif isinstance(su, ipdl.ast.UnionDecl):
                for c in su.components:
                    c.ipdltype.accept(typedeps)

            for typename in [t.fromtype.name for t in typedeps.usingTypedefs]:
                if typename in typesToIncludes:
                    aggregateTypeIncludes.add(typesToIncludes[typename])

        if len(aggregateTypeIncludes) != 0:
            hf.addthing(Whitespace.NL)
            hf.addthings([ Whitespace("// Headers for typedefs"), Whitespace.NL ])

            for headername in sorted(iter(aggregateTypeIncludes)):
                hf.addthing(CppDirective('include', '"' + headername + '"'))

        # Manually run Visitor.visitTranslationUnit. For dependency resolution
        # we need to handle structs and unions separately.
        for cxxInc in tu.cxxIncludes:
            cxxInc.accept(self)
        for inc in tu.includes:
            inc.accept(self)
        self.generateStructsAndUnions(tu)
        for using in tu.builtinUsing:
            using.accept(self)
        for using in tu.using:
            using.accept(self)
        if tu.protocol:
            tu.protocol.accept(self)

        if tu.filetype == 'header':
            self.cppIncludeHeaders.append(_ipdlhHeaderName(tu) + '.h')

        hf.addthing(Whitespace.NL)
        hf.addthings(_includeGuardEnd(hf))

        cf = self.cppfile
        cf.addthings((
            [ _DISCLAIMER, Whitespace.NL ]
            + [ CppDirective('include','"'+h+'"')
                for h in self.cppIncludeHeaders ]
            + [ Whitespace.NL ]
        ))

        if self.protocol:
            # construct the namespace into which we'll stick all our defns
            ns = Namespace(self.protocol.name)
            cf.addthing(_putInNamespaces(ns, self.protocol.namespaces))
            ns.addstmts(([ Whitespace.NL]
                         + self.funcDefns
                         +[ Whitespace.NL ]))

        cf.addthings(self.structUnionDefns)


    def visitBuiltinCxxInclude(self, inc):
        self.hdrfile.addthing(CppDirective('include', '"'+ inc.file +'"'))

    def visitCxxInclude(self, inc):
        self.cppIncludeHeaders.append(inc.file)

    def visitInclude(self, inc):
        if inc.tu.filetype == 'header':
            self.hdrfile.addthing(CppDirective(
                    'include', '"'+ _ipdlhHeaderName(inc.tu) +'.h"'))
        else:
            self.cppIncludeHeaders += [
                _protocolHeaderName(inc.tu.protocol, 'parent') + '.h',
                _protocolHeaderName(inc.tu.protocol, 'child') + '.h',
            ]

    def generateStructsAndUnions(self, tu):
        '''Generate the definitions for all structs and unions. This will
        re-order the declarations if needed in the C++ code such that
        dependencies have already been defined.'''
        decls = OrderedDict()
        for su in tu.structsAndUnions:
            if isinstance(su, StructDecl):
                which = 'struct'
                forwarddecls, fulldecltypes, cls = _generateCxxStruct(su)
                traitsdecl, traitsdefns = _ParamTraits.structPickling(su.decl.type)
            else:
                assert isinstance(su, UnionDecl)
                which = 'union'
                forwarddecls, fulldecltypes, cls = _generateCxxUnion(su)
                traitsdecl, traitsdefns = _ParamTraits.unionPickling(su.decl.type)

            clsdecl, methoddefns = _splitClassDeclDefn(cls)

            # Store the declarations in the decls map so we can emit in
            # dependency order.
            decls[su.decl.type] = (
                fulldecltypes,
                [  Whitespace.NL ]
                + forwarddecls
                + [ Whitespace("""
//-----------------------------------------------------------------------------
// Declaration of the IPDL type |%s %s|
//
"""% (which, su.name)),
                    _putInNamespaces(clsdecl, su.namespaces),
                ]
                + [ Whitespace.NL,
                    traitsdecl ])

            self.structUnionDefns.extend([
                Whitespace("""
//-----------------------------------------------------------------------------
// Method definitions for the IPDL type |%s %s|
//
"""% (which, su.name)),
                _putInNamespaces(methoddefns, su.namespaces),
                Whitespace.NL,
                traitsdefns,
            ])

        # Generate the declarations structs in dependency order.
        def gen_struct(deps, defn):
            for dep in deps:
                if dep in decls:
                    d, t = decls[dep]
                    del decls[dep]
                    gen_struct(d, t)
            self.hdrfile.addthings(defn)
        while len(decls) > 0:
            _, (d, t) = decls.popitem(False)
            gen_struct(d, t)

    def visitProtocol(self, p):
        self.cppIncludeHeaders.append(_protocolHeaderName(self.protocol, '') + '.h')

        # Forward declare our own actors.
        self.hdrfile.addthings([
            Whitespace.NL,
            _makeForwardDeclForActor(p.decl.type, 'Parent'),
            _makeForwardDeclForActor(p.decl.type, 'Child')
        ])

        self.hdrfile.addthing(Whitespace("""
//-----------------------------------------------------------------------------
// Code common to %sChild and %sParent
//
"""% (p.name, p.name)))

        # construct the namespace into which we'll stick all our decls
        ns = Namespace(self.protocol.name)
        self.hdrfile.addthing(_putInNamespaces(ns, p.namespaces))
        ns.addstmt(Whitespace.NL)

        edecl, edefn = _splitFuncDeclDefn(self.genEndpointFunc())
        ns.addstmts([ edecl, Whitespace.NL ])
        self.funcDefns.append(edefn)

        # spit out message type enum and classes
        msgenum = msgenums(self.protocol)
        ns.addstmts([ StmtDecl(Decl(msgenum, '')), Whitespace.NL ])

        for md in p.messageDecls:
            decls = []

            # Look up the segment capacity used for serializing this
            # message. If the capacity is not specified, use '0' for
            # the default capacity (defined in ipc_message.cc)
            name = '%s::%s' % (md.namespace, md.decl.progname)
            segmentcapacity = self.segmentcapacitydict.get(name, 0)

            mfDecl, mfDefn = _splitFuncDeclDefn(
                _generateMessageConstructor(md, segmentcapacity, p,
                                            forReply=False))
            decls.append(mfDecl)
            self.funcDefns.append(mfDefn)

            if md.hasReply():
                rfDecl, rfDefn = _splitFuncDeclDefn(
                    _generateMessageConstructor(md, 0, p, forReply=True))
                decls.append(rfDecl)
                self.funcDefns.append(rfDefn)

            decls.append(Whitespace.NL)
            ns.addstmts(decls)

        ns.addstmts([ Whitespace.NL, Whitespace.NL ])


    # Generate code for PFoo::CreateEndpoints.
    def genEndpointFunc(self):
        p = self.protocol.decl.type
        tparent = _cxxBareType(ActorType(p), 'Parent', fq=1)
        tchild = _cxxBareType(ActorType(p), 'Child', fq=1)
        methodvar = ExprVar('CreateEndpoints')
        rettype = Type.NSRESULT
        parentpidvar = ExprVar('aParentDestPid')
        childpidvar = ExprVar('aChildDestPid')
        parentvar = ExprVar('aParent')
        childvar = ExprVar('aChild')

        openfunc = MethodDefn(MethodDecl(
            methodvar.name,
            params=[ Decl(Type('base::ProcessId'), parentpidvar.name),
                     Decl(Type('base::ProcessId'), childpidvar.name),
                     Decl(Type('mozilla::ipc::Endpoint<' + tparent.name + '>', ptr=1), parentvar.name),
                     Decl(Type('mozilla::ipc::Endpoint<' + tchild.name + '>', ptr=1), childvar.name) ],
            ret=rettype))
        openfunc.addstmt(StmtReturn(ExprCall(
            ExprVar('mozilla::ipc::CreateEndpoints'),
            args=[ _backstagePass(),
                   parentpidvar, childpidvar,
                   parentvar, childvar
                   ])))
        return openfunc



##--------------------------------------------------

def _generateMessageConstructor(md, segmentSize, protocol, forReply=False):
    if forReply:
        clsname = md.replyCtorFunc()
        msgid = md.replyId()
        replyEnum = 'REPLY'
    else:
        clsname = md.msgCtorFunc()
        msgid = md.msgId()
        replyEnum = 'NOT_REPLY'

    nested = md.decl.type.nested
    prio = md.decl.type.prio
    compress = md.decl.type.compress

    routingId = ExprVar('routingId')

    func = FunctionDefn(FunctionDecl(
        clsname,
        params=[ Decl(Type('int32_t'), routingId.name) ],
        ret=Type('IPC::Message', ptr=1)))

    if compress == 'compress':
        compression = 'COMPRESSION_ENABLED'
    elif compress:
        assert compress == 'compressall'
        compression = 'COMPRESSION_ALL'
    else:
        compression = 'COMPRESSION_NONE'

    if nested == ipdl.ast.NOT_NESTED:
        nestedEnum = 'NOT_NESTED'
    elif nested == ipdl.ast.INSIDE_SYNC_NESTED:
        nestedEnum = 'NESTED_INSIDE_SYNC'
    else:
        assert nested == ipdl.ast.INSIDE_CPOW_NESTED
        nestedEnum = 'NESTED_INSIDE_CPOW'

    if prio == ipdl.ast.NORMAL_PRIORITY:
        prioEnum = 'NORMAL_PRIORITY'
    elif prio == ipdl.ast.INPUT_PRIORITY:
        prioEnum = 'INPUT_PRIORITY'
    else:
        prioEnum = 'HIGH_PRIORITY'

    if md.decl.type.isSync():
        syncEnum = 'SYNC'
    else:
        syncEnum = 'ASYNC'

    if md.decl.type.isInterrupt():
        interruptEnum = 'INTERRUPT'
    else:
        interruptEnum = 'NOT_INTERRUPT'

    if md.decl.type.isCtor():
        ctorEnum = 'CONSTRUCTOR'
    else:
        ctorEnum = 'NOT_CONSTRUCTOR'

    def messageEnum(valname):
        return ExprVar('IPC::Message::' + valname)

    flags = ExprCall(ExprVar('IPC::Message::HeaderFlags'),
                     args=[ messageEnum(nestedEnum),
                            messageEnum(prioEnum),
                            messageEnum(compression),
                            messageEnum(ctorEnum),
                            messageEnum(syncEnum),
                            messageEnum(interruptEnum),
                            messageEnum(replyEnum) ])

    segmentSize = int(segmentSize)
    if segmentSize:
        func.addstmt(
            StmtReturn(ExprNew(Type('IPC::Message'),
                               args=[ routingId,
                                      ExprVar(msgid),
                                      ExprLiteral.Int(int(segmentSize)),
                                      flags,
                                      # Pass `true` to recordWriteLatency to collect telemetry
                                      ExprLiteral.TRUE ])))
    else:
        func.addstmt(
            StmtReturn(ExprCall(ExprVar('IPC::Message::IPDLMessage'),
                               args=[ routingId,
                                      ExprVar(msgid),
                                      flags ])))

    return func

##--------------------------------------------------

class _ParamTraits():
    var = ExprVar('aVar')
    msgvar = ExprVar('aMsg')
    itervar = ExprVar('aIter')
    actor = ExprVar('aActor')

    @classmethod
    def ifsideis(cls, side, then, els=None):
        cxxside = ExprVar('mozilla::ipc::ChildSide')
        if side == 'parent':
            cxxside = ExprVar('mozilla::ipc::ParentSide')

        ifstmt = StmtIf(ExprBinary(cxxside, '==',
                            ExprCall(ExprSelect(cls.actor, '->', 'GetSide'))))
        ifstmt.addifstmt(then)
        if els is not None:
            ifstmt.addelsestmt(els)
        return ifstmt

    @classmethod
    def fatalError(cls, reason):
        return StmtExpr(ExprCall(ExprSelect(cls.actor, '->', 'FatalError'),
                                 args=[ ExprLiteral.String(reason) ]))

    @classmethod
    def write(cls, var, msgvar, actor):
        # WARNING: This doesn't set AutoForActor for you, make sure this is
        # only called when the actor is already correctly set.
        return ExprCall(ExprVar('WriteIPDLParam'), args=[ msgvar, actor, var ])

    @classmethod
    def checkedWrite(cls, ipdltype, var, msgvar, sentinelKey, actor):
        assert sentinelKey
        block = Block()

        # Assert we aren't serializing a null non-nullable actor
        if ipdltype and ipdltype.isIPDL() and ipdltype.isActor() and not ipdltype.nullable:
            block.addstmt(_abortIfFalse(var, 'NULL actor value passed to non-nullable param'))

        block.addstmts([
            StmtExpr(cls.write(var, msgvar, actor)),
            Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n', indent=1),
            StmtExpr(ExprCall(ExprSelect(msgvar, '->', 'WriteSentinel'),
                              args=[ ExprLiteral.Int(hashfunc(sentinelKey)) ]))
        ])
        return block

    @classmethod
    def checkedRead(cls, ipdltype, var,
                    msgvar, itervar, errfn,
                    paramtype, sentinelKey,
                    errfnSentinel, actor):
        block = Block()

        # Read the data
        ifbad = StmtIf(ExprNot(ExprCall(ExprVar('ReadIPDLParam'),
                                        args=[ msgvar, itervar, actor, var ])))
        if not isinstance(paramtype, list):
            paramtype = ['Error deserializing ' + paramtype]
        ifbad.addifstmts(errfn(*paramtype))
        block.addstmt(ifbad)

        # Check if we got a null non-nullable actor
        if ipdltype and ipdltype.isIPDL() and ipdltype.isActor() and not ipdltype.nullable:
            ifnull = StmtIf(ExprNot(ExprDeref(var)))
            ifnull.addifstmts(errfn(*paramtype))
            block.addstmt(ifnull)

        # Read the sentinel
        assert sentinelKey
        block.addstmt(Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n',
                                 indent=1))
        read = ExprCall(ExprSelect(msgvar, '->', 'ReadSentinel'),
                        args=[ itervar, ExprLiteral.Int(hashfunc(sentinelKey)) ])
        ifsentinel = StmtIf(ExprNot(read))
        ifsentinel.addifstmts(errfnSentinel(*paramtype))
        block.addstmt(ifsentinel)

        return block

    # Helper wrapper for checkedRead for use within _ParamTraits
    @classmethod
    def _checkedRead(cls, ipdltype, var, sentinelKey, what):
        def errfn(msg):
            return [ cls.fatalError(msg), StmtReturn.FALSE ]

        return cls.checkedRead(
            ipdltype, var, cls.msgvar, cls.itervar,
            errfn=errfn,
            paramtype=what,
            sentinelKey=sentinelKey,
            errfnSentinel=errfnSentinel(),
            actor=cls.actor)

    @classmethod
    def generateDecl(cls, fortype, write, read, constin=1):
        # IPDLParamTraits impls are selected ignoring constness, references,
        # and pointers.
        pt = Class('IPDLParamTraits',
                   specializes=Type(fortype.name,
                                    T=fortype.T,
                                    inner=fortype.inner),
                   struct=True)

        # typedef T paramType;
        pt.addstmt(Typedef(fortype, 'paramType'))

        iprotocoltype = Type('mozilla::ipc::IProtocol', ptr=1)

        # static void Write(Message*, const T&);
        intype = Type('paramType', ref=1, const=constin)
        writemthd = MethodDefn(
            MethodDecl('Write',
                       params=[ Decl(Type('IPC::Message', ptr=1),
                                     cls.msgvar.name),
                                Decl(iprotocoltype,
                                     cls.actor.name),
                                Decl(intype,
                                     cls.var.name) ],
                       methodspec=MethodSpec.STATIC))
        writemthd.addstmts(write)
        pt.addstmt(writemthd)

        # static bool Read(const Message*, PickleIterator*, T*);
        outtype = Type('paramType', ptr=1)
        readmthd = MethodDefn(
            MethodDecl('Read',
                       params=[ Decl(Type('IPC::Message', ptr=1, const=1),
                                     cls.msgvar.name),
                                Decl(_iterType(ptr=1),
                                     cls.itervar.name),
                                Decl(iprotocoltype,
                                     cls.actor.name),
                                Decl(outtype,
                                     cls.var.name) ],
                        ret=Type.BOOL,
                        methodspec=MethodSpec.STATIC))
        readmthd.addstmts(read)
        pt.addstmt(readmthd)

        # Split the class into declaration and definition
        clsdecl, methoddefns = _splitClassDeclDefn(pt)

        namespaces = [ Namespace('mozilla'), Namespace('ipc') ]
        clsns = _putInNamespaces(clsdecl, namespaces)
        defns = _putInNamespaces(methoddefns, namespaces)
        return clsns, defns

    @classmethod
    def actorPickling(cls, actortype, side):
        """Generates pickling for IPDL actors. This is a |nullable| deserializer.
        Write and read callers will perform nullability validation."""

        cxxtype = _cxxBareType(actortype, side, fq=1)
        idvar = ExprVar('id')

        # void Write(..) impl
        write = [
            # id_t id;
            StmtDecl(Decl(_actorIdType(), idvar.name)),
        ]

        # if (!var) id = NULL_ID
        ifnull = StmtIf(ExprNot(cls.var))
        ifnull.addifstmt(StmtExpr(ExprAssn(idvar, _NULL_ACTOR_ID)))

        # else
        #   id = var->mId
        ifnull.addelsestmt(StmtExpr(ExprAssn(idvar, _actorId(cls.var))))
        #   if (id == FREED_ID)
        #     abort()
        iffreed = StmtIf(ExprBinary(_FREED_ACTOR_ID, '==', idvar))
        iffreed.addifstmt(cls.fatalError("actor has been |delete|d"))
        ifnull.addelsestmt(iffreed)

        # IPC::WriteParam(..)
        write += [ ifnull,
                   StmtExpr(cls.write(idvar, cls.msgvar, cls.actor)) ]


        # bool Read(..) impl
        actorvar = ExprVar('actor')
        read = [
            StmtDecl(Decl(Type('mozilla::Maybe', T=Type('mozilla::ipc::IProtocol', ptr=1)), actorvar.name),
                     init=ExprCall(ExprSelect(cls.actor, '->', 'ReadActor'),
                                   args=[ cls.msgvar,
                                          cls.itervar,
                                          ExprLiteral.TRUE, # XXX(nika): Do we still need this arg?
                                          ExprLiteral.String(actortype.name()),
                                          _protocolId(actortype) ])),
        ]

        # if (actor.isNothing())
        #   return false
        ifnothing = StmtIf(ExprCall(ExprSelect(actorvar, '.', 'isNothing')))
        ifnothing.addifstmts([ StmtReturn.FALSE ])
        read += [
            ifnothing,
            Whitespace.NL,
            StmtExpr(ExprAssn(ExprDeref(cls.var),
                              ExprCast(ExprCall(ExprSelect(actorvar, '.', 'value')),
                                       cxxtype, static=1))),
            StmtReturn.TRUE,
        ]

        return cls.generateDecl(cxxtype, write, read)

    @classmethod
    def structPickling(cls, structtype):
        sd = structtype._ast
        # NOTE: Not using _cxxBareType here as we don't have a side
        cxxtype = Type(structtype.fullname())

        def get(sel, f):
            return ExprCall(f.getMethod(thisexpr=cls.var, sel=sel))

        write = []
        read = []

        for f in sd.fields:
            writefield = cls.checkedWrite(f.ipdltype,
                                          get('.', f),
                                          cls.msgvar,
                                          sentinelKey=f.basename,
                                          actor=cls.actor)
            readfield = cls._checkedRead(f.ipdltype,
                                         ExprAddrOf(get('->', f)), f.basename,
                                         '\'' + f.getMethod().name + '\' ' +
                                         '(' + f.ipdltype.name() + ') member of ' +
                                         '\'' + structtype.name() + '\'')

            # Wrap the read/write in a side check if the field is special.
            if f.special:
                writefield = cls.ifsideis(f.side, writefield)
                readfield = cls.ifsideis(f.side, readfield)

            write.append(writefield)
            read.append(readfield)

        read.append(StmtReturn.TRUE)

        return cls.generateDecl(cxxtype, write, read)

    @classmethod
    def unionPickling(cls, uniontype):
        # NOTE: Not using _cxxBareType here as we don't have a side
        cxxtype = Type(uniontype.fullname())
        ud = uniontype._ast

        # Use typedef to set up an alias so it's easier to reference the struct type.
        alias = 'union__'
        typevar = ExprVar('type')

        prelude = [
            Typedef(cxxtype, alias),
            StmtDecl(Decl(Type.INT, typevar.name)),
        ]

        writeswitch = StmtSwitch(typevar)
        write = prelude + [
            StmtExpr(ExprAssn(typevar, ud.callType(cls.var))),
            cls.checkedWrite(None,
                             typevar,
                             cls.msgvar,
                             sentinelKey=uniontype.name(),
                             actor=cls.actor),
            Whitespace.NL,
            writeswitch,
        ]

        readswitch = StmtSwitch(typevar)
        read = prelude + [
            cls._checkedRead(None,
                             ExprAddrOf(typevar),
                             uniontype.name(),
                             'type of union ' + uniontype.name()),
            Whitespace.NL,
            readswitch,
        ]

        for c in ud.components:
            ct = c.ipdltype
            caselabel = CaseLabel(alias + '::' + c.enum())
            origenum = c.enum()

            writecase = StmtBlock()
            wstmt = cls.checkedWrite(c.ipdltype,
                                     ExprCall(ExprSelect(cls.var, '.',
                                                         c.getTypeName())),
                                     cls.msgvar, sentinelKey=c.enum(),
                                     actor=cls.actor)
            if c.special:
                # Report an error if the type is special and the side is wrong
                wstmt = cls.ifsideis(c.side, wstmt,
                                     els=cls.fatalError('wrong side!'))
            writecase.addstmts([ wstmt, StmtReturn() ])
            writeswitch.addcase(caselabel, writecase)

            readcase = StmtBlock()
            if c.special:
                # The type comes across flipped from what the actor will be on
                # this side; i.e. child->parent messages will have PFooChild
                # when received on the parent side. Report an error if the sides
                # match, and handle c.other instead.
                readcase.addstmt(cls.ifsideis(c.side,
                                              StmtBlock([ cls.fatalError('wrong side!'),
                                                          StmtReturn.FALSE ])))
                c = c.other
            tmpvar = ExprVar('tmp')
            ct = c.bareType(fq=1)
            readcase.addstmts([
                StmtDecl(Decl(ct, tmpvar.name), init=c.defaultValue(fq=1)),
                StmtExpr(ExprAssn(ExprDeref(cls.var), tmpvar)),
                cls._checkedRead(c.ipdltype,
                                 ExprAddrOf(ExprCall(ExprSelect(cls.var, '->',
                                                                c.getTypeName()))),
                                 origenum,
                                 'variant ' + origenum + ' of union ' + uniontype.name()),
                StmtReturn.TRUE,
            ])
            readswitch.addcase(caselabel, readcase)

        # Add the error default case
        writeswitch.addcase(DefaultLabel(),
                            StmtBlock([ cls.fatalError('unknown union type'),
                                        StmtReturn() ]))
        readswitch.addcase(DefaultLabel(),
                           StmtBlock([ cls.fatalError('unknown union type'),
                                       StmtReturn.FALSE ]))

        return cls.generateDecl(cxxtype, write, read)

##--------------------------------------------------

class _ComputeTypeDeps(TypeVisitor):
    '''Pass that gathers the C++ types that a particular IPDL type
(recursively) depends on.  There are three kinds of dependencies: (i)
types that need forward declaration; (ii) types that need a |using|
stmt; (iii) IPDL structs or unions which must be fully declared
before this struct.  Some types generate multiple kinds.'''

    def __init__(self, fortype, unqualifiedTypedefs=False):
        ipdl.type.TypeVisitor.__init__(self)
        self.usingTypedefs = [ ]
        self.forwardDeclStmts = [ ]
        self.fullDeclTypes = [ ]
        self.fortype = fortype
        self.unqualifiedTypedefs = unqualifiedTypedefs

    def maybeTypedef(self, fqname, name):
        if fqname != name or self.unqualifiedTypedefs:
            self.usingTypedefs.append(Typedef(Type(fqname), name))

    def visitImportedCxxType(self, t):
        if t in self.visited: return
        self.visited.add(t)
        self.maybeTypedef(t.fullname(), t.name())

    def visitActorType(self, t):
        if t in self.visited: return
        self.visited.add(t)

        fqname, name = t.fullname(), t.name()

        self.maybeTypedef(_actorName(fqname, 'Parent'),
                          _actorName(name, 'Parent'))
        self.maybeTypedef(_actorName(fqname, 'Child'),
                          _actorName(name, 'Child'))

        self.forwardDeclStmts.extend([
            _makeForwardDeclForActor(t.protocol, 'parent'), Whitespace.NL,
            _makeForwardDeclForActor(t.protocol, 'child'), Whitespace.NL
        ])

    def visitStructOrUnionType(self, su, defaultVisit):
        if su in self.visited or su == self.fortype: return
        self.visited.add(su)
        self.maybeTypedef(su.fullname(), su.name())

        # Mutually recursive fields in unions are behind indirection, so we only
        # need a forward decl, and don't need a full type declaration.
        if isinstance(self.fortype, UnionType) and self.fortype.mutuallyRecursiveWith(su):
            self.forwardDeclStmts.append(_makeForwardDecl(su))
        else:
            self.fullDeclTypes.append(su)

        return defaultVisit(self, su)

    def visitStructType(self, t):
        return self.visitStructOrUnionType(t, TypeVisitor.visitStructType)

    def visitUnionType(self, t):
        return self.visitStructOrUnionType(t, TypeVisitor.visitUnionType)

    def visitArrayType(self, t):
        return TypeVisitor.visitArrayType(self, t)

    def visitShmemType(self, s):
        if s in self.visited: return
        self.visited.add(s)
        self.maybeTypedef('mozilla::ipc::Shmem', 'Shmem')

    def visitByteBufType(self, s):
        if s in self.visited: return
        self.visited.add(s)
        self.maybeTypedef('mozilla::ipc::ByteBuf', 'ByteBuf')

    def visitFDType(self, s):
        if s in self.visited: return
        self.visited.add(s)
        self.maybeTypedef('mozilla::ipc::FileDescriptor', 'FileDescriptor')

    def visitVoidType(self, v): assert 0
    def visitMessageType(self, v): assert 0
    def visitProtocolType(self, v): assert 0
    def visitStateType(self, v): assert 0


def _generateCxxStruct(sd):
    ''' '''
    # compute all the typedefs and forward decls we need to make
    gettypedeps = _ComputeTypeDeps(sd.decl.type)
    for f in sd.fields:
        f.ipdltype.accept(gettypedeps)

    usingTypedefs = gettypedeps.usingTypedefs
    forwarddeclstmts = gettypedeps.forwardDeclStmts
    fulldecltypes = gettypedeps.fullDeclTypes

    struct = Class(sd.name, final=1)
    struct.addstmts([ Label.PRIVATE ]
                    + usingTypedefs
                    + [ Whitespace.NL, Label.PUBLIC ])

    constreftype = Type(sd.name, const=1, ref=1)

    def fieldsAsParamList():
        return [ Decl(f.inType(), f.argVar().name) for f in sd.fields ]

    def assignFromOther(oexpr):
        return ExprCall(assignvar,
                        args=[ f.initExpr(oexpr) for f in sd.fields ])

    # If this is an empty struct (no fields), then the default ctor
    # and "create-with-fields" ctors are equivalent.  So don't bother
    # with the default ctor.
    if len(sd.fields):
        # Struct()
        defctor = ConstructorDefn(ConstructorDecl(sd.name, force_inline=1))

        # We want to explicitly default-construct every member of the struct.
        # This will initialize all primitives which wouldn't be initialized
        # normally to their default values, and will initialize any actor member
        # pointers to the correct default value of `nullptr`. Other C++ types
        # with custom constructors must also provide a default constructor.
        defctor.memberinits = [ ExprMemberInit(f.memberVar()) for f in sd.fields ]
        struct.addstmts([ defctor, Whitespace.NL ])

    # Struct(const field1& _f1, ...)
    valctor = ConstructorDefn(ConstructorDecl(sd.name,
                                              params=fieldsAsParamList(),
                                              force_inline=1))
    valctor.memberinits = [ ExprMemberInit(f.memberVar(),
                                           args=[ f.argVar() ])
                            for f in sd.fields ]
    struct.addstmts([ valctor, Whitespace.NL ])

    # The default copy, move, and assignment constructors, and the default
    # destructor, will do the right thing.

    # bool operator==(const Struct& _o)
    ovar = ExprVar('_o')
    opeqeq = MethodDefn(MethodDecl(
        'operator==',
        params=[ Decl(constreftype, ovar.name) ],
        ret=Type.BOOL,
        const=1))
    for f in sd.fields:
        ifneq = StmtIf(ExprNot(
            ExprBinary(ExprCall(f.getMethod()), '==',
                       ExprCall(f.getMethod(ovar)))))
        ifneq.addifstmt(StmtReturn.FALSE)
        opeqeq.addstmt(ifneq)
    opeqeq.addstmt(StmtReturn.TRUE)
    struct.addstmts([ opeqeq, Whitespace.NL ])

    # bool operator!=(const Struct& _o)
    opneq = MethodDefn(MethodDecl(
        'operator!=',
        params=[ Decl(constreftype, ovar.name) ],
        ret=Type.BOOL,
        const=1))
    opneq.addstmt(StmtReturn(ExprNot(ExprCall(ExprVar('operator=='),
                                              args=[ ovar ]))))
    struct.addstmts([ opneq, Whitespace.NL ])

    # field1& f1()
    # const field1& f1() const
    for f in sd.fields:
        get = MethodDefn(MethodDecl(f.getMethod().name,
                                    params=[ ],
                                    ret=f.refType(),
                                    force_inline=1))
        get.addstmt(StmtReturn(f.refExpr()))

        getconstdecl = deepcopy(get.decl)
        getconstdecl.ret = f.constRefType()
        getconstdecl.const = 1
        getconst = MethodDefn(getconstdecl)
        getconst.addstmt(StmtReturn(f.constRefExpr()))

        struct.addstmts([ get, getconst, Whitespace.NL ])

    # private:
    struct.addstmt(Label.PRIVATE)

    # members
    struct.addstmts([ StmtDecl(Decl(f.bareType(), f.memberVar().name))
                      for f in sd.fields ])

    return forwarddeclstmts, fulldecltypes, struct

##--------------------------------------------------

def _generateCxxUnion(ud):
    # This Union class basically consists of a type (enum) and a
    # union for storage.  The union can contain POD and non-POD
    # types.  Each type needs a copy/move ctor, assignment operators,
    # and dtor.
    #
    # Rather than templating this class and only providing
    # specializations for the types we support, which is slightly
    # "unsafe" in that C++ code can add additional specializations
    # without the IPDL compiler's knowledge, we instead explicitly
    # implement non-templated methods for each supported type.
    #
    # The one complication that arises is that C++, for arcane
    # reasons, does not allow the placement destructor of a
    # builtin type, like int, to be directly invoked.  So we need
    # to hack around this by internally typedef'ing all
    # constituent types.  Sigh.
    #
    # So, for each type, this "Union" class needs:
    # (private)
    #  - entry in the type enum
    #  - entry in the storage union
    #  - [type]ptr() method to get a type* from the underlying union
    #  - same as above to get a const type*
    #  - typedef to hack around placement delete limitations
    # (public)
    #  - placement delete case for dtor
    #  - copy ctor
    #  - move ctor
    #  - case in generic copy ctor
    #  - copy operator= impl
    #  - move operator= impl
    #  - case in generic operator=
    #  - operator [type&]
    #  - operator [const type&] const
    #  - [type&] get_[type]()
    #  - [const type&] get_[type]() const
    #
    cls = Class(ud.name, final=1)
    # const Union&, i.e., Union type with inparam semantics
    inClsType = Type(ud.name, const=1, ref=1)
    refClsType = Type(ud.name, ref=1)
    rvalueRefClsType = Type(ud.name, ref=2)
    typetype = Type('Type')
    valuetype = Type('Value')
    mtypevar = ExprVar('mType')
    mvaluevar = ExprVar('mValue')
    maybedtorvar = ExprVar('MaybeDestroy')
    assertsanityvar = ExprVar('AssertSanity')
    tnonevar = ExprVar('T__None')
    tlastvar = ExprVar('T__Last')

    def callAssertSanity(uvar=None, expectTypeVar=None):
        func = assertsanityvar
        args = [ ]
        if uvar is not None:
            func = ExprSelect(uvar, '.', assertsanityvar.name)
        if expectTypeVar is not None:
            args.append(expectTypeVar)
        return ExprCall(func, args=args)

    def callMaybeDestroy(newTypeVar):
        return ExprCall(maybedtorvar, args=[ newTypeVar ])

    def maybeReconstruct(memb, newTypeVar):
        ifdied = StmtIf(callMaybeDestroy(newTypeVar))
        ifdied.addifstmt(StmtExpr(memb.callCtor()))
        return ifdied

    def voidCast(expr):
        return ExprCast(expr, Type.VOID, static=1)

    # compute all the typedefs and forward decls we need to make
    gettypedeps = _ComputeTypeDeps(ud.decl.type)
    for c in ud.components:
        c.ipdltype.accept(gettypedeps)

    usingTypedefs = gettypedeps.usingTypedefs
    forwarddeclstmts = gettypedeps.forwardDeclStmts
    fulldecltypes = gettypedeps.fullDeclTypes

    # the |Type| enum, used to switch on the discunion's real type
    cls.addstmt(Label.PUBLIC)
    typeenum = TypeEnum(typetype.name)
    typeenum.addId(tnonevar.name, 0)
    firstid = ud.components[0].enum()
    typeenum.addId(firstid, 1)
    for c in ud.components[1:]:
        typeenum.addId(c.enum())
    typeenum.addId(tlastvar.name, ud.components[-1].enum())
    cls.addstmts([ StmtDecl(Decl(typeenum,'')),
                   Whitespace.NL ])

    cls.addstmt(Label.PRIVATE)
    cls.addstmts(
        usingTypedefs
        # hacky typedef's that allow placement dtors of builtins
        + [ Typedef(c.internalType(), c.typedef()) for c in ud.components ])
    cls.addstmt(Whitespace.NL)

    # the C++ union the discunion use for storage
    valueunion = TypeUnion(valuetype.name)
    for c in ud.components:
        valueunion.addComponent(c.unionType(), c.name)
    cls.addstmts([ StmtDecl(Decl(valueunion,'')),
                       Whitespace.NL ])

    # for each constituent type T, add private accessors that
    # return a pointer to the Value union storage casted to |T*|
    # and |const T*|
    for c in ud.components:
        getptr = MethodDefn(MethodDecl(
            c.getPtrName(), params=[ ], ret=c.ptrToInternalType(),
            force_inline=1))
        getptr.addstmt(StmtReturn(c.ptrToSelfExpr()))

        getptrconst = MethodDefn(MethodDecl(
            c.getConstPtrName(), params=[ ], ret=c.constPtrToType(),
            const=1, force_inline=1))
        getptrconst.addstmt(StmtReturn(c.constptrToSelfExpr()))

        cls.addstmts([ getptr, getptrconst ])
    cls.addstmt(Whitespace.NL)

    # add a helper method that invokes the placement dtor on the
    # current underlying value, only if |aNewType| is different
    # than the current type, and returns true if the underlying
    # value needs to be re-constructed
    newtypevar = ExprVar('aNewType')
    maybedtor = MethodDefn(MethodDecl(
        maybedtorvar.name,
        params=[ Decl(typetype, newtypevar.name) ],
        ret=Type.BOOL))
    # wasn't /actually/ dtor'd, but it needs to be re-constructed
    ifnone = StmtIf(ExprBinary(mtypevar, '==', tnonevar))
    ifnone.addifstmt(StmtReturn.TRUE)
    # same type, nothing to see here
    ifnochange = StmtIf(ExprBinary(mtypevar, '==', newtypevar))
    ifnochange.addifstmt(StmtReturn.FALSE)
    # need to destroy.  switch on underlying type
    dtorswitch = StmtSwitch(mtypevar)
    for c in ud.components:
        dtorswitch.addcase(
            CaseLabel(c.enum()),
            StmtBlock([ StmtExpr(c.callDtor()),
                        StmtBreak() ]))
    dtorswitch.addcase(
        DefaultLabel(),
        StmtBlock([ _logicError("not reached"), StmtBreak() ]))
    maybedtor.addstmts([
        ifnone,
        ifnochange,
        dtorswitch,
        StmtReturn.TRUE
    ])
    cls.addstmts([ maybedtor, Whitespace.NL ])

    # add helper methods that ensure the discunion has a
    # valid type
    sanity = MethodDefn(MethodDecl(
        assertsanityvar.name, ret=Type.VOID, const=1, force_inline=1))
    sanity.addstmts([
        _abortIfFalse(ExprBinary(tnonevar, '<=', mtypevar),
                      'invalid type tag'),
        _abortIfFalse(ExprBinary(mtypevar, '<=', tlastvar),
                      'invalid type tag') ])
    cls.addstmt(sanity)

    atypevar = ExprVar('aType')
    sanity2 = MethodDefn(
        MethodDecl(assertsanityvar.name,
                       params=[ Decl(typetype, atypevar.name) ],
                       ret=Type.VOID,
                       const=1, force_inline=1))
    sanity2.addstmts([
        StmtExpr(ExprCall(assertsanityvar)),
        _abortIfFalse(ExprBinary(mtypevar, '==', atypevar),
                      'unexpected type tag') ])
    cls.addstmts([ sanity2, Whitespace.NL ])

    ## ---- begin public methods -----

    # Union() default ctor
    cls.addstmts([
        Label.PUBLIC,
        ConstructorDefn(
            ConstructorDecl(ud.name, force_inline=1),
            memberinits=[ ExprMemberInit(mtypevar, [ tnonevar ]) ]),
        Whitespace.NL
    ])

    # Union(const T&) copy & Union(T&&) move ctors
    othervar = ExprVar('aOther')
    for c in ud.components:
        copyctor = ConstructorDefn(ConstructorDecl(
            ud.name, params=[ Decl(c.inType(), othervar.name) ]))
        copyctor.addstmts([
            StmtExpr(c.callCtor(othervar)),
            StmtExpr(ExprAssn(mtypevar, c.enumvar())) ])
        cls.addstmts([ copyctor, Whitespace.NL ])

        if not _cxxTypeCanMove(c.ipdltype):
            continue
        movector = ConstructorDefn(ConstructorDecl(
            ud.name, params=[ Decl(c.forceMoveType(), othervar.name) ]))
        movector.addstmts([
            StmtExpr(c.callCtor(ExprMove(othervar))),
            StmtExpr(ExprAssn(mtypevar, c.enumvar())) ])
        cls.addstmts([ movector, Whitespace.NL ])

    # Union(const Union&) copy ctor
    copyctor = ConstructorDefn(ConstructorDecl(
        ud.name, params=[ Decl(inClsType, othervar.name) ]))
    othertype = ud.callType(othervar)
    copyswitch = StmtSwitch(othertype)
    for c in ud.components:
        copyswitch.addcase(
            CaseLabel(c.enum()),
            StmtBlock([
                StmtExpr(c.callCtor(
                    ExprCall(ExprSelect(othervar,
                                        '.', c.getConstTypeName())))),
                StmtBreak()
            ]))
    copyswitch.addcase(CaseLabel(tnonevar.name),
                       StmtBlock([ StmtBreak() ]))
    copyswitch.addcase(
        DefaultLabel(),
        StmtBlock([ _logicError('unreached'), StmtReturn() ]))
    copyctor.addstmts([
        StmtExpr(callAssertSanity(uvar=othervar)),
        copyswitch,
        StmtExpr(ExprAssn(mtypevar, othertype))
    ])
    cls.addstmts([ copyctor, Whitespace.NL ])

    # Union(Union&&) move ctor
    movector = ConstructorDefn(ConstructorDecl(
        ud.name, params=[ Decl(rvalueRefClsType, othervar.name) ]))
    othertypevar = ExprVar("t")
    moveswitch = StmtSwitch(othertypevar)
    for c in ud.components:
        case = StmtBlock()
        if c.recursive:
            # This is sound as we set othervar.mTypeVar to T__None after the
            # switch. The pointer in the union will be left dangling.
            case.addstmts([
                # ptr_C() = other.ptr_C()
                StmtExpr(ExprAssn(c.callGetPtr(),
                                  ExprCall(ExprSelect(othervar, '.', ExprVar(c.getPtrName())))))
            ])
        else:
            case.addstmts([
                # new ... (Move(other.get_C()))
                StmtExpr(c.callCtor(ExprMove(ExprCall(ExprSelect(othervar, '.', c.getTypeName()))))),
                # other.MaybeDestroy(T__None)
                StmtExpr(voidCast(ExprCall(ExprSelect(othervar, '.', maybedtorvar), args=[ tnonevar ]))),
            ])
        case.addstmts([ StmtBreak() ])
        moveswitch.addcase(CaseLabel(c.enum()), case)
    moveswitch.addcase(CaseLabel(tnonevar.name),
                       StmtBlock([ StmtBreak() ]))
    moveswitch.addcase(
        DefaultLabel(),
        StmtBlock([ _logicError('unreached'), StmtReturn() ]))
    movector.addstmts([
        StmtExpr(callAssertSanity(uvar=othervar)),
        StmtDecl(Decl(typetype, othertypevar.name), init=ud.callType(othervar)),
        moveswitch,
        StmtExpr(ExprAssn(ExprSelect(othervar, '.', mtypevar), tnonevar)),
        StmtExpr(ExprAssn(mtypevar, othertypevar))
    ])
    cls.addstmts([ movector, Whitespace.NL ])

    # ~Union()
    dtor = DestructorDefn(DestructorDecl(ud.name))
    # The void cast prevents Coverity from complaining about missing return
    # value checks.
    dtor.addstmt(StmtExpr(voidCast(callMaybeDestroy(tnonevar))))
    cls.addstmts([ dtor, Whitespace.NL ])

    # type()
    typemeth = MethodDefn(MethodDecl('type', ret=typetype,
                                     const=1, force_inline=1))
    typemeth.addstmt(StmtReturn(mtypevar))
    cls.addstmts([ typemeth, Whitespace.NL ])

    # Union& operator= methods
    rhsvar = ExprVar('aRhs')
    for c in ud.components:
        # Union& operator=(const T&)
        opeq = MethodDefn(MethodDecl(
            'operator=',
            params=[ Decl(c.inType(), rhsvar.name) ],
            ret=refClsType))
        opeq.addstmts([
            # might need to placement-delete old value first
            maybeReconstruct(c, c.enumvar()),
            StmtExpr(c.callOperatorEq(rhsvar)),
            StmtExpr(ExprAssn(mtypevar, c.enumvar())),
            StmtReturn(ExprDeref(ExprVar.THIS))
        ])
        cls.addstmts([ opeq, Whitespace.NL ])

        # Union& operator=(T&&)
        if not _cxxTypeCanMove(c.ipdltype):
            continue

        opeq = MethodDefn(MethodDecl(
            'operator=',
            params=[ Decl(c.forceMoveType(), rhsvar.name) ],
            ret=refClsType))
        opeq.addstmts([
            # might need to placement-delete old value first
            maybeReconstruct(c, c.enumvar()),
            StmtExpr(c.callOperatorEq(ExprMove(rhsvar))),
            StmtExpr(ExprAssn(mtypevar, c.enumvar())),
            StmtReturn(ExprDeref(ExprVar.THIS))
        ])
        cls.addstmts([ opeq, Whitespace.NL ])

    # Union& operator=(const Union&)
    opeq = MethodDefn(MethodDecl(
        'operator=',
        params=[ Decl(inClsType, rhsvar.name) ],
        ret=refClsType))
    rhstypevar = ExprVar('t')
    opeqswitch = StmtSwitch(rhstypevar)
    for c in ud.components:
        case = StmtBlock()
        case.addstmts([
            maybeReconstruct(c, rhstypevar),
            StmtExpr(c.callOperatorEq(
                ExprCall(ExprSelect(rhsvar, '.', c.getConstTypeName())))),
            StmtBreak()
        ])
        opeqswitch.addcase(CaseLabel(c.enum()), case)
    opeqswitch.addcase(
        CaseLabel(tnonevar.name),
        # The void cast prevents Coverity from complaining about missing return
        # value checks.
        StmtBlock([ StmtExpr(ExprCast(callMaybeDestroy(rhstypevar), Type.VOID,
                                      static=1)),
                    StmtBreak() ])
    )
    opeqswitch.addcase(
        DefaultLabel(),
        StmtBlock([ _logicError('unreached'), StmtBreak() ]))
    opeq.addstmts([
        StmtExpr(callAssertSanity(uvar=rhsvar)),
        StmtDecl(Decl(typetype, rhstypevar.name), init=ud.callType(rhsvar)),
        opeqswitch,
        StmtExpr(ExprAssn(mtypevar, rhstypevar)),
        StmtReturn(ExprDeref(ExprVar.THIS))
    ])
    cls.addstmts([ opeq, Whitespace.NL ])

    # Union& operator=(Union&&)
    opeq = MethodDefn(MethodDecl(
        'operator=',
        params=[ Decl(rvalueRefClsType, rhsvar.name) ],
        ret=refClsType))
    rhstypevar = ExprVar('t')
    opeqswitch = StmtSwitch(rhstypevar)
    for c in ud.components:
        case = StmtBlock()
        if c.recursive:
            case.addstmts([
                StmtExpr(voidCast(callMaybeDestroy(tnonevar))),
                StmtExpr(ExprAssn(c.callGetPtr(),
                                  ExprCall(ExprSelect(rhsvar, '.', ExprVar(c.getPtrName()))))),
            ])
        else:
            case.addstmts([
                maybeReconstruct(c, rhstypevar),
                StmtExpr(c.callOperatorEq(
                    ExprMove(ExprCall(ExprSelect(rhsvar, '.', c.getTypeName()))))),
                # other.MaybeDestroy(T__None)
                StmtExpr(voidCast(ExprCall(ExprSelect(rhsvar, '.', maybedtorvar), args=[ tnonevar ]))),
            ])
        case.addstmts([ StmtBreak() ])
        opeqswitch.addcase(CaseLabel(c.enum()), case)
    opeqswitch.addcase(
        CaseLabel(tnonevar.name),
        # The void cast prevents Coverity from complaining about missing return
        # value checks.
        StmtBlock([ StmtExpr(voidCast(callMaybeDestroy(rhstypevar))),
                    StmtBreak() ])
    )
    opeqswitch.addcase(
        DefaultLabel(),
        StmtBlock([ _logicError('unreached'), StmtBreak() ]))
    opeq.addstmts([
        StmtExpr(callAssertSanity(uvar=rhsvar)),
        StmtDecl(Decl(typetype, rhstypevar.name), init=ud.callType(rhsvar)),
        opeqswitch,
        StmtExpr(ExprAssn(ExprSelect(rhsvar, '.', mtypevar), tnonevar)),
        StmtExpr(ExprAssn(mtypevar, rhstypevar)),
        StmtReturn(ExprDeref(ExprVar.THIS))
    ])
    cls.addstmts([ opeq, Whitespace.NL ])

    # bool operator==(const T&)
    for c in ud.components:
        opeqeq = MethodDefn(MethodDecl(
            'operator==',
            params=[ Decl(c.inType(), rhsvar.name) ],
            ret=Type.BOOL,
            const=1))
        opeqeq.addstmt(StmtReturn(ExprBinary(
            ExprCall(ExprVar(c.getTypeName())), '==', rhsvar)))
        cls.addstmts([ opeqeq, Whitespace.NL ])

    # bool operator==(const Union&)
    opeqeq = MethodDefn(MethodDecl(
        'operator==',
        params=[ Decl(inClsType, rhsvar.name) ],
        ret=Type.BOOL,
        const=1))
    iftypesmismatch = StmtIf(ExprBinary(ud.callType(), '!=',
                                        ud.callType(rhsvar)))
    iftypesmismatch.addifstmt(StmtReturn.FALSE)
    opeqeq.addstmts([ iftypesmismatch, Whitespace.NL ])

    opeqeqswitch = StmtSwitch(ud.callType())
    for c in ud.components:
        case = StmtBlock()
        case.addstmt(StmtReturn(ExprBinary(
            ExprCall(ExprVar(c.getTypeName())), '==',
            ExprCall(ExprSelect(rhsvar, '.', c.getTypeName())))))
        opeqeqswitch.addcase(CaseLabel(c.enum()), case)
    opeqeqswitch.addcase(
        DefaultLabel(),
        StmtBlock([ _logicError('unreached'),
                    StmtReturn.FALSE ]))
    opeqeq.addstmt(opeqeqswitch)

    cls.addstmts([ opeqeq, Whitespace.NL ])

    # accessors for each type: operator T&, operator const T&,
    # T& get(), const T& get()
    for c in ud.components:
        getValueVar = ExprVar(c.getTypeName())
        getConstValueVar = ExprVar(c.getConstTypeName())

        getvalue = MethodDefn(MethodDecl(getValueVar.name,
                                         ret=c.refType(),
                                         force_inline=1))
        getvalue.addstmts([
            StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())),
            StmtReturn(ExprDeref(c.callGetPtr()))
        ])

        getconstvalue = MethodDefn(MethodDecl(
            getConstValueVar.name, ret=c.constRefType(),
            const=1, force_inline=1))
        getconstvalue.addstmts([
            StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())),
            StmtReturn(c.getConstValue())
        ])

        readvalue = MethodDefn(MethodDecl(
            'get', ret=Type.VOID, const=1,
            params=[Decl(c.ptrToType(), 'aOutValue')]))
        readvalue.addstmts([
            StmtExpr(ExprAssn(ExprDeref(ExprVar('aOutValue')),
                              ExprCall(getConstValueVar)))
        ])

        optype = MethodDefn(MethodDecl('', typeop=c.refType(), force_inline=1))
        optype.addstmt(StmtReturn(ExprCall(getValueVar)))
        opconsttype = MethodDefn(MethodDecl(
            '', const=1, typeop=c.constRefType(), force_inline=1))
        opconsttype.addstmt(StmtReturn(ExprCall(getConstValueVar)))

        cls.addstmts([ getvalue, getconstvalue, readvalue,
                       optype, opconsttype,
                       Whitespace.NL ])

    # private vars
    cls.addstmts([
        Label.PRIVATE,
        StmtDecl(Decl(valuetype, mvaluevar.name)),
        StmtDecl(Decl(typetype, mtypevar.name))
    ])

    return forwarddeclstmts, fulldecltypes, cls

##-----------------------------------------------------------------------------

class _FindFriends(ipdl.ast.Visitor):
    def __init__(self):
        self.mytype = None              # ProtocolType
        self.vtype = None               # ProtocolType
        self.friends = set()            # set<ProtocolType>

    def findFriends(self, ptype):
        self.mytype = ptype
        for toplvl in ptype.toplevels():
            self.walkDownTheProtocolTree(toplvl);
        return self.friends

    # TODO could make this into a _iterProtocolTreeHelper ...
    def walkDownTheProtocolTree(self, ptype):
        if ptype != self.mytype:
            # don't want to |friend| ourself!
            self.visit(ptype)
        for mtype in ptype.manages:
            if mtype is not ptype:
                self.walkDownTheProtocolTree(mtype)

    def visit(self, ptype):
        # |vtype| is the type currently being visited
        savedptype = self.vtype
        self.vtype = ptype
        ptype._ast.accept(self)
        self.vtype = savedptype

    def visitMessageDecl(self, md):
        for it in self.iterActorParams(md):
            if it.protocol == self.mytype:
                self.friends.add(self.vtype)

    def iterActorParams(self, md):
        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 _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.hdrfile = None
        self.cppfile = None
        self.ns = None
        self.cls = None
        self.includedActorTypedefs = [ ]
        self.protocolCxxIncludes = [ ]
        self.actorForwardDecls = [ ]
        self.usingDecls = [ ]
        self.externalIncludes = set()
        self.nonForwardDeclaredHeaders = set()

    def lower(self, tu, clsname, cxxHeaderFile, cxxFile):
        self.clsname = clsname
        self.hdrfile = cxxHeaderFile
        self.cppfile = cxxFile
        tu.accept(self)

    def standardTypedefs(self):
        return [
            Typedef(Type('mozilla::ipc::IProtocol'), 'IProtocol'),
            Typedef(Type('IPC::Message'), 'Message'),
            Typedef(Type('base::ProcessHandle'), 'ProcessHandle'),
            Typedef(Type('mozilla::ipc::MessageChannel'), 'MessageChannel'),
            Typedef(Type('mozilla::ipc::SharedMemory'), 'SharedMemory'),
        ]


    def visitTranslationUnit(self, tu):
        self.protocol = tu.protocol

        hf = self.hdrfile
        cf = self.cppfile

        # make the C++ header
        hf.addthings(
            [ _DISCLAIMER ]
            + _includeGuardStart(hf)
            +[
                Whitespace.NL,
                CppDirective(
                    'include',
                    '"'+ _protocolHeaderName(tu.protocol) +'.h"')
            ])

        for inc in tu.includes:
            inc.accept(self)
        for inc in tu.cxxIncludes:
            inc.accept(self)

        for using in tu.using:
            using.accept(self)

        # this generates the actor's full impl in self.cls
        tu.protocol.accept(self)

        clsdecl, clsdefn = _splitClassDeclDefn(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 setToIncludes(s):
            return [ CppDirective('include', '"%s"' % i)
                     for i in sorted(iter(s)) ]

        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

        if len(self.nonForwardDeclaredHeaders) != 0:
            self.hdrfile.addthings(
                [ Whitespace('// Headers for things that cannot be forward declared'),
                  Whitespace.NL ]
                + setToIncludes(self.nonForwardDeclaredHeaders)
                + [ Whitespace.NL ]
            )
        self.hdrfile.addthings(self.actorForwardDecls)
        self.hdrfile.addthings(self.usingDecls)

        hdrns = makeNamespace(self.protocol, self.hdrfile)
        hdrns.addstmts([
            Whitespace.NL,
            Whitespace.NL,
            clsdecl,
            Whitespace.NL,
            Whitespace.NL
        ])

        actortype = ActorType(tu.protocol.decl.type)
        traitsdecl, traitsdefn = _ParamTraits.actorPickling(actortype, self.side)

        self.hdrfile.addthings(
            ([
                traitsdecl,
                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"') ]
            + setToIncludes(self.externalIncludes))

        cppheaders = [CppDirective('include', '"%s"' % filename)
                      for filename in ipdl.builtin.CppIncludes]

        cf.addthings((
            [ Whitespace.NL ]
            + [ CppDirective(
                'include',
                '"%s.h"' % (inc)) for inc in self.protocolCxxIncludes ]
            + [ Whitespace.NL ]
            + cppheaders
            + [ Whitespace.NL ]))

        cppns = makeNamespace(self.protocol, cf)
        cppns.addstmts([
            Whitespace.NL,
            Whitespace.NL,
            clsdefn,
            Whitespace.NL,
            Whitespace.NL
        ])

        cf.addthing(traitsdefn)

    def visitUsingStmt(self, using):
        if using.header is None:
            return

        if using.canBeForwardDeclared():
            spec = using.type.spec

            self.usingDecls.extend([
                _makeForwardDeclForQClass(spec.baseid, spec.quals,
                                          cls=using.isClass(),
                                          struct=using.isStruct()),
                Whitespace.NL
            ])
            self.externalIncludes.add(using.header)
        else:
            self.nonForwardDeclaredHeaders.add(using.header)

    def visitCxxInclude(self, inc):
        self.nonForwardDeclaredHeaders.add(inc.file)

    def visitInclude(self, inc):
        ip = inc.tu.protocol
        if not ip:
            return

        self.actorForwardDecls.extend([
            _makeForwardDeclForActor(ip.decl.type, self.side),
            _makeForwardDeclForActor(ip.decl.type, _otherSide(self.side)),
            Whitespace.NL
        ])
        self.protocolCxxIncludes.append(_protocolHeaderName(ip, self.side))

        if ip.decl.fullname is not None:
            self.includedActorTypedefs.append(Typedef(
                Type(_actorName(ip.decl.fullname, self.side.title())),
                _actorName(ip.decl.shortname, self.side.title())))

            self.includedActorTypedefs.append(Typedef(
                Type(_actorName(ip.decl.fullname, _otherSide(self.side).title())),
                _actorName(ip.decl.shortname, _otherSide(self.side).title())))


    def visitProtocol(self, p):
        self.hdrfile.addthings([
            CppDirective('ifdef', 'DEBUG'),
            CppDirective('include', '"prenv.h"'),
            CppDirective('endif', '// DEBUG')
        ])

        self.protocol = p
        ptype = p.decl.type
        toplevel = p.decl.type.toplevel()

        # FIXME: all actors impl Iface for now
        if ptype.isManager() or 1:
            self.hdrfile.addthing(CppDirective('include', '"base/id_map.h"'))

        self.hdrfile.addthings([
            CppDirective('include', '"mozilla/ipc/MessageChannel.h"'),
            Whitespace.NL ])
        self.hdrfile.addthings([
            CppDirective('include', '"mozilla/ipc/ProtocolUtils.h"'),
            Whitespace.NL ])

        hasAsyncReturns = False
        for md in p.messageDecls:
            if md.hasAsyncReturns():
                hasAsyncReturns = True
                break

        inherits = []
        if ptype.isToplevel():
            inherits.append(Inherit(p.openedProtocolInterfaceType(),
                                    viz='public'))
        else:
            inherits.append(Inherit(p.managerInterfaceType(), viz='public'))

        if hasAsyncReturns:
            inherits.append(Inherit(Type('SupportsWeakPtr', T=ExprVar(self.clsname)),
                                    viz='public'))
            self.hdrfile.addthing(CppDirective('include', '"mozilla/WeakPtr.h"'))

        if ptype.isToplevel() and self.side is 'parent':
            self.hdrfile.addthings([
                    _makeForwardDeclForQClass('nsIFile', []),
                    Whitespace.NL
                    ])

        self.cls = Class(
            self.clsname,
            inherits=inherits,
            abstract=True)

        if hasAsyncReturns:
            self.cls.addstmts([
                Label.PUBLIC,
                Whitespace('', indent=1),
                ExprCall(ExprVar('MOZ_DECLARE_WEAKREFERENCE_TYPENAME'),
                         [ ExprVar(self.clsname) ]),
                Whitespace.NL
            ])

        self.cls.addstmt(Label.PRIVATE)
        friends = _FindFriends().findFriends(ptype)
        if ptype.isManaged():
            friends.update(ptype.managers)

        # |friend| managed actors so that they can call our Dealloc*()
        friends.update(ptype.manages)

        # don't friend ourself if we're a self-managed protocol
        friends.discard(ptype)

        for friend in friends:
            self.actorForwardDecls.extend([
                _makeForwardDeclForActor(friend, self.prettyside),
                Whitespace.NL
            ])
            self.cls.addstmt(FriendClassDecl(_actorName(friend.fullname(),
                                                        self.prettyside)))

        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)

        if hasAsyncReturns:
            self.cls.addstmt(Label.PUBLIC)
            for md in p.messageDecls:
                if self.sendsMessage(md) and md.hasAsyncReturns():
                    self.cls.addstmt(
                        Typedef(_makePromise(md.returns, self.side),
                                md.promiseName()))
                if self.receivesMessage(md) and md.hasAsyncReturns():
                    self.cls.addstmt(
                        Typedef(_makeResolver(md.returns, self.side),
                                md.resolverName()))
            self.cls.addstmt(Whitespace.NL)

        self.cls.addstmt(Label.PROTECTED)
        # interface methods that the concrete subclass has to impl
        for md in p.messageDecls:
            isctor, isdtor = md.decl.type.isCtor(), md.decl.type.isDtor()

            if self.receivesMessage(md):
                # generate Recv/Answer* interface
                implicit = (not isdtor)
                returnsems = 'resolver' if md.decl.type.isAsync() else 'out'
                recvDecl = MethodDecl(
                    md.recvMethod().name,
                    params=md.makeCxxParams(paramsems='move', returnsems=returnsems,
                                            side=self.side, implicit=implicit),
                    ret=Type('mozilla::ipc::IPCResult'),
                    methodspec=MethodSpec.VIRTUAL)

                if isctor or isdtor:
                    defaultRecv = MethodDefn(recvDecl)
                    defaultRecv.addstmt(StmtReturn(ExprCall(ExprVar('IPC_OK'))))
                    self.cls.addstmt(defaultRecv)
                else:
                    recvDecl.methodspec = MethodSpec.PURE
                    self.cls.addstmt(StmtDecl(recvDecl))

        for md in p.messageDecls:
            managed = md.decl.type.constructedType()
            if not ptype.isManagerOf(managed) or md.decl.type.isDtor():
                continue

            # add the Alloc/Dealloc interface for managed actors
            actortype = md.actorDecl().bareType(self.side)

            self.cls.addstmt(StmtDecl(MethodDecl(
                _allocMethod(managed, self.side).name,
                params=md.makeCxxParams(side=self.side, implicit=0),
                ret=actortype, methodspec=MethodSpec.PURE)))

            self.cls.addstmt(StmtDecl(MethodDecl(
                _deallocMethod(managed, self.side).name,
                params=[ Decl(actortype, 'aActor') ],
                ret=Type.BOOL, methodspec=MethodSpec.PURE)))

        # ActorDestroy() method; default is no-op
        if self.side == 'parent':
            methodspec = MethodSpec.PURE
        else:
            methodspec = MethodSpec.VIRTUAL

        self.cls.addstmts([
            Whitespace.NL,
            MethodDefn(MethodDecl(
                _destroyMethod().name,
                params=[ Decl(_DestroyReason.Type(), 'aWhy') ],
                ret=Type.VOID, methodspec=methodspec)),
            Whitespace.NL
        ])

        if ptype.isToplevel():
            # void ProcessingError(code); default to no-op
            processingerror = MethodDefn(
                MethodDecl(p.processingErrorVar().name,
                           params=[ Param(_Result.Type(), 'aCode'),
                                    Param(Type('char', const=1, ptr=1), 'aReason') ],
                           methodspec=MethodSpec.OVERRIDE))

            # bool ShouldContinueFromReplyTimeout(); default to |true|
            shouldcontinue = MethodDefn(
                MethodDecl(p.shouldContinueFromTimeoutVar().name,
                           ret=Type.BOOL, methodspec=MethodSpec.OVERRIDE))
            shouldcontinue.addstmt(StmtReturn.TRUE)

            # void Entered*()/Exited*(); default to no-op
            entered = MethodDefn(
                MethodDecl(p.enteredCxxStackVar().name, methodspec=MethodSpec.OVERRIDE))
            exited = MethodDefn(
                MethodDecl(p.exitedCxxStackVar().name, methodspec=MethodSpec.OVERRIDE))
            enteredcall = MethodDefn(
                MethodDecl(p.enteredCallVar().name, methodspec=MethodSpec.OVERRIDE))
            exitedcall = MethodDefn(
                MethodDecl(p.exitedCallVar().name, methodspec=MethodSpec.OVERRIDE))

            self.cls.addstmts([ processingerror,
                                shouldcontinue,
                                entered, exited,
                                enteredcall, exitedcall,
                                Whitespace.NL ])

        self.cls.addstmts((
            [ Label.PUBLIC ]
            + self.standardTypedefs()
            + [ Whitespace.NL ]
        ))

        self.cls.addstmt(Label.PUBLIC)
        # Actor()
        ctor = ConstructorDefn(ConstructorDecl(self.clsname))
        side = ExprVar('mozilla::ipc::' + self.side.title() + 'Side')
        if ptype.isToplevel():
            name = ExprLiteral.String(_actorName(p.name, self.side))
            ctor.memberinits = [
                ExprMemberInit(ExprVar('mozilla::ipc::IToplevelProtocol'),
                               [name, _protocolId(ptype), side]),
                ExprMemberInit(p.stateVar(),
                               [ p.startState() ])
            ]
        else:
            ctor.memberinits = [
                ExprMemberInit(ExprVar('mozilla::ipc::IProtocol'), [side]),
                ExprMemberInit(p.stateVar(),
                               [ p.deadState() ])
            ]

        ctor.addstmt(StmtExpr(ExprCall(ExprVar('MOZ_COUNT_CTOR'),
                                       [ ExprVar(self.clsname) ])))
        self.cls.addstmts([ ctor, Whitespace.NL ])

        # ~Actor()
        dtor = DestructorDefn(
            DestructorDecl(self.clsname, methodspec=MethodSpec.VIRTUAL))
        dtor.addstmt(StmtExpr(ExprCall(ExprVar('MOZ_COUNT_DTOR'),
                                               [ ExprVar(self.clsname) ])))

        self.cls.addstmts([ dtor, Whitespace.NL ])

        if not ptype.isToplevel():
            if 1 == len(p.managers):
                ## manager() const
                managertype = p.managerActorType(self.side, ptr=1)
                managermeth = MethodDefn(MethodDecl(
                    'Manager', ret=managertype, const=1))
                managerexp = ExprCall(ExprVar('IProtocol::Manager'), args=[])
                managermeth.addstmt(StmtReturn(
                    ExprCast(managerexp, managertype, static=1)))

                self.cls.addstmts([ managermeth, Whitespace.NL ])

        def actorFromIter(itervar):
            return ExprCall(ExprSelect(ExprCall(ExprSelect(itervar, '.', 'Get')),
                                       '->', 'GetKey'))
        def forLoopOverHashtable(hashtable, itervar, const=False):
            return StmtFor(
                init=Param(Type.AUTO, itervar.name,
                           ExprCall(ExprSelect(hashtable, '.', 'ConstIter' if const else 'Iter'))),
                cond=ExprNot(ExprCall(ExprSelect(itervar, '.', 'Done'))),
                update=ExprCall(ExprSelect(itervar, '.', 'Next')))

        ## Managed[T](Array& inout) const
        ## const Array<T>& Managed() const
        for managed in ptype.manages:
            arrvar = ExprVar('aArr')
            meth = MethodDefn(MethodDecl(
                p.managedMethod(managed, self.side).name,
                params=[ Decl(_cxxArrayType(p.managedCxxType(managed, self.side), ref=1),
                              arrvar.name) ],
                const=1))
            meth.addstmt(StmtExpr(
                ExprCall(ExprSelect(p.managedVar(managed, self.side),
                                    '.', 'ToArray'),
                         args=[ arrvar ])))

            refmeth = MethodDefn(MethodDecl(
                p.managedMethod(managed, self.side).name,
                params=[ ],
                ret=p.managedVarType(managed, self.side, const=1, ref=1),
                const=1))
            refmeth.addstmt(StmtReturn(p.managedVar(managed, self.side)))

            self.cls.addstmts([ meth, refmeth, Whitespace.NL ])

        ## OnMessageReceived()/OnCallReceived()

        # save these away for use in message handler case stmts
        msgvar = ExprVar('msg__')
        self.msgvar = msgvar
        replyvar = ExprVar('reply__')
        self.replyvar = replyvar
        itervar = ExprVar('iter__')
        self.itervar = itervar
        var = ExprVar('v__')
        self.var = var
        # for ctor recv cases, we can't read the actor ID into a PFoo*
        # because it doesn't exist on this side yet.  Use a "special"
        # actor handle instead
        handlevar = ExprVar('handle__')
        self.handlevar = handlevar

        msgtype = ExprCall(ExprSelect(msgvar, '.', 'type'), [ ])
        self.asyncSwitch = StmtSwitch(msgtype)
        self.syncSwitch = None
        self.interruptSwitch = None
        if toplevel.isSync() or toplevel.isInterrupt():
            self.syncSwitch = StmtSwitch(msgtype)
            if toplevel.isInterrupt():
                self.interruptSwitch = StmtSwitch(msgtype)

        # implement Send*() methods and add dispatcher cases to
        # message switch()es
        for md in p.messageDecls:
            self.visitMessageDecl(md)

        # add default cases
        default = StmtBlock()
        default.addstmt(StmtReturn(_Result.NotKnown))
        self.asyncSwitch.addcase(DefaultLabel(), default)
        if toplevel.isSync() or toplevel.isInterrupt():
            self.syncSwitch.addcase(DefaultLabel(), default)
            if toplevel.isInterrupt():
                self.interruptSwitch.addcase(DefaultLabel(), default)

        # FIXME/bug 535053: only manager protocols and non-manager
        # protocols with union types need Lookup().  we'll give it to
        # all for the time being (simpler)
        if 1 or ptype.isManager():
            self.cls.addstmts(self.implementManagerIface())

        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, methodspec=MethodSpec.OVERRIDE,
                                           params=params, ret=_Result.Type()))

            if not switch:
              crash = StmtExpr(ExprCall(ExprVar('MOZ_ASSERT_UNREACHABLE'),
                               args=[ExprLiteral.String('message protocol not supported')]))
              method.addstmts([crash, StmtReturn(_Result.NotKnown)])
              return method

            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))
                routedvar = ExprVar('routed__')
                routeif.ifb.addstmt(
                    StmtDecl(Decl(Type('IProtocol', ptr=1),
                                  routedvar.name),
                             _lookupListener(routevar)))
                failif = StmtIf(ExprPrefixUnop(routedvar, '!'))
                failif.ifb.addstmt(StmtReturn(_Result.RouteError))
                routeif.ifb.addstmt(failif)

                routeif.ifb.addstmt(StmtReturn(ExprCall(
                    ExprSelect(routedvar, '->', name),
                    args=[ ExprVar(p.name) for p in params ])))

                method.addstmts([ routedecl, routeif, Whitespace.NL ])

            # in the event of an Interrupt delete message, we want to loudly complain about
            # messages that are received that are not a reply to the original message
            if ptype.hasReentrantDelete:
                msgVar = ExprVar(params[0].name)
                ifdying = StmtIf(ExprBinary(
                    ExprBinary(ExprVar('mState'), '==', self.protocol.dyingState()),
                    '&&',
                    ExprBinary(
                        ExprBinary(ExprCall(ExprSelect(msgVar, '.', 'is_reply')), '!=', ExprLiteral.TRUE),
                        '||',
                        ExprBinary(ExprCall(ExprSelect(msgVar, '.', 'is_interrupt')), '!=', ExprLiteral.TRUE))))
                ifdying.addifstmts([_fatalError('incoming message racing with actor deletion'),
                                    StmtReturn(_Result.Processed)])
                method.addstmt(ifdying)

            # bug 509581: don't generate the switch stmt if there
            # is only the default case; MSVC doesn't like that
            if switch.nr_cases > 1:
                method.addstmt(switch)
            else:
                method.addstmt(StmtReturn(_Result.NotKnown))

            return method

        dispatches = (ptype.isToplevel() and ptype.isManager())
        self.cls.addstmts([
            makeHandlerMethod('OnMessageReceived', self.asyncSwitch,
                              hasReply=0, dispatches=dispatches),
            Whitespace.NL
        ])
        self.cls.addstmts([
            makeHandlerMethod('OnMessageReceived', self.syncSwitch,
                              hasReply=1, dispatches=dispatches),
            Whitespace.NL
        ])
        self.cls.addstmts([
            makeHandlerMethod('OnCallReceived', self.interruptSwitch,
                              hasReply=1, dispatches=dispatches),
            Whitespace.NL
        ])

        destroysubtreevar = ExprVar('DestroySubtree')
        deallocsubtreevar = ExprVar('DeallocSubtree')
        deallocshmemvar = ExprVar('DeallocShmems')
        deallocselfvar = ExprVar('Dealloc' + _actorName(ptype.name(), self.side))

        # int32_t GetProtocolTypeId() { return PFoo; }
        gettypetag = MethodDefn(
            MethodDecl('GetProtocolTypeId', ret=_actorTypeTagType(),
                       methodspec=MethodSpec.OVERRIDE))
        gettypetag.addstmt(StmtReturn(_protocolId(ptype)))
        self.cls.addstmts([ gettypetag, Whitespace.NL ])

        if ptype.isToplevel():
            # OnChannelClose()
            onclose = MethodDefn(MethodDecl('OnChannelClose',
                                            methodspec=MethodSpec.OVERRIDE))
            onclose.addstmts([
                StmtExpr(ExprCall(destroysubtreevar,
                                  args=[ _DestroyReason.NormalShutdown ])),
                StmtExpr(ExprCall(deallocsubtreevar)),
                StmtExpr(ExprCall(deallocshmemvar)),
                StmtExpr(ExprCall(deallocselfvar))
            ])
            self.cls.addstmts([ onclose, Whitespace.NL ])

            # OnChannelError()
            onerror = MethodDefn(MethodDecl('OnChannelError',
                                            methodspec=MethodSpec.OVERRIDE))
            onerror.addstmts([
                StmtExpr(ExprCall(destroysubtreevar,
                                  args=[ _DestroyReason.AbnormalShutdown ])),
                StmtExpr(ExprCall(deallocsubtreevar)),
                StmtExpr(ExprCall(deallocshmemvar)),
                StmtExpr(ExprCall(deallocselfvar))
            ])
            self.cls.addstmts([ onerror, Whitespace.NL ])

        if (ptype.isToplevel() and ptype.isInterrupt()):

            processnative = MethodDefn(
                MethodDecl('ProcessNativeEventsInInterruptCall', ret=Type.VOID))

            processnative.addstmts([
                    CppDirective('ifdef', 'OS_WIN'),
                    StmtExpr(ExprCall(
                        ExprSelect(ExprCall(ExprSelect(ExprCall(ExprVar('DowncastState')),
                                                       '->',
                                                       'GetIPCChannel')),
                                   '->',
                                   'ProcessNativeEventsInInterruptCall'))),
                    CppDirective('else'),
                    _fatalError('This method is Windows-only'),
                    CppDirective('endif'),
                    ])

            self.cls.addstmts([ processnative, Whitespace.NL ])

        ## private methods
        self.cls.addstmt(Label.PRIVATE)

        ## DestroySubtree(bool normal)
        whyvar = ExprVar('why')
        subtreewhyvar = ExprVar('subtreewhy')
        kidsvar = ExprVar('kids')
        ivar = ExprVar('i')
        itervar = ExprVar('iter')
        ithkid = ExprIndex(kidsvar, ivar)

        destroysubtree = MethodDefn(MethodDecl(
            destroysubtreevar.name,
            params=[ Decl(_DestroyReason.Type(), whyvar.name) ]))

        if ptype.isManaged():
            destroysubtree.addstmt(
                Whitespace('// Unregister from our manager.\n', indent=1))
            destroysubtree.addstmts(self.unregisterActor())
            destroysubtree.addstmt(Whitespace.NL)

        if ptype.isManager():
            # only declare this for managers to avoid unused var warnings
            destroysubtree.addstmts([
                StmtDecl(
                    Decl(_DestroyReason.Type(), subtreewhyvar.name),
                    init=ExprConditional(
                        ExprBinary(
                            ExprBinary(whyvar, '==',
                                       _DestroyReason.Deletion),
                            '||',
                            ExprBinary(whyvar, '==',
                                       _DestroyReason.FailedConstructor)),
                        _DestroyReason.AncestorDeletion, whyvar)),
                Whitespace.NL
            ])

        for managed in ptype.manages:
            managedVar = p.managedVar(managed, self.side)
            lenvar = ExprVar('len')
            kidvar = ExprVar('kid')

            foreachdestroy = StmtRangedFor(kidvar, kidsvar)

            foreachdestroy.addstmt(
                Whitespace('// Guarding against a child removing a sibling from the list during the iteration.\n', indent=1))
            ifhas = StmtIf(_callHasManagedActor(managedVar, kidvar))
            ifhas.addifstmt(StmtExpr(ExprCall(
                ExprSelect(kidvar, '->', destroysubtreevar.name),
                args=[ subtreewhyvar ])))
            foreachdestroy.addstmt(ifhas)

            block = StmtBlock()
            block.addstmts([
                Whitespace(
                    '// Recursively shutting down %s kids\n'% (managed.name()),
                    indent=1),
                StmtDecl(
                    Decl(_cxxArrayType(p.managedCxxType(managed, self.side)), kidsvar.name)),
                Whitespace(
                    '// Accumulate kids into a stable structure to iterate over\n',
                    indent=1),
                StmtExpr(ExprCall(p.managedMethod(managed, self.side),
                                  args=[ kidsvar ])),
                foreachdestroy,
            ])
            destroysubtree.addstmt(block)

        if len(ptype.manages):
            destroysubtree.addstmt(Whitespace.NL)

        # Reject pending responses for actor before calling ActorDestroy().
        rejectPendingResponsesMethod = ExprSelect(self.protocol.callGetChannel(),
                                                  '->',
                                                  'RejectPendingResponsesForActor')
        destroysubtree.addstmts([ Whitespace('// Reject owning pending responses.\n',
                                             indent=1),
                                  StmtExpr(ExprCall(rejectPendingResponsesMethod,
                                                    args=[ ExprVar('this') ])),
                                  Whitespace.NL
                                 ])

        destroysubtree.addstmts([ Whitespace('// Finally, destroy "us".\n',
                                             indent=1),
                                  StmtExpr(ExprCall(_destroyMethod(),
                                                    args=[ whyvar ]))
                                ])

        self.cls.addstmts([ destroysubtree, Whitespace.NL ])

        ## DeallocSubtree()
        deallocsubtree = MethodDefn(MethodDecl(deallocsubtreevar.name))
        for managed in ptype.manages:
            managedVar = p.managedVar(managed, self.side)

            foreachrecurse = forLoopOverHashtable(managedVar, itervar)
            foreachrecurse.addstmt(StmtExpr(ExprCall(
                ExprSelect(actorFromIter(itervar), '->', deallocsubtreevar.name))))

            foreachdealloc = forLoopOverHashtable(managedVar, itervar)
            foreachdealloc.addstmts([
                StmtExpr(ExprCall(_deallocMethod(managed, self.side),
                                  args=[ actorFromIter(itervar) ]))
            ])

            block = StmtBlock()
            block.addstmts([
                Whitespace(
                    '// Recursively deleting %s kids\n'% (managed.name()),
                    indent=1),
                foreachrecurse,
                Whitespace.NL,
                foreachdealloc,
                StmtExpr(_callClearManagedActors(managedVar)),

            ])
            deallocsubtree.addstmt(block)
        # don't delete outselves: either the manager will do it, or
        # we're toplevel
        self.cls.addstmts([ deallocsubtree, Whitespace.NL ])

        if ptype.isToplevel():
            deallocself = MethodDefn(MethodDecl(deallocselfvar.name, methodspec=MethodSpec.VIRTUAL))
            self.cls.addstmts([ deallocself, Whitespace.NL ])

        ## private members
        self.cls.addstmt(StmtDecl(Decl(self.protocol.fqStateType(), p.stateVar().name)))

        for managed in ptype.manages:
            self.cls.addstmts([
                StmtDecl(Decl(
                    p.managedVarType(managed, self.side),
                    p.managedVar(managed, self.side).name)) ])

    def implementManagerIface(self):
        p = self.protocol
        routedvar = ExprVar('aRouted')
        idvar = ExprVar('aId')
        shmemvar = ExprVar('shmem')
        rawvar = ExprVar('segment')
        sizevar = ExprVar('aSize')
        typevar = ExprVar('aType')
        unsafevar = ExprVar('aUnsafe')
        protocolbase = Type('IProtocol', ptr=1)
        sourcevar = ExprVar('aSource')
        ivar = ExprVar('i')
        kidsvar = ExprVar('kids')
        ithkid = ExprIndex(kidsvar, ivar)

        methods = []

        if p.decl.type.isToplevel():
            tmpvar = ExprVar('tmp')

            # "private" message that passes shmem mappings from one process
            # to the other
            if p.subtreeUsesShmem():
                self.asyncSwitch.addcase(
                    CaseLabel('SHMEM_CREATED_MESSAGE_TYPE'),
                    self.genShmemCreatedHandler())
                self.asyncSwitch.addcase(
                    CaseLabel('SHMEM_DESTROYED_MESSAGE_TYPE'),
                    self.genShmemDestroyedHandler())
            else:
                abort = StmtBlock()
                abort.addstmts([
                    _fatalError('this protocol tree does not use shmem'),
                    StmtReturn(_Result.NotKnown)
                ])
                self.asyncSwitch.addcase(
                    CaseLabel('SHMEM_CREATED_MESSAGE_TYPE'), abort)
                self.asyncSwitch.addcase(
                    CaseLabel('SHMEM_DESTROYED_MESSAGE_TYPE'), abort)

        othervar = ExprVar('other')
        managertype = Type(_actorName(p.name, self.side), ptr=1)

        # Keep track of types created with an INOUT ctor. We need to call
        # Register() or RegisterID() for them depending on the side the managee
        # is created.
        inoutCtorTypes = []
        for msg in p.messageDecls:
            msgtype = msg.decl.type
            if msgtype.isCtor() and msgtype.isInout():
                inoutCtorTypes.append(msgtype.constructedType())

        # all protocols share the "same" RemoveManagee() implementation
        pvar = ExprVar('aProtocolId')
        listenervar = ExprVar('aListener')
        removemanagee = MethodDefn(MethodDecl(
            p.removeManageeMethod().name,
            params=[ Decl(_protocolIdType(), pvar.name),
                     Decl(protocolbase, listenervar.name) ],
            methodspec=MethodSpec.OVERRIDE))

        if not len(p.managesStmts):
            removemanagee.addstmts([ _fatalError('unreached'), StmtReturn() ])
        else:
            switchontype = StmtSwitch(pvar)
            for managee in p.managesStmts:
                case = StmtBlock()
                actorvar = ExprVar('actor')
                manageeipdltype = managee.decl.type
                manageecxxtype = _cxxBareType(ipdl.type.ActorType(manageeipdltype),
                                              self.side)
                manageearray = p.managedVar(manageeipdltype, self.side)
                containervar = ExprVar('container')

                case.addstmts([
                    StmtDecl(Decl(manageecxxtype, actorvar.name),
                             ExprCast(listenervar, manageecxxtype, static=1)),
                    # Use a temporary variable here so all the assertion expressions
                    # in the _abortIfFalse call below are textually identical; the
                    # linker can then merge the strings from the assertion macro(s).
                    StmtDecl(Decl(Type('auto', ref=1), containervar.name),
                             manageearray),
                    _abortIfFalse(
                        _callHasManagedActor(containervar, actorvar),
                        "actor not managed by this!"),
                    Whitespace.NL,
                    StmtExpr(_callRemoveManagedActor(containervar, actorvar)),
                    StmtExpr(ExprCall(_deallocMethod(manageeipdltype, self.side),
                                      args=[ actorvar ])),
                    StmtReturn()
                ])
                switchontype.addcase(CaseLabel(_protocolId(manageeipdltype).name),
                                     case)
            default = StmtBlock()
            default.addstmts([ _fatalError('unreached'), StmtReturn() ])
            switchontype.addcase(DefaultLabel(), default)
            removemanagee.addstmt(switchontype)

        return methods + [removemanagee, Whitespace.NL]

    def genShmemCreatedHandler(self):
        p = self.protocol
        assert p.decl.type.isToplevel()

        case = StmtBlock()

        ifstmt = StmtIf(ExprNot(ExprCall(ExprVar('ShmemCreated'), args=[self.msgvar])))
        case.addstmts([
            ifstmt,
            StmtReturn(_Result.Processed)
        ])
        ifstmt.addifstmt(StmtReturn(_Result.PayloadError))

        return case

    def genShmemDestroyedHandler(self):
        p = self.protocol
        assert p.decl.type.isToplevel()

        case = StmtBlock()

        ifstmt = StmtIf(ExprNot(ExprCall(ExprVar('ShmemDestroyed'), args=[self.msgvar])))
        case.addstmts([
            ifstmt,
            StmtReturn(_Result.Processed)
        ])
        ifstmt.addifstmt(StmtReturn(_Result.PayloadError))

        return case


    ##-------------------------------------------------------------------------
    ## The next few functions are the crux of the IPDL code generator.
    ## They generate code for all the nasty work of message
    ## serialization/deserialization and dispatching handlers for
    ## received messages.
    ##
    def visitMessageDecl(self, md):
        isctor = md.decl.type.isCtor()
        isdtor = md.decl.type.isDtor()
        decltype = md.decl.type
        sendmethod = None
        promisesendmethod = None
        helpermethod = None
        recvlbl, recvcase = None, None

        def addRecvCase(lbl, case):
            if decltype.isAsync():
                self.asyncSwitch.addcase(lbl, case)
            elif decltype.isSync():
                self.syncSwitch.addcase(lbl, case)
            elif decltype.isInterrupt():
                self.interruptSwitch.addcase(lbl, case)
            else: assert 0

        if self.sendsMessage(md):
            isasync = decltype.isAsync()

            if isctor:
                self.cls.addstmts([ self.genHelperCtor(md), Whitespace.NL ])

            if isctor and isasync:
                sendmethod, (recvlbl, recvcase) = self.genAsyncCtor(md)
            elif isctor:
                sendmethod = self.genBlockingCtorMethod(md)
            elif isdtor and isasync:
                sendmethod, (recvlbl, recvcase) = self.genAsyncDtor(md)
            elif isdtor:
                sendmethod = self.genBlockingDtorMethod(md)
            elif isasync:
                sendmethod, promisesendmethod, (recvlbl, recvcase) = \
                    self.genAsyncSendMethod(md)
            else:
                sendmethod = self.genBlockingSendMethod(md)

        # XXX figure out what to do here
        if isdtor and md.decl.type.constructedType().isToplevel():
            sendmethod = None

        if sendmethod is not None:
            self.cls.addstmts([ sendmethod, Whitespace.NL ])
        if promisesendmethod is not None:
            self.cls.addstmts([ promisesendmethod, Whitespace.NL ])
        if recvcase is not None:
            addRecvCase(recvlbl, recvcase)
            recvlbl, recvcase = None, None

        if self.receivesMessage(md):
            if isctor:
                recvlbl, recvcase = self.genCtorRecvCase(md)
            elif isdtor:
                recvlbl, recvcase = self.genDtorRecvCase(md)
            else:
                recvlbl, recvcase = self.genRecvCase(md)

            # XXX figure out what to do here
            if isdtor and md.decl.type.constructedType().isToplevel():
                return

            addRecvCase(recvlbl, recvcase)


    def genAsyncCtor(self, md):
        actor = md.actorDecl()
        method = MethodDefn(self.makeSendMethodDecl(md))
        method.addstmts(self.ctorPrologue(md) + [ Whitespace.NL ])

        msgvar, stmts = self.makeMessage(md, errfnSendCtor)
        sendok, sendstmts = self.sendAsync(md, msgvar)
        method.addstmts(
            stmts
            + self.genVerifyMessage(md.decl.type.verify, md.params,
                                    errfnSendCtor, ExprVar('msg__'))
            + sendstmts
            + self.failCtorIf(md, ExprNot(sendok))
            + [ StmtReturn(actor.var()) ])

        lbl = CaseLabel(md.pqReplyId())
        case = StmtBlock()
        case.addstmt(StmtReturn(_Result.Processed))
        # TODO not really sure what to do with async ctor "replies" yet.
        # destroy actor if there was an error?  tricky ...

        return method, (lbl, case)


    def genBlockingCtorMethod(self, md):
        actor = md.actorDecl()
        method = MethodDefn(self.makeSendMethodDecl(md))
        method.addstmts(self.ctorPrologue(md) + [ Whitespace.NL ])

        msgvar, stmts = self.makeMessage(md, errfnSendCtor)

        replyvar = self.replyvar
        sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar)
        method.addstmts(
            stmts
            + [ Whitespace.NL,
                StmtDecl(Decl(Type('Message'), replyvar.name)) ]
            + self.genVerifyMessage(md.decl.type.verify, md.params,
                                    errfnSendCtor, ExprVar('msg__'))
            + sendstmts
            + self.failCtorIf(md, ExprNot(sendok)))

        def errfnCleanupCtor(msg):
            return self.failCtorIf(md, ExprLiteral.TRUE)
        stmts = self.deserializeReply(
            md, ExprAddrOf(replyvar), self.side,
            errfnCleanupCtor, errfnSentinel(ExprLiteral.NULL))
        method.addstmts(stmts + [ StmtReturn(actor.var()) ])

        return method


    def ctorPrologue(self, md, errfn=ExprLiteral.NULL, idexpr=None):
        actordecl = md.actorDecl()
        actorvar = actordecl.var()
        actorproto = actordecl.ipdltype.protocol
        actortype = ipdl.type.ActorType(actorproto)

        if idexpr is None:
            setManagerArgs = [ExprVar.THIS]
        else:
            setManagerArgs = [ExprVar.THIS, idexpr]
        setmanager = ExprCall(ExprSelect(actorvar, '->', 'SetManagerAndRegister'),
                              args=setManagerArgs)

        return [
            self.failIfNullActor(actorvar, errfn, msg="Error constructing actor %s" % actortype.name() + self.side.capitalize()),
            StmtExpr(setmanager),
            StmtExpr(_callInsertManagedActor(
                self.protocol.managedVar(md.decl.type.constructedType(),
                                         self.side),
                actorvar)),
            StmtExpr(ExprAssn(_actorState(actorvar),
                              _startState(md.decl.type.cdtype.hasReentrantDelete)))
        ]

    def failCtorIf(self, md, cond):
        actorvar = md.actorDecl().var()
        type = md.decl.type.constructedType()
        failif = StmtIf(cond)

        if self.side=='child':
            # in the child process this should not fail
            failif.addifstmt(_fatalError('constructor for actor failed'))
        else:
            failif.addifstmts(self.destroyActor(md, actorvar,
                              why=_DestroyReason.FailedConstructor))

        failif.addifstmt(StmtReturn(ExprLiteral.NULL))
        return [ failif ]

    def genHelperCtor(self, md):
        helperdecl = self.makeSendMethodDecl(md)
        helperdecl.params = helperdecl.params[1:]
        helper = MethodDefn(helperdecl)

        callctor = self.callAllocActor(md, retsems='out', side=self.side)
        helper.addstmt(StmtReturn(ExprCall(
            ExprVar(helperdecl.name), args=[ callctor ] + callctor.args)))
        return helper


    def genAsyncDtor(self, md):
        actor = md.actorDecl()
        actorvar = actor.var()
        method = MethodDefn(self.makeDtorMethodDecl(md))

        method.addstmts(self.dtorPrologue(actorvar))

        msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar)
        sendok, sendstmts = self.sendAsync(md, msgvar, actorvar)
        method.addstmts(
            stmts
            + self.genVerifyMessage(md.decl.type.verify, md.params,
                                    errfnSendDtor, ExprVar('msg__'))
            + sendstmts
            + [ Whitespace.NL ]
            + self.dtorEpilogue(md, actor.var())
            + [ StmtReturn(sendok) ])

        lbl = CaseLabel(md.pqReplyId())
        case = StmtBlock()
        case.addstmt(StmtReturn(_Result.Processed))
        # TODO if the dtor is "inherently racy", keep the actor alive
        # until the other side acks

        return method, (lbl, case)


    def genBlockingDtorMethod(self, md):
        actor = md.actorDecl()
        actorvar = actor.var()
        method = MethodDefn(self.makeDtorMethodDecl(md))

        method.addstmts(self.dtorPrologue(actorvar))

        msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar)

        replyvar = self.replyvar
        sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar, actorvar)
        method.addstmts(
            stmts
            + self.genVerifyMessage(md.decl.type.verify, md.params,
                                    errfnSendDtor, ExprVar('msg__'))
            + [ Whitespace.NL,
                StmtDecl(Decl(Type('Message'), replyvar.name)) ]
            + sendstmts)

        destmts = self.deserializeReply(
            md, ExprAddrOf(replyvar), self.side, errfnSend,
            errfnSentinel(), actorvar)
        ifsendok = StmtIf(ExprLiteral.FALSE)
        ifsendok.addifstmts(destmts)
        ifsendok.addifstmts([ Whitespace.NL,
                              StmtExpr(ExprAssn(sendok, ExprLiteral.FALSE, '&=')) ])

        method.addstmt(ifsendok)

        if self.protocol.decl.type.hasReentrantDelete:
            method.addstmts(self.transition(md, actor.var(), reply=True, errorfn=errfnUnreachable))

        method.addstmts(
            self.dtorEpilogue(md, actor.var())
            + [ Whitespace.NL, StmtReturn(sendok) ])

        return method

    def destroyActor(self, md, actorexpr, why=_DestroyReason.Deletion):
        if md.decl.type.isCtor():
            destroyedType = md.decl.type.constructedType()
        else:
            destroyedType = self.protocol.decl.type
        managervar = ExprVar('mgr')
        return ([ StmtDecl(Decl(Type('IProtocol', ptr=1), managervar.name),
                           init=self.protocol.managerVar(actorexpr)),
                  StmtExpr(self.callActorDestroy(actorexpr, why)),
                  StmtExpr(self.callDeallocSubtree(md, actorexpr)),
                  StmtExpr(self.callRemoveActor(
                      actorexpr,
                      manager=managervar,
                      ipdltype=destroyedType))
                ])

    def dtorPrologue(self, actorexpr):
        return [ self.failIfNullActor(actorexpr), Whitespace.NL ]

    def dtorEpilogue(self, md, actorexpr):
        return self.destroyActor(md, actorexpr)

    def genRecvAsyncReplyCase(self, md):
        lbl = CaseLabel(md.pqReplyId())
        case = StmtBlock()
        resolve, reason, prologue, desrej, desstmts = self.deserializeAsyncReply(
            md, self.side, errfnRecv, errfnSentinel(_Result.ValuError))
        ifnocallback = StmtIf(ExprNot(ExprVar('callback')))
        ifnocallback.addifstmts(errfnRecv("Error unknown callback",
                                          _Result.ProcessingError))

        if len(md.returns) > 1:
            resolvetype = _tuple([d.bareType(self.side) for d in md.returns])
            resolvearg = ExprCall(ExprVar('MakeTuple'),
                                  args=[ExprMove(p.var()) for p in md.returns])
        else:
            resolvetype = md.returns[0].bareType(self.side)
            resolvearg = ExprMove(md.returns[0].var())

        untypedcallback = Type("MessageChannel::UntypedCallbackHolder")
        callbackptr = Type("MessageChannel::CallbackHolder", T=resolvetype)
        callbackptr.ptr = 1
        callback = ExprVar('callback')

        getcallback = [ Whitespace.NL,
                        # Get the untyped callback object by calling PopCallback()
                        StmtDecl(Decl(_uniqueptr(untypedcallback), 'untypedCallback'),
                                 init=ExprCall(ExprSelect(self.protocol.callGetChannel(),
                                                          '->', 'PopCallback'),
                                               args=[ self.msgvar ])),
                        # Cast the untyped callback pointer to the correct poiner type
                        StmtDecl(Decl(callbackptr, callback.name),
                                 init=ExprCast(ExprCall(ExprSelect(ExprVar('untypedCallback'),
                                                                   '.', 'get')),
                                               callbackptr,
                                               static=1)),
                        ifnocallback ]

        resolvecallback = [ StmtExpr(ExprCall(ExprSelect(callback, '->', 'Resolve'),
                                              args=[ resolvearg ])) ]
        rejectcallback = [ StmtExpr(ExprCall(ExprSelect(callback, '->', 'Reject'),
                                             args=[ reason ])) ]
        ifresolve = StmtIf(resolve)
        ifresolve.addifstmts(desstmts)
        ifresolve.addifstmts(resolvecallback)
        ifresolve.addelsestmts(desrej)
        ifresolve.addelsestmts(rejectcallback)
        case.addstmts(prologue)
        case.addstmts(getcallback)
        case.addstmt(ifresolve)
        case.addstmt(StmtReturn(_Result.Processed))
        return (lbl, case)

    def genAsyncSendMethod(self, md):
        method = MethodDefn(self.makeSendMethodDecl(md))
        msgvar, stmts = self.makeMessage(md, errfnSend)
        retvar, sendstmts = self.sendAsync(md, msgvar)

        method.addstmts(stmts
                        +[ Whitespace.NL ]
                        + self.genVerifyMessage(md.decl.type.verify, md.params,
                                                errfnSend, ExprVar('msg__'))
                        + sendstmts
                        +[ StmtReturn(retvar) ])

        # Add the promise overload if we need one.
        if md.returns:
            promisemethod = MethodDefn(self.makeSendMethodDecl(md, promise=True))
            stmts = self.sendAsyncWithPromise(md)
            promisemethod.addstmts(stmts)

            (lbl, case) = self.genRecvAsyncReplyCase(md)
        else:
            (promisemethod, lbl, case) = (None, None, None)

        return method, promisemethod, (lbl, case)

    def genBlockingSendMethod(self, md, fromActor=None):
        method = MethodDefn(self.makeSendMethodDecl(md))

        msgvar, serstmts = self.makeMessage(md, errfnSend, fromActor)
        replyvar = self.replyvar

        sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar)
        failif = StmtIf(ExprNot(sendok))
        failif.addifstmt(StmtReturn.FALSE)

        desstmts = self.deserializeReply(
            md, ExprAddrOf(replyvar), self.side, errfnSend, errfnSentinel())

        method.addstmts(
            serstmts
            + self.genVerifyMessage(md.decl.type.verify, md.params, errfnSend,
                                    ExprVar('msg__'))
            + [ Whitespace.NL,
                StmtDecl(Decl(Type('Message'), replyvar.name)) ]
            + sendstmts
            + [ failif ]
            + desstmts
            + [ Whitespace.NL,
                StmtReturn.TRUE ])

        return method


    def genCtorRecvCase(self, md):
        lbl = CaseLabel(md.pqMsgId())
        case = StmtBlock()
        actorvar = md.actorDecl().var()
        actorhandle = self.handlevar

        stmts = self.deserializeMessage(md, self.side, errfnRecv,
                                        errfnSent=errfnSentinel(_Result.ValuError))

        idvar, saveIdStmts = self.saveActorId(md)
        case.addstmts(
            stmts
            + self.transition(md, errorfn=errfnRecv)
            + [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
                for r in md.returns ]
            # alloc the actor, register it under the foreign ID
            + [ StmtExpr(ExprAssn(
                actorvar,
                self.callAllocActor(md, retsems='in', side=self.side))) ]
            + self.ctorPrologue(md, errfn=_Result.ValuError,
                                idexpr=_actorHId(actorhandle))
            + [ Whitespace.NL ]
            + saveIdStmts
            + self.invokeRecvHandler(md)
            + self.makeReply(md, errfnRecv, idvar)
            + self.genVerifyMessage(md.decl.type.verify, md.returns, errfnRecv,
                                    self.replyvar)
            + [ Whitespace.NL,
                StmtReturn(_Result.Processed) ])

        return lbl, case


    def genDtorRecvCase(self, md):
        lbl = CaseLabel(md.pqMsgId())
        case = StmtBlock()

        stmts = self.deserializeMessage(md, self.side, errfnRecv,
                                        errfnSent=errfnSentinel(_Result.ValuError))

        idvar, saveIdStmts = self.saveActorId(md)
        case.addstmts(
            stmts
            + self.transition(md, errorfn=errfnRecv)
            + [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
                for r in md.returns ]
            + self.invokeRecvHandler(md, implicit=0)
            + [ Whitespace.NL ]
            + saveIdStmts
            + self.makeReply(md, errfnRecv, routingId=idvar)
            + [ Whitespace.NL ]
            + self.genVerifyMessage(md.decl.type.verify, md.returns, errfnRecv,
                                    self.replyvar)
            + self.dtorEpilogue(md, md.actorDecl().var())
            + [ Whitespace.NL,
                StmtReturn(_Result.Processed) ])

        return lbl, case


    def genRecvCase(self, md):
        lbl = CaseLabel(md.pqMsgId())
        case = StmtBlock()

        stmts = self.deserializeMessage(md, self.side, errfn=errfnRecv,
                                        errfnSent=errfnSentinel(_Result.ValuError))

        idvar, saveIdStmts = self.saveActorId(md)
        declstmts = [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
                      for r in md.returns ]
        if md.decl.type.isAsync() and md.returns:
            declstmts = self.makeResolver(md, errfnRecv, routingId=idvar)
        case.addstmts(
            stmts
            + self.transition(md, errorfn=errfnRecv)
            + saveIdStmts
            + declstmts
            + self.invokeRecvHandler(md)
            + [ Whitespace.NL ]
            + self.makeReply(md, errfnRecv, routingId=idvar)
            + self.genVerifyMessage(md.decl.type.verify, md.returns, errfnRecv,
                                    self.replyvar)
            + [ StmtReturn(_Result.Processed) ])

        return lbl, case


    # helper methods

    def failIfNullActor(self, actorExpr, retOnNull=ExprLiteral.FALSE, msg=None):
        failif = StmtIf(ExprNot(actorExpr))
        if msg:
            failif.addifstmt(_printWarningMessage(msg))
        failif.addifstmt(StmtReturn(retOnNull))
        return failif

    def unregisterActor(self):
        return [ StmtExpr(ExprCall(self.protocol.unregisterMethod(),
                                   args=[ _actorId() ])) ]

    def makeMessage(self, md, errfn, fromActor=None):
        msgvar = self.msgvar
        routingId = self.protocol.routingId(fromActor)
        this = ExprVar.THIS
        if md.decl.type.isDtor():  this = md.actorDecl().var()

        stmts = ([ StmtDecl(Decl(Type('IPC::Message', ptr=1), msgvar.name),
                            init=ExprCall(ExprVar(md.pqMsgCtorFunc()),
                                          args=[ routingId ])) ]
                 + [ Whitespace.NL ]
                 + [ _ParamTraits.checkedWrite(p.ipdltype, p.var(), msgvar,
                                               sentinelKey=p.name, actor=this)
                     for p in md.params ]
                 + [ Whitespace.NL ]
                 + self.setMessageFlags(md, msgvar))
        return msgvar, stmts


    def makeResolver(self, md, errfn, routingId):
        if routingId is None:
            routingId = self.protocol.routingId()
        if not md.decl.type.isAsync() or not md.hasReply():
            return [ ]

        sendok = ExprVar('sendok__')
        seqno = ExprVar('seqno__')
        resolve = ExprVar('resolve__')
        reason = ExprVar('reason__')
        resolvertype = Type(md.resolverName())
        failifsendok = StmtIf(ExprNot(sendok))
        failifsendok.addifstmt(_printWarningMessage('Error sending reply'))
        sendmsg = (self.setMessageFlags(md, self.replyvar, seqno=seqno)
                   + [ self.logMessage(md, self.replyvar, 'Sending reply '),
                       StmtDecl(Decl(Type.BOOL, sendok.name),
                                init=ExprCall(
                                    ExprSelect(self.protocol.callGetChannel(),
                                               '->', 'Send'),
                                    args=[ self.replyvar ])),
                       failifsendok ])
        if len(md.returns) > 1:
            resolvedecl = Decl(_tuple([p.moveType(self.side) for p in md.returns],
                                      const=1, ref=1),
                               'aParam')
            destructexpr = ExprCall(ExprVar('Tie'),
                                    args=[ p.var() for p in md.returns ])
        else:
            resolvedecl = Decl(md.returns[0].moveType(self.side), 'aParam')
            destructexpr = md.returns[0].var()
        selfvar = ExprVar('self__')
        ifactorisdead = StmtIf(ExprNot(selfvar))
        ifactorisdead.addifstmts([_printWarningMessage("Not resolving response because actor is dead."),
                                  StmtReturn()])
        ifactorisdestroyed = StmtIf(ExprBinary(self.protocol.stateVar(), '==',
                                               self.protocol.deadState()))
        ifactorisdestroyed.addifstmts([_printWarningMessage("Not resolving response because actor is destroyed."),
                                  StmtReturn()])
        returnifactorisdead = [ ifactorisdead,
                                ifactorisdestroyed ]
        resolverfn = ExprLambda([ExprVar.THIS, selfvar, routingId, seqno],
                                 [resolvedecl])
        resolverfn.addstmts(returnifactorisdead
                            + [ StmtDecl(Decl(Type.BOOL, resolve.name),
                                         init=ExprLiteral.TRUE) ]
                            + [ StmtDecl(Decl(p.bareType(self.side), p.var().name))
                               for p in md.returns ]
                            + [ StmtExpr(ExprAssn(destructexpr, ExprMove(ExprVar('aParam')))),
                                StmtDecl(Decl(Type('IPC::Message', ptr=1), self.replyvar.name),
                                         init=ExprCall(ExprVar(md.pqReplyCtorFunc()),
                                                       args=[ routingId ])) ]
                            + [ _ParamTraits.checkedWrite(None, resolve, self.replyvar,
                                                          sentinelKey=resolve.name, actor=selfvar) ]
                            + [ _ParamTraits.checkedWrite(r.ipdltype, r.var(), self.replyvar,
                                                          sentinelKey=r.name, actor=selfvar)
                                for r in md.returns ])
        resolverfn.addstmts(sendmsg)

        makeresolver = [ Whitespace.NL,
                         StmtDecl(Decl(Type.INT32, seqno.name),
                                  init=ExprCall(ExprSelect(self.msgvar, '.', 'seqno'))),
                         StmtDecl(Decl(Type('WeakPtr', T=ExprVar(self.clsname)),
                                       selfvar.name),
                                  init=ExprVar.THIS),
                         StmtDecl(Decl(resolvertype, 'resolver'),
                                  init=resolverfn) ]
        return makeresolver

    def makeReply(self, md, errfn, routingId):
        if routingId is None:
            routingId = self.protocol.routingId()
        # TODO special cases for async ctor/dtor replies
        if not md.decl.type.hasReply():
            return [ ]
        if md.decl.type.isAsync() and md.decl.type.hasReply():
            return [ ]

        replyvar = self.replyvar
        return (
            [ StmtExpr(ExprAssn(
                replyvar, ExprCall(ExprVar(md.pqReplyCtorFunc()), args=[ routingId ]))),
              Whitespace.NL ]
            + [ _ParamTraits.checkedWrite(r.ipdltype, r.var(), replyvar,
                                          sentinelKey=r.name, actor=ExprVar.THIS)
                for r in md.returns ]
            + self.setMessageFlags(md, replyvar)
            + [ self.logMessage(md, replyvar, 'Sending reply ') ])

    def genVerifyMessage(self, verify, params, errfn, msgsrcVar):
        stmts = [ ]
        if not verify:
            return stmts
        if len(params) == 0:
            return stmts

        msgvar = ExprVar('msgverify__')
        side = self.side

        msgexpr = ExprAddrOf(msgvar)
        itervar = ExprVar('msgverifyIter__')
        # IPC::Message msgverify__ = Move(*(reply__)); or
        # IPC::Message msgverify__ = Move(*(msg__));
        stmts.append(StmtDecl(Decl(Type('IPC::Message', ptr=0), 'msgverify__'),
                                   init=ExprMove(ExprDeref(msgsrcVar))))

        stmts.extend((
            # PickleIterator msgverifyIter__(msgverify__);
            [ StmtDecl(Decl(_iterType(ptr=0), itervar.name),
                       initargs=[ msgvar ]) ]
            # declare varCopy for each variable to deserialize.
            + [ StmtDecl(Decl(p.bareType(side), p.var().name + 'Copy'))
                      for p in params ]
            + [ Whitespace.NL ]
            #  checked Read(&(varCopy), &(msgverify__), &(msgverifyIter__))
            + [ _ParamTraits.checkedRead(p.ipdltype,
                                         ExprAddrOf(ExprVar(p.var().name + 'Copy')),
                                         msgexpr, ExprAddrOf(itervar),
                                         errfn, p.bareType(side).name,
                                         sentinelKey=p.name,
                                         errfnSentinel=errfnSentinel(),
                                         actor=ExprVar.THIS)
                for p in params ]
            + [ self.endRead(msgvar, itervar) ]
            # Move the message back to its source before sending.
            + [ StmtExpr(ExprAssn(ExprDeref(msgsrcVar), ExprMove(msgvar))) ]
            ))

        return stmts

    def setMessageFlags(self, md, var, seqno=None):
        stmts = [ ]

        if seqno:
            stmts.append(StmtExpr(ExprCall(
                ExprSelect(var, '->', 'set_seqno'),
                args=[ seqno ])))

        return stmts + [ Whitespace.NL ]


    def deserializeMessage(self, md, side, errfn, errfnSent):
        msgvar = self.msgvar
        itervar = self.itervar
        msgexpr = ExprAddrOf(msgvar)
        isctor = md.decl.type.isCtor()
        stmts = ([
            self.logMessage(md, msgexpr, 'Received ',
                            receiving=True),
            self.profilerLabel(md),
            Whitespace.NL
        ])

        if 0 == len(md.params):
            return stmts

        start, decls, reads = 0, [], []
        if isctor:
            # return the raw actor handle so that its ID can be used
            # to construct the "real" actor
            handlevar = self.handlevar
            handletype = Type('ActorHandle')
            decls = [ StmtDecl(Decl(handletype, handlevar.name)) ]
            reads = [ _ParamTraits.checkedRead(None, ExprAddrOf(handlevar), msgexpr,
                                               ExprAddrOf(self.itervar),
                                               errfn, "'%s'" % handletype.name,
                                               sentinelKey='actor', errfnSentinel=errfnSent,
                                               actor=ExprVar.THIS) ]
            start = 1

        stmts.extend((
            [ StmtDecl(Decl(_iterType(ptr=0), self.itervar.name),
                       initargs=[ msgvar ]) ]
            + decls + [ StmtDecl(Decl(p.bareType(side), p.var().name))
                      for p in md.params ]
            + [ Whitespace.NL ]
            + reads + [ _ParamTraits.checkedRead(p.ipdltype, ExprAddrOf(p.var()),
                                                 msgexpr, ExprAddrOf(itervar),
                                                 errfn, "'%s'" % p.bareType(side).name,
                                                 sentinelKey=p.name, errfnSentinel=errfnSent,
                                                 actor=ExprVar.THIS)
                        for p in md.params[start:] ]
            + [ self.endRead(msgvar, itervar) ]))

        return stmts

    def deserializeAsyncReply(self, md, side, errfn, errfnSent):
        msgvar = self.msgvar
        itervar = self.itervar
        msgexpr = ExprAddrOf(msgvar)
        isctor = md.decl.type.isCtor()
        resolve = ExprVar('resolve__')
        reason = ExprVar('reason__')
        desresolve = [ StmtDecl(Decl(Type.BOOL, resolve.name)),
                       _ParamTraits.checkedRead(None, ExprAddrOf(resolve), msgexpr,
                                                ExprAddrOf(itervar),
                                                errfn, "'%s'" % resolve.name,
                                                sentinelKey=resolve.name, errfnSentinel=errfnSent,
                                                actor=ExprVar.THIS) ]
        desrej = [ StmtDecl(Decl(_ResponseRejectReason.Type(), reason.name)),
                   _ParamTraits.checkedRead(None, ExprAddrOf(reason), msgexpr,
                                            ExprAddrOf(itervar),
                                            errfn, "'%s'" % reason.name,
                                            sentinelKey=reason.name, errfnSentinel=errfnSent,
                                            actor=ExprVar.THIS),
                   self.endRead(msgvar, itervar) ]
        prologue = ([
            self.logMessage(md, msgexpr, 'Received ',
                            receiving=True),
            self.profilerLabel(md),
            Whitespace.NL
        ])

        if not md.returns:
            return prologue

        prologue.extend([ StmtDecl(Decl(_iterType(ptr=0), itervar.name),
                                   initargs=[ msgvar ]) ]
                        + desresolve)

        start, decls, reads = 0, [], []
        if isctor:
            # return the raw actor handle so that its ID can be used
            # to construct the "real" actor
            handlevar = self.handlevar
            handletype = Type('ActorHandle')
            decls = [ StmtDecl(Decl(handletype, handlevar.name)) ]
            reads = [ _ParamTraits.checkedRead(None, ExprAddrOf(handlevar), msgexpr,
                                               ExprAddrOf(itervar),
                                               errfn, "'%s'" % handletype.name,
                                               sentinelKey='actor', errfnSentinel=errfnSent,
                                               actor=ExprVar.THIS) ]
            start = 1

        stmts = (
            decls + [ StmtDecl(Decl(p.bareType(side), p.var().name))
                      for p in md.returns ]
            + [ Whitespace.NL ]
            + reads + [ _ParamTraits.checkedRead(p.ipdltype, ExprAddrOf(p.var()),
                                                 msgexpr, ExprAddrOf(itervar),
                                                 errfn, "'%s'" % p.bareType(side).name,
                                                 sentinelKey=p.name, errfnSentinel=errfnSent,
                                                 actor=ExprVar.THIS)
                        for p in md.returns[start:] ]
            + [ self.endRead(msgvar, itervar) ])

        return resolve, reason, prologue, desrej, stmts

    def deserializeReply(self, md, replyexpr, side, errfn, errfnSentinel, actor=None, decls=False):
        stmts = [ Whitespace.NL,
                   self.logMessage(md, replyexpr,
                                   'Received reply ', actor, receiving=True) ]
        if 0 == len(md.returns):
            return stmts

        itervar = self.itervar
        declstmts = []
        if decls:
             declstmts = [ StmtDecl(Decl(p.bareType(side), p.var().name))
                           for p in md.returns ]
        stmts.extend(
            [ Whitespace.NL,
              StmtDecl(Decl(_iterType(ptr=0), itervar.name),
                       initargs= [ self.replyvar ]) ]
            + declstmts
            + [ Whitespace.NL ]
            + [ _ParamTraits.checkedRead(r.ipdltype, r.var(),
                                         ExprAddrOf(self.replyvar),
                                         ExprAddrOf(self.itervar),
                                         errfn, "'%s'" % r.bareType(side).name,
                                         sentinelKey=r.name, errfnSentinel=errfnSentinel,
                                         actor=ExprVar.THIS)
                for r in md.returns ]
            + [ self.endRead(self.replyvar, itervar) ])

        return stmts

    def sendAsync(self, md, msgexpr, actor=None):
        sendok = ExprVar('sendok__')
        resolvefn = ExprVar('aResolve')
        rejectfn = ExprVar('aReject')

        sendargs = [ msgexpr ]
        stmts = [ Whitespace.NL,
                  self.logMessage(md, msgexpr, 'Sending ', actor),
                  self.profilerLabel(md) ] + self.transition(md, actor, errorfn=errfnUnreachable)
        stmts.append(Whitespace.NL)

        # Generate the actual call expression.
        send = ExprSelect(self.protocol.callGetChannel(actor), '->', 'Send')
        if md.returns:
            stmts.append(StmtExpr(ExprCall(send, args=[ msgexpr,
                                                        ExprVar('this'),
                                                        ExprMove(resolvefn),
                                                        ExprMove(rejectfn) ])))
            retvar = None
        else:
            stmts.append(StmtDecl(Decl(Type.BOOL, sendok.name),
                                  init=ExprCall(send, args=[ msgexpr ])))
            retvar = sendok

        return (retvar, stmts)

    def sendBlocking(self, md, msgexpr, replyexpr, actor=None):
        sendok = ExprVar('sendok__')
        return (
            sendok,
            ([ Whitespace.NL,
               self.logMessage(md, msgexpr, 'Sending ', actor),
               self.profilerLabel(md) ]
            + self.transition(md, actor, errorfn=errfnUnreachable)
            + [ Whitespace.NL,
                StmtDecl(Decl(Type.BOOL, sendok.name)),
                StmtBlock([
                    StmtExpr(ExprCall(ExprVar('AUTO_PROFILER_TRACING'),
                             [ ExprLiteral.String("IPC"),
                               ExprLiteral.String(self.protocol.name + "::" + md.prettyMsgName()) ])),
                    StmtExpr(ExprAssn(sendok,
                                      ExprCall(ExprSelect(self.protocol.callGetChannel(actor),
                                                          '->',
                                                          _sendPrefix(md.decl.type)),
                                               args=[ msgexpr, ExprAddrOf(replyexpr) ]))),
                ])
            ])
        )

    def sendAsyncWithPromise(self, md):
        # Create a new promise, and forward to the callback send overload.
        retpromise = ExprVar('promise__')
        promise = _makePromise(md.returns, self.side, resolver=True)
        stmts = [ Whitespace.NL,
                  StmtDecl(Decl(_refptr(promise), retpromise.name),
                           init=ExprNew(promise, args=[ExprVar('__func__')])) ]

        if len(md.returns) > 1:
            resolvetype = _tuple([d.bareType(self.side) for d in md.returns])
        else:
            resolvetype = md.returns[0].bareType(self.side)
        resolvetype.ref = 2

        resolvefn = ExprLambda([ retpromise ],
                               [ Decl(resolvetype, "aValue") ])
        resolvefn.addstmts([
            StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Resolve'),
                              args=[ ExprMove(ExprVar('aValue')),
                                     ExprVar('__func__') ])),
        ])

        rejectfn = ExprLambda([ retpromise ],
                              [ Decl(_ResponseRejectReason.Type(), "aReason") ])
        rejectfn.addstmts([
            StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Reject'),
                              args=[ ExprVar('aReason'),
                                     ExprVar('__func__') ])),
        ])

        args = [ p.var() for p in md.params ] + [ resolvefn, rejectfn ]
        stmts += [ Whitespace.NL,
                   StmtExpr(ExprCall(ExprVar(md.sendMethod().name), args=args)),
                   StmtReturn(retpromise) ]
        return stmts

    def callAllocActor(self, md, retsems, side):
        return ExprCall(
            _allocMethod(md.decl.type.constructedType(), side),
            args=md.makeCxxArgs(retsems=retsems, retcallsems='out',
                                implicit=0))

    def callActorDestroy(self, actorexpr, why=_DestroyReason.Deletion):
        return ExprCall(ExprSelect(actorexpr, '->', 'DestroySubtree'),
                        args=[ why ])

    def callRemoveActor(self, actorexpr, manager=None, ipdltype=None):
        if ipdltype is None: ipdltype = self.protocol.decl.type

        if not ipdltype.isManaged():
            return Whitespace('// unmanaged protocol')

        removefunc = self.protocol.removeManageeMethod()
        if manager is not None:
            removefunc = ExprSelect(manager, '->', removefunc.name)

        return ExprCall(removefunc,
                        args=[ _protocolId(ipdltype),
                               actorexpr ])

    def callDeallocSubtree(self, md, actorexpr):
        return ExprCall(ExprSelect(actorexpr, '->', 'DeallocSubtree'))

    def invokeRecvHandler(self, md, implicit=1):
        retsems = 'in'
        if md.decl.type.isAsync() and md.returns:
            retsems = 'resolver'
        failif = StmtIf(ExprNot(
            ExprCall(md.recvMethod(),
                     args=md.makeCxxArgs(paramsems='move', retsems=retsems,
                                         retcallsems='out',
                                         implicit=implicit))))
        failif.addifstmts([
            _protocolErrorBreakpoint('Handler returned error code!'),
            Whitespace('// Error handled in mozilla::ipc::IPCResult\n', indent=1),
            StmtReturn(_Result.ProcessingError)
        ])
        return [ failif ]

    def makeDtorMethodDecl(self, md):
        decl = self.makeSendMethodDecl(md)
        decl.methodspec = MethodSpec.STATIC
        return decl

    def makeSendMethodDecl(self, md, promise=False):
        implicit = md.decl.type.hasImplicitActorParam()
        if md.decl.type.isAsync() and md.returns:
            if promise:
                returnsems = 'promise'
                rettype = _refptr(Type(md.promiseName()))
            else:
                returnsems = 'callback'
                rettype = Type.VOID
        else:
            assert not promise
            returnsems = 'out'
            rettype = Type.BOOL
        decl = MethodDecl(
            md.sendMethod().name,
            params=md.makeCxxParams(paramsems='in', returnsems=returnsems,
                                    side=self.side, implicit=implicit),
            warn_unused=(self.side == 'parent' and returnsems != 'callback'),
            ret=rettype)
        if md.decl.type.isCtor():
            decl.ret = md.actorDecl().bareType(self.side)
        return decl

    def logMessage(self, md, msgptr, pfx, actor=None, receiving=False):
        actorname = _actorName(self.protocol.name, self.side)

        return _ifLogging(ExprLiteral.String(actorname),
                          [ StmtExpr(ExprCall(
                              ExprVar('mozilla::ipc::LogMessageForProtocol'),
                              args=[ ExprLiteral.String(actorname),
                                     self.protocol.callOtherPid(actor),
                                     ExprLiteral.String(pfx),
                                     ExprCall(ExprSelect(msgptr, '->', 'type')),
                                     ExprVar('mozilla::ipc::MessageDirection::eReceiving'
                                             if receiving
                                             else 'mozilla::ipc::MessageDirection::eSending') ])) ])

    def profilerLabel(self, md):
        labelStr = self.protocol.name + '::' + md.prettyMsgName()
        return StmtExpr(ExprCall(ExprVar('AUTO_PROFILER_LABEL'),
                                 [ ExprLiteral.String(labelStr),
                                   ExprVar('OTHER') ]))

    def saveActorId(self, md):
        idvar = ExprVar('id__')
        if md.decl.type.hasReply():
            # only save the ID if we're actually going to use it, to
            # avoid unused-variable warnings
            saveIdStmts = [ StmtDecl(Decl(_actorIdType(), idvar.name),
                                     self.protocol.routingId()) ]
        else:
            saveIdStmts = [ ]
        return idvar, saveIdStmts

    def transition(self, md, actor=None, reply=False, errorfn=None):
        msgid = md.msgId() if not reply else md.replyId()
        args = [
            ExprVar('true' if _deleteId().name == msgid else 'false'),
        ]
        if self.protocol.decl.type.hasReentrantDelete:
            function = 'ReEntrantDeleteStateTransition'
            args.append(
                ExprVar('true' if _deleteReplyId().name == msgid else 'false'),
            )
        else:
            function = 'StateTransition'

        if actor is not None:
            stateexpr = _actorState(actor)
        else:
            stateexpr = self.protocol.stateVar()

        args.append(ExprAddrOf(stateexpr))

        ifstmt = StmtIf(ExprNot(ExprCall(ExprVar(function), args=args)))
        ifstmt.addifstmts(errorfn('Transition error'))
        return [ifstmt]

    def endRead(self, msgexpr, iterexpr):
        msgtype = ExprCall(ExprSelect(msgexpr, '.', 'type'), [ ])
        return StmtExpr(ExprCall(ExprSelect(msgexpr, '.', 'EndRead'),
                                 args=[ iterexpr, msgtype ]))

class _GenerateProtocolParentCode(_GenerateProtocolActorCode):
    def __init__(self):
        _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 _GenerateProtocolChildCode(_GenerateProtocolActorCode):
    def __init__(self):
        _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()


##-----------------------------------------------------------------------------
## Utility passes
##

def _splitClassDeclDefn(cls):
    """Destructively split |cls| methods into declarations and
definitions (if |not methodDecl.force_inline|).  Return classDecl,
methodDefns."""
    defns = Block()

    for i, stmt in enumerate(cls.stmts):
        if isinstance(stmt, MethodDefn) and not stmt.decl.force_inline:
            decl, defn = _splitMethodDefn(stmt, cls)
            cls.stmts[i] = StmtDecl(decl)
            defns.addstmts([ defn, Whitespace.NL ])

    return cls, defns

def _splitMethodDefn(md, cls):
    saveddecl = deepcopy(md.decl)
    md.decl.cls = cls
    md.decl.methodspec = MethodSpec.NONE
    md.decl.warn_unused = 0
    md.decl.only_for_definition = True
    for param in md.decl.params:
        if isinstance(param, Param):
            param.default = None
    return saveddecl, md


def _splitFuncDeclDefn(fun):
    assert not fun.decl.force_inline
    return StmtDecl(fun.decl), fun


# XXX this is tantalizingly similar to _splitClassDeclDefn, 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, namespaces):
        self.name = name
        self.cls = None
        self.namespaces = namespaces
        self.methodimpls = Block()

    def fromclass(self, cls):
        cls.accept(self)

        nsclass = _putInNamespaces(self.cls, self.namespaces)
        nsmethodimpls = _putInNamespaces(self.methodimpls, self.namespaces)

        return [
            Whitespace('''
//-----------------------------------------------------------------------------
// Skeleton implementation of abstract actor class

'''),
            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 md.methodspec != MethodSpec.PURE:
            return
        decl = deepcopy(md)
        decl.methodspec = MethodSpec.OVERRIDE
        impl = MethodDefn(MethodDecl(self.implname(md.name),
                                             params=md.params,
                                             ret=md.ret))
        if md.ret.ptr:
            impl.addstmt(StmtReturn(ExprLiteral.ZERO))
        elif md.ret == Type.BOOL:
            impl.addstmt(StmtReturn(ExprVar('false')))

        self.cls.addstmts([ StmtDecl(decl), Whitespace.NL ])
        self.addmethodimpl(impl)

    def visitConstructorDecl(self, cd):
        self.cls.addstmt(StmtDecl(ConstructorDecl(self.name)))
        ctor = ConstructorDefn(ConstructorDecl(self.implname(self.name)))
        ctor.addstmt(StmtExpr(ExprCall(ExprVar( 'MOZ_COUNT_CTOR'),
                                               [ ExprVar(self.name) ])))
        self.addmethodimpl(ctor)

    def visitDestructorDecl(self, dd):
        self.cls.addstmt(
            StmtDecl(DestructorDecl(self.name, methodspec=MethodSpec.VIRTUAL)))
        # 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.addstmts([ impl, Whitespace.NL ])

    def implname(self, method):
        return self.name +'::'+ method
