# 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, 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 _nullState(proto=None):
    pfx = ''
    if proto is not None:  pfx = proto.name() +'::'
    return ExprVar(pfx +'__Null')

def _deadState(proto=None):
    pfx = ''
    if proto is not None:  pfx = proto.name() +'::'
    return ExprVar(pfx +'__Dead')

def _dyingState(proto=None):
    pfx = ''
    if proto is not None:  pfx = proto.name() +'::'
    return ExprVar(pfx +'__Dying')

def _startState(proto=None, fq=False):
    pfx = ''
    if proto:
        if fq:  pfx = proto.fullname() +'::'
        else:   pfx = proto.name() +'::'
    return ExprVar(pfx +'__Start')

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

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

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

def _shmemType(ptr=0, const=1, ref=0):
    return Type('Shmem', ptr=ptr, ref=ref)

def _rawShmemType(ptr=0):
    return Type('Shmem::SharedMemory', ptr=ptr)

def _shmemIdType(ptr=0):
    return Type('Shmem::id_t', ptr=ptr)

def _shmemTypeType():
    return Type('Shmem::SharedMemory::SharedMemoryType')

def _shmemBackstagePass():
    return ExprCall(ExprVar(
        'Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead'))

def _shmemCtor(rawmem, idexpr):
    return ExprCall(ExprVar('Shmem'),
                    args=[ _shmemBackstagePass(), rawmem, idexpr ])

def _shmemId(shmemexpr):
    return ExprCall(ExprSelect(shmemexpr, '.', 'Id'),
                    args=[ _shmemBackstagePass() ])

def _shmemSegment(shmemexpr):
    return ExprCall(ExprSelect(shmemexpr, '.', 'Segment'),
                    args=[ _shmemBackstagePass() ])

def _shmemAlloc(size, type, unsafe):
    # starts out UNprotected
    return ExprCall(ExprVar('Shmem::Alloc'),
                    args=[ _shmemBackstagePass(), size, type, unsafe ])

def _shmemDealloc(rawmemvar):
    return ExprCall(ExprVar('Shmem::Dealloc'),
                    args=[ _shmemBackstagePass(), rawmemvar ])

def _shmemShareTo(shmemvar, processvar, route):
    return ExprCall(ExprSelect(shmemvar, '.', 'ShareTo'),
                    args=[ _shmemBackstagePass(),
                           processvar, route ])

def _shmemOpenExisting(descriptor, outid):
    # starts out protected
    return ExprCall(ExprVar('Shmem::OpenExisting'),
                    args=[ _shmemBackstagePass(),
                           # true => protect
                           descriptor, outid, ExprLiteral.TRUE ])

def _shmemUnshareFrom(shmemvar, processvar, route):
    return ExprCall(ExprSelect(shmemvar, '.', 'UnshareFrom'),
                    args=[ _shmemBackstagePass(),
                           processvar, route ])

def _shmemForget(shmemexpr):
    return ExprCall(ExprSelect(shmemexpr, '.', 'forget'),
                    args=[ _shmemBackstagePass() ])

def _shmemRevokeRights(shmemexpr):
    return ExprCall(ExprSelect(shmemexpr, '.', 'RevokeRights'),
                    args=[ _shmemBackstagePass() ])

def _lookupShmem(idexpr):
    return ExprCall(ExprVar('LookupSharedMemory'), 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,
                    _PromiseRejectReason.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)
    ]

# used in Read() methods
def errfnRead(msg):
    return [ _fatalError(msg), StmtReturn.FALSE ]

def errfnArrayLength(elementname):
    return [ _arrayLengthReadError(elementname), StmtReturn.FALSE ]

def errfnUnionType(unionname):
    return [ _unionTypeReadError(unionname), StmtReturn.FALSE ]

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

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

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

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

class _PromiseRejectReason:
    @staticmethod
    def Type():
        return Type('PromiseRejectReason')

    SendError = ExprVar('PromiseRejectReason::SendError')
    ChannelClosed = ExprVar('PromiseRejectReason::ChannelClosed')
    HandlerRejected = ExprVar('PromiseRejectReason::HandlerRejected')
    ActorDestroyed = ExprVar('PromiseRejectReason::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):
        return Type(self.typename(t))

    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 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
    t.const = 1
    t.ref = 1
    return t

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

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

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 bareType(self, side):
        """Return this decl's unqualified C++ type."""
        return _cxxBareType(self.ipdltype, side)

    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

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

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)
        self.recursive = ct.decl.type.mutuallyRecursiveWith(ipdltype)

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

    # @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):
        return _HybridDecl.bareType(self, self.side)
    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)


class StructDecl(ipdl.ast.StructDecl, HasFQName):
    @staticmethod
    def upgrade(structDecl):
        assert isinstance(structDecl, ipdl.ast.StructDecl)
        structDecl.__class__ = StructDecl
        return 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)
        if self.recursive:
            ref = ExprDeref(ref)
        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 '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 + '_')

    def initStmts(self):
        if self.recursive:
            return [ StmtExpr(ExprAssn(self.memberVar(),
                                       ExprNew(self.bareType()))) ]
        elif self.ipdltype.isIPDL() and self.ipdltype.isActor():
            return [ StmtExpr(ExprAssn(self.memberVar(),
                                       ExprLiteral.NULL)) ]
        else:
            return []

    def destructStmts(self):
        if self.recursive:
            return [ StmtExpr(ExprDelete(self.memberVar())) ]
        else:
            return []


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
        return 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)

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

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

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

    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):
        # 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())

    def getConstValue(self):
        v = ExprDeref(self.callGetConstPtr())
        # sigh
        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')

        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 '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([ ExprMove(p.var()) 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
        return messageDecl

##--------------------------------------------------
def _semsToChannelParts(sems):
    return [ 'mozilla', 'ipc', 'MessageChannel' ]

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

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

    def sendSems(self):
        return self.decl.type.toplevel().sendSemantics

    def channelName(self):
        return '::'.join(_semsToChannelParts(self.sendSems()))

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

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

    def channelHeaderFile(self):
        return '/'.join(_semsToChannelParts(self.sendSems())) +'.h'

    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 Type(self.decl.type.name() +'::State')

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

    def nullState(self):
        return _nullState(self.decl.type)

    def deadState(self):
        return _deadState(self.decl.type)

    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
        return protocol


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

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

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::PromiseRejectReason'),
                                        'PromiseRejectReason') ])
        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 + '"'))

        ipdl.ast.Visitor.visitTranslationUnit(self, tu)
        if tu.filetype == 'header':
            self.cppIncludeHeaders.append(_ipdlhHeaderName(tu))

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

        cf = self.cppfile
        cf.addthings((
            [ _DISCLAIMER, Whitespace.NL ]
            + [ CppDirective('include','"'+h+'.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 visitInclude(self, inc):
        if inc.tu.filetype == 'header':
            self.hdrfile.addthing(CppDirective(
                    'include', '"'+ _ipdlhHeaderName(inc.tu) +'.h"'))

    def processStructOrUnionClass(self, su, which, forwarddecls, cls):
        clsdecl, methoddefns = _splitClassDeclDefn(cls)

        self.hdrfile.addthings(
            [  Whitespace.NL ]
            + forwarddecls
            + [ Whitespace("""
//-----------------------------------------------------------------------------
// Declaration of the IPDL type |%s %s|
//
"""% (which, su.name)),
                _putInNamespaces(clsdecl, su.namespaces),
            ])

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

    def visitStructDecl(self, sd):
        return self.processStructOrUnionClass(sd, 'struct',
                                              *_generateCxxStruct(sd))

    def visitUnionDecl(self, ud):
        return self.processStructOrUnionClass(ud, 'union',
                                              *_generateCxxUnion(ud))

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

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

        # state information
        stateenum = TypeEnum('State')
        # NB: __Dead is the first state on purpose, so that it has
        # value '0'
        stateenum.addId(_deadState().name)
        stateenum.addId(_nullState().name)
        if self.protocol.decl.type.hasReentrantDelete:
            stateenum.addId(_dyingState().name)
        stateenum.addId(_startState().name, _nullState().name)

        ns.addstmts([ StmtDecl(Decl(stateenum,'')), Whitespace.NL ])

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

        tfDecl, tfDefn = _splitFuncDeclDefn(self.genTransitionFunc())
        ns.addstmts([ tfDecl, Whitespace.NL ])
        self.funcDefns.append(tfDefn)

        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 genTransitionFunc(self):
        ptype = self.protocol.decl.type

        # bool Transition(MessageType msg, State* next)
        # The state we are transitioning from is stored in *next.
        msgtypevar = ExprVar('msg')
        nextvar = ExprVar('next')

        transitionfunc = FunctionDefn(FunctionDecl(
            'Transition',
            params=[ Decl(Type('MessageType'), msgtypevar.name),
                     Decl(Type('State', ptr=1), nextvar.name) ],
            ret=Type.VOID))

        fromswitch = StmtSwitch(ExprDeref(nextvar))

        # special case for Null
        nullerrorblock = Block()
        if ptype.hasDelete:
            ifdelete = StmtIf(ExprBinary(_deleteId(), '==', msgtypevar))
            if ptype.hasReentrantDelete:
                nextState = _dyingState()
            else:
                nextState = _deadState()
            ifdelete.addifstmt(
                StmtExpr(ExprAssn(ExprDeref(nextvar), nextState)))
            nullerrorblock.addstmt(ifdelete)
        nullerrorblock.addstmt(StmtBreak())
        fromswitch.addcase(CaseLabel(_nullState().name), nullerrorblock)

        # special case for Dead
        deadblock = Block()
        deadblock.addstmts([
            _logicError('__delete__()d actor'),
            StmtBreak() ])
        fromswitch.addcase(CaseLabel(_deadState().name), deadblock)

        # special case for Dying
        if ptype.hasReentrantDelete:
            dyingblock = Block()
            ifdelete = StmtIf(ExprBinary(_deleteReplyId(), '==', msgtypevar))
            ifdelete.addifstmt(
                StmtExpr(ExprAssn(ExprDeref(nextvar), _deadState())))
            dyingblock.addstmt(ifdelete)
            dyingblock.addstmt(StmtBreak())
            fromswitch.addcase(CaseLabel(_dyingState().name), dyingblock)

        unreachedblock = Block()
        unreachedblock.addstmts([
            _logicError('corrupted actor state'),
            StmtBreak() ])
        fromswitch.addcase(DefaultLabel(), unreachedblock)

        transitionfunc.addstmt(fromswitch)

        return transitionfunc

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

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 _ComputeTypeDeps(TypeVisitor):
    '''Pass that gathers the C++ types that a particular IPDL type
(recursively) depends on.  There are two kinds of dependencies: (i)
types that need forward declaration; (ii) types that need a |using|
stmt.  Some types generate both kinds.'''

    def __init__(self, fortype, unqualifiedTypedefs=False):
        ipdl.type.TypeVisitor.__init__(self)
        self.usingTypedefs = [ ]
        self.forwardDeclStmts = [ ]
        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())

        if su.mutuallyRecursiveWith(self.fortype):
            self.forwardDeclStmts.append(_makeForwardDecl(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 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

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

    constreftype = Type(sd.name, const=1, ref=1)
    initvar = ExprVar('Init')
    callinit = ExprCall(initvar)
    assignvar = ExprVar('Assign')

    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))
        defctor.addstmt(StmtExpr(callinit))
        defctor.memberinits = []
        for f in sd.fields:
          # Only generate default values for primitives.
          if not (f.ipdltype.isCxx() and f.ipdltype.isAtom()):
            continue
          defctor.memberinits.append(ExprMemberInit(f.memberVar()))
        struct.addstmts([ defctor, Whitespace.NL ])

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

    # Struct(const Struct& _o)
    ovar = ExprVar('_o')
    copyctor = ConstructorDefn(ConstructorDecl(
        sd.name,
        params=[ Decl(constreftype, ovar.name) ],
        force_inline=1))
    copyctor.addstmts([
        StmtExpr(callinit),
        StmtExpr(assignFromOther(ovar))
    ])
    struct.addstmts([ copyctor, Whitespace.NL ])

    # ~Struct()
    dtor = DestructorDefn(DestructorDecl(sd.name))
    for f in sd.fields:
        dtor.addstmts(f.destructStmts())
    struct.addstmts([ dtor, Whitespace.NL ])

    # Struct& operator=(const Struct& _o)
    opeq = MethodDefn(MethodDecl(
        'operator=',
        params=[ Decl(constreftype, ovar.name) ],
        force_inline=1))
    opeq.addstmt(StmtExpr(assignFromOther(ovar)))
    struct.addstmts([ opeq, Whitespace.NL ])

    # bool operator==(const Struct& _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)

    # Init()
    init = MethodDefn(MethodDecl(initvar.name))
    for f in sd.fields:
        init.addstmts(f.initStmts())
    struct.addstmts([ init, Whitespace.NL ])

    # Assign(const field1& _f1, ...)
    assign = MethodDefn(MethodDecl(assignvar.name,
                                   params=fieldsAsParamList()))
    assign.addstmts([ StmtExpr(ExprAssn(f.refExpr(), f.argVar()))
                      for f in sd.fields ])
    struct.addstmts([ assign, Whitespace.NL ])

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

    return forwarddeclstmts, 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 ctor, assignment operator,
    # 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
    #  - case in generic copy ctor
    #  - 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)
    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

    # 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

    # 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 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 ])

    # 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()
    dtor = DestructorDefn(DestructorDecl(ud.name))
    # The void cast prevents Coverity from complaining about missing return
    # value checks.
    dtor.addstmt(StmtExpr(ExprCast(callMaybeDestroy(tnonevar), Type.VOID,
                                   static=1)))
    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=(const T&) methods
    rhsvar = ExprVar('aRhs')
    for c in ud.components:
        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=(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 ])

    # 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, 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'), 'ProtocolBase'),
            Typedef(Type('IPC::Message'), 'Message'),
            Typedef(Type(self.protocol.channelName()), 'Channel'),
            Typedef(Type('mozilla::ipc::IProtocol'), 'ChannelListener'),
            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
        ])

        self.hdrfile.addthings(
            ([
                Whitespace.NL,
                CppDirective('if', '0') ])
            + _GenerateSkeletonImpl(
                _actorName(self.protocol.name, self.side)[1:],
                self.protocol.namespaces).fromclass(self.cls)
            +([
                CppDirective('endif', '// if 0'),
                Whitespace.NL ])
            + _includeGuardEnd(hf))

        # make the .cpp file
        cf.addthings([
            _DISCLAIMER,
            Whitespace.NL,
            CppDirective(
                'include',
                '"'+ _protocolHeaderName(self.protocol, self.side) +'.h"') ]
            + setToIncludes(self.externalIncludes))

        if self.protocol.decl.type.isToplevel():
            cf.addthings([
                CppDirective('ifdef', 'MOZ_CRASHREPORTER'),
                CppDirective('  include', '"nsXULAppAPI.h"'),
                CppDirective('endif')
            ])

        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
        ])

    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', '"'+ p.channelHeaderFile() +'"'),
            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)

        self.cls.addstmts([ Typedef(p.fqStateType(), 'State'), 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'), virtual=1)

                if isctor or isdtor:
                    defaultRecv = MethodDefn(recvDecl)
                    defaultRecv.addstmt(StmtReturn(ExprCall(ExprVar('IPC_OK'))))
                    self.cls.addstmt(defaultRecv)
                else:
                    recvDecl.pure = 1
                    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,
                virtual=1, pure=1)))

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

        # ActorDestroy() method; default is no-op
        self.cls.addstmts([
            Whitespace.NL,
            MethodDefn(MethodDecl(
                _destroyMethod().name,
                params=[ Decl(_DestroyReason.Type(), 'aWhy') ],
                ret=Type.VOID,
                virtual=1, pure=(self.side == 'parent'))),
            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') ],
                           virtual=1))

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

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

            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():
            ctor.memberinits = [
                ExprMemberInit(ExprVar('mozilla::ipc::IToplevelProtocol'),
                               [_protocolId(ptype), side]),
                ExprMemberInit(p.channelVar(), [
                    ExprLiteral.String(_actorName(p.name, self.side)),
                    ExprCall(ExprVar('ALLOW_THIS_IN_INITIALIZER_LIST'),
                             [ ExprVar.THIS ]) ]),
                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, virtual=True))
        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, virtual=True,
                                           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('ChannelListener', 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'), '==', _dyingState(ptype)),
                    '&&',
                    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()))
        gettypetag.addstmt(StmtReturn(_protocolId(ptype)))
        self.cls.addstmts([ gettypetag, Whitespace.NL ])

        if ptype.isToplevel():
            # OnChannelClose()
            onclose = MethodDefn(MethodDecl('OnChannelClose'))
            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'))
            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(p.channelVar(), '.',
                                       'ProcessNativeEventsInInterruptCall'))),
                    CppDirective('else'),
                    _fatalError('This method is Windows-only'),
                    CppDirective('endif'),
                    ])

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

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

        ## ProtocolName()
        actorname = _actorName(p.name, self.side)
        protocolname = MethodDefn(MethodDecl(
            'ProtocolName', params=[],
            const=1, virtual=1, ret=Type('char', const=1, ptr=1)))
        protocolname.addstmts([
            StmtReturn(ExprLiteral.String(actorname))
        ])
        self.cls.addstmts([ protocolname, Whitespace.NL ])

        ## 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 promises for actor before calling ActorDestroy().
        rejectPendingPromiseMethod = ExprSelect(self.protocol.callGetChannel(),
                                                '->',
                                                'RejectPendingPromisesForActor')
        destroysubtree.addstmts([ Whitespace('// Reject owning pending promises.\n',
                                             indent=1),
                                  StmtExpr(ExprCall(rejectPendingPromiseMethod,
                                                    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, virtual=1))
            self.cls.addstmts([ deallocself, Whitespace.NL ])

        self.implementPickling()

        ## private members
        if ptype.isToplevel():
            self.cls.addstmt(StmtDecl(Decl(p.channelType(), 'mChannel')))

        self.cls.addstmt(StmtDecl(Decl(Type('State'), 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('ProtocolBase', ptr=1)
        sourcevar = ExprVar('aSource')
        ivar = ExprVar('i')
        kidsvar = ExprVar('kids')
        ithkid = ExprIndex(kidsvar, ivar)

        methods = []

        if p.decl.type.isToplevel():
            getchannel = MethodDefn(MethodDecl(
                p.getChannelMethod().name,
                ret=Type('MessageChannel', ptr=1),
                virtual=1))
            getchannel.addstmt(StmtReturn(ExprAddrOf(p.channelVar())))

            getchannelconst = MethodDefn(MethodDecl(
                p.getChannelMethod().name,
                ret=Type('MessageChannel', ptr=1, const=1),
                virtual=1, const=1))
            getchannelconst.addstmt(StmtReturn(ExprAddrOf(p.channelVar())))

            methods += [ getchannel,
                         getchannelconst ]

        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) ],
            virtual=1))

        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 implementPickling(self):
        # pickling of "normal", non-IPDL types
        self.implementGenericPickling()

        # pickling for IPDL types
        specialtypes = set()
        class findSpecialTypes(TypeVisitor):
            def visitActorType(self, a): specialtypes.add(a)
            def visitShmemType(self, s): specialtypes.add(s)
            def visitFDType(self, s): specialtypes.add(s)
            def visitStructType(self, s):
                specialtypes.add(s)
                return TypeVisitor.visitStructType(self, s)
            def visitUnionType(self, u):
                specialtypes.add(u)
                return TypeVisitor.visitUnionType(self, u)
            def visitArrayType(self, a):
                if a.basetype.isIPDL():
                    specialtypes.add(a)
                    return a.basetype.accept(self)

        for md in self.protocol.messageDecls:
            for param in md.params:
                mtype = md.decl.type
                # special case for top-level __delete__(), which isn't
                # understood yet
                if mtype.isDtor() and mtype.constructedType().isToplevel():
                    continue
                param.ipdltype.accept(findSpecialTypes())
            for ret in md.returns:
                ret.ipdltype.accept(findSpecialTypes())

        for t in specialtypes:
            if t.isActor():    self.implementActorPickling(t)
            elif t.isArray():  self.implementSpecialArrayPickling(t)
            elif t.isShmem():  self.implementShmemPickling(t)
            elif t.isFD():     self.implementFDPickling(t)
            elif t.isStruct(): self.implementStructPickling(t)
            elif t.isUnion():  self.implementUnionPickling(t)
            else:
                assert 0 and 'unknown special type'

    def implementGenericPickling(self):
        var = self.var
        msgvar = self.msgvar
        itervar = self.itervar

        write = MethodDefn(self.writeMethodDecl(
            Type('T', const=1, ref=1), var, template=Type('T')))
        write.addstmt(StmtExpr(ExprCall(ExprVar('IPC::WriteParam'),
                                        args=[ msgvar, var ])))

        read = MethodDefn(self.readMethodDecl(
            Type('T', ptr=1), var, template=Type('T')))
        read.addstmt(StmtReturn(ExprCall(ExprVar('IPC::ReadParam'),
                                         args=[ msgvar, itervar, var ])))

        self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])

    def implementActorPickling(self, actortype):
        # Note that we pickle based on *protocol* type and *not* actor
        # type.  The actor type includes a |nullable| qualifier, but
        # this method is not specialized based on nullability.  The
        # |actortype| nullability is ignored in this method.
        var = self.var
        idvar = ExprVar('id')
        intype = _cxxConstRefType(actortype, self.side)
        # XXX the writer code can treat the actor as logically const; many
        # other places that call _cxxConstRefType cannot treat the actor
        # as logically const, particularly callers that can leak out to
        # Gecko directly.
        intype.const = 1
        cxxtype = _cxxBareType(actortype, self.side)
        outtype = _cxxPtrToType(actortype, self.side)

        ## Write([const] PFoo* var)
        write = MethodDefn(self.writeMethodDecl(intype, var))
        nullablevar = ExprVar('nullable__')
        write.decl.params.append(Decl(Type.BOOL, nullablevar.name))
        # id_t id;
        # if (!var)
        #   if(!nullable)
        #     abort()
        #   id = NULL_ID
        write.addstmt(StmtDecl(Decl(_actorIdType(), idvar.name)))

        ifnull = StmtIf(ExprNot(var))
        ifnotnullable = StmtIf(ExprNot(nullablevar))
        ifnotnullable.addifstmt(
            _fatalError("NULL actor value passed to non-nullable param"))
        ifnull.addifstmt(ifnotnullable)
        ifnull.addifstmt(StmtExpr(ExprAssn(idvar, _NULL_ACTOR_ID)))
        # else
        #   id = var->mId
        #   if (id == FREED_ID)
        #     abort()
        # Write(msg, id)
        ifnull.addelsestmt(StmtExpr(ExprAssn(idvar, _actorId(var))))
        iffreed = StmtIf(ExprBinary(_FREED_ACTOR_ID, '==', idvar))
        # this is always a hard-abort, because it means that some C++
        # code has a live pointer to a freed actor, so we're playing
        # Russian roulette with invalid memory
        iffreed.addifstmt(_fatalError("actor has been |delete|d"))
        ifnull.addelsestmt(iffreed)

        write.addstmts([
            ifnull,
            Whitespace.NL,
            StmtExpr(self.write(None, idvar, self.msgvar))
        ])

        ## Read(PFoo** var)
        read = MethodDefn(self.readMethodDecl(outtype, var))
        read.decl.params.append(Decl(Type.BOOL, nullablevar.name))

        actorvar = ExprVar('actor')
        read.addstmts([
            StmtDecl(Decl(Type('Maybe', T=Type('mozilla::ipc::IProtocol', ptr=1)), actorvar.name),
                     init=ExprCall(ExprVar('ReadActor'),
                                   args=[ self.msgvar, self.itervar, nullablevar,
                                          ExprLiteral.String(actortype.name()),
                                          _protocolId(actortype) ])),
        ])

        # if (actor.isNothing())
        #   return false
        #
        # Reading the actor failed in some way, and the appropriate error was raised.
        ifnothing = StmtIf(ExprCall(ExprSelect(actorvar, '.', 'isNothing')))
        ifnothing.addifstmts([
            StmtReturn.FALSE,
        ])

        read.addstmts([ ifnothing, Whitespace.NL ])

        read.addstmts([
                StmtExpr(ExprAssn(ExprDeref(var),
                                  ExprCast(ExprCall(ExprSelect(actorvar, '.', 'value')), cxxtype, static=1))),
                StmtReturn.TRUE
        ])

        self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])


    def implementSpecialArrayPickling(self, arraytype):
        var = self.var
        msgvar = self.msgvar
        itervar = self.itervar
        lenvar = ExprVar('length')
        ivar = ExprVar('i')
        eltipdltype = arraytype.basetype
        intype = _cxxConstRefType(arraytype, self.side)
        outtype = _cxxPtrToType(arraytype, self.side)

        # We access elements directly in Read and Write to avoid array bounds
        # checking.
        directtype = _cxxBareType(arraytype.basetype, self.side)
        if directtype.ptr:
            typeinit = { 'ptrptr': 1 }
        else:
            typeinit = { 'ptr': 1 }
        directtype = Type(directtype.name, **typeinit)
        elemsvar = ExprVar('elems')
        elemvar = ExprVar('elem')

        write = MethodDefn(self.writeMethodDecl(intype, var))
        forwrite = StmtRangedFor(elemvar, var)
        forwrite.addstmt(
            self.checkedWrite(eltipdltype, elemvar, msgvar,
                              sentinelKey=arraytype.name()))
        write.addstmts([
            StmtDecl(Decl(Type.UINT32, lenvar.name),
                     init=_callCxxArrayLength(var)),
            self.checkedWrite(None, lenvar, msgvar, sentinelKey=('length', arraytype.name())),
            Whitespace.NL,
            forwrite
        ])

        read = MethodDefn(self.readMethodDecl(outtype, var))
        favar = ExprVar('fa')
        forread = StmtFor(init=ExprAssn(Decl(Type.UINT32, ivar.name),
                                        ExprLiteral.ZERO),
                          cond=ExprBinary(ivar, '<', lenvar),
                          update=ExprPrefixUnop(ivar, '++'))
        forread.addstmt(
            self.checkedRead(eltipdltype, ExprAddrOf(ExprIndex(elemsvar, ivar)),
                             msgvar, itervar, errfnRead,
                             '\'' + eltipdltype.name() + '[i]\'',
                             sentinelKey=arraytype.name(),
                             errfnSentinel=errfnSentinel()))
        appendstmt = StmtDecl(Decl(directtype, elemsvar.name),
                              init=ExprCall(ExprSelect(favar, '.', 'AppendElements'),
                                            args=[ lenvar ]))
        read.addstmts([
            StmtDecl(Decl(_cxxArrayType(_cxxBareType(arraytype.basetype, self.side)), favar.name)),
            StmtDecl(Decl(Type.UINT32, lenvar.name)),
            self.checkedRead(None, ExprAddrOf(lenvar),
                             msgvar, itervar, errfnArrayLength,
                             [ arraytype.name() ],
                             sentinelKey=('length', arraytype.name()),
                             errfnSentinel=errfnSentinel()),
            Whitespace.NL,
            appendstmt,
            forread,
            StmtExpr(_callCxxSwapArrayElements(var, favar, '->')),
            StmtReturn.TRUE
        ])

        self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])


    def implementShmemPickling(self, shmemtype):
        msgvar = self.msgvar
        itervar = self.itervar
        var = self.var
        tmpvar = ExprVar('tmp')
        idvar = ExprVar('shmemid')
        rawvar = ExprVar('rawmem')
        baretype = _cxxBareType(shmemtype, self.side)
        intype = _cxxConstRefType(shmemtype, self.side)
        outtype = _cxxPtrToType(shmemtype, self.side)

        write = MethodDefn(self.writeMethodDecl(intype, var))
        write.addstmts([
            StmtExpr(ExprCall(ExprVar('IPC::WriteParam'),
                              args=[ msgvar, var ])),
            StmtExpr(_shmemRevokeRights(var)),
            StmtExpr(_shmemForget(var))
        ])

        read = MethodDefn(self.readMethodDecl(outtype, var))
        ifread = StmtIf(ExprNot(ExprCall(ExprVar('IPC::ReadParam'),
                                         args=[ msgvar, itervar,
                                                ExprAddrOf(tmpvar) ])))
        ifread.addifstmt(StmtReturn.FALSE)

        iffound = StmtIf(rawvar)
        iffound.addifstmt(StmtExpr(ExprAssn(
            ExprDeref(var), _shmemCtor(rawvar, idvar))))
        iffound.addifstmt(StmtReturn.TRUE)

        read.addstmts([
            StmtDecl(Decl(_shmemType(), tmpvar.name)),
            ifread,
            Whitespace.NL,
            StmtDecl(Decl(_shmemIdType(), idvar.name),
                     init=_shmemId(tmpvar)),
            StmtDecl(Decl(_rawShmemType(ptr=1), rawvar.name),
                     init=_lookupShmem(idvar)),
            iffound,
            # This is ugly: we failed to look the shmem up, most likely because
            # we failed to map it the first time it was deserialized. we create
            # an empty shmem and let the user of the shmem deal with it.
            # if we returned false here we would crash.
            StmtExpr(ExprAssn(ExprDeref(var), ExprCall(ExprVar('Shmem'), args=[]) )),
            StmtReturn.TRUE
        ])

        self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])

    def implementFDPickling(self, fdtype):
        msgvar = self.msgvar
        itervar = self.itervar
        var = self.var
        tmpvar = ExprVar('fd')
        picklevar = ExprVar('pfd')
        intype = _cxxConstRefType(fdtype, self.side)
        outtype = _cxxPtrToType(fdtype, self.side)

        def _fdType():
            return Type('FileDescriptor')

        def _fdPickleType():
            return Type('FileDescriptor::PickleType')

        def _fdBackstagePass():
            return ExprCall(ExprVar('FileDescriptor::IPDLPrivate'))

        write = MethodDefn(self.writeMethodDecl(intype, var))
        write.addstmts([
            StmtDecl(Decl(_fdPickleType(), picklevar.name),
                     init=ExprCall(ExprSelect(var, '.', 'ShareTo'),
                                   args=[ _fdBackstagePass(),
                                          self.protocol.callOtherPid() ])),
            StmtExpr(ExprCall(ExprVar('IPC::WriteParam'),
                              args=[ msgvar, picklevar ])),
        ])

        read = MethodDefn(self.readMethodDecl(outtype, var))
        ifread = StmtIf(ExprNot(ExprCall(ExprVar('IPC::ReadParam'),
                                         args=[ msgvar, itervar,
                                                ExprAddrOf(picklevar) ])))
        ifread.addifstmt(StmtReturn.FALSE)

        ifnvalid = StmtIf(ExprNot(ExprCall(ExprSelect(tmpvar, '.', 'IsValid'))))
        ifnvalid.addifstmt(
            _protocolErrorBreakpoint('[' +
                                     _actorName(self.protocol.name, self.side) +
                                     '] Received an invalid file descriptor!'))

        read.addstmts([
            StmtDecl(Decl(_fdPickleType(), picklevar.name)),
            ifread,
            Whitespace.NL,
            StmtDecl(Decl(_fdType(), tmpvar.name),
                     init=ExprCall(ExprVar('FileDescriptor'),
                                   args=[ _fdBackstagePass(), picklevar ])),
            ifnvalid,
            Whitespace.NL,
            StmtExpr(ExprAssn(ExprDeref(var), tmpvar)),
            StmtReturn.TRUE
        ])

        self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])

    def implementStructPickling(self, structtype):
        msgvar = self.msgvar
        itervar = self.itervar
        var = self.var
        intype = _cxxConstRefType(structtype, self.side)
        outtype = _cxxPtrToType(structtype, self.side)
        sd = structtype._ast

        write = MethodDefn(self.writeMethodDecl(intype, var))
        read = MethodDefn(self.readMethodDecl(outtype, var))

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

        for f in sd.fields:
            desc = '\'' + f.getMethod().name + '\' (' + f.ipdltype.name() + \
                   ') member of \'' + intype.name + '\''
            writefield = self.checkedWrite(f.ipdltype, get('.', f), msgvar, sentinelKey=f.basename)
            readfield = self.checkedRead(f.ipdltype,
                                         ExprAddrOf(get('->', f)),
                                         msgvar, itervar, errfnRead, desc,
                                         sentinelKey=f.basename, errfnSentinel=errfnSentinel())
            if f.special and f.side != self.side:
                writefield = Whitespace(
                    "// skipping actor field that's meaningless on this side\n", indent=1)
                readfield = Whitespace(
                    "// skipping actor field that's meaningless on this side\n", indent=1)
            write.addstmt(writefield)
            read.addstmt(readfield)

        read.addstmt(StmtReturn.TRUE)

        self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])


    def implementUnionPickling(self, uniontype):
        msgvar = self.msgvar
        itervar = self.itervar
        var = self.var
        intype = _cxxConstRefType(uniontype, self.side)
        outtype = _cxxPtrToType(uniontype, self.side)
        ud = uniontype._ast

        typename = 'type__'
        uniontdef = Typedef(_cxxBareType(uniontype, typename), typename)

        typevar = ExprVar('type')
        writeswitch = StmtSwitch(ud.callType(var))
        readswitch = StmtSwitch(typevar)

        for c in ud.components:
            ct = c.ipdltype
            isactor = (ct.isIPDL() and ct.isActor())
            caselabel = CaseLabel(typename +'::'+ c.enum())
            origenum = c.enum()

            writecase = StmtBlock()
            if c.special and c.side != self.side:
                writecase.addstmt(_fatalError('wrong side!'))
            else:
                wexpr = ExprCall(ExprSelect(var, '.', c.getTypeName()))
                writecase.addstmt(self.checkedWrite(ct, wexpr, msgvar, sentinelKey=c.enum()))

            writecase.addstmt(StmtReturn())
            writeswitch.addcase(caselabel, writecase)

            readcase = StmtBlock()
            if c.special and c.side == self.side:
                # the type comes across flipped from what the actor
                # will be on this side; i.e. child->parent messages
                # have type PFooChild when received on the parent side
                # XXX: better error message
                readcase.addstmt(StmtReturn.FALSE)
            else:
                if c.special:
                    c = c.other       # see above
                tmpvar = ExprVar('tmp')
                ct = c.bareType()
                readcase.addstmts([
                    StmtDecl(Decl(ct, tmpvar.name), init=c.defaultValue()),
                    StmtExpr(ExprAssn(ExprDeref(var), tmpvar)),
                    self.checkedRead(
                        c.ipdltype,
                        ExprAddrOf(ExprCall(ExprSelect(var, '->',
                                                       c.getTypeName()))),
                        msgvar, itervar, errfnRead, 'Union type',
                        sentinelKey=origenum, errfnSentinel=errfnSentinel()),
                    StmtReturn(ExprLiteral.TRUE)
                ])

            readswitch.addcase(caselabel, readcase)

        unknowntype = 'unknown union type'
        writeswitch.addcase(DefaultLabel(),
                            StmtBlock([ _fatalError(unknowntype),
                                        StmtReturn() ]))
        readswitch.addcase(DefaultLabel(), StmtBlock(errfnRead(unknowntype)))

        write = MethodDefn(self.writeMethodDecl(intype, var))
        write.addstmts([
            uniontdef,
            self.checkedWrite(
                None, ExprCall(Type.INT, args=[ ud.callType(var) ]), msgvar,
                sentinelKey=uniontype.name()),
            Whitespace.NL,
            writeswitch
        ])

        read = MethodDefn(self.readMethodDecl(outtype, var))
        read.addstmts([
            uniontdef,
            StmtDecl(Decl(Type.INT, typevar.name)),
            self.checkedRead(
                None, ExprAddrOf(typevar), msgvar, itervar, errfnUnionType,
                [ uniontype.name() ],
                sentinelKey=uniontype.name(), errfnSentinel=errfnSentinel()),
            Whitespace.NL,
            readswitch,
        ])

        self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])


    def writeMethodDecl(self, intype, var, template=None):
        return MethodDecl(
            'Write',
            params=[ Decl(intype, var.name),
                     Decl(Type('Message', ptr=1), self.msgvar.name) ],
            T=template)

    def readMethodDecl(self, outtype, var, template=None):
        return MethodDecl(
            'Read',
            params=[ Decl(outtype, var.name),
                     Decl(Type('Message', ptr=1, const=1),
                          self.msgvar.name),
                     Decl(_iterType(ptr=1), self.itervar.name)],
            warn_unused=not template,
            T=template,
            ret=Type.BOOL)

    def maybeAddNullabilityArg(self, ipdltype, call):
        if ipdltype and ipdltype.isIPDL() and ipdltype.isActor():
            if ipdltype.nullable:
                call.args.append(ExprLiteral.TRUE)
            else:
                call.args.append(ExprLiteral.FALSE)
        return call

    def write(self, ipdltype, expr, to, this=None):
        write = ExprVar('Write')
        if this:  write = ExprSelect(this, '->', write.name)
        return self.maybeAddNullabilityArg(ipdltype,
                                           ExprCall(write, args=[ expr, to ]))

    def read(self, ipdltype, expr, from_, iterexpr, this=None):
        read = ExprVar('Read')
        if this:  read = ExprSelect(this, '->', read.name)
        return self.maybeAddNullabilityArg(
            ipdltype, ExprCall(read, args=[ expr, from_, iterexpr ]))

    def checkedWrite(self, ipdltype, expr, msgvar, sentinelKey, this=None):
        assert sentinelKey

        write = StmtExpr(self.write(ipdltype, expr, msgvar, this))

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


    def visitMessageDecl(self, md):
        isctor = md.decl.type.isCtor()
        isdtor = md.decl.type.isDtor()
        decltype = md.decl.type
        sendmethod = 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, (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 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:
            registerexpr = ExprCall(self.protocol.registerMethod(),
                                    args=[ actorvar ])
        else:
            registerexpr = ExprCall(self.protocol.registerIDMethod(),
                                    args=[ actorvar, idexpr ])

        return [
            self.failIfNullActor(actorvar, errfn, msg="Error constructing actor %s" % actortype.name() + self.side.capitalize()),
            # set manager in prior to register to inherit EventTarget from manager.
            StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetManager'), args=[ExprVar.THIS])),
            StmtExpr(registerexpr),
            StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetIPCChannel'),
                              args=[self.protocol.callGetChannel()])),
            StmtExpr(_callInsertManagedActor(
                self.protocol.managedVar(md.decl.type.constructedType(),
                                         self.side),
                actorvar)),
            StmtExpr(ExprAssn(_actorState(actorvar),
                              _startState(actorproto, fq=1)))
        ]

    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))

        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))
        ifnotpromise = StmtIf(ExprNot(ExprVar('promise')))
        ifnotpromise.addifstmts(errfnRecv("Error unknown promise",
                                          _Result.ProcessingError))
        promise = _makePromise(md.returns, self.side, resolver=True)
        promiseptr = _makePromise(md.returns, self.side, resolver=True)
        promiseptr.ptr = 1
        getpromise = [ Whitespace.NL,
                       StmtDecl(Decl(_refptr(promise), 'promise'),
                                init=ExprCall(ExprSelect(ExprCall(ExprSelect(self.protocol.callGetChannel(), '->', 'PopPromise'),
                                                                  args=[ self.msgvar ]),
                                                         '.', Type('downcast', T=promise)))),
                       ifnotpromise ]
        if len(md.returns) > 1:
            resolvearg = ExprCall(ExprVar('MakeTuple'),
                                  args=[ExprMove(p.var()) for p in md.returns])
        else:
            resolvearg = ExprMove(md.returns[0].var())

        resolvepromise = [ StmtExpr(ExprCall(ExprSelect(ExprVar('promise'), '->', 'Resolve'),
                                             args=[ resolvearg,
                                                    ExprVar('__func__')])) ]
        rejectpromise = [ StmtExpr(ExprCall(ExprSelect(ExprVar('promise'), '->', 'Reject'),
                                            args=[ reason, ExprVar('__func__') ])) ]
        ifresolve = StmtIf(resolve)
        ifresolve.addifstmts(desstmts)
        ifresolve.addifstmts(resolvepromise)
        ifresolve.addelsestmts(desrej)
        ifresolve.addelsestmts(rejectpromise)
        case.addstmts(prologue)
        case.addstmts(getpromise)
        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) ])

        (lbl, case) = self.genRecvAsyncReplyCase(md) if md.returns else (None, None)
        return method, (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)
            + [ 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)
            + [ 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)
            + 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 = None
        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 ]
                 + [ self.checkedWrite(p.ipdltype, p.var(), msgvar, sentinelKey=p.name, this=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 promise because actor is dead."),
                                  StmtReturn()])
        ifactorisdestroyed = StmtIf(ExprBinary(self.protocol.stateVar(), '==',
                                               self.protocol.deadState()))
        ifactorisdestroyed.addifstmts([_printWarningMessage("Not resolving promise 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 ])) ]
                            + [ self.checkedWrite(None, resolve, self.replyvar,
                                                  sentinelKey=resolve.name) ]
                            + [ self.checkedWrite(r.ipdltype, r.var(), self.replyvar,
                                                  sentinelKey=r.name)
                                 for r in md.returns ])
        resolverfn.addstmts(sendmsg)

        makepromise = [ 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 makepromise

    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 ]
            + [ self.checkedWrite(r.ipdltype, r.var(), replyvar, sentinelKey=r.name)
                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__))
            + [ self.checkedRead(p.ipdltype,
                                 ExprAddrOf(ExprVar(p.var().name + 'Copy')),
                                 msgexpr, ExprAddrOf(itervar),
                                 errfn, p.bareType(side).name,
                                 sentinelKey=p.name,
                                 errfnSentinel=errfnSentinel())
                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 = [ self.checkedRead(None, ExprAddrOf(handlevar), msgexpr,
                                       ExprAddrOf(self.itervar),
                                       errfn, "'%s'" % handletype.name,
                                       sentinelKey='actor', errfnSentinel=errfnSent) ]
            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 + [ self.checkedRead(p.ipdltype, ExprAddrOf(p.var()),
                                         msgexpr, ExprAddrOf(itervar),
                                         errfn, "'%s'" % p.bareType(side).name,
                                         sentinelKey=p.name, errfnSentinel=errfnSent)
                        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)),
                       self.checkedRead(None, ExprAddrOf(resolve), msgexpr,
                                        ExprAddrOf(itervar),
                                        errfn, "'%s'" % resolve.name,
                                        sentinelKey=resolve.name, errfnSentinel=errfnSent) ]
        desrej = [ StmtDecl(Decl(_PromiseRejectReason.Type(), reason.name)),
                   self.checkedRead(None, ExprAddrOf(reason), msgexpr,
                                    ExprAddrOf(itervar),
                                    errfn, "'%s'" % reason.name,
                                    sentinelKey=reason.name, errfnSentinel=errfnSent),
                   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 = [ self.checkedRead(None, ExprAddrOf(handlevar), msgexpr,
                                       ExprAddrOf(itervar),
                                       errfn, "'%s'" % handletype.name,
                                       sentinelKey='actor', errfnSentinel=errfnSent) ]
            start = 1

        stmts = (
            decls + [ StmtDecl(Decl(p.bareType(side), p.var().name))
                      for p in md.returns ]
            + [ Whitespace.NL ]
            + reads + [ self.checkedRead(p.ipdltype, ExprAddrOf(p.var()),
                                         msgexpr, ExprAddrOf(itervar),
                                         errfn, "'%s'" % p.bareType(side).name,
                                         sentinelKey=p.name, errfnSentinel=errfnSent)
                        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 ]
            + [ self.checkedRead(r.ipdltype, r.var(),
                                 ExprAddrOf(self.replyvar),
                                 ExprAddrOf(self.itervar),
                                 errfn, "'%s'" % r.bareType(side).name,
                                 sentinelKey=r.name, errfnSentinel=errfnSentinel)
                for r in md.returns ]
            + [ self.endRead(self.replyvar, itervar) ])

        return stmts

    def sendAsync(self, md, msgexpr, actor=None):
        sendok = ExprVar('sendok__')
        retvar = sendok
        if md.returns:
            retpromise = ExprVar('promise__')
            promise = _makePromise(md.returns, self.side, resolver=True)
            promisedecl = [ Whitespace.NL,
                            StmtDecl(Decl(_refptr(promise), retpromise.name),
                                     init=ExprNew(promise, args=[ExprVar('__func__')])) ]
            rejectifsendok = StmtIf(ExprNot(sendok))
            rejectifsendok.addifstmts(
                [ StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Reject'),
                                    args=[ _PromiseRejectReason.SendError,
                                           ExprVar('__func__') ])) ])
        sendargs = [ msgexpr ]
        stmts = [ Whitespace.NL,
                  self.logMessage(md, msgexpr, 'Sending ', actor),
                  self.profilerLabel(md) ] + self.transition(md, actor)

        if md.returns:
            sendargs.append(ExprCall(ExprSelect(retpromise, '.', 'get')))
            sendargs.append(ExprVar('this'))
            stmts.extend(promisedecl)
            retvar = retpromise

        stmts.extend([ Whitespace.NL,
                       StmtDecl(Decl(Type.BOOL, sendok.name),
                                init=ExprCall(
                                    ExprSelect(self.protocol.callGetChannel(actor),
                                               '->', 'Send'),
                                    args=sendargs)) ])
        if md.returns:
            stmts.append(rejectifsendok)

        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)
            + [ 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 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.static = 1
        return decl

    def makeSendMethodDecl(self, md):
        implicit = md.decl.type.hasImplicitActorParam()
        if md.decl.type.isAsync() and md.returns:
            returnsems = 'promise'
            rettype = _refptr(Type(md.promiseName()))
        else:
            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'),
            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):
        if actor is not None:  stateexpr = _actorState(actor)
        else:                  stateexpr = self.protocol.stateVar()

        msgid = md.pqMsgId() if not reply else md.pqReplyId()
        return [ StmtExpr(ExprCall(ExprVar(self.protocol.name +'::Transition'),
                                   args=[ ExprVar(msgid),
                                   ExprAddrOf(stateexpr) ])) ]

    def checkedRead(self, ipdltype, expr, msgexpr, iterexpr, errfn, paramtype, sentinelKey, errfnSentinel, sentinel=True):
        ifbad = StmtIf(ExprNot(self.read(ipdltype, expr, msgexpr, iterexpr)))
        if isinstance(paramtype, list):
            errorcall = errfn(*paramtype)
            senterrorcall = errfnSentinel(*paramtype)
        else:
            errorcall = errfn('Error deserializing ' + paramtype)
            senterrorcall = errfnSentinel('Error deserializing ' + paramtype)
        ifbad.addifstmts(errorcall)

        block = Block()
        block.addstmt(ifbad)

        if sentinel:
            assert sentinelKey
            block.addstmt(Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n', indent=1))
            read = ExprCall(ExprSelect(msgexpr, '->', 'ReadSentinel'),
                                  args=[ iterexpr, ExprLiteral.Int(hashfunc(sentinelKey)) ])
            ifsentinel = StmtIf(ExprNot(read))
            ifsentinel.addifstmts(senterrorcall)
            block.addstmt(ifsentinel)

        return block

    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.name)
            cls.stmts[i] = StmtDecl(decl)
            defns.addstmts([ defn, Whitespace.NL ])

    return cls, defns

def _splitMethodDefn(md, clsname):
    saveddecl = deepcopy(md.decl)
    md.decl.name = (clsname +'::'+ md.decl.name)
    md.decl.virtual = 0
    md.decl.static = 0
    md.decl.warn_unused = 0
    md.decl.never_inline = 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.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 not md.pure:
            return
        decl = deepcopy(md)
        decl.pure = 0
        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, virtual=1)))
        # FIXME/cjones: hack!
        dtor = DestructorDefn(ConstructorDecl(self.implname('~' +self.name)))
        dtor.addstmt(StmtExpr(ExprCall(ExprVar( 'MOZ_COUNT_DTOR'),
                                               [ ExprVar(self.name) ])))
        self.addmethodimpl(dtor)

    def addmethodimpl(self, impl):
        self.methodimpls.addstmts([ impl, Whitespace.NL ])

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