Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/ipc/ipdl/ipdl/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 176 kB image not shown  

Quelle  lower.py   Sprache: Python

 
# 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 re
from copy import deepcopy
from collections import OrderedDict
import itertools

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


# -----------------------------------------------------------------------------
# "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_str(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 _deleteId():
    return ExprVar("Msg___delete____ID")


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


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


def _makeForwardDeclForQClass(clsname, quals, cls=True, struct=False):
    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|."""
    return "Send"


def _recvPrefix(msgtype):
    """Prefix of the name of the C++ method that handles |msgtype|."""
    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 not ipdltype.isIPDL():
        return ipdltype.name()
    if ipdltype.isArray():
        return "ArrayOf" + _flatTypeName(ipdltype.basetype)
    if ipdltype.isMaybe():
        return "Maybe" + _flatTypeName(ipdltype.basetype)
    # NotNull and UniquePtr types just assume the underlying variant name
    # to avoid unnecessary noise, as eg a NotNull<T> and T should never exist
    # in the same union.
    if ipdltype.isNotNull() or ipdltype.isUniquePtr():
        return _flatTypeName(ipdltype.basetype)
    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.hasBaseType() and _hasVisibleActor(ipdltype.basetype))
    )


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


def _refptr(T):
    return Type("RefPtr", T=T)


def _alreadyaddrefed(T):
    return Type("already_AddRefed", T=T)


def _tuple(types, const=False, ref=False):
    return Type("std::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)

    # MozPromise is purposefully made to be exclusive only. Really, we mean it.
    return _promise(
        resolvetype, _ResponseRejectReason.Type(), ExprLiteral.TRUE, resolver=resolver
    )


def _resolveType(returns, side):
    if len(returns) > 1:
        return _tuple([d.inType(side, "send"for d in returns])
    return returns[0].inType(side, "send")


def _makeResolver(returns, side):
    return TypeFunction([Decl(_resolveType(returns, side), "")])


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


def _cxxSpanType(basetype, const=False, ref=False):
    basetype = deepcopy(basetype)
    basetype.rightconst = True
    return Type(
        "mozilla::Span", T=basetype, const=const, ref=ref, hasimplicitcopyctor=True
    )


def _cxxMaybeType(basetype, const=False, ref=False):
    return Type(
        "mozilla::Maybe",
        T=basetype,
        const=const,
        ref=ref,
        hasimplicitcopyctor=basetype.hasimplicitcopyctor,
    )


def _cxxReadResultType(basetype, const=False, ref=False):
    return Type(
        "IPC::ReadResult",
        T=basetype,
        const=const,
        ref=ref,
        hasimplicitcopyctor=basetype.hasimplicitcopyctor,
    )


def _cxxNotNullType(basetype, const=False, ref=False):
    return Type(
        "mozilla::NotNull",
        T=basetype,
        const=const,
        ref=ref,
        hasimplicitcopyctor=basetype.hasimplicitcopyctor,
    )


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


def _cxxLifecycleProxyType(ptr=False):
    return Type("mozilla::ipc::ActorLifecycleProxy", ptr=ptr)


def _cxxSide(side):
    if side == "child":
        return ExprVar("mozilla::ipc::ChildSide")
    if side == "parent":
        return ExprVar("mozilla::ipc::ParentSide")
    assert 0


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


# 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 _sentinelReadError(classname):
    return StmtExpr(
        ExprCall(
            ExprVar("mozilla::ipc::SentinelReadError"),
            args=[ExprLiteral.String(classname)],
        )
    )


identifierRegExp = re.compile("[a-zA-Z_][a-zA-Z0-9_]*")


def _validCxxIdentifier(name):
    return identifierRegExp.fullmatch(name) is not None


# 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")
    Dropped = ExprVar("MsgDropped")
    NotKnown = ExprVar("MsgNotKnown")
    NotAllowed = ExprVar("MsgNotAllowed")
    PayloadError = ExprVar("MsgPayloadError")
    ProcessingError = ExprVar("MsgProcessingError")
    ValuError = ExprVar("MsgValueError")  # [sic]


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

# used in user-facing Send*() methods


def errfnSend(msg, errcode=ExprLiteral.FALSE):
    return [_fatalError(msg), StmtReturn(errcode)]


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


# TODO should this error handling be strengthened for dtors?


def errfnSendDtor(msg):
    return [_printErrorMessage(msg), StmtReturn.FALSE]


# used in |OnMessage*()| handlers that hand in-messages off to Recv*()
# interface methods


def errfnRecv(msg, errcode=_Result.ValuError):
    return [_fatalError(msg), StmtReturn(errcode)]


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

    return inner


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


def readResultError():
    return ExprCode("{}")


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

    Deletion = ExprVar("Deletion")
    AncestorDeletion = ExprVar("AncestorDeletion")
    NormalShutdown = ExprVar("NormalShutdown")
    AbnormalShutdown = ExprVar("AbnormalShutdown")
    FailedConstructor = ExprVar("FailedConstructor")
    ManagedEndpointDropped = ExprVar("ManagedEndpointDropped")


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

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


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


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

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

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

    def visitBuiltinCType(self, b):
        return Type(self.typename(b))

    def visitActorType(self, a):
        if self.side is None:
            return Type(
                "::mozilla::ipc::SideVariant",
                T=[
                    _cxxBareType(a, "parent", self.fq),
                    _cxxBareType(a, "child", self.fq),
                ],
            )
        return Type(_actorName(self.typename(a.protocol), self.side), ptr=True)

    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 visitMaybeType(self, m):
        basecxxtype = m.basetype.accept(self)
        return _cxxMaybeType(basecxxtype)

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

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

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

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

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

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

    def visitNotNullType(self, n):
        basecxxtype = n.basetype.accept(self)
        return _cxxNotNullType(basecxxtype)

    def visitProtocolType(self, p):
        assert 0

    def visitMessageType(self, m):
        assert 0

    def visitVoidType(self, v):
        assert 0


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


def _cxxRefType(ipdltype, side):
    t = _cxxBareType(ipdltype, side)
    t.ref = True
    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 = True
        return t
    if ipdltype.isIPDL() and ipdltype.isNotNull():
        # If the inner type chooses to use a raw pointer, wrap that instead.
        inner = _cxxConstRefType(ipdltype.basetype, side)
        if inner.ptr:
            t = _cxxNotNullType(inner)
            return t
    if ipdltype.isIPDL() and ipdltype.hasBaseType():
        # Keep same constness as inner type.
        inner = _cxxConstRefType(ipdltype.basetype, side)
        t.const = inner.const or not inner.ref
        t.ref = True
        return t
    if ipdltype.isCxx() and (ipdltype.isSendMoveOnly() or ipdltype.isDataMoveOnly()):
        t.const = True
        t.ref = True
        return t
    if ipdltype.isCxx() and ipdltype.isRefcounted():
        # Use T* instead of const RefPtr<T>&
        t = t.T
        t.ptr = True
        return t
    t.const = True
    t.ref = True
    return t


def _cxxTypeNeedsMoveForSend(ipdltype, context="root", visited=None):
    """Returns `True` if serializing ipdltype requires a mutable reference, e.g.
    because the underlying resource represented by the value is being
    transferred to another process. This is occasionally distinct from whether
    the C++ type exposes a copy constructor, such as for types which are not
    cheaply copiable, but are not mutated when serialized."""

    if visited is None:
        visited = set()

    visited.add(ipdltype)

    if ipdltype.isCxx():
        return ipdltype.isSendMoveOnly()

    if ipdltype.isIPDL():
        if ipdltype.hasBaseType():
            return _cxxTypeNeedsMoveForSend(ipdltype.basetype, "wrapper", visited)
        if ipdltype.isStruct() or ipdltype.isUnion():
            return any(
                _cxxTypeNeedsMoveForSend(t, "compound", visited)
                for t in ipdltype.itercomponents()
                if t not in visited
            )

        # For historical reasons, shmem is `const_cast` to a mutable reference
        # when being stored in a struct or union (see
        # `_StructField.constRefExpr` and `_UnionMember.getConstValue`), meaning
        # that they do not cause the containing struct to require move for
        # sending.
        if ipdltype.isShmem():
            return context != "compound"

        return (
            ipdltype.isByteBuf()
            or ipdltype.isEndpoint()
            or ipdltype.isManagedEndpoint()
        )

    return False


def _cxxTypeNeedsMoveForData(ipdltype, context="root", visited=None):
    """Returns `True` if the bare C++ type corresponding to ipdltype does not
    satisfy std::is_copy_constructible_v<T>. All C++ types supported by IPDL
    must support std::is_move_constructible_v<T>, so non-movable types must be
    passed behind a `UniquePtr`."""

    if visited is None:
        visited = set()

    visited.add(ipdltype)

    if ipdltype.isCxx():
        return ipdltype.isDataMoveOnly()

    if ipdltype.isIPDL():
        if ipdltype.isUniquePtr():
            return True

        # When nested within a maybe or array, arrays are no longer copyable.
        if context == "wrapper" and ipdltype.isArray():
            return True
        if ipdltype.hasBaseType():
            return _cxxTypeNeedsMoveForData(ipdltype.basetype, "wrapper", visited)
        if ipdltype.isStruct() or ipdltype.isUnion():
            return any(
                _cxxTypeNeedsMoveForData(t, "compound", visited)
                for t in ipdltype.itercomponents()
                if t not in visited
            )
        return (
            ipdltype.isByteBuf()
            or ipdltype.isEndpoint()
            or ipdltype.isManagedEndpoint()
        )

    return False


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


def _cxxForceMoveRefType(ipdltype, side):
    assert _cxxTypeCanMove(ipdltype)
    t = _cxxBareType(ipdltype, side)
    t.rvalref = True
    return t


def _cxxPtrToType(ipdltype, side):
    t = _cxxBareType(ipdltype, side)
    if ipdltype.isIPDL() and ipdltype.isActor() and side is not None:
        t.ptr = False
        t.ptrptr = True
        return t
    t.ptr = True
    return t


def _cxxConstPtrToType(ipdltype, side):
    t = _cxxBareType(ipdltype, side)
    if ipdltype.isIPDL() and ipdltype.isActor() and side is not None:
        t.ptr = False
        t.ptrconstptr = True
        return t
    t.const = True
    t.ptr = True
    return t


def _cxxInType(ipdltype, side, direction):
    t = _cxxBareType(ipdltype, side)
    if ipdltype.isIPDL() and ipdltype.isActor():
        return t
    if ipdltype.isIPDL() and ipdltype.isNotNull():
        # If the inner type chooses to use a raw pointer, wrap that instead.
        inner = _cxxInType(ipdltype.basetype, side, direction)
        if inner.ptr:
            t = _cxxNotNullType(inner)
            return t
    if _cxxTypeNeedsMoveForSend(ipdltype):
        t.rvalref = True
        return t
    if ipdltype.isCxx():
        if ipdltype.isRefcounted():
            # Use T* instead of const RefPtr<T>&
            t = t.T
            t.ptr = True
            return t
        if ipdltype.name() == "nsCString":
            t = Type("nsACString")
        if ipdltype.name() == "nsString":
            t = Type("nsAString")
    # Use Span<T const> rather than nsTArray<T> for array types which aren't
    # `_cxxTypeNeedsMoveForSend`. This is only done for the "send" side, and not
    # for recv signatures.
    if direction == "send" and ipdltype.isIPDL() and ipdltype.isArray():
        inner = _cxxBareType(ipdltype.basetype, side)
        return _cxxSpanType(inner)

    t.const = True
    t.ref = True
    return t


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


def _deallocMethod(ptype, side):
    return "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, attributes={}):
        self.ipdltype = ipdltype
        self.name = name
        self.attributes = attributes

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

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

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

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

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

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

    def inType(self, side, direction):
        """Return this decl's C++ Type with sending inparam semantics."""
        return _cxxInType(self.ipdltype, side, direction)

    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 = False
            t.ptrptr = True
            return t
        t.ptr = True
        return t

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


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


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


class _CompoundTypeComponent(_HybridDecl):
    # @override the following methods to make the side argument optional.
    def bareType(self, side=None, fq=False):
        return _HybridDecl.bareType(self, side, fq=fq)

    def refType(self, side=None):
        return _HybridDecl.refType(self, side)

    def constRefType(self, side=None):
        return _HybridDecl.constRefType(self, side)

    def ptrToType(self, side=None):
        return _HybridDecl.ptrToType(self, side)

    def constPtrToType(self, side=None):
        return _HybridDecl.constPtrToType(self, side)

    def forceMoveType(self, side=None):
        return _HybridDecl.forceMoveType(self, side)


class StructDecl(ipdl.ast.StructDecl, HasFQName):
    def fields_ipdl_order(self):
        for f in self.fields:
            yield f

    def fields_member_order(self):
        assert len(self.packed_field_order) == len(self.fields)

        for i in self.packed_field_order:
            yield self.fields[i]

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


class _StructField(_CompoundTypeComponent):
    def __init__(self, ipdltype, name, sd):
        self.basename = name

        _CompoundTypeComponent.__init__(self, ipdltype, name)

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

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

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

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

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


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

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


class _UnionMember(_CompoundTypeComponent):
    """Not in the AFL sense, but rather a member (e.g. |int;|) of an
    IPDL union type."""

    def __init__(self, ipdltype, ud):
        flatname = _flatTypeName(ipdltype)
        assert _validCxxIdentifier(flatname)

        _CompoundTypeComponent.__init__(self, ipdltype, "mV" + flatname)
        self.flattypename = flatname

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

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

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

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

    def unionType(self):
        """Type used for storage in generated C union decl."""
        if self.recursive:
            return self.ptrToType()
        else:
            return self.internalType()

    def unionValue(self):
        return ExprVar(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 callCtor(self, expr=None):
        assert not isinstance(expr, list)

        if expr is None:
            args = None
        elif (
            self.ipdltype.isIPDL()
            and self.ipdltype.isArray()
            and not isinstance(expr, ExprMove)
        ):
            args = [ExprCall(ExprSelect(expr, ".""Clone"), args=[])]
        else:
            args = [expr]

        if self.recursive:
            return ExprAssn(self.callGetPtr(), ExprNew(self.bareType(), args=args))
        else:
            return ExprNew(
                self.bareType(),
                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 ExprAddrOf(v)

    def constptrToSelfExpr(self):
        """|*constptrToSelfExpr()| has type |self.constType()|"""
        return self.ptrToSelfExpr()

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

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

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

    def getConstValue(self):
        v = ExprDeref(self.callGetConstPtr())
        # sigh
        if "Shmem" == self.ipdltype.name():
            v = ExprCast(v, Type("Shmem", ref=True), const=True)
        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 name

    def sendMethod(self):
        name = _sendPrefix(self.decl.type) + self.baseName()
        if self.decl.type.isCtor():
            name += "Constructor"
        return 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=True, direction=None
    ):
        """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 (
                self.decl.type.tainted
                and "NoTaint" not in d.attributes
                and direction == "recv"
            ):
                # Tainted types are passed by-value, allowing the receiver to move them if desired.
                assert sems != "out"
                return Decl(Type("Tainted", T=d.bareType(side)), d.name)

            if sems == "in":
                t = d.inType(side, direction)
                # If this is the `recv` side, and we're not using "move"
                # semantics, that means we're an alloc method, and cannot accept
                # values by rvalue reference. Downgrade to an lvalue reference.
                if direction == "recv" and t.rvalref:
                    t.rvalref = False
                    t.ref = True
                return Decl(t, d.name)
            elif sems == "move":
                assert direction == "recv"
                # For legacy reasons, use an rvalue reference when generating
                # parameters for recv methods which accept arrays.
                if d.ipdltype.isIPDL() and d.ipdltype.isArray():
                    t = d.bareType(side)
                    t.rvalref = True
                    return Decl(t, d.name)
                return Decl(d.inType(side, direction), d.name)
            elif sems == "out":
                return Decl(d.outType(side), d.name)
            else:
                assert 0

        def makeResolverDecl(returns):
            return Decl(Type(self.resolverName(), rvalref=True), "aResolve")

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

            return Decl(
                Type("mozilla::ipc::ResolveCallback", T=resolvetype, rvalref=True),
                "aResolve",
            )

        def makeCallbackRejectDecl(returns):
            return Decl(Type("mozilla::ipc::RejectCallback", rvalref=True), "aReject")

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

        if returnsems == "promise" and self.returns:
            pass
        elif returnsems == "callback" and self.returns:
            cxxparams.extend(
                [
                    makeCallbackResolveDecl(self.returns),
                    makeCallbackRejectDecl(self.returns),
                ]
            )
        elif returnsems == "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=True
    ):
        assert not retcallsems or retsems  # retcallsems => returnsems
        cxxargs = []

        if paramsems == "move":
            # We don't std::move() RefPtr<T> types because current Recv*()
            # implementors take these parameters as T*, and
            # std::move(RefPtr<T>) doesn't coerce to T*.
            # We also don't move NotNull, as it has no move constructor.
            cxxargs.extend(
                [
                    (
                        p.var()
                        if p.ipdltype.isRefcounted()
                        or (p.ipdltype.isIPDL() and p.ipdltype.isNotNull())
                        else ExprMove(p.var())
                    )
                    for p in self.params
                ]
            )
        elif paramsems == "in":
            cxxargs.extend([p.var() for p in self.params])
        else:
            assert False

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

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

        return cxxargs

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


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


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

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


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

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

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

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

    def deallocManageeMethod(self):
        return ExprVar("DeallocManagee")

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

    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=False, ref=False):
        assert self.decl.type.isManagerOf(actortype)
        return _cxxManagedContainerType(
            Type(_actorName(actortype.name(), side)), const=const, ref=ref
        )

    def subtreeUsesShmem(self):
        return _subtreeUsesShmem(self)

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


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


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

pod_types = {
    "::int8_t": 1,
    "::uint8_t": 1,
    "::int16_t": 2,
    "::uint16_t": 2,
    "::int32_t": 4,
    "::uint32_t": 4,
    "::int64_t": 8,
    "::uint64_t": 8,
    "float": 4,
    "double": 8,
}
max_pod_size = max(pod_types.values())
# We claim that all types we don't recognize are automatically "bigger"
# than pod types for ease of sorting.
pod_size_sentinel = max_pod_size * 2


def pod_size(ipdltype):
    if not ipdltype.isCxx():
        return pod_size_sentinel

    return pod_types.get(ipdltype.fullname(), pod_size_sentinel)


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

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

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

    def visitStructDecl(self, sd):
        if not isinstance(sd, StructDecl):
            newfields = [_StructField(f.decl.type, f.name, sd) for f in sd.fields]

            # Compute a permutation of the fields for in-memory storage such
            # that the memory layout of the structure will be well-packed.
            permutation = list(range(len(newfields)))

            # Note that the results of `pod_size` ensure that non-POD fields
            # sort before POD ones.
            def size(idx):
                return pod_size(newfields[idx].ipdltype)

            permutation.sort(key=size, reverse=True)

            sd.fields = newfields
            sd.packed_field_order = permutation
            StructDecl.upgrade(sd)

    def visitUnionDecl(self, ud):
        ud.components = [_UnionMember(ctype, ud) for ctype in ud.decl.type.components]
        UnionDecl.upgrade(ud)

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

    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)
            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, typesToIncludes)
            if isinstance(su, ipdl.ast.StructDecl):
                aggregateTypeIncludes.add("mozilla/ipc/IPDLStructMember.h")
                for f in su.fields:
                    f.ipdltype.accept(typedeps)
            elif isinstance(su, ipdl.ast.UnionDecl):
                for c in su.components:
                    c.ipdltype.accept(typedeps)

            aggregateTypeIncludes.update(typedeps.includeHeaders)

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

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

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

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

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

        cf = self.cppfile
        cf.addthings(
            (
                [_DISCLAIMER, Whitespace.NL]
                + [
                    CppDirective("include"'"' + h + '"')
                    for h in self.cppIncludeHeaders
                ]
                + [Whitespace.NL]
                + [
                    CppDirective("include"'"%s"' % filename)
                    for filename in ipdl.builtin.CppIncludes
                ]
                + [Whitespace.NL]
            )
        )

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

        cf.addthings(self.structUnionDefns)

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

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

    def visitInclude(self, inc):
        if inc.tu.filetype == "header":
            self.hdrfile.addthing(
                CppDirective("include"'"' + _ipdlhHeaderName(inc.tu) + '.h"')
            )
            # Inherit cpp includes defined by imported header files, as they may
            # be required to serialize an imported `using` type.
            for cxxinc in inc.tu.cxxIncludes:
                cxxinc.accept(self)
        else:
            self.cppIncludeHeaders += [
                _protocolHeaderName(inc.tu.protocol, "parent") + ".h",
                _protocolHeaderName(inc.tu.protocol, "child") + ".h",
            ]

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

            clsdecl, methoddefns = _splitClassDeclDefn(cls)

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

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

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

        while len(decls) > 0:
            _, (d, t) = decls.popitem(False)
            gen_struct(d, t)

    def visitProtocol(self, p):
        self.cppIncludeHeaders.append(_protocolHeaderName(self.protocol, "") + ".h")
        self.cppIncludeHeaders.append(
            _protocolHeaderName(self.protocol, "Parent") + ".h"
        )
        self.cppIncludeHeaders.append(
            _protocolHeaderName(self.protocol, "Child") + ".h"
        )

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

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

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

        for func in self.genEndpointFuncs():
            edecl, edefn = _splitFuncDeclDefn(func)
            ns.addstmts([edecl, Whitespace.NL])
            self.funcDefns.append(edefn)

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

        for md in p.messageDecls:
            decls = []

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

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

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

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

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

    # Generate code for PFoo::CreateEndpoints.
    def genEndpointFuncs(self):
        p = self.protocol.decl.type
        tparent = _cxxBareType(ActorType(p), "Parent", fq=True)
        tchild = _cxxBareType(ActorType(p), "Child", fq=True)

        def mkOverload(includepids):
            params = []
            if includepids:
                params = [
                    Decl(Type("mozilla::ipc::EndpointProcInfo"), "aParentDestInfo"),
                    Decl(Type("mozilla::ipc::EndpointProcInfo"), "aChildDestInfo"),
                ]
            params += [
                Decl(
                    Type("mozilla::ipc::Endpoint<" + tparent.name + ">", ptr=True),
                    "aParent",
                ),
                Decl(
                    Type("mozilla::ipc::Endpoint<" + tchild.name + ">", ptr=True),
                    "aChild",
                ),
            ]
            openfunc = MethodDefn(
                MethodDecl("CreateEndpoints", params=params, ret=Type.NSRESULT)
            )
            openfunc.addcode(
                """
                return mozilla::ipc::CreateEndpoints(
                    mozilla::ipc::PrivateIPDLInterface(),
                    $,{args});
                """,
                args=[ExprVar(d.name) for d in params],
            )
            return openfunc

        funcs = [mkOverload(True)]
        if not p.hasOtherPid():
            funcs.append(mkOverload(False))
        return funcs


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

cppPriorityList = list(
    map(lambda src: src.upper() + "_PRIORITY", ipdl.ast.priorityList)
)


def _generateMessageConstructor(md, segmentSize, protocol, forReply=False):
    if forReply:
        clsname = md.replyCtorFunc()
        msgid = md.replyId()
        replyEnum = "REPLY"
        prioEnum = cppPriorityList[md.decl.type.replyPrio]
    else:
        clsname = md.msgCtorFunc()
        msgid = md.msgId()
        replyEnum = "NOT_REPLY"
        prioEnum = cppPriorityList[md.decl.type.prio]

    nested = md.decl.type.nested
    compress = md.decl.type.compress
    lazySend = md.decl.type.lazySend

    routingId = ExprVar("routingId")

    func = FunctionDefn(
        FunctionDecl(
            clsname,
            params=[Decl(Type("int32_t"), routingId.name)],
            ret=Type("mozilla::UniquePtr"),
        )
    )

    if not compress:
        compression = "COMPRESSION_NONE"
    elif compress.value == "all":
        compression = "COMPRESSION_ALL"
    else:
        assert compress.value is None
        compression = "COMPRESSION_ENABLED"

    if lazySend:
        lazySendEnum = "LAZY_SEND"
    else:
        lazySendEnum = "EAGER_SEND"

    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 md.decl.type.isSync():
        syncEnum = "SYNC"
    else:
        syncEnum = "ASYNC"

    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(lazySendEnum),
            messageEnum(ctorEnum),
            messageEnum(syncEnum),
            messageEnum(replyEnum),
        ],
    )

    segmentSize = int(segmentSize)
    if not segmentSize:
        segmentSize = 0
    func.addstmt(
        StmtReturn(
            ExprCall(
                ExprVar("IPC::Message::IPDLMessage"),
                args=[
                    routingId,
                    ExprVar(msgid),
                    ExprLiteral.Int(int(segmentSize)),
                    flags,
                ],
            )
        )
    )

    return func


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


class _ParamTraits:
    var = ExprVar("aVar")
    writervar = ExprVar("aWriter")
    readervar = ExprVar("aReader")

    @classmethod
    def ifsideis(cls, rdrwtr, side, then, els=None):
        ifstmt = StmtIf(
            ExprBinary(
                _cxxSide(side),
                "==",
                ExprCode("${rdrwtr}->GetActor()->GetSide()", rdrwtr=rdrwtr),
            )
        )
        ifstmt.addifstmt(then)
        if els is not None:
            ifstmt.addelsestmt(els)
        return ifstmt

    @classmethod
    def fatalError(cls, rdrwtr, reason):
        return StmtCode(
            "${rdrwtr}->FatalError(${reason});",
            rdrwtr=rdrwtr,
            reason=ExprLiteral.String(reason),
        )

    @classmethod
    def writeSentinel(cls, writervar, sentinelKey):
        return [
            Whitespace("// Sentinel = " + repr(sentinelKey) + "\n", indent=True),
            StmtExpr(
                ExprCall(
                    ExprSelect(writervar, "->""WriteSentinel"),
                    args=[ExprLiteral.Int(hashfunc(sentinelKey))],
                )
            ),
        ]

    @classmethod
    def readSentinel(cls, readervar, sentinelKey, sentinelFail):
        # Read the sentinel
        read = ExprCall(
            ExprSelect(readervar, "->""ReadSentinel"),
            args=[ExprLiteral.Int(hashfunc(sentinelKey))],
        )
        ifsentinel = StmtIf(ExprNot(read))
        ifsentinel.addifstmts(sentinelFail)

        return [
            Whitespace("// Sentinel = " + repr(sentinelKey) + "\n", indent=True),
            ifsentinel,
        ]

    @classmethod
    def write(cls, var, writervar, ipdltype=None):
        if ipdltype and _cxxTypeNeedsMoveForSend(ipdltype):
            var = ExprMove(var)
        return ExprCall(ExprVar("IPC::WriteParam"), args=[writervar, var])

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

        block.addstmts(
            [
                StmtExpr(cls.write(var, writervar, ipdltype)),
            ]
        )
        block.addstmts(cls.writeSentinel(writervar, sentinelKey))
        return block

    @classmethod
    def bulkSentinelKey(cls, fields):
        return " | ".join(f.basename for f in fields)

    @classmethod
    def checkedBulkWrite(cls, var, size, fields):
        block = Block()
        first = fields[0]

        block.addstmts(
            [
                StmtExpr(
                    ExprCall(
                        ExprSelect(cls.writervar, "->""WriteBytes"),
                        args=[
                            ExprAddrOf(
                                ExprCall(first.getMethod(thisexpr=var, sel="."))
                            ),
                            ExprLiteral.Int(size * len(fields)),
                        ],
                    )
                )
            ]
        )
        block.addstmts(cls.writeSentinel(cls.writervar, cls.bulkSentinelKey(fields)))

        return block

    @classmethod
    def checkedBulkRead(cls, var, size, fields):
        block = Block()
        first = fields[0]

        readbytes = ExprCall(
            ExprSelect(cls.readervar, "->""ReadBytesInto"),
            args=[
                ExprAddrOf(ExprCall(first.getMethod(thisexpr=var, sel="->"))),
                ExprLiteral.Int(size * len(fields)),
            ],
        )
        ifbad = StmtIf(ExprNot(readbytes))
        errmsg = "Error bulk reading fields from %s" % first.ipdltype.name()
        ifbad.addifstmts(
            [cls.fatalError(cls.readervar, errmsg), StmtReturn(readResultError())]
        )
        block.addstmt(ifbad)
        block.addstmts(
            cls.readSentinel(
                cls.readervar,
                cls.bulkSentinelKey(fields),
                errfnSentinel(readResultError())(errmsg),
            )
        )

        return block

    @classmethod
    def checkedRead(
        cls,
        ipdltype,
        cxxtype,
        var,
        readervar,
        errfn,
        paramtype,
        sentinelKey,
        errfnSentinel,
    ):
        assert isinstance(var, ExprVar)

        if not isinstance(paramtype, list):
            paramtype = ["Error deserializing " + paramtype]

        block = Block()

        # Read the data
        block.addcode(
            """
            auto ${maybevar} = IPC::ReadParam<${ty}>(${reader});
            if (!${maybevar}) {
                $*{errfn}
            }
            auto& ${var} = *${maybevar};
            """,
            maybevar=ExprVar("maybe__" + var.name),
            ty=cxxtype,
            reader=readervar,
            errfn=errfn(*paramtype),
            var=var,
        )

        block.addstmts(
            cls.readSentinel(readervar, sentinelKey, errfnSentinel(*paramtype))
        )

        return block

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

        return cls.checkedRead(
            ipdltype,
            cxxtype,
            var,
            cls.readervar,
            errfn=errfn,
            paramtype=what,
            sentinelKey=sentinelKey,
            errfnSentinel=errfnSentinel(readResultError()),
        )

    @classmethod
    def generateDecl(cls, fortype, write, read, needsmove=False):
        # ParamTraits impls are selected ignoring constness, and references.
        pt = Class(
            "ParamTraits",
            specializes=Type(
                fortype.name, T=fortype.T, inner=fortype.inner, ptr=fortype.ptr
            ),
            struct=True,
        )

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

        # static void Write(Message*, const T&);
        if needsmove:
            intype = Type("paramType", rvalref=True)
        else:
            intype = Type("paramType", ref=True, const=True)
        writemthd = MethodDefn(
            MethodDecl(
                "Write",
                params=[
                    Decl(Type("IPC::MessageWriter", ptr=True), cls.writervar.name),
                    Decl(intype, cls.var.name),
                ],
                methodspec=MethodSpec.STATIC,
            )
        )
        writemthd.addstmts(write)
        pt.addstmt(writemthd)

        # static ReadResult<T> Read(MessageReader*);
        readmthd = MethodDefn(
            MethodDecl(
                "Read",
                params=[
                    Decl(Type("IPC::MessageReader", ptr=True), cls.readervar.name),
                ],
                ret=Type("IPC::ReadResult"),
                methodspec=MethodSpec.STATIC,
            )
        )
        readmthd.addstmts(read)
        pt.addstmt(readmthd)

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

        namespaces = [Namespace("IPC")]
        clsns = _putInNamespaces(clsdecl, namespaces)
        defns = _putInNamespaces(methoddefns, namespaces)
        return clsns, defns

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

        cxxtype = _cxxBareType(actortype, side, fq=True)

        write = StmtCode(
            """
            MOZ_RELEASE_ASSERT(
                ${writervar}->GetActor(),
                "Cannot serialize managed actors without an actor");

            int32_t id;
            if (!${var}) {
                id = 0;  // kNullActorId
            } else {
                id = ${var}->Id();
                if (id == 1) {  // kFreedActorId
                    ${var}->FatalError("Actor has been |delete|d");
                }
                MOZ_RELEASE_ASSERT(
                    ${writervar}->GetActor()->GetIPCChannel() == ${var}->GetIPCChannel(),
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=93 G=94

¤ Dauer der Verarbeitung: 0.32 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.