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