tenfourfox/ipc/ipdl/ipdl/lower.py
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

5619 lines
205 KiB
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 os, re, sys
from copy import deepcopy
from collections import OrderedDict
import ipdl.ast
import ipdl.builtin
from ipdl.cxx.ast import *
from ipdl.type import Actor, ActorType, ProcessGraph, TypeVisitor, builtinHeaderIncludes
# FIXME/cjones: the chromium Message logging code doesn't work on
# gcc/POSIX, because it wprintf()s across the chromium/mozilla
# boundary. one side builds with -fshort-wchar, the other doesn't.
# this code will remain off until the chromium base lib is replaced
EMIT_LOGGING_CODE = ('win32' == sys.platform)
##-----------------------------------------------------------------------------
## "Public" interface to lowering
##
class LowerToCxx:
def lower(self, tu):
'''returns |[ 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)
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
##
_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 ExprSelect(actor, '->', 'mId')
return ExprVar('mId')
def _actorHId(actorhandle):
return ExprSelect(actorhandle, '.', 'mId')
def _actorChannel(actor):
return ExprSelect(actor, '->', 'mChannel')
def _actorManager(actor):
return ExprSelect(actor, '->', 'mManager')
def _actorState(actor):
return ExprSelect(actor, '->', 'mState')
def _backstagePass():
return ExprCall(ExprVar('mozilla::ipc::PrivateIPDLInterface'))
def _nullState(proto=None):
pfx = ''
if proto is not None: pfx = proto.name() +'::'
return ExprVar(pfx +'__Null')
def _errorState(proto=None):
pfx = ''
if proto is not None: pfx = proto.name() +'::'
return ExprVar(pfx +'__Error')
def _deadState(proto=None):
pfx = ''
if proto is not None: pfx = proto.name() +'::'
return ExprVar(pfx +'__Dead')
def _dyingState(proto=None):
pfx = ''
if proto is not None: pfx = proto.name() +'::'
return ExprVar(pfx +'__Dying')
def _startState(proto=None, fq=False):
pfx = ''
if proto:
if fq: pfx = proto.fullname() +'::'
else: pfx = proto.name() +'::'
return ExprVar(pfx +'__Start')
def _deleteId():
return ExprVar('Msg___delete____ID')
def _deleteReplyId():
return ExprVar('Reply___delete____ID')
def _lookupListener(idexpr):
return ExprCall(ExprVar('Lookup'), args=[ idexpr ])
def _shmemType(ptr=0, const=1, ref=0):
return Type('Shmem', ptr=ptr, ref=ref)
def _rawShmemType(ptr=0):
return Type('Shmem::SharedMemory', ptr=ptr)
def _shmemIdType(ptr=0):
return Type('Shmem::id_t', ptr=ptr)
def _shmemTypeType():
return Type('Shmem::SharedMemory::SharedMemoryType')
def _shmemBackstagePass():
return ExprCall(ExprVar(
'Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead'))
def _shmemCtor(rawmem, idexpr):
return ExprCall(ExprVar('Shmem'),
args=[ _shmemBackstagePass(), rawmem, idexpr ])
def _shmemId(shmemexpr):
return ExprCall(ExprSelect(shmemexpr, '.', 'Id'),
args=[ _shmemBackstagePass() ])
def _shmemSegment(shmemexpr):
return ExprCall(ExprSelect(shmemexpr, '.', 'Segment'),
args=[ _shmemBackstagePass() ])
def _shmemAlloc(size, type, unsafe):
# starts out UNprotected
return ExprCall(ExprVar('Shmem::Alloc'),
args=[ _shmemBackstagePass(), size, type, unsafe ])
def _shmemDealloc(rawmemvar):
return ExprCall(ExprVar('Shmem::Dealloc'),
args=[ _shmemBackstagePass(), rawmemvar ])
def _shmemShareTo(shmemvar, processvar, route):
return ExprCall(ExprSelect(shmemvar, '.', 'ShareTo'),
args=[ _shmemBackstagePass(),
processvar, route ])
def _shmemOpenExisting(descriptor, outid):
# starts out protected
return ExprCall(ExprVar('Shmem::OpenExisting'),
args=[ _shmemBackstagePass(),
# true => protect
descriptor, outid, ExprLiteral.TRUE ])
def _shmemUnshareFrom(shmemvar, processvar, route):
return ExprCall(ExprSelect(shmemvar, '.', 'UnshareFrom'),
args=[ _shmemBackstagePass(),
processvar, route ])
def _shmemForget(shmemexpr):
return ExprCall(ExprSelect(shmemexpr, '.', 'forget'),
args=[ _shmemBackstagePass() ])
def _shmemRevokeRights(shmemexpr):
return ExprCall(ExprSelect(shmemexpr, '.', 'RevokeRights'),
args=[ _shmemBackstagePass() ])
def _lookupShmem(idexpr):
return ExprCall(ExprVar('LookupSharedMemory'), args=[ idexpr ])
def _makeForwardDeclForQClass(clsname, quals, cls=1, struct=0):
fd = ForwardDecl(clsname, cls=cls, struct=struct)
if 0 == len(quals):
return fd
outerns = Namespace(quals[0])
innerns = outerns
for ns in quals[1:]:
tmpns = Namespace(ns)
innerns.addstmt(tmpns)
innerns = tmpns
innerns.addstmt(fd)
return outerns
def _makeForwardDeclForActor(ptype, side):
return _makeForwardDeclForQClass(_actorName(ptype.qname.baseid, side),
ptype.qname.quals)
def _makeForwardDecl(type):
return _makeForwardDeclForQClass(type.name(), type.qname.quals)
def _putInNamespaces(cxxthing, namespaces):
"""|namespaces| is in order [ outer, ..., inner ]"""
if 0 == len(namespaces): return cxxthing
outerns = Namespace(namespaces[0].name)
innerns = outerns
for ns in namespaces[1:]:
newns = Namespace(ns.name)
innerns.addstmt(newns)
innerns = newns
innerns.addstmt(cxxthing)
return outerns
def _sendPrefix(msgtype):
"""Prefix of the name of the C++ method that sends |msgtype|."""
if msgtype.isInterrupt():
return 'Call'
return 'Send'
def _recvPrefix(msgtype):
"""Prefix of the name of the C++ method that handles |msgtype|."""
if msgtype.isInterrupt():
return 'Answer'
return 'Recv'
def _flatTypeName(ipdltype):
"""Return a 'flattened' IPDL type name that can be used as an
identifier.
E.g., |Foo[]| --> |ArrayOfFoo|."""
# NB: this logic depends heavily on what IPDL types are allowed to
# be constructed; e.g., Foo[][] is disallowed. needs to be kept in
# sync with grammar.
if ipdltype.isIPDL() and ipdltype.isArray():
return 'ArrayOf'+ ipdltype.basetype.name()
return ipdltype.name()
def _hasVisibleActor(ipdltype):
"""Return true iff a C++ decl of |ipdltype| would have an Actor* type.
For example: |Actor[]| would turn into |Array<ActorParent*>|, so this
function would return true for |Actor[]|."""
return (ipdltype.isIPDL()
and (ipdltype.isActor()
or (ipdltype.isArray()
and _hasVisibleActor(ipdltype.basetype))))
def _abortIfFalse(cond, msg):
return StmtExpr(ExprCall(
ExprVar('MOZ_ASSERT'),
[ cond, ExprLiteral.String(msg) ]))
def _runtimeAbort(msg):
if isinstance(msg, str):
msg = ExprLiteral.String(msg)
return StmtExpr(
ExprCall(ExprVar('NS_RUNTIMEABORT'), args=[ msg ]))
def _refptr(T):
return Type('RefPtr', T=T)
def _refptrGet(expr):
return ExprCall(ExprSelect(expr, '.', 'get'))
def _refptrForget(expr):
return ExprCall(ExprSelect(expr, '.', 'forget'))
def _refptrTake(expr):
return ExprCall(ExprSelect(expr, '.', 'take'))
def _cxxArrayType(basetype, const=0, ref=0):
return Type('nsTArray', T=basetype, const=const, ref=ref, hasimplicitcopyctor=False)
def _cxxManagedContainerType(basetype, const=0, ref=0):
return Type('ManagedContainer', T=basetype,
const=const, ref=ref, hasimplicitcopyctor=False)
def _cxxFallibleArrayType(basetype, const=0, ref=0):
return Type('FallibleTArray', T=basetype, const=const, ref=ref)
def _callCxxArrayLength(arr):
return ExprCall(ExprSelect(arr, '.', 'Length'))
def _callCxxCheckedArraySetLength(arr, lenexpr, sel='.'):
ifbad = StmtIf(ExprNot(ExprCall(ExprSelect(arr, sel, 'SetLength'),
args=[ lenexpr, ExprVar('mozilla::fallible') ])))
ifbad.addifstmt(_fatalError('Error setting the array length'))
ifbad.addifstmt(StmtReturn.FALSE)
return ifbad
def _callCxxSwapArrayElements(arr1, arr2, sel='.'):
return ExprCall(ExprSelect(arr1, sel, 'SwapElements'),
args=[ arr2 ])
def _callInsertManagedActor(managees, actor):
return ExprCall(ExprSelect(managees, '.', 'PutEntry'),
args=[ actor ])
def _callRemoveManagedActor(managees, actor):
return ExprCall(ExprSelect(managees, '.', 'RemoveEntry'),
args=[ actor ])
def _callClearManagedActors(managees):
return ExprCall(ExprSelect(managees, '.', 'Clear'))
def _callHasManagedActor(managees, actor):
return ExprCall(ExprSelect(managees, '.', 'Contains'), args=[ actor ])
def _otherSide(side):
if side == 'child': return 'parent'
if side == 'parent': return 'child'
assert 0
def _sideToTransportMode(side):
if side == 'parent': mode = 'SERVER'
elif side == 'child': mode = 'CLIENT'
return ExprVar('mozilla::ipc::Transport::MODE_'+ mode)
def _ifLogging(topLevelProtocol, stmts):
iflogging = StmtIf(ExprCall(ExprVar('mozilla::ipc::LoggingEnabledFor'),
args=[ topLevelProtocol ]))
iflogging.addifstmts(stmts)
return iflogging
# XXX we need to remove these and install proper error handling
def _printErrorMessage(msg):
if isinstance(msg, str):
msg = ExprLiteral.String(msg)
return StmtExpr(
ExprCall(ExprVar('NS_ERROR'), args=[ msg ]))
def _protocolErrorBreakpoint(msg):
if isinstance(msg, str):
msg = ExprLiteral.String(msg)
return StmtExpr(ExprCall(ExprVar('mozilla::ipc::ProtocolErrorBreakpoint'),
args=[ msg ]))
def _ipcFatalError(name, msg, otherpid, isparent):
if isinstance(name, str):
name = ExprLiteral.String(name)
if isinstance(msg, str):
msg = ExprLiteral.String(msg)
return StmtExpr(ExprCall(ExprVar('mozilla::ipc::FatalError'),
args=[ name, msg, otherpid, isparent ]))
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 _killProcess(pid):
return ExprCall(
ExprVar('base::KillProcess'),
args=[ pid,
# XXX this is meaningless on POSIX
ExprVar('base::PROCESS_END_KILLED_BY_USER'),
ExprLiteral.FALSE ])
def _badTransition():
# FIXME: make this a FatalError()
return [ _printWarningMessage('bad state transition!') ]
# Results that IPDL-generated code returns back to *Channel code.
# Users never see these
class _Result:
@staticmethod
def Type():
return Type('Result')
Processed = ExprVar('MsgProcessed')
NotKnown = ExprVar('MsgNotKnown')
NotAllowed = ExprVar('MsgNotAllowed')
PayloadError = ExprVar('MsgPayloadError')
ProcessingError = ExprVar('MsgProcessingError')
RouteError = ExprVar('MsgRouteError')
ValuError = ExprVar('MsgValueError') # [sic]
# these |errfn*| are functions that generate code to be executed on an
# error, such as "bad actor ID". each is given a Python string
# containing a description of the error
# used in user-facing Send*() methods
def errfnSend(msg, errcode=ExprLiteral.FALSE):
return [
_fatalError(msg),
StmtReturn(errcode)
]
def errfnSendCtor(msg): return errfnSend(msg, errcode=ExprLiteral.NULL)
# TODO should this error handling be strengthened for dtors?
def errfnSendDtor(msg):
return [
_printErrorMessage(msg),
StmtReturn.FALSE
]
# used in |OnMessage*()| handlers that hand in-messages off to Recv*()
# interface methods
def errfnRecv(msg, errcode=_Result.ValuError):
return [
_fatalError(msg),
StmtReturn(errcode)
]
# used in Read() methods
def errfnRead(msg):
return [ _fatalError(msg), StmtReturn.FALSE ]
def _destroyMethod():
return ExprVar('ActorDestroy')
class _DestroyReason:
@staticmethod
def Type(): return Type('ActorDestroyReason')
Deletion = ExprVar('Deletion')
AncestorDeletion = ExprVar('AncestorDeletion')
NormalShutdown = ExprVar('NormalShutdown')
AbnormalShutdown = ExprVar('AbnormalShutdown')
FailedConstructor = ExprVar('FailedConstructor')
##-----------------------------------------------------------------------------
## 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 visitBuiltinCxxType(self, t):
return Type(self.typename(t))
def visitImportedCxxType(self, t):
return Type(self.typename(t))
def visitActorType(self, a):
return Type(_actorName(self.typename(a.protocol), self.side), ptr=1)
def visitStructType(self, s):
return Type(self.typename(s))
def visitUnionType(self, u):
return Type(self.typename(u))
def visitArrayType(self, a):
basecxxtype = a.basetype.accept(self)
return _cxxArrayType(basecxxtype)
def visitShmemType(self, s):
return Type(self.typename(s))
def visitFDType(self, s):
return Type(self.typename(s))
def visitProtocolType(self, p): assert 0
def visitMessageType(self, m): assert 0
def visitVoidType(self, v): assert 0
def visitStateType(self, st): assert 0
def _cxxBareType(ipdltype, side, fq=0):
return ipdltype.accept(_ConvertToCxxType(side, fq))
def _cxxRefType(ipdltype, side):
t = _cxxBareType(ipdltype, side)
t.ref = 1
return t
def _cxxConstRefType(ipdltype, side):
t = _cxxBareType(ipdltype, side)
if ipdltype.isIPDL() and ipdltype.isActor():
return t
if ipdltype.isIPDL() and ipdltype.isShmem():
t.ref = 1
return t
t.const = 1
t.ref = 1
return t
def _cxxMoveRefType(ipdltype, side):
t = _cxxBareType(ipdltype, side)
if ipdltype.isIPDL() and (ipdltype.isArray() or ipdltype.isShmem()):
t.ref = 2
return t
return _cxxConstRefType(ipdltype, side)
def _cxxPtrToType(ipdltype, side):
t = _cxxBareType(ipdltype, side)
if ipdltype.isIPDL() and ipdltype.isActor():
t.ptr = 0
t.ptrptr = 1
return t
t.ptr = 1
return t
def _cxxConstPtrToType(ipdltype, side):
t = _cxxBareType(ipdltype, side)
if ipdltype.isIPDL() and ipdltype.isActor():
t.ptr = 0
t.ptrconstptr = 1
return t
t.const = 1
t.ptrconst = 1
return t
def _allocMethod(ptype, side):
return ExprVar('Alloc'+ str(Actor(ptype, side)))
def _deallocMethod(ptype, side):
return ExprVar('Dealloc'+ str(Actor(ptype, side)))
##
## A _HybridDecl straddles IPDL and C++ decls. It knows which C++
## types correspond to which IPDL types, and it also knows how
## serialize and deserialize "special" IPDL C++ types.
##
class _HybridDecl:
"""A hybrid decl stores both an IPDL type and all the C++ type
info needed by later passes, along with a basic name for the decl."""
def __init__(self, ipdltype, name):
self.ipdltype = ipdltype
self.name = name
self.idnum = 0
def var(self):
return ExprVar(self.name)
def bareType(self, side):
"""Return this decl's unqualified C++ type."""
return _cxxBareType(self.ipdltype, side)
def refType(self, side):
"""Return this decl's C++ type as a 'reference' type, which is not
necessarily a C++ reference."""
return _cxxRefType(self.ipdltype, side)
def constRefType(self, side):
"""Return this decl's C++ type as a const, 'reference' type."""
return _cxxConstRefType(self.ipdltype, side)
def rvalueRefType(self, side):
"""Return this decl's C++ type as an r-value 'reference' type."""
return _cxxMoveRefType(self.ipdltype, side)
def ptrToType(self, side):
return _cxxPtrToType(self.ipdltype, side)
def constPtrToType(self, side):
return _cxxConstPtrToType(self.ipdltype, side)
def inType(self, side):
"""Return this decl's C++ Type with inparam semantics."""
if self.ipdltype.isIPDL() and self.ipdltype.isActor():
return self.bareType(side)
return self.constRefType(side)
def moveType(self, side):
"""Return this decl's C++ Type with move semantics."""
if self.ipdltype.isIPDL() and self.ipdltype.isActor():
return self.bareType(side)
return self.rvalueRefType(side);
def outType(self, side):
"""Return this decl's C++ Type with outparam semantics."""
t = self.bareType(side)
if self.ipdltype.isIPDL() and self.ipdltype.isActor():
t.ptr = 0; t.ptrptr = 1
return t
t.ptr = 1
return t
##--------------------------------------------------
class HasFQName:
def fqClassName(self):
return self.decl.type.fullname()
class _CompoundTypeComponent(_HybridDecl):
def __init__(self, ipdltype, name, side, ct):
_HybridDecl.__init__(self, ipdltype, name)
self.side = side
self.special = _hasVisibleActor(ipdltype)
self.recursive = ct.decl.type.mutuallyRecursiveWith(ipdltype)
def internalType(self):
if self.recursive:
return self.ptrToType()
else:
return self.bareType()
# @override the following methods to pass |self.side| instead of
# forcing the caller to remember which side we're declared to
# represent.
def bareType(self, side=None):
return _HybridDecl.bareType(self, self.side)
def refType(self, side=None):
return _HybridDecl.refType(self, self.side)
def constRefType(self, side=None):
return _HybridDecl.constRefType(self, self.side)
def ptrToType(self, side=None):
return _HybridDecl.ptrToType(self, self.side)
def constPtrToType(self, side=None):
return _HybridDecl.constPtrToType(self, self.side)
def inType(self, side=None):
return _HybridDecl.inType(self, self.side)
class StructDecl(ipdl.ast.StructDecl, HasFQName):
@staticmethod
def upgrade(structDecl):
assert isinstance(structDecl, ipdl.ast.StructDecl)
structDecl.__class__ = StructDecl
return structDecl
class _StructField(_CompoundTypeComponent):
def __init__(self, ipdltype, name, sd, side=None):
fname = name
special = _hasVisibleActor(ipdltype)
if special:
fname += side.title()
_CompoundTypeComponent.__init__(self, ipdltype, fname, side, sd)
def getMethod(self, thisexpr=None, sel='.'):
meth = self.var()
if thisexpr is not None:
return ExprSelect(thisexpr, sel, meth.name)
return meth
def initExpr(self, thisexpr):
expr = ExprCall(self.getMethod(thisexpr=thisexpr))
if self.ipdltype.isIPDL() and self.ipdltype.isActor():
expr = ExprCast(expr, self.bareType(), const=1)
return expr
def refExpr(self, thisexpr=None):
ref = self.memberVar()
if thisexpr is not None:
ref = ExprSelect(thisexpr, '.', ref.name)
if self.recursive:
ref = ExprDeref(ref)
return ref
def constRefExpr(self, thisexpr=None):
# sigh, gross hack
refexpr = self.refExpr(thisexpr)
if 'Shmem' == self.ipdltype.name():
refexpr = ExprCast(refexpr, Type('Shmem', ref=1), const=1)
if 'FileDescriptor' == self.ipdltype.name():
refexpr = ExprCast(refexpr, Type('FileDescriptor', ref=1), const=1)
return refexpr
def argVar(self):
return ExprVar('_'+ self.name)
def memberVar(self):
return ExprVar(self.name + '_')
def initStmts(self):
if self.recursive:
return [ StmtExpr(ExprAssn(self.memberVar(),
ExprNew(self.bareType()))) ]
elif self.ipdltype.isIPDL() and self.ipdltype.isActor():
return [ StmtExpr(ExprAssn(self.memberVar(),
ExprLiteral.NULL)) ]
else:
return []
def destructStmts(self):
if self.recursive:
return [ StmtExpr(ExprDelete(self.memberVar())) ]
else:
return []
class UnionDecl(ipdl.ast.UnionDecl, HasFQName):
def callType(self, var=None):
func = ExprVar('type')
if var is not None:
func = ExprSelect(var, '.', func.name)
return ExprCall(func)
@staticmethod
def upgrade(unionDecl):
assert isinstance(unionDecl, ipdl.ast.UnionDecl)
unionDecl.__class__ = UnionDecl
return unionDecl
class _UnionMember(_CompoundTypeComponent):
"""Not in the AFL sense, but rather a member (e.g. |int;|) of an
IPDL union type."""
def __init__(self, ipdltype, ud, side=None, other=None):
flatname = _flatTypeName(ipdltype)
special = _hasVisibleActor(ipdltype)
if special:
flatname += side.title()
_CompoundTypeComponent.__init__(self, ipdltype, 'V'+ flatname, side, ud)
self.flattypename = flatname
if special:
if other is not None:
self.other = other
else:
self.other = _UnionMember(ipdltype, ud, _otherSide(side), self)
def enum(self):
return 'T' + self.flattypename
def pqEnum(self):
return self.ud.name +'::'+ self.enum()
def enumvar(self):
return ExprVar(self.enum())
def unionType(self):
"""Type used for storage in generated C union decl."""
if self.recursive:
return self.ptrToType()
else:
return Type('mozilla::AlignedStorage2', T=self.internalType())
def unionValue(self):
# NB: knows that Union's storage C union is named |mValue|
return ExprSelect(ExprVar('mValue'), '.', self.name)
def typedef(self):
return self.flattypename +'__tdef'
def callGetConstPtr(self):
"""Return an expression of type self.constptrToSelfType()"""
return ExprCall(ExprVar(self.getConstPtrName()))
def callGetPtr(self):
"""Return an expression of type self.ptrToSelfType()"""
return ExprCall(ExprVar(self.getPtrName()))
def callOperatorEq(self, rhs):
if self.ipdltype.isIPDL() and self.ipdltype.isActor():
rhs = ExprCast(rhs, self.bareType(), const=1)
return ExprAssn(ExprDeref(self.callGetPtr()), rhs)
def callCtor(self, expr=None):
assert not isinstance(expr, list)
if expr is None:
args = None
elif self.ipdltype.isIPDL() and self.ipdltype.isActor():
args = [ ExprCast(expr, self.bareType(), const=1) ]
else:
args = [ expr ]
if self.recursive:
return ExprAssn(self.callGetPtr(),
ExprNew(self.bareType(self.side),
args=args))
else:
return ExprNew(self.bareType(self.side),
args=args,
newargs=[ self.callGetPtr() ])
def callDtor(self):
if self.recursive:
return ExprDelete(self.callGetPtr())
else:
return ExprCall(
ExprSelect(self.callGetPtr(), '->', '~'+ self.typedef()))
def getTypeName(self): return 'get_'+ self.flattypename
def getConstTypeName(self): return 'get_'+ self.flattypename
def getOtherTypeName(self): return 'get_'+ self.otherflattypename
def getPtrName(self): return 'ptr_'+ self.flattypename
def getConstPtrName(self): return 'constptr_'+ self.flattypename
def ptrToSelfExpr(self):
"""|*ptrToSelfExpr()| has type |self.bareType()|"""
v = self.unionValue()
if self.recursive:
return v
else:
return ExprCall(ExprSelect(v, '.', 'addr'))
def constptrToSelfExpr(self):
"""|*constptrToSelfExpr()| has type |self.constType()|"""
v = self.unionValue()
if self.recursive:
return v
return ExprCall(ExprSelect(v, '.', 'addr'))
def ptrToInternalType(self):
t = self.ptrToType()
if self.recursive:
t.ref = 1
return t
def defaultValue(self):
# Use the default constructor for any class that does not have an
# implicit copy constructor.
if not self.bareType().hasimplicitcopyctor:
return None
if self.ipdltype.isIPDL() and self.ipdltype.isActor():
return ExprLiteral.NULL
# XXX sneaky here, maybe need ExprCtor()?
return ExprCall(self.bareType())
def getConstValue(self):
v = ExprDeref(self.callGetConstPtr())
# sigh
if 'Shmem' == self.ipdltype.name():
v = ExprCast(v, Type('Shmem', ref=1), const=1)
if 'FileDescriptor' == self.ipdltype.name():
v = ExprCast(v, Type('FileDescriptor', ref=1), const=1)
return v
##--------------------------------------------------
class MessageDecl(ipdl.ast.MessageDecl):
def baseName(self):
return self.name
def recvMethod(self):
name = _recvPrefix(self.decl.type) + self.baseName()
if self.decl.type.isCtor():
name += 'Constructor'
return ExprVar(name)
def sendMethod(self):
name = _sendPrefix(self.decl.type) + self.baseName()
if self.decl.type.isCtor():
name += 'Constructor'
return ExprVar(name)
def hasReply(self):
return (self.decl.type.hasReply()
or self.decl.type.isCtor()
or self.decl.type.isDtor())
def msgClass(self):
return 'Msg_%s'% (self.decl.progname)
def prettyMsgName(self, pfx=''):
return pfx + self.msgClass()
def pqMsgClass(self):
return '%s::%s'% (self.namespace, self.msgClass())
def msgCast(self, msgexpr):
return ExprCast(msgexpr, self.msgCxxType(const=1, ptr=1), static=1)
def msgCxxType(self, const=0, ref=0, ptr=0):
return Type(self.pqMsgClass(), const=const, ref=ref, ptr=ptr)
def msgId(self): return self.msgClass()+ '__ID'
def pqMsgId(self):
return '%s::%s'% (self.namespace, self.msgId())
def replyClass(self):
return 'Reply_%s'% (self.decl.progname)
def pqReplyClass(self):
return '%s::%s'% (self.namespace, self.replyClass())
def replyCast(self, replyexpr):
return ExprCast(replyexpr, Type(self.pqReplyClass(), const=1, ptr=1),
static=1)
def replyId(self): return self.replyClass()+ '__ID'
def pqReplyId(self):
return '%s::%s'% (self.namespace, self.replyId())
def prettyReplyName(self, pfx=''):
return pfx + self.replyClass()
def actorDecl(self):
return self.params[0]
def makeCxxParams(self, paramsems='in', returnsems='out',
side=None, implicit=1):
"""Return a list of C++ decls per the spec'd configuration.
|params| and |returns| is the C++ semantics of those: 'in', 'out', or None."""
def makeDecl(d, sems):
if sems is 'in':
return Decl(d.inType(side), d.name)
elif sems is 'move':
return Decl(d.moveType(side), d.name)
elif sems is 'out':
return Decl(d.outType(side), d.name)
else: assert 0
cxxparams = [ ]
if paramsems is not None:
cxxparams.extend([ makeDecl(d, paramsems) for d in self.params ])
if 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, params=1, retsems='out', retcallsems='out',
implicit=1):
assert not implicit or params # implicit => params
assert not retcallsems or retsems # retcallsems => returnsems
cxxargs = [ ]
if params:
cxxargs.extend([ ExprMove(p.var()) for p in self.params ])
for ret in self.returns:
if retsems is 'in':
if retcallsems is 'in':
cxxargs.append(ret.var())
elif retcallsems is 'out':
cxxargs.append(ExprAddrOf(ret.var()))
else: assert 0
elif retsems is 'out':
if retcallsems is 'in':
cxxargs.append(ExprDeref(ret.var()))
elif retcallsems is 'out':
cxxargs.append(ret.var())
else: assert 0
if not implicit:
assert self.decl.type.hasImplicitActorParam()
cxxargs = cxxargs[1:]
return cxxargs
@staticmethod
def upgrade(messageDecl):
assert isinstance(messageDecl, ipdl.ast.MessageDecl)
if messageDecl.decl.type.hasImplicitActorParam():
messageDecl.params.insert(
0,
_HybridDecl(
ipdl.type.ActorType(
messageDecl.decl.type.constructedType()),
'actor'))
messageDecl.__class__ = MessageDecl
return messageDecl
##--------------------------------------------------
def _semsToChannelParts(sems):
return [ 'mozilla', 'ipc', 'MessageChannel' ]
def _usesShmem(p):
for md in p.messageDecls:
for param in md.inParams:
if ipdl.type.hasshmem(param.type):
return True
for ret in md.outParams:
if ipdl.type.hasshmem(ret.type):
return True
return False
def _subtreeUsesShmem(p):
if _usesShmem(p):
return True
ptype = p.decl.type
for mgd in ptype.manages:
if ptype is not mgd:
if _subtreeUsesShmem(mgd._ast):
return True
return False
class Protocol(ipdl.ast.Protocol):
def cxxTypedefs(self):
return self.decl.cxxtypedefs
def sendSems(self):
return self.decl.type.toplevel().sendSemantics
def channelName(self):
return '::'.join(_semsToChannelParts(self.sendSems()))
def channelSel(self):
if self.decl.type.isToplevel(): return '.'
return '->'
def channelType(self):
return Type('Channel', ptr=not self.decl.type.isToplevel())
def channelHeaderFile(self):
return '/'.join(_semsToChannelParts(self.sendSems())) +'.h'
def fqListenerName(self):
return 'mozilla::ipc::MessageListener'
def fqBaseClass(self):
return 'mozilla::ipc::IProtocol'
def managerInterfaceType(self, ptr=0):
return Type('mozilla::ipc::IProtocolManager',
ptr=ptr,
T=Type(self.fqBaseClass()))
def openedProtocolInterfaceType(self, ptr=0):
return Type('mozilla::ipc::IToplevelProtocol',
ptr=ptr)
def _ipdlmgrtype(self):
assert 1 == len(self.decl.type.managers)
for mgr in self.decl.type.managers: return mgr
def managerActorType(self, side, ptr=0):
return Type(_actorName(self._ipdlmgrtype().name(), side),
ptr=ptr)
def managerMethod(self, actorThis=None):
_ = self._ipdlmgrtype()
if actorThis is not None:
return ExprSelect(actorThis, '->', 'Manager')
return ExprVar('Manager');
def stateMethod(self):
return ExprVar('state');
def registerMethod(self):
return ExprVar('Register')
def registerIDMethod(self):
return ExprVar('RegisterID')
def lookupIDMethod(self):
return ExprVar('Lookup')
def unregisterMethod(self, actorThis=None):
if actorThis is not None:
return ExprSelect(actorThis, '->', 'Unregister')
return ExprVar('Unregister')
def removeManageeMethod(self):
return ExprVar('RemoveManagee')
def createSharedMemory(self):
return ExprVar('CreateSharedMemory')
def adoptSharedMemory(self):
return ExprVar('AdoptSharedMemory')
def lookupSharedMemory(self):
return ExprVar('LookupSharedMemory')
def isTrackingSharedMemory(self):
return ExprVar('IsTrackingSharedMemory')
def destroySharedMemory(self):
return ExprVar('DestroySharedMemory')
def otherPidMethod(self):
return ExprVar('OtherPid')
def callOtherPid(self, actorThis=None):
fn = self.otherPidMethod()
if actorThis is not None:
fn = ExprSelect(actorThis, '->', fn.name)
return ExprCall(fn)
def getChannelMethod(self):
return ExprVar('GetIPCChannel')
def callGetChannel(self, actorThis=None):
fn = self.getChannelMethod()
if actorThis is not None:
fn = ExprSelect(actorThis, '->', fn.name)
return ExprCall(fn)
def cloneManagees(self):
return ExprVar('CloneManagees')
def cloneProtocol(self):
return ExprVar('CloneProtocol')
def processingErrorVar(self):
assert self.decl.type.isToplevel()
return ExprVar('ProcessingError')
def shouldContinueFromTimeoutVar(self):
assert self.decl.type.isToplevel()
return ExprVar('ShouldContinueFromReplyTimeout')
def enteredCxxStackVar(self):
assert self.decl.type.isToplevel()
return ExprVar('EnteredCxxStack')
def exitedCxxStackVar(self):
assert self.decl.type.isToplevel()
return ExprVar('ExitedCxxStack')
def enteredCallVar(self):
assert self.decl.type.isToplevel()
return ExprVar('EnteredCall')
def exitedCallVar(self):
assert self.decl.type.isToplevel()
return ExprVar('ExitedCall')
def onCxxStackVar(self):
assert self.decl.type.isToplevel()
return ExprVar('IsOnCxxStack')
def nextActorIdExpr(self, side):
assert self.decl.type.isToplevel()
if side is 'parent': op = '++'
elif side is 'child': op = '--'
else: assert 0
return ExprPrefixUnop(self.lastActorIdVar(), op)
def actorIdInit(self, side):
assert self.decl.type.isToplevel()
# parents go up from FREED, children go down from NULL
if side is 'parent': return _FREED_ACTOR_ID
elif side is 'child': return _NULL_ACTOR_ID
else: assert 0
# an actor's C++ private variables
def lastActorIdVar(self):
assert self.decl.type.isToplevel()
return ExprVar('mLastRouteId')
def actorMapVar(self):
assert self.decl.type.isToplevel()
return ExprVar('mActorMap')
def channelVar(self, actorThis=None):
if actorThis is not None:
return ExprSelect(actorThis, '->', 'mChannel')
return ExprVar('mChannel')
def channelForSubactor(self):
if self.decl.type.isToplevel():
return ExprAddrOf(self.channelVar())
return self.channelVar()
def routingId(self, actorThis=None):
if self.decl.type.isToplevel():
return ExprVar('MSG_ROUTING_CONTROL')
if actorThis is not None:
return ExprSelect(actorThis, '->', self.idVar().name)
return self.idVar()
def idVar(self):
assert not self.decl.type.isToplevel()
return ExprVar('mId')
def stateVar(self, actorThis=None):
if actorThis is not None:
return ExprSelect(actorThis, '->', 'mState')
return ExprVar('mState')
def fqStateType(self):
return Type(self.decl.type.name() +'::State')
def startState(self):
return _startState(self.decl.type)
def nullState(self):
return _nullState(self.decl.type)
def deadState(self):
return _deadState(self.decl.type)
def managerVar(self, thisexpr=None):
assert thisexpr is not None or not self.decl.type.isToplevel()
mvar = ExprVar('mManager')
if thisexpr is not None:
mvar = ExprSelect(thisexpr, '->', mvar.name)
return mvar
def otherPidVar(self):
assert self.decl.type.isToplevel()
return ExprVar('mOtherPid')
def managedCxxType(self, actortype, side):
assert self.decl.type.isManagerOf(actortype)
return Type(_actorName(actortype.name(), side), ptr=1)
def managedMethod(self, actortype, side):
assert self.decl.type.isManagerOf(actortype)
return ExprVar('Managed'+ _actorName(actortype.name(), side))
def managedVar(self, actortype, side):
assert self.decl.type.isManagerOf(actortype)
return ExprVar('mManaged'+ _actorName(actortype.name(), side))
def managedVarType(self, actortype, side, const=0, ref=0):
assert self.decl.type.isManagerOf(actortype)
return _cxxManagedContainerType(Type(_actorName(actortype.name(), side)),
const=const, ref=ref)
def managerArrayExpr(self, thisvar, side):
"""The member var my manager keeps of actors of my type."""
assert self.decl.type.isManaged()
return ExprSelect(
ExprCall(self.managerMethod(thisvar)),
'->', 'mManaged'+ _actorName(self.decl.type.name(), side))
# shmem stuff
def shmemMapType(self):
assert self.decl.type.isToplevel()
return Type('IDMap', T=_rawShmemType())
def shmemIteratorType(self):
assert self.decl.type.isToplevel()
# XXX breaks abstractions
return Type('IDMap<SharedMemory>::const_iterator')
def shmemMapVar(self):
assert self.decl.type.isToplevel()
return ExprVar('mShmemMap')
def lastShmemIdVar(self):
assert self.decl.type.isToplevel()
return ExprVar('mLastShmemId')
def shmemIdInit(self, side):
assert self.decl.type.isToplevel()
# use the same scheme for shmem IDs as actor IDs
if side is 'parent': return _FREED_ACTOR_ID
elif side is 'child': return _NULL_ACTOR_ID
else: assert 0
def nextShmemIdExpr(self, side):
assert self.decl.type.isToplevel()
if side is 'parent': op = '++'
elif side is 'child': op = '--'
return ExprPrefixUnop(self.lastShmemIdVar(), op)
def removeShmemId(self, idexpr):
return ExprCall(ExprSelect(self.shmemMapVar(), '.', 'Remove'),
args=[ idexpr ])
# XXX this is sucky, fix
def usesShmem(self):
return _usesShmem(self)
def subtreeUsesShmem(self):
return _subtreeUsesShmem(self)
@staticmethod
def upgrade(protocol):
assert isinstance(protocol, ipdl.ast.Protocol)
protocol.__class__ = Protocol
return protocol
class TranslationUnit(ipdl.ast.TranslationUnit):
@staticmethod
def upgrade(tu):
assert isinstance(tu, ipdl.ast.TranslationUnit)
tu.__class__ = TranslationUnit
return tu
##-----------------------------------------------------------------------------
class _DecorateWithCxxStuff(ipdl.ast.Visitor):
"""Phase 1 of lowering: decorate the IPDL AST with information
relevant to C++ code generation.
This pass results in an AST that is a poor man's "IR"; in reality, a
"hybrid" AST mainly consisting of IPDL nodes with new C++ info along
with some new IPDL/C++ nodes that are tuned for C++ codegen."""
def __init__(self):
self.visitedTus = set()
# the set of typedefs that allow generated classes to
# reference known C++ types by their "short name" rather than
# fully-qualified name. e.g. |Foo| rather than |a::b::Foo|.
self.typedefs = [ ]
self.typedefSet = set([ Typedef(Type('mozilla::ipc::ActorHandle'),
'ActorHandle'),
Typedef(Type('base::ProcessId'),
'ProcessId'),
Typedef(Type('mozilla::ipc::ProtocolId'),
'ProtocolId'),
Typedef(Type('mozilla::ipc::Transport'),
'Transport'),
Typedef(Type('mozilla::ipc::TransportDescriptor'),
'TransportDescriptor') ])
self.protocolName = None
def visitTranslationUnit(self, tu):
if tu not in self.visitedTus:
self.visitedTus.add(tu)
ipdl.ast.Visitor.visitTranslationUnit(self, tu)
if not isinstance(tu, TranslationUnit):
TranslationUnit.upgrade(tu)
self.typedefs[:] = sorted(list(self.typedefSet))
def visitInclude(self, inc):
if inc.tu.filetype == 'header':
inc.tu.accept(self)
def visitProtocol(self, pro):
self.protocolName = pro.name
pro.decl.cxxtypedefs = self.typedefs
Protocol.upgrade(pro)
return ipdl.ast.Visitor.visitProtocol(self, pro)
def visitUsingStmt(self, using):
if using.decl.fullname is not None:
self.typedefSet.add(Typedef(Type(using.decl.fullname),
using.decl.shortname))
def visitStructDecl(self, sd):
if not isinstance(sd, StructDecl):
sd.decl.special = 0
newfields = [ ]
for f in sd.fields:
ftype = f.decl.type
if _hasVisibleActor(ftype):
sd.decl.special = 1
# if ftype has a visible actor, we need both
# |ActorParent| and |ActorChild| fields
newfields.append(_StructField(ftype, f.name, sd,
side='parent'))
newfields.append(_StructField(ftype, f.name, sd,
side='child'))
else:
newfields.append(_StructField(ftype, f.name, sd))
sd.fields = newfields
StructDecl.upgrade(sd)
if sd.decl.fullname is not None:
self.typedefSet.add(Typedef(Type(sd.fqClassName()), sd.name))
def visitUnionDecl(self, ud):
ud.decl.special = 0
newcomponents = [ ]
for ctype in ud.decl.type.components:
if _hasVisibleActor(ctype):
ud.decl.special = 1
# if ctype has a visible actor, we need both
# |ActorParent| and |ActorChild| union members
newcomponents.append(_UnionMember(ctype, ud, side='parent'))
newcomponents.append(_UnionMember(ctype, ud, side='child'))
else:
newcomponents.append(_UnionMember(ctype, ud))
ud.components = newcomponents
UnionDecl.upgrade(ud)
if ud.decl.fullname is not None:
self.typedefSet.add(Typedef(Type(ud.fqClassName()), ud.name))
def visitDecl(self, decl):
return _HybridDecl(decl.type, decl.progname)
def visitMessageDecl(self, md):
md.namespace = self.protocolName
md.params = [ param.accept(self) for param in md.inParams ]
md.returns = [ ret.accept(self) for ret in md.outParams ]
MessageDecl.upgrade(md)
def visitTransitionStmt(self, ts):
name = ts.state.decl.progname
ts.state.decl.cxxname = name
ts.state.decl.cxxenum = ExprVar(self.protocolName +'::'+ name)
##-----------------------------------------------------------------------------
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):
self.protocol = tu.protocol
self.hdrfile = cxxHeaderFile
self.cppfile = cxxFile
tu.accept(self)
def visitTranslationUnit(self, tu):
hf = self.hdrfile
hf.addthing(_DISCLAIMER)
hf.addthings(_includeGuardStart(hf))
hf.addthing(Whitespace.NL)
for inc in builtinHeaderIncludes:
self.visitBuiltinCxxInclude(inc)
# Compute the set of includes we need for declared structure/union
# classes for this protocol.
typesToIncludes = {}
for using in tu.using:
typestr = str(using.type.spec)
assert typestr not in typesToIncludes
typesToIncludes[typestr] = using.header
aggregateTypeIncludes = set()
for su in tu.structsAndUnions:
typedeps = _ComputeTypeDeps(su.decl.type, True)
if isinstance(su, ipdl.ast.StructDecl):
for f in su.fields:
f.ipdltype.accept(typedeps)
elif isinstance(su, ipdl.ast.UnionDecl):
for c in su.components:
c.ipdltype.accept(typedeps)
for typename in [t.fromtype.name for t in typedeps.usingTypedefs]:
if typename in typesToIncludes:
aggregateTypeIncludes.add(typesToIncludes[typename])
if len(aggregateTypeIncludes) != 0:
hf.addthing(Whitespace.NL)
hf.addthings([ Whitespace("// Headers for typedefs"), Whitespace.NL ])
for headername in sorted(iter(aggregateTypeIncludes)):
hf.addthing(CppDirective('include', '"' + headername + '"'))
ipdl.ast.Visitor.visitTranslationUnit(self, tu)
if tu.filetype == 'header':
self.cppIncludeHeaders.append(_ipdlhHeaderName(tu))
hf.addthing(Whitespace.NL)
hf.addthings(_includeGuardEnd(hf))
cf = self.cppfile
cf.addthings((
[ _DISCLAIMER, Whitespace.NL ]
+ [ CppDirective('include','"'+h+'.h"')
for h in self.cppIncludeHeaders ]
+ [ Whitespace.NL ]
))
if self.protocol:
# construct the namespace into which we'll stick all our defns
ns = Namespace(self.protocol.name)
cf.addthing(_putInNamespaces(ns, self.protocol.namespaces))
ns.addstmts(([ Whitespace.NL]
+ self.funcDefns
+[ Whitespace.NL ]))
cf.addthings(self.structUnionDefns)
def visitBuiltinCxxInclude(self, inc):
self.hdrfile.addthing(CppDirective('include', '"'+ inc.file +'"'))
def visitInclude(self, inc):
if inc.tu.filetype == 'header':
self.hdrfile.addthing(CppDirective(
'include', '"'+ _ipdlhHeaderName(inc.tu) +'.h"'))
def processStructOrUnionClass(self, su, which, forwarddecls, cls):
clsdecl, methoddefns = _splitClassDeclDefn(cls)
self.hdrfile.addthings(
[ Whitespace.NL ]
+ forwarddecls
+ [ Whitespace("""
//-----------------------------------------------------------------------------
// Declaration of the IPDL type |%s %s|
//
"""% (which, su.name)),
_putInNamespaces(clsdecl, su.namespaces),
])
self.structUnionDefns.extend([
Whitespace("""
//-----------------------------------------------------------------------------
// Method definitions for the IPDL type |%s %s|
//
"""% (which, su.name)),
_putInNamespaces(methoddefns, su.namespaces),
])
def visitStructDecl(self, sd):
return self.processStructOrUnionClass(sd, 'struct',
*_generateCxxStruct(sd))
def visitUnionDecl(self, ud):
return self.processStructOrUnionClass(ud, 'union',
*_generateCxxUnion(ud))
def visitProtocol(self, p):
self.cppIncludeHeaders.append(_protocolHeaderName(self.protocol, ''))
bridges = ProcessGraph.bridgesOf(p.decl.type)
for bridge in bridges:
ppt, pside = bridge.parent.ptype, _otherSide(bridge.parent.side)
cpt, cside = bridge.child.ptype, _otherSide(bridge.child.side)
self.hdrfile.addthings([
Whitespace.NL,
_makeForwardDeclForActor(ppt, pside),
_makeForwardDeclForActor(cpt, cside)
])
self.cppIncludeHeaders.append(_protocolHeaderName(ppt._ast, pside))
self.cppIncludeHeaders.append(_protocolHeaderName(cpt._ast, cside))
opens = ProcessGraph.opensOf(p.decl.type)
for o in opens:
optype, oside = o.opener.ptype, o.opener.side
self.hdrfile.addthings([
Whitespace.NL,
_makeForwardDeclForActor(optype, oside)
])
self.cppIncludeHeaders.append(_protocolHeaderName(optype._ast, oside))
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)
# user-facing methods for connecting two process with a new channel
for bridge in bridges:
bdecl, bdefn = _splitFuncDeclDefn(self.genBridgeFunc(bridge))
ns.addstmts([ bdecl, Whitespace.NL ])
self.funcDefns.append(bdefn)
# user-facing methods for opening a new channel across two
# existing endpoints
for o in opens:
odecl, odefn = _splitFuncDeclDefn(self.genOpenFunc(o))
ns.addstmts([ odecl, Whitespace.NL ])
self.funcDefns.append(odefn)
# state information
stateenum = TypeEnum('State')
# NB: __Dead is the first state on purpose, so that it has
# value '0'
stateenum.addId(_deadState().name)
stateenum.addId(_nullState().name)
stateenum.addId(_errorState().name)
stateenum.addId(_dyingState().name)
for ts in p.transitionStmts:
stateenum.addId(ts.state.decl.cxxname)
if len(p.transitionStmts):
startstate = p.transitionStmts[0].state.decl.cxxname
else:
startstate = _nullState().name
stateenum.addId(_startState().name, startstate)
ns.addstmts([ StmtDecl(Decl(stateenum,'')), Whitespace.NL ])
# spit out message type enum and classes
msgenum = TypeEnum('MessageType')
msgstart = _messageStartName(self.protocol.decl.type) +' << 16'
msgenum.addId(self.protocol.name + 'Start', msgstart)
#msgenum.addId(self.protocol.name +'PreStart', '('+ msgstart +') - 1')
for md in p.messageDecls:
msgenum.addId(md.msgId())
if md.hasReply():
msgenum.addId(md.replyId())
msgenum.addId(self.protocol.name +'End')
ns.addstmts([ StmtDecl(Decl(msgenum, '')), Whitespace.NL ])
tfDecl, tfDefn = _splitFuncDeclDefn(self.genTransitionFunc())
ns.addstmts([ tfDecl, Whitespace.NL ])
self.funcDefns.append(tfDefn)
for md in p.messageDecls:
ns.addstmts([
_generateMessageClass(md.msgClass(), md.msgId(),
md.decl.type.priority,
md.prettyMsgName(p.name+'::'),
md.decl.type.compress),
Whitespace.NL ])
if md.hasReply():
ns.addstmts([
_generateMessageClass(
md.replyClass(), md.replyId(),
md.decl.type.priority,
md.prettyReplyName(p.name+'::'),
md.decl.type.compress),
Whitespace.NL ])
ns.addstmts([ Whitespace.NL, Whitespace.NL ])
def genBridgeFunc(self, bridge):
p = self.protocol
parentHandleType = _cxxBareType(ActorType(bridge.parent.ptype),
_otherSide(bridge.parent.side),
fq=1)
parentvar = ExprVar('parentHandle')
childHandleType = _cxxBareType(ActorType(bridge.child.ptype),
_otherSide(bridge.child.side),
fq=1)
childvar = ExprVar('childHandle')
bridgefunc = MethodDefn(MethodDecl(
'Bridge',
params=[ Decl(parentHandleType, parentvar.name),
Decl(childHandleType, childvar.name) ],
ret=Type.NSRESULT))
bridgefunc.addstmt(StmtReturn(ExprCall(
ExprVar('mozilla::ipc::Bridge'),
args=[ _backstagePass(),
p.callGetChannel(parentvar), p.callOtherPid(parentvar),
p.callGetChannel(childvar), p.callOtherPid(childvar),
_protocolId(p.decl.type),
ExprVar(_messageStartName(p.decl.type) + 'Child')
])))
return bridgefunc
def genOpenFunc(self, o):
p = self.protocol
localside = o.opener.side
openertype = _cxxBareType(ActorType(o.opener.ptype), o.opener.side,
fq=1)
openervar = ExprVar('opener')
openfunc = MethodDefn(MethodDecl(
'Open',
params=[ Decl(openertype, openervar.name) ],
ret=Type.BOOL))
openfunc.addstmt(StmtReturn(ExprCall(
ExprVar('mozilla::ipc::Open'),
args=[ _backstagePass(),
p.callGetChannel(openervar), p.callOtherPid(openervar),
_sideToTransportMode(localside),
_protocolId(p.decl.type),
ExprVar(_messageStartName(p.decl.type) + 'Child')
])))
return openfunc
def genTransitionFunc(self):
ptype = self.protocol.decl.type
usesend, sendvar = set(), ExprVar('Send__')
userecv, recvvar = set(), ExprVar('Recv__')
def sameTrigger(trigger, actionexpr):
if trigger is ipdl.ast.SEND or trigger is ipdl.ast.CALL:
usesend.add('yes')
return ExprBinary(sendvar, '==', actionexpr)
else:
userecv.add('yes')
return ExprBinary(recvvar, '==',
actionexpr)
def stateEnum(s):
if s is ipdl.ast.State.DEAD:
return _deadState()
else:
return ExprVar(s.decl.cxxname)
# bool Transition(State from, Trigger trigger, State* next)
fromvar = ExprVar('from')
triggervar = ExprVar('trigger')
nextvar = ExprVar('next')
msgexpr = ExprSelect(triggervar, '.', 'mMsg')
actionexpr = ExprSelect(triggervar, '.', 'mAction')
transitionfunc = FunctionDefn(FunctionDecl(
'Transition',
params=[ Decl(Type('State'), fromvar.name),
Decl(Type('mozilla::ipc::Trigger'), triggervar.name),
Decl(Type('State', ptr=1), nextvar.name) ],
ret=Type.BOOL))
fromswitch = StmtSwitch(fromvar)
for ts in self.protocol.transitionStmts:
msgswitch = StmtSwitch(msgexpr)
msgToTransitions = { }
for t in ts.transitions:
msgid = t.msg._md.msgId()
ifsametrigger = StmtIf(sameTrigger(t.trigger, actionexpr))
# FIXME multi-out states
for nextstate in t.toStates: break
ifsametrigger.addifstmts([
StmtExpr(ExprAssn(ExprDeref(nextvar),
stateEnum(nextstate))),
StmtReturn(ExprLiteral.TRUE)
])
transitions = msgToTransitions.get(msgid, [ ])
transitions.append(ifsametrigger)
msgToTransitions[msgid] = transitions
for msgid, transitions in msgToTransitions.iteritems():
block = Block()
block.addstmts(transitions +[ StmtBreak() ])
msgswitch.addcase(CaseLabel(msgid), block)
msgblock = Block()
msgblock.addstmts([
msgswitch,
StmtBreak()
])
fromswitch.addcase(CaseLabel(ts.state.decl.cxxname), msgblock)
# special cases for Null and Error
nullerrorblock = Block()
if ptype.hasDelete:
ifdelete = StmtIf(ExprBinary(_deleteId(), '==', msgexpr))
if ptype.hasReentrantDelete:
nextState = _dyingState()
else:
nextState = _deadState()
ifdelete.addifstmts([
StmtExpr(ExprAssn(ExprDeref(nextvar), nextState)),
StmtReturn(ExprLiteral.TRUE) ])
nullerrorblock.addstmt(ifdelete)
nullerrorblock.addstmt(
StmtReturn(ExprBinary(_nullState(), '==', fromvar)))
fromswitch.addfallthrough(CaseLabel(_nullState().name))
fromswitch.addcase(CaseLabel(_errorState().name), nullerrorblock)
# special case for Dead
deadblock = Block()
deadblock.addstmts([
_runtimeAbort('__delete__()d actor'),
StmtReturn(ExprLiteral.FALSE) ])
fromswitch.addcase(CaseLabel(_deadState().name), deadblock)
# special case for Dying
dyingblock = Block()
if ptype.hasReentrantDelete:
ifdelete = StmtIf(ExprBinary(_deleteReplyId(), '==', msgexpr))
ifdelete.addifstmt(
StmtExpr(ExprAssn(ExprDeref(nextvar), _deadState())))
dyingblock.addstmt(ifdelete)
dyingblock.addstmt(
StmtReturn(ExprLiteral.TRUE))
else:
dyingblock.addstmts([
_runtimeAbort('__delete__()d (and unexpectedly dying) actor'),
StmtReturn(ExprLiteral.FALSE) ])
fromswitch.addcase(CaseLabel(_dyingState().name), dyingblock)
unreachedblock = Block()
unreachedblock.addstmts([
_runtimeAbort('corrupted actor state'),
StmtReturn(ExprLiteral.FALSE) ])
fromswitch.addcase(DefaultLabel(), unreachedblock)
if usesend:
transitionfunc.addstmt(
StmtDecl(Decl(Type('int32_t', const=1), sendvar.name),
init=ExprVar('mozilla::ipc::Trigger::Send')))
if userecv:
transitionfunc.addstmt(
StmtDecl(Decl(Type('int32_t', const=1), recvvar.name),
init=ExprVar('mozilla::ipc::Trigger::Recv')))
if usesend or userecv:
transitionfunc.addstmt(Whitespace.NL)
transitionfunc.addstmt(fromswitch)
# all --> Error transitions break to here. But only insert this
# block if there is any possibility of such transitions.
if self.protocol.transitionStmts:
transitionfunc.addstmts([
StmtExpr(ExprAssn(ExprDeref(nextvar), _errorState())),
StmtReturn(ExprLiteral.FALSE),
])
return transitionfunc
##--------------------------------------------------
def _generateMessageClass(clsname, msgid, priority, prettyName, compress):
cls = Class(name=clsname, inherits=[ Inherit(Type('IPC::Message')) ])
cls.addstmt(Label.PUBLIC)
idenum = TypeEnum()
idenum.addId('ID', msgid)
cls.addstmt(StmtDecl(Decl(idenum, '')))
# make the message constructor
if compress == 'compress':
compression = ExprVar('COMPRESSION_ENABLED')
elif compress:
assert compress == 'compressall'
compression = ExprVar('COMPRESSION_ALL')
else:
compression = ExprVar('COMPRESSION_NONE')
if priority == ipdl.ast.NORMAL_PRIORITY:
priorityEnum = 'IPC::Message::PRIORITY_NORMAL'
elif priority == ipdl.ast.HIGH_PRIORITY:
priorityEnum = 'IPC::Message::PRIORITY_HIGH'
else:
assert priority == ipdl.ast.URGENT_PRIORITY
priorityEnum = 'IPC::Message::PRIORITY_URGENT'
routingId = ExprVar('routingId')
ctor = ConstructorDefn(
ConstructorDecl(clsname, params=[ Decl(Type('int32_t'), routingId.name) ]),
memberinits=[ ExprMemberInit(ExprVar('IPC::Message'),
[ routingId,
ExprVar('ID'),
ExprVar(priorityEnum),
compression,
ExprLiteral.String(prettyName) ]) ])
cls.addstmts([ ctor, Whitespace.NL ])
# generate a logging function
# 'pfx' will be something like "[FooParent] sent"
pfxvar = ExprVar('pfx__')
otherpid = ExprVar('otherPid__')
receiving = ExprVar('receiving__')
logger = MethodDefn(MethodDecl(
'Log',
params=([ Decl(Type('std::string', const=1, ref=1), pfxvar.name),
Decl(Type('base::ProcessId'), otherpid.name),
Decl(Type('bool'), receiving.name) ]),
const=1))
# TODO/cjones: allow selecting what information is printed to
# the log
msgvar = ExprVar('logmsg__')
logger.addstmt(StmtDecl(Decl(Type('std::string'), msgvar.name)))
def appendToMsg(thing):
return StmtExpr(ExprCall(ExprSelect(msgvar, '.', 'append'),
args=[ thing ]))
logger.addstmts([
StmtExpr(ExprCall(
ExprVar('StringAppendF'),
args=[ ExprAddrOf(msgvar),
ExprLiteral.String('[time:%" PRId64 "][%d%s%d]'),
ExprCall(ExprVar('PR_Now')),
ExprCall(ExprVar('base::GetCurrentProcId')),
ExprConditional(receiving, ExprLiteral.String('<-'),
ExprLiteral.String('->')),
otherpid ])),
appendToMsg(pfxvar),
appendToMsg(ExprLiteral.String(clsname +'(')),
Whitespace.NL
])
# TODO turn this back on when string stuff is sorted
logger.addstmt(appendToMsg(ExprLiteral.String('[TODO])\\n')))
logger.addstmts([
CppDirective('ifdef', 'ANDROID'),
StmtExpr(ExprCall(
ExprVar('__android_log_write'),
args=[ ExprVar('ANDROID_LOG_INFO'),
ExprLiteral.String('GeckoIPC'),
ExprCall(ExprSelect(msgvar, '.', 'c_str')) ])),
CppDirective('endif')
])
# and actually print the log message
logger.addstmt(StmtExpr(ExprCall(
ExprVar('fputs'),
args=[ ExprCall(ExprSelect(msgvar, '.', 'c_str')),
ExprVar('stderr') ])))
cls.addstmt(logger)
return cls
##--------------------------------------------------
class _ComputeTypeDeps(TypeVisitor):
'''Pass that gathers the C++ types that a particular IPDL type
(recursively) depends on. There are two kinds of dependencies: (i)
types that need forward declaration; (ii) types that need a |using|
stmt. Some types generate both kinds.'''
def __init__(self, fortype, unqualifiedTypedefs=False):
ipdl.type.TypeVisitor.__init__(self)
self.usingTypedefs = [ ]
self.forwardDeclStmts = [ ]
self.fortype = fortype
self.unqualifiedTypedefs = unqualifiedTypedefs
def maybeTypedef(self, fqname, name):
if fqname != name or self.unqualifiedTypedefs:
self.usingTypedefs.append(Typedef(Type(fqname), name))
def visitBuiltinCxxType(self, t):
if t in self.visited: return
self.visited.add(t)
self.maybeTypedef(t.fullname(), t.name())
def visitImportedCxxType(self, t):
if t in self.visited: return
self.visited.add(t)
self.maybeTypedef(t.fullname(), t.name())
def visitActorType(self, t):
if t in self.visited: return
self.visited.add(t)
fqname, name = t.fullname(), t.name()
self.maybeTypedef(_actorName(fqname, 'Parent'),
_actorName(name, 'Parent'))
self.maybeTypedef(_actorName(fqname, 'Child'),
_actorName(name, 'Child'))
self.forwardDeclStmts.extend([
_makeForwardDeclForActor(t.protocol, 'parent'), Whitespace.NL,
_makeForwardDeclForActor(t.protocol, 'child'), Whitespace.NL
])
def visitStructOrUnionType(self, su, defaultVisit):
if su in self.visited or su == self.fortype: return
self.visited.add(su)
self.maybeTypedef(su.fullname(), su.name())
if su.mutuallyRecursiveWith(self.fortype):
self.forwardDeclStmts.append(_makeForwardDecl(su))
return defaultVisit(self, su)
def visitStructType(self, t):
return self.visitStructOrUnionType(t, TypeVisitor.visitStructType)
def visitUnionType(self, t):
return self.visitStructOrUnionType(t, TypeVisitor.visitUnionType)
def visitArrayType(self, t):
return TypeVisitor.visitArrayType(self, t)
def visitShmemType(self, s):
if s in self.visited: return
self.visited.add(s)
self.maybeTypedef('mozilla::ipc::Shmem', 'Shmem')
def visitFDType(self, s):
if s in self.visited: return
self.visited.add(s)
self.maybeTypedef('mozilla::ipc::FileDescriptor', 'FileDescriptor')
def visitVoidType(self, v): assert 0
def visitMessageType(self, v): assert 0
def visitProtocolType(self, v): assert 0
def visitStateType(self, v): assert 0
def _generateCxxStruct(sd):
''' '''
# compute all the typedefs and forward decls we need to make
gettypedeps = _ComputeTypeDeps(sd.decl.type)
for f in sd.fields:
f.ipdltype.accept(gettypedeps)
usingTypedefs = gettypedeps.usingTypedefs
forwarddeclstmts = gettypedeps.forwardDeclStmts
struct = Class(sd.name, final=1)
struct.addstmts([ Label.PRIVATE ]
+ usingTypedefs
+ [ Whitespace.NL, Label.PUBLIC ])
constreftype = Type(sd.name, const=1, ref=1)
initvar = ExprVar('Init')
callinit = ExprCall(initvar)
assignvar = ExprVar('Assign')
def fieldsAsParamList():
return [ Decl(f.inType(), f.argVar().name) for f in sd.fields ]
def assignFromOther(oexpr):
return ExprCall(assignvar,
args=[ f.initExpr(oexpr) for f in sd.fields ])
# If this is an empty struct (no fields), then the default ctor
# and "create-with-fields" ctors are equivalent. So don't bother
# with the default ctor.
if len(sd.fields):
# Struct()
defctor = ConstructorDefn(ConstructorDecl(sd.name))
defctor.addstmt(StmtExpr(callinit))
defctor.memberinits = []
for f in sd.fields:
# Only generate default values for primitives.
if not (f.ipdltype.isCxx() and f.ipdltype.isAtom()):
continue
defctor.memberinits.append(ExprMemberInit(f.memberVar()))
struct.addstmts([ defctor, Whitespace.NL ])
# Struct(const field1& _f1, ...)
valctor = ConstructorDefn(ConstructorDecl(sd.name,
params=fieldsAsParamList(),
force_inline=1))
valctor.addstmts([
StmtExpr(callinit),
StmtExpr(ExprCall(assignvar,
args=[ f.argVar() for f in sd.fields ]))
])
struct.addstmts([ valctor, Whitespace.NL ])
# Struct(const Struct& _o)
ovar = ExprVar('_o')
copyctor = ConstructorDefn(ConstructorDecl(
sd.name,
params=[ Decl(constreftype, ovar.name) ],
force_inline=1))
copyctor.addstmts([
StmtExpr(callinit),
StmtExpr(assignFromOther(ovar))
])
struct.addstmts([ copyctor, Whitespace.NL ])
# ~Struct()
dtor = DestructorDefn(DestructorDecl(sd.name))
for f in sd.fields:
dtor.addstmts(f.destructStmts())
struct.addstmts([ dtor, Whitespace.NL ])
# Struct& operator=(const Struct& _o)
opeq = MethodDefn(MethodDecl(
'operator=',
params=[ Decl(constreftype, ovar.name) ],
force_inline=1))
opeq.addstmt(StmtExpr(assignFromOther(ovar)))
struct.addstmts([ opeq, Whitespace.NL ])
# bool operator==(const Struct& _o)
opeqeq = MethodDefn(MethodDecl(
'operator==',
params=[ Decl(constreftype, ovar.name) ],
ret=Type.BOOL,
const=1))
for f in sd.fields:
ifneq = StmtIf(ExprNot(
ExprBinary(ExprCall(f.getMethod()), '==',
ExprCall(f.getMethod(ovar)))))
ifneq.addifstmt(StmtReturn.FALSE)
opeqeq.addstmt(ifneq)
opeqeq.addstmt(StmtReturn.TRUE)
struct.addstmts([ opeqeq, Whitespace.NL ])
# field1& f1()
# const field1& f1() const
for f in sd.fields:
get = MethodDefn(MethodDecl(f.getMethod().name,
params=[ ],
ret=f.refType(),
force_inline=1))
get.addstmt(StmtReturn(f.refExpr()))
getconstdecl = deepcopy(get.decl)
getconstdecl.ret = f.constRefType()
getconstdecl.const = 1
getconst = MethodDefn(getconstdecl)
getconst.addstmt(StmtReturn(f.constRefExpr()))
struct.addstmts([ get, getconst, Whitespace.NL ])
# private:
struct.addstmt(Label.PRIVATE)
# Init()
init = MethodDefn(MethodDecl(initvar.name))
for f in sd.fields:
init.addstmts(f.initStmts())
struct.addstmts([ init, Whitespace.NL ])
# Assign(const field1& _f1, ...)
assign = MethodDefn(MethodDecl(assignvar.name,
params=fieldsAsParamList()))
assign.addstmts([ StmtExpr(ExprAssn(f.refExpr(), f.argVar()))
for f in sd.fields ])
struct.addstmts([ assign, Whitespace.NL ])
# members
struct.addstmts([ StmtDecl(Decl(f.internalType(), f.memberVar().name))
for f in sd.fields ])
return forwarddeclstmts, struct
##--------------------------------------------------
def _generateCxxUnion(ud):
# This Union class basically consists of a type (enum) and a
# union for storage. The union can contain POD and non-POD
# types. Each type needs a copy ctor, assignment operator,
# and dtor.
#
# Rather than templating this class and only providing
# specializations for the types we support, which is slightly
# "unsafe" in that C++ code can add additional specializations
# without the IPDL compiler's knowledge, we instead explicitly
# implement non-templated methods for each supported type.
#
# The one complication that arises is that C++, for arcane
# reasons, does not allow the placement destructor of a
# builtin type, like int, to be directly invoked. So we need
# to hack around this by internally typedef'ing all
# constituent types. Sigh.
#
# So, for each type, this "Union" class needs:
# (private)
# - entry in the type enum
# - entry in the storage union
# - [type]ptr() method to get a type* from the underlying union
# - same as above to get a const type*
# - typedef to hack around placement delete limitations
# (public)
# - placement delete case for dtor
# - copy ctor
# - case in generic copy ctor
# - operator= impl
# - case in generic operator=
# - operator [type&]
# - operator [const type&] const
# - [type&] get_[type]()
# - [const type&] get_[type]() const
#
cls = Class(ud.name, final=1)
# const Union&, i.e., Union type with inparam semantics
inClsType = Type(ud.name, const=1, ref=1)
refClsType = Type(ud.name, ref=1)
typetype = Type('Type')
valuetype = Type('Value')
mtypevar = ExprVar('mType')
mvaluevar = ExprVar('mValue')
maybedtorvar = ExprVar('MaybeDestroy')
assertsanityvar = ExprVar('AssertSanity')
tnonevar = ExprVar('T__None')
tlastvar = ExprVar('T__Last')
def callAssertSanity(uvar=None, expectTypeVar=None):
func = assertsanityvar
args = [ ]
if uvar is not None:
func = ExprSelect(uvar, '.', assertsanityvar.name)
if expectTypeVar is not None:
args.append(expectTypeVar)
return ExprCall(func, args=args)
def callMaybeDestroy(newTypeVar):
return ExprCall(maybedtorvar, args=[ newTypeVar ])
def maybeReconstruct(memb, newTypeVar):
ifdied = StmtIf(callMaybeDestroy(newTypeVar))
ifdied.addifstmt(StmtExpr(memb.callCtor()))
return ifdied
# compute all the typedefs and forward decls we need to make
gettypedeps = _ComputeTypeDeps(ud.decl.type)
for c in ud.components:
c.ipdltype.accept(gettypedeps)
usingTypedefs = gettypedeps.usingTypedefs
forwarddeclstmts = gettypedeps.forwardDeclStmts
# the |Type| enum, used to switch on the discunion's real type
cls.addstmt(Label.PUBLIC)
typeenum = TypeEnum(typetype.name)
typeenum.addId(tnonevar.name, 0)
firstid = ud.components[0].enum()
typeenum.addId(firstid, 1)
for c in ud.components[1:]:
typeenum.addId(c.enum())
typeenum.addId(tlastvar.name, ud.components[-1].enum())
cls.addstmts([ StmtDecl(Decl(typeenum,'')),
Whitespace.NL ])
cls.addstmt(Label.PRIVATE)
cls.addstmts(
usingTypedefs
# hacky typedef's that allow placement dtors of builtins
+ [ Typedef(c.internalType(), c.typedef()) for c in ud.components ])
cls.addstmt(Whitespace.NL)
# the C++ union the discunion use for storage
valueunion = TypeUnion(valuetype.name)
for c in ud.components:
valueunion.addComponent(c.unionType(), c.name)
cls.addstmts([ StmtDecl(Decl(valueunion,'')),
Whitespace.NL ])
# for each constituent type T, add private accessors that
# return a pointer to the Value union storage casted to |T*|
# and |const T*|
for c in ud.components:
getptr = MethodDefn(MethodDecl(
c.getPtrName(), params=[ ], ret=c.ptrToInternalType(),
force_inline=1))
getptr.addstmt(StmtReturn(c.ptrToSelfExpr()))
getptrconst = MethodDefn(MethodDecl(
c.getConstPtrName(), params=[ ], ret=c.constPtrToType(),
const=1, force_inline=1))
getptrconst.addstmt(StmtReturn(c.constptrToSelfExpr()))
cls.addstmts([ getptr, getptrconst ])
cls.addstmt(Whitespace.NL)
# add a helper method that invokes the placement dtor on the
# current underlying value, only if |aNewType| is different
# than the current type, and returns true if the underlying
# value needs to be re-constructed
newtypevar = ExprVar('aNewType')
maybedtor = MethodDefn(MethodDecl(
maybedtorvar.name,
params=[ Decl(typetype, newtypevar.name) ],
ret=Type.BOOL))
# wasn't /actually/ dtor'd, but it needs to be re-constructed
ifnone = StmtIf(ExprBinary(mtypevar, '==', tnonevar))
ifnone.addifstmt(StmtReturn.TRUE)
# same type, nothing to see here
ifnochange = StmtIf(ExprBinary(mtypevar, '==', newtypevar))
ifnochange.addifstmt(StmtReturn.FALSE)
# need to destroy. switch on underlying type
dtorswitch = StmtSwitch(mtypevar)
for c in ud.components:
dtorswitch.addcase(
CaseLabel(c.enum()),
StmtBlock([ StmtExpr(c.callDtor()),
StmtBreak() ]))
dtorswitch.addcase(
DefaultLabel(),
StmtBlock([ _runtimeAbort("not reached"), StmtBreak() ]))
maybedtor.addstmts([
ifnone,
ifnochange,
dtorswitch,
StmtReturn.TRUE
])
cls.addstmts([ maybedtor, Whitespace.NL ])
# add helper methods that ensure the discunion has a
# valid type
sanity = MethodDefn(MethodDecl(
assertsanityvar.name, ret=Type.VOID, const=1, force_inline=1))
sanity.addstmts([
_abortIfFalse(ExprBinary(tnonevar, '<=', mtypevar),
'invalid type tag'),
_abortIfFalse(ExprBinary(mtypevar, '<=', tlastvar),
'invalid type tag') ])
cls.addstmt(sanity)
atypevar = ExprVar('aType')
sanity2 = MethodDefn(
MethodDecl(assertsanityvar.name,
params=[ Decl(typetype, atypevar.name) ],
ret=Type.VOID,
const=1, force_inline=1))
sanity2.addstmts([
StmtExpr(ExprCall(assertsanityvar)),
_abortIfFalse(ExprBinary(mtypevar, '==', atypevar),
'unexpected type tag') ])
cls.addstmts([ sanity2, Whitespace.NL ])
## ---- begin public methods -----
# Union() default ctor
cls.addstmts([
Label.PUBLIC,
ConstructorDefn(
ConstructorDecl(ud.name, force_inline=1),
memberinits=[ ExprMemberInit(mtypevar, [ tnonevar ]) ]),
Whitespace.NL
])
# Union(const T&) copy ctors
othervar = ExprVar('aOther')
for c in ud.components:
copyctor = ConstructorDefn(ConstructorDecl(
ud.name, params=[ Decl(c.inType(), othervar.name) ]))
copyctor.addstmts([
StmtExpr(c.callCtor(othervar)),
StmtExpr(ExprAssn(mtypevar, c.enumvar())) ])
cls.addstmts([ copyctor, Whitespace.NL ])
# Union(const Union&) copy ctor
copyctor = ConstructorDefn(ConstructorDecl(
ud.name, params=[ Decl(inClsType, othervar.name) ]))
othertype = ud.callType(othervar)
copyswitch = StmtSwitch(othertype)
for c in ud.components:
copyswitch.addcase(
CaseLabel(c.enum()),
StmtBlock([
StmtExpr(c.callCtor(
ExprCall(ExprSelect(othervar,
'.', c.getConstTypeName())))),
StmtBreak()
]))
copyswitch.addcase(CaseLabel(tnonevar.name),
StmtBlock([ StmtBreak() ]))
copyswitch.addcase(
DefaultLabel(),
StmtBlock([ _runtimeAbort('unreached'), StmtReturn() ]))
copyctor.addstmts([
StmtExpr(callAssertSanity(uvar=othervar)),
copyswitch,
StmtExpr(ExprAssn(mtypevar, othertype))
])
cls.addstmts([ copyctor, Whitespace.NL ])
# ~Union()
dtor = DestructorDefn(DestructorDecl(ud.name))
dtor.addstmt(StmtExpr(callMaybeDestroy(tnonevar)))
cls.addstmts([ dtor, Whitespace.NL ])
# type()
typemeth = MethodDefn(MethodDecl('type', ret=typetype,
const=1, force_inline=1))
typemeth.addstmt(StmtReturn(mtypevar))
cls.addstmts([ typemeth, Whitespace.NL ])
# Union& operator=(const T&) methods
rhsvar = ExprVar('aRhs')
for c in ud.components:
opeq = MethodDefn(MethodDecl(
'operator=',
params=[ Decl(c.inType(), rhsvar.name) ],
ret=refClsType))
opeq.addstmts([
# might need to placement-delete old value first
maybeReconstruct(c, c.enumvar()),
StmtExpr(c.callOperatorEq(rhsvar)),
StmtExpr(ExprAssn(mtypevar, c.enumvar())),
StmtReturn(ExprDeref(ExprVar.THIS))
])
cls.addstmts([ opeq, Whitespace.NL ])
# Union& operator=(const Union&)
opeq = MethodDefn(MethodDecl(
'operator=',
params=[ Decl(inClsType, rhsvar.name) ],
ret=refClsType))
rhstypevar = ExprVar('t')
opeqswitch = StmtSwitch(rhstypevar)
for c in ud.components:
case = StmtBlock()
case.addstmts([
maybeReconstruct(c, rhstypevar),
StmtExpr(c.callOperatorEq(
ExprCall(ExprSelect(rhsvar, '.', c.getConstTypeName())))),
StmtBreak()
])
opeqswitch.addcase(CaseLabel(c.enum()), case)
opeqswitch.addcase(CaseLabel(tnonevar.name),
StmtBlock([ StmtExpr(callMaybeDestroy(rhstypevar)),
StmtBreak() ]))
opeqswitch.addcase(
DefaultLabel(),
StmtBlock([ _runtimeAbort('unreached'), StmtBreak() ]))
opeq.addstmts([
StmtExpr(callAssertSanity(uvar=rhsvar)),
StmtDecl(Decl(typetype, rhstypevar.name), init=ud.callType(rhsvar)),
opeqswitch,
StmtExpr(ExprAssn(mtypevar, rhstypevar)),
StmtReturn(ExprDeref(ExprVar.THIS))
])
cls.addstmts([ opeq, Whitespace.NL ])
# bool operator==(const T&)
for c in ud.components:
opeqeq = MethodDefn(MethodDecl(
'operator==',
params=[ Decl(c.inType(), rhsvar.name) ],
ret=Type.BOOL,
const=1))
opeqeq.addstmt(StmtReturn(ExprBinary(
ExprCall(ExprVar(c.getTypeName())), '==', rhsvar)))
cls.addstmts([ opeqeq, Whitespace.NL ])
# bool operator==(const Union&)
opeqeq = MethodDefn(MethodDecl(
'operator==',
params=[ Decl(inClsType, rhsvar.name) ],
ret=Type.BOOL,
const=1))
iftypesmismatch = StmtIf(ExprBinary(ud.callType(), '!=',
ud.callType(rhsvar)))
iftypesmismatch.addifstmt(StmtReturn.FALSE)
opeqeq.addstmts([ iftypesmismatch, Whitespace.NL ])
opeqeqswitch = StmtSwitch(ud.callType())
for c in ud.components:
case = StmtBlock()
case.addstmt(StmtReturn(ExprBinary(
ExprCall(ExprVar(c.getTypeName())), '==',
ExprCall(ExprSelect(rhsvar, '.', c.getTypeName())))))
opeqeqswitch.addcase(CaseLabel(c.enum()), case)
opeqeqswitch.addcase(
DefaultLabel(),
StmtBlock([ _runtimeAbort('unreached'),
StmtReturn.FALSE ]))
opeqeq.addstmt(opeqeqswitch)
cls.addstmts([ opeqeq, Whitespace.NL ])
# accessors for each type: operator T&, operator const T&,
# T& get(), const T& get()
for c in ud.components:
getValueVar = ExprVar(c.getTypeName())
getConstValueVar = ExprVar(c.getConstTypeName())
getvalue = MethodDefn(MethodDecl(getValueVar.name,
ret=c.refType(),
force_inline=1))
getvalue.addstmts([
StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())),
StmtReturn(ExprDeref(c.callGetPtr()))
])
getconstvalue = MethodDefn(MethodDecl(
getConstValueVar.name, ret=c.constRefType(),
const=1, force_inline=1))
getconstvalue.addstmts([
StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())),
StmtReturn(c.getConstValue())
])
optype = MethodDefn(MethodDecl('', typeop=c.refType(), force_inline=1))
optype.addstmt(StmtReturn(ExprCall(getValueVar)))
opconsttype = MethodDefn(MethodDecl(
'', const=1, typeop=c.constRefType(), force_inline=1))
opconsttype.addstmt(StmtReturn(ExprCall(getConstValueVar)))
cls.addstmts([ getvalue, getconstvalue,
optype, opconsttype,
Whitespace.NL ])
# private vars
cls.addstmts([
Label.PRIVATE,
StmtDecl(Decl(valuetype, mvaluevar.name)),
StmtDecl(Decl(typetype, mtypevar.name))
])
return forwarddeclstmts, cls
##-----------------------------------------------------------------------------
class _FindFriends(ipdl.ast.Visitor):
def __init__(self):
self.mytype = None # ProtocolType
self.vtype = None # ProtocolType
self.friends = set() # set<ProtocolType>
def findFriends(self, ptype):
self.mytype = ptype
for toplvl in ptype.toplevels():
self.walkDownTheProtocolTree(toplvl);
return self.friends
# TODO could make this into a _iterProtocolTreeHelper ...
def walkDownTheProtocolTree(self, ptype):
if ptype != self.mytype:
# don't want to |friend| ourself!
self.visit(ptype)
for mtype in ptype.manages:
if mtype is not ptype:
self.walkDownTheProtocolTree(mtype)
def visit(self, ptype):
# |vtype| is the type currently being visited
savedptype = self.vtype
self.vtype = ptype
ptype._ast.accept(self)
self.vtype = savedptype
def visitMessageDecl(self, md):
for it in self.iterActorParams(md):
if it.protocol == self.mytype:
self.friends.add(self.vtype)
def iterActorParams(self, md):
for param in md.inParams:
for actor in ipdl.type.iteractortypes(param.type):
yield actor
for ret in md.outParams:
for actor in ipdl.type.iteractortypes(ret.type):
yield actor
class _GenerateProtocolActorCode(ipdl.ast.Visitor):
def __init__(self, myside):
self.side = myside # "parent" or "child"
self.prettyside = myside.title()
self.clsname = None
self.protocol = None
self.hdrfile = None
self.cppfile = None
self.ns = None
self.cls = None
self.includedActorTypedefs = [ ]
self.protocolCxxIncludes = [ ]
self.actorForwardDecls = [ ]
self.usingDecls = [ ]
self.externalIncludes = set()
self.nonForwardDeclaredHeaders = set()
def lower(self, tu, clsname, cxxHeaderFile, cxxFile):
self.clsname = clsname
self.hdrfile = cxxHeaderFile
self.cppfile = cxxFile
tu.accept(self)
def standardTypedefs(self):
return [
Typedef(Type(self.protocol.fqBaseClass()), 'ProtocolBase'),
Typedef(Type('IPC::Message'), 'Message'),
Typedef(Type(self.protocol.channelName()), 'Channel'),
Typedef(Type(self.protocol.fqListenerName()), 'ChannelListener'),
Typedef(Type('base::ProcessHandle'), 'ProcessHandle'),
Typedef(Type('mozilla::ipc::MessageChannel'), 'MessageChannel'),
Typedef(Type('mozilla::ipc::SharedMemory'), 'SharedMemory'),
Typedef(Type('mozilla::ipc::Trigger'), 'Trigger'),
Typedef(Type('mozilla::ipc::ProtocolCloneContext'),
'ProtocolCloneContext')
]
def visitTranslationUnit(self, tu):
self.protocol = tu.protocol
hf = self.hdrfile
cf = self.cppfile
# make the C++ header
hf.addthings(
[ _DISCLAIMER ]
+ _includeGuardStart(hf)
+[
Whitespace.NL,
CppDirective(
'include',
'"'+ _protocolHeaderName(tu.protocol) +'.h"')
])
for inc in tu.includes:
inc.accept(self)
for inc in tu.cxxIncludes:
inc.accept(self)
for using in tu.using:
using.accept(self)
# this generates the actor's full impl in self.cls
tu.protocol.accept(self)
clsdecl, clsdefn = _splitClassDeclDefn(self.cls)
# XXX damn C++ ... return types in the method defn aren't in
# class scope
for stmt in clsdefn.stmts:
if isinstance(stmt, MethodDefn):
if stmt.decl.ret and stmt.decl.ret.name == 'Result':
stmt.decl.ret.name = clsdecl.name +'::'+ stmt.decl.ret.name
def setToIncludes(s):
return [ CppDirective('include', '"%s"' % i)
for i in sorted(iter(s)) ]
def makeNamespace(p, file):
if 0 == len(p.namespaces):
return file
ns = Namespace(p.namespaces[-1].name)
outerns = _putInNamespaces(ns, p.namespaces[:-1])
file.addthing(outerns)
return ns
if len(self.nonForwardDeclaredHeaders) != 0:
self.hdrfile.addthings(
[ Whitespace('// Headers for things that cannot be forward declared'),
Whitespace.NL ]
+ setToIncludes(self.nonForwardDeclaredHeaders)
+ [ Whitespace.NL ]
)
self.hdrfile.addthings(self.actorForwardDecls)
self.hdrfile.addthings(self.usingDecls)
hdrns = makeNamespace(self.protocol, self.hdrfile)
hdrns.addstmts([
Whitespace.NL,
Whitespace.NL,
clsdecl,
Whitespace.NL,
Whitespace.NL
])
self.hdrfile.addthings(
([
Whitespace.NL,
CppDirective('if', '0') ])
+ _GenerateSkeletonImpl(
_actorName(self.protocol.name, self.side)[1:],
self.protocol.namespaces).fromclass(self.cls)
+([
CppDirective('endif', '// if 0'),
Whitespace.NL ])
+ _includeGuardEnd(hf))
# make the .cpp file
cf.addthings([
_DISCLAIMER,
Whitespace.NL,
CppDirective(
'include',
'"'+ _protocolHeaderName(self.protocol, self.side) +'.h"') ]
+ setToIncludes(self.externalIncludes))
if self.protocol.decl.type.isToplevel():
cf.addthings([
CppDirective('ifdef', 'MOZ_CRASHREPORTER'),
CppDirective(' include', '"nsXULAppAPI.h"'),
CppDirective('endif')
])
cppheaders = [CppDirective('include', '"%s"' % filename)
for filename in ipdl.builtin.CppIncludes]
cf.addthings((
[ Whitespace.NL ]
+ [ CppDirective(
'include',
'"%s.h"' % (inc)) for inc in self.protocolCxxIncludes ]
+ [ Whitespace.NL ]
+ cppheaders
+ [ Whitespace.NL ]))
cppns = makeNamespace(self.protocol, cf)
cppns.addstmts([
Whitespace.NL,
Whitespace.NL,
clsdefn,
Whitespace.NL,
Whitespace.NL
])
def visitUsingStmt(self, using):
if using.header is None:
return
if using.canBeForwardDeclared():
spec = using.type.spec
self.usingDecls.extend([
_makeForwardDeclForQClass(spec.baseid, spec.quals,
cls=using.isClass(),
struct=using.isStruct()),
Whitespace.NL
])
self.externalIncludes.add(using.header)
else:
self.nonForwardDeclaredHeaders.add(using.header)
def visitCxxInclude(self, inc):
self.nonForwardDeclaredHeaders.add(inc.file)
def visitInclude(self, inc):
ip = inc.tu.protocol
if not ip:
return
self.actorForwardDecls.extend([
_makeForwardDeclForActor(ip.decl.type, self.side),
_makeForwardDeclForActor(ip.decl.type, _otherSide(self.side)),
Whitespace.NL
])
self.protocolCxxIncludes.append(_protocolHeaderName(ip, self.side))
if ip.decl.fullname is not None:
self.includedActorTypedefs.append(Typedef(
Type(_actorName(ip.decl.fullname, self.side.title())),
_actorName(ip.decl.shortname, self.side.title())))
self.includedActorTypedefs.append(Typedef(
Type(_actorName(ip.decl.fullname, _otherSide(self.side).title())),
_actorName(ip.decl.shortname, _otherSide(self.side).title())))
def visitProtocol(self, p):
self.hdrfile.addthings([
CppDirective('ifdef', 'DEBUG'),
CppDirective('include', '"prenv.h"'),
CppDirective('endif', '// DEBUG')
])
self.protocol = p
ptype = p.decl.type
toplevel = p.decl.type.toplevel()
# FIXME: all actors impl Iface for now
if ptype.isManager() or 1:
self.hdrfile.addthing(CppDirective('include', '"base/id_map.h"'))
self.hdrfile.addthings([
CppDirective('include', '"'+ p.channelHeaderFile() +'"'),
Whitespace.NL ])
optinherits = []
if ptype.isToplevel():
optinherits.append(Inherit(p.openedProtocolInterfaceType(),
viz='public'))
if ptype.isToplevel() and self.side is 'parent':
self.hdrfile.addthings([
_makeForwardDeclForQClass('nsIFile', []),
Whitespace.NL
])
self.cls = Class(
self.clsname,
inherits=[ Inherit(Type(p.fqBaseClass()), viz='public'),
Inherit(p.managerInterfaceType(), viz='protected') ] +
optinherits,
abstract=True)
bridgeActorsCreated = ProcessGraph.bridgeEndpointsOf(ptype, self.side)
opensActorsCreated = ProcessGraph.opensEndpointsOf(ptype, self.side)
channelOpenedActors = OrderedDict.fromkeys(bridgeActorsCreated + opensActorsCreated, None)
friends = _FindFriends().findFriends(ptype)
if ptype.isManaged():
friends.update(ptype.managers)
# |friend| managed actors so that they can call our Dealloc*()
friends.update(ptype.manages)
# don't friend ourself if we're a self-managed protocol
friends.discard(ptype)
for friend in friends:
self.actorForwardDecls.extend([
_makeForwardDeclForActor(friend, self.prettyside),
Whitespace.NL
])
self.cls.addstmts([
FriendClassDecl(_actorName(friend.fullname(),
self.prettyside)),
Whitespace.NL ])
for actor in channelOpenedActors:
self.hdrfile.addthings([
Whitespace.NL,
_makeForwardDeclForActor(actor.ptype, actor.side),
Whitespace.NL
])
self.cls.addstmt(Label.PROTECTED)
for typedef in p.cxxTypedefs():
self.cls.addstmt(typedef)
for typedef in self.includedActorTypedefs:
self.cls.addstmt(typedef)
self.cls.addstmt(Whitespace.NL)
self.cls.addstmts([ Typedef(p.fqStateType(), 'State'), Whitespace.NL ])
# interface methods that the concrete subclass has to impl
for md in p.messageDecls:
isctor, isdtor = md.decl.type.isCtor(), md.decl.type.isDtor()
if self.receivesMessage(md):
# generate Recv/Answer* interface
implicit = (not isdtor)
recvDecl = MethodDecl(
md.recvMethod().name,
params=md.makeCxxParams(paramsems='move', returnsems='out',
side=self.side, implicit=implicit),
ret=Type.BOOL, virtual=1)
if isctor or isdtor:
defaultRecv = MethodDefn(recvDecl)
defaultRecv.addstmt(StmtReturn.TRUE)
self.cls.addstmt(defaultRecv)
else:
recvDecl.pure = 1
self.cls.addstmt(StmtDecl(recvDecl))
for md in p.messageDecls:
managed = md.decl.type.constructedType()
if not ptype.isManagerOf(managed) or md.decl.type.isDtor():
continue
# add the Alloc/Dealloc interface for managed actors
actortype = md.actorDecl().bareType(self.side)
self.cls.addstmt(StmtDecl(MethodDecl(
_allocMethod(managed, self.side).name,
params=md.makeCxxParams(side=self.side, implicit=0),
ret=actortype,
virtual=1, pure=1)))
self.cls.addstmt(StmtDecl(MethodDecl(
_deallocMethod(managed, self.side).name,
params=[ Decl(actortype, 'aActor') ],
ret=Type.BOOL,
virtual=1, pure=1)))
for actor in channelOpenedActors:
# add the Alloc interface for actors created when a
# new channel is opened
actortype = _cxxBareType(actor.asType(), actor.side)
self.cls.addstmt(StmtDecl(MethodDecl(
_allocMethod(actor.ptype, actor.side).name,
params=[ Decl(Type('Transport', ptr=1), 'aTransport'),
Decl(Type('ProcessId'), 'aOtherPid') ],
ret=actortype,
virtual=1, pure=1)))
# ActorDestroy() method; default is no-op
self.cls.addstmts([
Whitespace.NL,
MethodDefn(MethodDecl(
_destroyMethod().name,
params=[ Decl(_DestroyReason.Type(), 'aWhy') ],
ret=Type.VOID,
virtual=1, pure=(self.side == 'parent'))),
Whitespace.NL
])
if ptype.isToplevel():
# void ProcessingError(code); default to no-op
processingerror = MethodDefn(
MethodDecl(p.processingErrorVar().name,
params=[ Param(_Result.Type(), 'aCode'),
Param(Type('char', const=1, ptr=1), 'aReason') ],
virtual=1))
# bool ShouldContinueFromReplyTimeout(); default to |true|
shouldcontinue = MethodDefn(
MethodDecl(p.shouldContinueFromTimeoutVar().name,
ret=Type.BOOL, virtual=1))
shouldcontinue.addstmt(StmtReturn.TRUE)
# void Entered*()/Exited*(); default to no-op
entered = MethodDefn(
MethodDecl(p.enteredCxxStackVar().name, virtual=1))
exited = MethodDefn(
MethodDecl(p.exitedCxxStackVar().name, virtual=1))
enteredcall = MethodDefn(
MethodDecl(p.enteredCallVar().name, virtual=1))
exitedcall = MethodDefn(
MethodDecl(p.exitedCallVar().name, virtual=1))
self.cls.addstmts([ processingerror,
shouldcontinue,
entered, exited,
enteredcall, exitedcall,
Whitespace.NL ])
self.cls.addstmts((
[ Label.PUBLIC ]
+ self.standardTypedefs()
+ [ Whitespace.NL ]
))
self.cls.addstmt(Label.PUBLIC)
# Actor()
ctor = ConstructorDefn(ConstructorDecl(self.clsname))
if ptype.isToplevel():
ctor.memberinits = [
ExprMemberInit(p.channelVar(), [
ExprCall(ExprVar('ALLOW_THIS_IN_INITIALIZER_LIST'),
[ ExprVar.THIS ]) ]),
ExprMemberInit(p.lastActorIdVar(),
[ p.actorIdInit(self.side) ]),
ExprMemberInit(p.otherPidVar(),
[ ExprVar('mozilla::ipc::kInvalidProcessId') ]),
ExprMemberInit(p.lastShmemIdVar(),
[ p.shmemIdInit(self.side) ]),
ExprMemberInit(p.stateVar(),
[ p.startState() ])
]
if ptype.isToplevel():
ctor.memberinits = [ExprMemberInit(
p.openedProtocolInterfaceType(),
[ _protocolId(ptype) ])] + ctor.memberinits
else:
ctor.memberinits = [
ExprMemberInit(p.idVar(), [ ExprLiteral.ZERO ]),
ExprMemberInit(p.stateVar(),
[ p.deadState() ])
]
ctor.addstmt(StmtExpr(ExprCall(ExprVar('MOZ_COUNT_CTOR'),
[ ExprVar(self.clsname) ])))
self.cls.addstmts([ ctor, Whitespace.NL ])
# ~Actor()
dtor = DestructorDefn(
DestructorDecl(self.clsname, virtual=True))
dtor.addstmt(StmtExpr(ExprCall(ExprVar('MOZ_COUNT_DTOR'),
[ ExprVar(self.clsname) ])))
self.cls.addstmts([ dtor, Whitespace.NL ])
if ptype.isToplevel():
# Open(Transport*, ProcessId, MessageLoop*, Side)
aTransportVar = ExprVar('aTransport')
aThreadVar = ExprVar('aThread')
otherPidVar = ExprVar('aOtherPid')
sidevar = ExprVar('aSide')
openmeth = MethodDefn(
MethodDecl(
'Open',
params=[ Decl(Type('Channel::Transport', ptr=True),
aTransportVar.name),
Decl(Type('base::ProcessId'), otherPidVar.name),
Param(Type('MessageLoop', ptr=True),
aThreadVar.name,
default=ExprLiteral.NULL),
Param(Type('mozilla::ipc::Side'),
sidevar.name,
default=ExprVar('mozilla::ipc::UnknownSide')) ],
ret=Type.BOOL))
openmeth.addstmts([
StmtExpr(ExprAssn(p.otherPidVar(), otherPidVar)),
StmtReturn(ExprCall(ExprSelect(p.channelVar(), '.', 'Open'),
[ aTransportVar, aThreadVar, sidevar ]))
])
self.cls.addstmts([
openmeth,
Whitespace.NL ])
# Open(MessageChannel *, MessageLoop *, Side)
aChannel = ExprVar('aChannel')
aMessageLoop = ExprVar('aMessageLoop')
sidevar = ExprVar('aSide')
openmeth = MethodDefn(
MethodDecl(
'Open',
params=[ Decl(Type('MessageChannel', ptr=True),
aChannel.name),
Param(Type('MessageLoop', ptr=True),
aMessageLoop.name),
Param(Type('mozilla::ipc::Side'),
sidevar.name,
default=ExprVar('mozilla::ipc::UnknownSide')) ],
ret=Type.BOOL))
openmeth.addstmts([
StmtExpr(ExprAssn(p.otherPidVar(), ExprCall(ExprVar('base::GetCurrentProcId')))),
StmtReturn(ExprCall(ExprSelect(p.channelVar(), '.', 'Open'),
[ aChannel, aMessageLoop, sidevar ]))
])
self.cls.addstmts([
openmeth,
Whitespace.NL ])
# Close()
closemeth = MethodDefn(MethodDecl('Close'))
closemeth.addstmt(StmtExpr(
ExprCall(ExprSelect(p.channelVar(), '.', 'Close'))))
self.cls.addstmts([ closemeth, Whitespace.NL ])
if ptype.isSync() or ptype.isInterrupt():
# SetReplyTimeoutMs()
timeoutvar = ExprVar('aTimeoutMs')
settimeout = MethodDefn(MethodDecl(
'SetReplyTimeoutMs',
params=[ Decl(Type.INT32, timeoutvar.name) ]))
settimeout.addstmt(StmtExpr(
ExprCall(
ExprSelect(p.channelVar(), '.', 'SetReplyTimeoutMs'),
args=[ timeoutvar ])))
self.cls.addstmts([ settimeout, Whitespace.NL ])
if not ptype.isToplevel():
if 1 == len(p.managers):
## manager() const
managertype = p.managerActorType(self.side, ptr=1)
managermeth = MethodDefn(MethodDecl(
p.managerMethod().name, ret=managertype, const=1))
managermeth.addstmt(StmtReturn(
ExprCast(p.managerVar(), managertype, static=1)))
self.cls.addstmts([ managermeth, Whitespace.NL ])
def actorFromIter(itervar):
return ExprCall(ExprSelect(ExprCall(ExprSelect(itervar, '.', 'Get')),
'->', 'GetKey'))
def forLoopOverHashtable(hashtable, itervar, const=False):
return StmtFor(
init=Param(Type.AUTO, itervar.name,
ExprCall(ExprSelect(hashtable, '.', 'ConstIter' if const else 'Iter'))),
cond=ExprNot(ExprCall(ExprSelect(itervar, '.', 'Done'))),
update=ExprCall(ExprSelect(itervar, '.', 'Next')))
## Managed[T](Array& inout) const
## const Array<T>& Managed() const
for managed in ptype.manages:
arrvar = ExprVar('aArr')
meth = MethodDefn(MethodDecl(
p.managedMethod(managed, self.side).name,
params=[ Decl(_cxxArrayType(p.managedCxxType(managed, self.side), ref=1),
arrvar.name) ],
const=1))
ivar = ExprVar('i')
elementsvar = ExprVar('elements')
itervar = ExprVar('iter')
meth.addstmt(StmtDecl(Decl(Type.UINT32, ivar.name),
init=ExprLiteral.ZERO))
meth.addstmt(StmtDecl(Decl(Type(_actorName(managed.name(), self.side), ptrptr=1), elementsvar.name),
init=ExprCall(ExprSelect(arrvar, '.', 'AppendElements'),
args=[ ExprCall(ExprSelect(p.managedVar(managed, self.side),
'.', 'Count')) ])))
foreachaccumulate = forLoopOverHashtable(p.managedVar(managed, self.side),
itervar, const=True)
foreachaccumulate.addstmt(StmtExpr(
ExprAssn(ExprIndex(elementsvar, ivar),
actorFromIter(itervar))))
foreachaccumulate.addstmt(StmtExpr(ExprPrefixUnop(ivar, '++')))
meth.addstmt(foreachaccumulate)
refmeth = MethodDefn(MethodDecl(
p.managedMethod(managed, self.side).name,
params=[ ],
ret=p.managedVarType(managed, self.side, const=1, ref=1),
const=1))
refmeth.addstmt(StmtReturn(p.managedVar(managed, self.side)))
self.cls.addstmts([ meth, refmeth, Whitespace.NL ])
statemethod = MethodDefn(MethodDecl(
p.stateMethod().name,
ret=p.fqStateType()))
statemethod.addstmt(StmtReturn(p.stateVar()))
self.cls.addstmts([ statemethod, Whitespace.NL ])
## OnMessageReceived()/OnCallReceived()
# save these away for use in message handler case stmts
msgvar = ExprVar('msg__')
self.msgvar = msgvar
replyvar = ExprVar('reply__')
self.replyvar = replyvar
itervar = ExprVar('iter__')
self.itervar = itervar
var = ExprVar('v__')
self.var = var
# for ctor recv cases, we can't read the actor ID into a PFoo*
# because it doesn't exist on this side yet. Use a "special"
# actor handle instead
handlevar = ExprVar('handle__')
self.handlevar = handlevar
msgtype = ExprCall(ExprSelect(msgvar, '.', 'type'), [ ])
self.asyncSwitch = StmtSwitch(msgtype)
self.syncSwitch = None
self.interruptSwitch = None
if toplevel.isSync() or toplevel.isInterrupt():
self.syncSwitch = StmtSwitch(msgtype)
if toplevel.isInterrupt():
self.interruptSwitch = StmtSwitch(msgtype)
# implement Send*() methods and add dispatcher cases to
# message switch()es
for md in p.messageDecls:
self.visitMessageDecl(md)
# Handlers for the creation of actors when a new channel is
# opened
if len(channelOpenedActors):
self.makeChannelOpenedHandlers(channelOpenedActors)
# add default cases
default = StmtBlock()
default.addstmt(StmtReturn(_Result.NotKnown))
self.asyncSwitch.addcase(DefaultLabel(), default)
if toplevel.isSync() or toplevel.isInterrupt():
self.syncSwitch.addcase(DefaultLabel(), default)
if toplevel.isInterrupt():
self.interruptSwitch.addcase(DefaultLabel(), default)
# FIXME/bug 535053: only manager protocols and non-manager
# protocols with union types need Lookup(). we'll give it to
# all for the time being (simpler)
if 1 or ptype.isManager():
self.cls.addstmts(self.implementManagerIface())
def makeHandlerMethod(name, switch, hasReply, dispatches=0):
params = [ Decl(Type('Message', const=1, ref=1), msgvar.name) ]
if hasReply:
params.append(Decl(Type('Message', ref=1, ptr=1),
replyvar.name))
method = MethodDefn(MethodDecl(name, virtual=True,
params=params, ret=_Result.Type()))
if not switch:
crash = StmtExpr(ExprCall(ExprVar('MOZ_ASSERT_UNREACHABLE'),
args=[ExprLiteral.String('message protocol not supported')]))
method.addstmts([crash, StmtReturn(_Result.NotKnown)])
return method
if dispatches:
routevar = ExprVar('route__')
routedecl = StmtDecl(
Decl(_actorIdType(), routevar.name),
init=ExprCall(ExprSelect(msgvar, '.', 'routing_id')))
routeif = StmtIf(ExprBinary(
ExprVar('MSG_ROUTING_CONTROL'), '!=', routevar))
routedvar = ExprVar('routed__')
routeif.ifb.addstmt(
StmtDecl(Decl(Type('ChannelListener', ptr=1),
routedvar.name),
_lookupListener(routevar)))
failif = StmtIf(ExprPrefixUnop(routedvar, '!'))
failif.ifb.addstmt(StmtReturn(_Result.RouteError))
routeif.ifb.addstmt(failif)
routeif.ifb.addstmt(StmtReturn(ExprCall(
ExprSelect(routedvar, '->', name),
args=[ ExprVar(p.name) for p in params ])))
method.addstmts([ routedecl, routeif, Whitespace.NL ])
# in the event of an Interrupt delete message, we want to loudly complain about
# messages that are received that are not a reply to the original message
if ptype.hasReentrantDelete:
msgVar = ExprVar(params[0].name)
ifdying = StmtIf(ExprBinary(
ExprBinary(ExprVar('mState'), '==', _dyingState(ptype)),
'&&',
ExprBinary(
ExprBinary(ExprCall(ExprSelect(msgVar, '.', 'is_reply')), '!=', ExprLiteral.TRUE),
'||',
ExprBinary(ExprCall(ExprSelect(msgVar, '.', 'is_interrupt')), '!=', ExprLiteral.TRUE))))
ifdying.addifstmts([_fatalError('incoming message racing with actor deletion'),
StmtReturn(_Result.Processed)])
method.addstmt(ifdying)
# bug 509581: don't generate the switch stmt if there
# is only the default case; MSVC doesn't like that
if switch.nr_cases > 1:
method.addstmt(switch)
else:
method.addstmt(StmtReturn(_Result.NotKnown))
return method
dispatches = (ptype.isToplevel() and ptype.isManager())
self.cls.addstmts([
makeHandlerMethod('OnMessageReceived', self.asyncSwitch,
hasReply=0, dispatches=dispatches),
Whitespace.NL
])
self.cls.addstmts([
makeHandlerMethod('OnMessageReceived', self.syncSwitch,
hasReply=1, dispatches=dispatches),
Whitespace.NL
])
self.cls.addstmts([
makeHandlerMethod('OnCallReceived', self.interruptSwitch,
hasReply=1, dispatches=dispatches),
Whitespace.NL
])
destroysubtreevar = ExprVar('DestroySubtree')
deallocsubtreevar = ExprVar('DeallocSubtree')
deallocshmemvar = ExprVar('DeallocShmems')
# OnProcesingError(code)
codevar = ExprVar('aCode')
reasonvar = ExprVar('aReason')
onprocessingerror = MethodDefn(
MethodDecl('OnProcessingError',
params=[ Param(_Result.Type(), codevar.name),
Param(Type('char', const=1, ptr=1), reasonvar.name) ]))
if ptype.isToplevel():
onprocessingerror.addstmt(StmtReturn(
ExprCall(p.processingErrorVar(), args=[ codevar, reasonvar ])))
else:
onprocessingerror.addstmt(
_runtimeAbort("`OnProcessingError' called on non-toplevel actor"))
self.cls.addstmts([ onprocessingerror, Whitespace.NL ])
# int32_t GetProtocolTypeId() { return PFoo; }
gettypetag = MethodDefn(
MethodDecl('GetProtocolTypeId', ret=_actorTypeTagType()))
gettypetag.addstmt(StmtReturn(_protocolId(ptype)))
self.cls.addstmts([ gettypetag, Whitespace.NL ])
# OnReplyTimeout()
if toplevel.isSync() or toplevel.isInterrupt():
ontimeout = MethodDefn(
MethodDecl('OnReplyTimeout', ret=Type.BOOL))
if ptype.isToplevel():
ontimeout.addstmt(StmtReturn(
ExprCall(p.shouldContinueFromTimeoutVar())))
else:
ontimeout.addstmts([
_runtimeAbort("`OnReplyTimeout' called on non-toplevel actor"),
StmtReturn.FALSE
])
self.cls.addstmts([ ontimeout, Whitespace.NL ])
# C++-stack-related methods
if ptype.isToplevel():
# OnEnteredCxxStack()
onentered = MethodDefn(MethodDecl('OnEnteredCxxStack'))
onentered.addstmt(StmtReturn(ExprCall(p.enteredCxxStackVar())))
# OnExitedCxxStack()
onexited = MethodDefn(MethodDecl('OnExitedCxxStack'))
onexited.addstmt(StmtReturn(ExprCall(p.exitedCxxStackVar())))
# OnEnteredCxxStack()
onenteredcall = MethodDefn(MethodDecl('OnEnteredCall'))
onenteredcall.addstmt(StmtReturn(ExprCall(p.enteredCallVar())))
# OnExitedCxxStack()
onexitedcall = MethodDefn(MethodDecl('OnExitedCall'))
onexitedcall.addstmt(StmtReturn(ExprCall(p.exitedCallVar())))
# bool IsOnCxxStack()
onstack = MethodDefn(
MethodDecl(p.onCxxStackVar().name, ret=Type.BOOL, const=1))
onstack.addstmt(StmtReturn(ExprCall(
ExprSelect(p.channelVar(), '.', p.onCxxStackVar().name))))
# void ProcessIncomingRacingInterruptCall
processincoming = MethodDefn(
MethodDecl('FlushPendingInterruptQueue', ret=Type.VOID))
processincoming.addstmt(StmtExpr(ExprCall(ExprSelect(_actorChannel(ExprVar.THIS), '.', 'FlushPendingInterruptQueue'))))
self.cls.addstmts([ onentered, onexited,
onenteredcall, onexitedcall,
onstack, processincoming, Whitespace.NL ])
# OnChannelClose()
onclose = MethodDefn(MethodDecl('OnChannelClose'))
if ptype.isToplevel():
onclose.addstmts([
StmtExpr(ExprCall(destroysubtreevar,
args=[ _DestroyReason.NormalShutdown ])),
StmtExpr(ExprCall(deallocsubtreevar)),
StmtExpr(ExprCall(deallocshmemvar))
])
else:
onclose.addstmt(
_runtimeAbort("`OnClose' called on non-toplevel actor"))
self.cls.addstmts([ onclose, Whitespace.NL ])
# OnChannelError()
onerror = MethodDefn(MethodDecl('OnChannelError'))
if ptype.isToplevel():
onerror.addstmts([
StmtExpr(ExprCall(destroysubtreevar,
args=[ _DestroyReason.AbnormalShutdown ])),
StmtExpr(ExprCall(deallocsubtreevar)),
StmtExpr(ExprCall(deallocshmemvar))
])
else:
onerror.addstmt(
_runtimeAbort("`OnError' called on non-toplevel actor"))
self.cls.addstmts([ onerror, Whitespace.NL ])
# OnChannelConnected()
onconnected = MethodDefn(MethodDecl('OnChannelConnected',
params=[ Decl(Type.INT32, 'aPid') ]))
if not ptype.isToplevel():
onconnected.addstmt(
_runtimeAbort("'OnConnected' called on non-toplevel actor"))
self.cls.addstmts([ onconnected, Whitespace.NL ])
# User-facing shmem methods
self.cls.addstmts(self.makeShmemIface())
if (ptype.isToplevel() and ptype.isInterrupt()):
processnative = MethodDefn(
MethodDecl('ProcessNativeEventsInInterruptCall', ret=Type.VOID))
processnative.addstmts([
CppDirective('ifdef', 'OS_WIN'),
StmtExpr(ExprCall(
ExprSelect(p.channelVar(), '.',
'ProcessNativeEventsInInterruptCall'))),
CppDirective('else'),
_runtimeAbort('This method is Windows-only'),
CppDirective('endif'),
])
self.cls.addstmts([ processnative, Whitespace.NL ])
if ptype.isToplevel() and self.side is 'parent':
## void SetOtherProcessId(ProcessId aOtherPid)
otherpidvar = ExprVar('aOtherPid')
setotherprocessid = MethodDefn(MethodDecl(
'SetOtherProcessId',
params=[ Decl(Type('base::ProcessId'), otherpidvar.name)]))
setotherprocessid.addstmts([
StmtExpr(ExprAssn(p.otherPidVar(), otherpidvar)),
])
self.cls.addstmts([
setotherprocessid,
Whitespace.NL])
## bool GetMinidump(nsIFile** dump)
self.cls.addstmt(Label.PROTECTED)
dumpvar = ExprVar('aDump')
seqvar = ExprVar('aSequence')
getdump = MethodDefn(MethodDecl(
'TakeMinidump',
params=[ Decl(Type('nsIFile', ptrptr=1), dumpvar.name),
Decl(Type.UINT32PTR, seqvar.name)],
ret=Type.BOOL,
const=1))
getdump.addstmts([
CppDirective('ifdef', 'MOZ_CRASHREPORTER'),
StmtReturn(ExprCall(
ExprVar('XRE_TakeMinidumpForChild'),
args=[ ExprCall(p.otherPidMethod()), dumpvar, seqvar ])),
CppDirective('else'),
StmtReturn.FALSE,
CppDirective('endif')
])
self.cls.addstmts([ getdump, Whitespace.NL ])
## private methods
self.cls.addstmt(Label.PRIVATE)
## FatalError()
msgparam = ExprVar('aMsg')
msgvar = ExprVar('formattedMessage')
actorname = _actorName(p.name, self.side)
fatalerror = MethodDefn(MethodDecl(
'FatalError',
params=[ Decl(Type('char', const=1, ptrconst=1), msgparam.name) ],
const=1, never_inline=1))
if self.side is 'parent':
otherpid = p.callOtherPid()
isparent = ExprLiteral.TRUE
else:
otherpid = ExprLiteral.ZERO
isparent = ExprLiteral.FALSE
fatalerror.addstmts([
_ipcFatalError(actorname, msgparam, otherpid, isparent)
])
self.cls.addstmts([ fatalerror, Whitespace.NL ])
## DestroySubtree(bool normal)
whyvar = ExprVar('why')
subtreewhyvar = ExprVar('subtreewhy')
kidsvar = ExprVar('kids')
ivar = ExprVar('i')
itervar = ExprVar('iter')
ithkid = ExprIndex(kidsvar, ivar)
destroysubtree = MethodDefn(MethodDecl(
destroysubtreevar.name,
params=[ Decl(_DestroyReason.Type(), whyvar.name) ]))
if ptype.isManaged():
destroysubtree.addstmt(
Whitespace('// Unregister from our manager.\n', indent=1))
destroysubtree.addstmts(self.unregisterActor())
destroysubtree.addstmt(Whitespace.NL)
if ptype.isManager():
# only declare this for managers to avoid unused var warnings
destroysubtree.addstmts([
StmtDecl(
Decl(_DestroyReason.Type(), subtreewhyvar.name),
init=ExprConditional(
ExprBinary(
ExprBinary(whyvar, '==',
_DestroyReason.Deletion),
'||',
ExprBinary(whyvar, '==',
_DestroyReason.FailedConstructor)),
_DestroyReason.AncestorDeletion, whyvar)),
Whitespace.NL
])
for managed in ptype.manages:
managedVar = p.managedVar(managed, self.side)
foreachdestroy = StmtFor(
init=Param(Type.UINT32, ivar.name, ExprLiteral.ZERO),
cond=ExprBinary(ivar, '<', _callCxxArrayLength(kidsvar)),
update=ExprPrefixUnop(ivar, '++'))
foreachdestroy.addstmt(StmtExpr(ExprCall(
ExprSelect(ithkid, '->', destroysubtreevar.name),
args=[ subtreewhyvar ])))
block = StmtBlock()
block.addstmts([
Whitespace(
'// Recursively shutting down %s kids\n'% (managed.name()),
indent=1),
StmtDecl(
Decl(_cxxArrayType(p.managedCxxType(managed, self.side)), kidsvar.name),
initargs=[ ExprCall(ExprSelect(managedVar, '.', 'Count')) ]),
Whitespace(
'// Accumulate kids into a stable structure to iterate over\n',
indent=1),
StmtExpr(ExprCall(p.managedMethod(managed, self.side),
args=[ kidsvar ])),
foreachdestroy,
])
destroysubtree.addstmt(block)
if len(ptype.manages):
destroysubtree.addstmt(Whitespace.NL)
destroysubtree.addstmts([ Whitespace('// Finally, destroy "us".\n',
indent=1),
StmtExpr(ExprCall(_destroyMethod(),
args=[ whyvar ]))
])
self.cls.addstmts([ destroysubtree, Whitespace.NL ])
## DeallocSubtree()
deallocsubtree = MethodDefn(MethodDecl(deallocsubtreevar.name))
for managed in ptype.manages:
managedVar = p.managedVar(managed, self.side)
foreachrecurse = forLoopOverHashtable(managedVar, itervar)
foreachrecurse.addstmt(StmtExpr(ExprCall(
ExprSelect(actorFromIter(itervar), '->', deallocsubtreevar.name))))
foreachdealloc = forLoopOverHashtable(managedVar, itervar)
foreachdealloc.addstmts([
StmtExpr(ExprCall(_deallocMethod(managed, self.side),
args=[ actorFromIter(itervar) ]))
])
block = StmtBlock()
block.addstmts([
Whitespace(
'// Recursively deleting %s kids\n'% (managed.name()),
indent=1),
foreachrecurse,
Whitespace.NL,
foreachdealloc,
StmtExpr(_callClearManagedActors(managedVar)),
])
deallocsubtree.addstmt(block)
# don't delete outselves: either the manager will do it, or
# we're toplevel
self.cls.addstmts([ deallocsubtree, Whitespace.NL ])
if ptype.isToplevel():
## DeallocShmem():
# for (cit = map.begin(); cit != map.end(); ++cit)
# Dealloc(cit->second)
# map.Clear()
deallocshmem = MethodDefn(MethodDecl(deallocshmemvar.name))
citvar = ExprVar('cit')
begin = ExprCall(ExprSelect(p.shmemMapVar(), '.', 'begin'))
end = ExprCall(ExprSelect(p.shmemMapVar(), '.', 'end'))
shmem = ExprSelect(citvar, '->', 'second')
foreachdealloc = StmtFor(
Param(p.shmemIteratorType(), citvar.name, begin),
ExprBinary(citvar, '!=', end),
ExprPrefixUnop(citvar, '++'))
foreachdealloc.addstmt(StmtExpr(_shmemDealloc(shmem)))
deallocshmem.addstmts([
foreachdealloc,
StmtExpr(ExprCall(ExprSelect(p.shmemMapVar(), '.', 'Clear')))
])
self.cls.addstmts([ deallocshmem, Whitespace.NL ])
self.implementPickling()
## private members
self.cls.addstmt(StmtDecl(Decl(p.channelType(), 'mChannel')))
if ptype.isToplevel():
self.cls.addstmts([
StmtDecl(Decl(Type('IDMap', T=Type('ProtocolBase')),
p.actorMapVar().name)),
StmtDecl(Decl(_actorIdType(), p.lastActorIdVar().name)),
StmtDecl(Decl(Type('base::ProcessId'),
p.otherPidVar().name))
])
elif ptype.isManaged():
self.cls.addstmts([
StmtDecl(Decl(p.managerInterfaceType(ptr=1),
p.managerVar().name)),
StmtDecl(Decl(_actorIdType(), p.idVar().name))
])
if p.decl.type.isToplevel():
self.cls.addstmts([
StmtDecl(Decl(p.shmemMapType(), p.shmemMapVar().name)),
StmtDecl(Decl(_shmemIdType(), p.lastShmemIdVar().name))
])
self.cls.addstmt(StmtDecl(Decl(Type('State'), p.stateVar().name)))
for managed in ptype.manages:
self.cls.addstmts([
StmtDecl(Decl(
p.managedVarType(managed, self.side),
p.managedVar(managed, self.side).name)) ])
def implementManagerIface(self):
p = self.protocol
routedvar = ExprVar('aRouted')
idvar = ExprVar('aId')
shmemvar = ExprVar('shmem')
rawvar = ExprVar('segment')
sizevar = ExprVar('aSize')
typevar = ExprVar('aType')
unsafevar = ExprVar('aUnsafe')
protocolbase = Type('ProtocolBase', ptr=1)
sourcevar = ExprVar('aSource')
ivar = ExprVar('i')
kidsvar = ExprVar('kids')
ithkid = ExprIndex(kidsvar, ivar)
clonecontexttype = Type('ProtocolCloneContext', ptr=1)
clonecontextvar = ExprVar('aCtx')
register = MethodDefn(MethodDecl(
p.registerMethod().name,
params=[ Decl(protocolbase, routedvar.name) ],
ret=_actorIdType(), virtual=1))
registerid = MethodDefn(MethodDecl(
p.registerIDMethod().name,
params=[ Decl(protocolbase, routedvar.name),
Decl(_actorIdType(), idvar.name) ],
ret=_actorIdType(),
virtual=1))
lookup = MethodDefn(MethodDecl(
p.lookupIDMethod().name,
params=[ Decl(_actorIdType(), idvar.name) ],
ret=protocolbase, virtual=1))
unregister = MethodDefn(MethodDecl(
p.unregisterMethod().name,
params=[ Decl(_actorIdType(), idvar.name) ],
virtual=1))
createshmem = MethodDefn(MethodDecl(
p.createSharedMemory().name,
ret=_rawShmemType(ptr=1),
params=[ Decl(Type.SIZE, sizevar.name),
Decl(_shmemTypeType(), typevar.name),
Decl(Type.BOOL, unsafevar.name),
Decl(_shmemIdType(ptr=1), idvar.name) ],
virtual=1))
adoptshmem = MethodDefn(MethodDecl(
p.adoptSharedMemory().name,
ret=Type.BOOL,
params=[ Decl(_rawShmemType(ptr=1), rawvar.name),
Decl(_shmemIdType(ptr=1), idvar.name) ],
virtual=1))
lookupshmem = MethodDefn(MethodDecl(
p.lookupSharedMemory().name,
ret=_rawShmemType(ptr=1),
params=[ Decl(_shmemIdType(), idvar.name) ],
virtual=1))
destroyshmem = MethodDefn(MethodDecl(
p.destroySharedMemory().name,
ret=Type.BOOL,
params=[ Decl(_shmemType(ref=1), shmemvar.name) ],
virtual=1))
istracking = MethodDefn(MethodDecl(
p.isTrackingSharedMemory().name,
ret=Type.BOOL,
params=[ Decl(_rawShmemType(ptr=1), rawvar.name) ],
virtual=1))
otherpid = MethodDefn(MethodDecl(
p.otherPidMethod().name,
ret=Type('base::ProcessId'),
const=1,
virtual=1))
getchannel = MethodDefn(MethodDecl(
p.getChannelMethod().name,
ret=Type('MessageChannel', ptr=1),
virtual=1))
clonemanagees = MethodDefn(MethodDecl(
p.cloneManagees().name,
params=[ Decl(protocolbase, sourcevar.name),
Decl(clonecontexttype, clonecontextvar.name) ],
virtual=1))
cloneprotocol = MethodDefn(MethodDecl(
p.cloneProtocol().name,
params=[ Decl(Type('Channel', ptr=True), 'aChannel'),
Decl(clonecontexttype, clonecontextvar.name) ],
ret=Type(p.fqBaseClass(), ptr=1),
virtual=1))
if p.decl.type.isToplevel():
tmpvar = ExprVar('tmp')
register.addstmts([
StmtDecl(Decl(_actorIdType(), tmpvar.name),
p.nextActorIdExpr(self.side)),
StmtExpr(ExprCall(
ExprSelect(p.actorMapVar(), '.', 'AddWithID'),
[ routedvar, tmpvar ])),
StmtReturn(tmpvar)
])
registerid.addstmts([
StmtExpr(
ExprCall(ExprSelect(p.actorMapVar(), '.', 'AddWithID'),
[ routedvar, idvar ])),
StmtReturn(idvar)
])
lookup.addstmt(StmtReturn(
ExprCall(ExprSelect(p.actorMapVar(), '.', 'Lookup'),
[ idvar ])))
unregister.addstmt(StmtReturn(
ExprCall(ExprSelect(p.actorMapVar(), '.', 'Remove'),
[ idvar ])))
# SharedMemory* CreateSharedMemory(size_t aSize, Type aType, bool aUnsafe, id_t* aId):
# RefPtr<SharedMemory> segment(Shmem::Alloc(aSize, aType, aUnsafe));
# if (!segment)
# return nullptr;
# Shmem shmem(segment.get(), [nextshmemid]);
# Message descriptor = shmem.ShareTo(subprocess, mId, descriptor);
# if (!descriptor)
# return nullptr;
# mChannel.Send(descriptor);
# *aId = shmem.Id();
# SharedMemory* rawSegment = segment.get();
# mShmemMap.Add(segment.forget().take(), *aId);
# return rawSegment;
createshmem.addstmt(StmtDecl(
Decl(_refptr(_rawShmemType()), rawvar.name),
initargs=[ _shmemAlloc(sizevar, typevar, unsafevar) ]))
failif = StmtIf(ExprNot(rawvar))
failif.addifstmt(StmtReturn(ExprLiteral.NULL))
createshmem.addstmt(failif)
descriptorvar = ExprVar('descriptor')
createshmem.addstmts([
StmtDecl(
Decl(_shmemType(), shmemvar.name),
initargs=[ _shmemBackstagePass(),
_refptrGet(rawvar),
p.nextShmemIdExpr(self.side) ]),
StmtDecl(Decl(Type('Message', ptr=1), descriptorvar.name),
init=_shmemShareTo(shmemvar,
p.callOtherPid(),
p.routingId()))
])
failif = StmtIf(ExprNot(descriptorvar))
failif.addifstmt(StmtReturn(ExprLiteral.NULL))
createshmem.addstmt(failif)
failif = StmtIf(ExprNot(ExprCall(
ExprSelect(p.channelVar(), p.channelSel(), 'Send'),
args=[ descriptorvar ])))
createshmem.addstmt(failif)
rawsegmentvar = ExprVar('rawSegment')
createshmem.addstmts([
StmtExpr(ExprAssn(ExprDeref(idvar), _shmemId(shmemvar))),
StmtDecl(Decl(_rawShmemType(ptr=1), rawsegmentvar.name),
init=_refptrGet(rawvar)),
StmtExpr(ExprCall(
ExprSelect(p.shmemMapVar(), '.', 'AddWithID'),
args=[ _refptrTake(_refptrForget(rawvar)), ExprDeref(idvar) ])),
StmtReturn(rawsegmentvar)
])
# SharedMemory* AdoptSharedMemory(SharedMemory*, id_t*):
# Shmem s(seg, [nextshmemid]);
# Message descriptor;
# if (!s->ShareTo(subprocess, mId, descriptor) ||
# !Send(descriptor))
# return false;
# mShmemMap.Add(seg, id);
# seg->AddRef();
# return true;
# XXX this is close to the same code as above, could be
# refactored
descriptorvar = ExprVar('descriptor')
adoptshmem.addstmts([
StmtDecl(
Decl(_shmemType(), shmemvar.name),
initargs=[ _shmemBackstagePass(),
rawvar,
p.nextShmemIdExpr(self.side) ]),
StmtDecl(Decl(Type('Message', ptr=1), descriptorvar.name),
init=_shmemShareTo(shmemvar,
p.callOtherPid(),
p.routingId()))
])
failif = StmtIf(ExprNot(descriptorvar))
failif.addifstmt(StmtReturn.FALSE)
adoptshmem.addstmt(failif)
failif = StmtIf(ExprNot(ExprCall(
ExprSelect(p.channelVar(), p.channelSel(), 'Send'),
args=[ descriptorvar ])))
adoptshmem.addstmt(failif)
adoptshmem.addstmts([
StmtExpr(ExprAssn(ExprDeref(idvar), _shmemId(shmemvar))),
StmtExpr(ExprCall(
ExprSelect(p.shmemMapVar(), '.', 'AddWithID'),
args=[ rawvar, ExprDeref(idvar) ])),
StmtExpr(ExprCall(ExprSelect(rawvar, '->', 'AddRef'))),
StmtReturn.TRUE
])
# SharedMemory* Lookup(id)
lookupshmem.addstmt(StmtReturn(ExprCall(
ExprSelect(p.shmemMapVar(), '.', 'Lookup'),
args=[ idvar ])))
# bool IsTrackingSharedMemory(mem)
istracking.addstmt(StmtReturn(ExprCall(
ExprSelect(p.shmemMapVar(), '.', 'HasData'),
args=[ rawvar ])))
# bool DestroySharedMemory(shmem):
# id = shmem.Id()
# SharedMemory* rawmem = Lookup(id)
# if (!rawmem)
# return false;
# Message descriptor = UnShare(subprocess, mId, descriptor)
# mShmemMap.Remove(id)
# Shmem::Dealloc(rawmem)
# if (!mChannel.CanSend()) {
# delete descriptor;
# return true;
# }
# return descriptor && Send(descriptor)
destroyshmem.addstmts([
StmtDecl(Decl(_shmemIdType(), idvar.name),
init=_shmemId(shmemvar)),
StmtDecl(Decl(_rawShmemType(ptr=1), rawvar.name),
init=_lookupShmem(idvar))
])
failif = StmtIf(ExprNot(rawvar))
failif.addifstmt(StmtReturn.FALSE)
cansend = ExprCall(ExprSelect(p.channelVar(), '.', 'CanSend'), [])
returnif = StmtIf(ExprNot(cansend))
returnif.addifstmts([
StmtExpr(ExprDelete(descriptorvar)),
StmtReturn.TRUE])
destroyshmem.addstmts([
failif,
Whitespace.NL,
StmtDecl(Decl(Type('Message', ptr=1), descriptorvar.name),
init=_shmemUnshareFrom(
shmemvar,
p.callOtherPid(),
p.routingId())),
Whitespace.NL,
StmtExpr(p.removeShmemId(idvar)),
StmtExpr(_shmemDealloc(rawvar)),
Whitespace.NL,
returnif,
Whitespace.NL,
StmtReturn(ExprBinary(
descriptorvar, '&&',
ExprCall(
ExprSelect(p.channelVar(), p.channelSel(), 'Send'),
args=[ descriptorvar ])))
])
# "private" message that passes shmem mappings from one process
# to the other
if p.subtreeUsesShmem():
self.asyncSwitch.addcase(
CaseLabel('SHMEM_CREATED_MESSAGE_TYPE'),
self.genShmemCreatedHandler())
self.asyncSwitch.addcase(
CaseLabel('SHMEM_DESTROYED_MESSAGE_TYPE'),
self.genShmemDestroyedHandler())
else:
abort = StmtBlock()
abort.addstmts([
_runtimeAbort('this protocol tree does not use shmem'),
StmtReturn(_Result.NotKnown)
])
self.asyncSwitch.addcase(
CaseLabel('SHMEM_CREATED_MESSAGE_TYPE'), abort)
self.asyncSwitch.addcase(
CaseLabel('SHMEM_DESTROYED_MESSAGE_TYPE'), abort)
otherpid.addstmt(StmtReturn(p.otherPidVar()))
getchannel.addstmt(StmtReturn(ExprAddrOf(p.channelVar())))
else:
# delegate registration to manager
register.addstmt(StmtReturn(ExprCall(
ExprSelect(p.managerVar(), '->', p.registerMethod().name),
[ routedvar ])))
registerid.addstmt(StmtReturn(ExprCall(
ExprSelect(p.managerVar(), '->', p.registerIDMethod().name),
[ routedvar, idvar ])))
lookup.addstmt(StmtReturn(ExprCall(
ExprSelect(p.managerVar(), '->', p.lookupIDMethod().name),
[ idvar ])))
unregister.addstmt(StmtReturn(ExprCall(
ExprSelect(p.managerVar(), '->', p.unregisterMethod().name),
[ idvar ])))
createshmem.addstmt(StmtReturn(ExprCall(
ExprSelect(p.managerVar(), '->', p.createSharedMemory().name),
[ sizevar, typevar, unsafevar, idvar ])))
adoptshmem.addstmt(StmtReturn(ExprCall(
ExprSelect(p.managerVar(), '->', p.adoptSharedMemory().name),
[ rawvar, idvar ])))
lookupshmem.addstmt(StmtReturn(ExprCall(
ExprSelect(p.managerVar(), '->', p.lookupSharedMemory().name),
[ idvar ])))
istracking.addstmt(StmtReturn(ExprCall(
ExprSelect(p.managerVar(), '->',
p.isTrackingSharedMemory().name),
[ rawvar ])))
destroyshmem.addstmt(StmtReturn(ExprCall(
ExprSelect(p.managerVar(), '->', p.destroySharedMemory().name),
[ shmemvar ])))
otherpid.addstmt(StmtReturn(
p.callOtherPid(p.managerVar())))
getchannel.addstmt(StmtReturn(p.channelVar()))
cloneprotocol.addstmts([
_runtimeAbort('Clone() for ' +
p.name +
' has not yet been implemented'),
StmtReturn(ExprLiteral.NULL)
])
othervar = ExprVar('other')
managertype = Type(_actorName(p.name, self.side), ptr=1)
if len(p.managesStmts):
otherstmt = StmtDecl(Decl(managertype,
othervar.name),
init=ExprCast(sourcevar,
managertype,
static=1))
clonemanagees.addstmt(otherstmt)
# Keep track of types created with an INOUT ctor. We need to call
# Register() or RegisterID() for them depending on the side the managee
# is created.
inoutCtorTypes = []
for msg in p.messageDecls:
msgtype = msg.decl.type
if msgtype.isCtor() and msgtype.isInout():
inoutCtorTypes.append(msgtype.constructedType())
actorvar = ExprVar('actor')
for managee in p.managesStmts:
block = StmtBlock()
manageeipdltype = managee.decl.type
actortype = ipdl.type.ActorType(manageeipdltype)
manageecxxtype = _cxxBareType(actortype, self.side)
manageearray = p.managedVar(manageeipdltype, self.side)
abortstmt = StmtIf(ExprBinary(actorvar, '==', ExprLiteral.NULL))
abortstmt.addifstmts([
_runtimeAbort('can not clone an ' + actortype.name() + ' actor'),
StmtReturn()])
forstmt = StmtFor(
init=Param(Type.UINT32, ivar.name, ExprLiteral.ZERO),
cond=ExprBinary(ivar, '<', _callCxxArrayLength(kidsvar)),
update=ExprPrefixUnop(ivar, '++'))
registerstmt = StmtExpr(ExprCall(p.registerIDMethod(),
args=[actorvar, _actorId(actorvar)]))
# Implement if (actor id > 0) then Register() else RegisterID()
if manageeipdltype in inoutCtorTypes:
registerif = StmtIf(ExprBinary(_actorId(actorvar),
'>',
ExprLiteral.ZERO))
registerif.addifstmt(StmtExpr(ExprCall(p.registerMethod(),
args=[actorvar])))
registerif.addelsestmt(registerstmt)
registerstmt = registerif
forstmt.addstmts([
StmtExpr(ExprAssn(
actorvar,
ExprCast(
ExprCall(
ExprSelect(ithkid,
'->',
p.cloneProtocol().name),
args=[ p.channelForSubactor(),
clonecontextvar ]),
manageecxxtype,
static=1))),
abortstmt,
StmtExpr(ExprAssn(_actorId(actorvar), _actorId(ithkid))),
StmtExpr(ExprAssn(_actorManager(actorvar), ExprVar.THIS)),
StmtExpr(ExprAssn(
_actorChannel(actorvar),
p.channelForSubactor())),
StmtExpr(ExprAssn(_actorState(actorvar), _actorState(ithkid))),
StmtExpr(_callInsertManagedActor(manageearray, actorvar)),
registerstmt,
StmtExpr(ExprCall(
ExprSelect(actorvar,
'->',
p.cloneManagees().name),
args=[ ithkid, clonecontextvar ]))
])
block.addstmts([
StmtDecl(Decl(_cxxArrayType(manageecxxtype, ref=0),
kidsvar.name)),
StmtExpr(ExprCall(ExprSelect(othervar, '->',
p.managedMethod(manageeipdltype, self.side).name),
args=[ kidsvar ])),
StmtDecl(Decl(manageecxxtype, actorvar.name)),
forstmt])
clonemanagees.addstmt(block)
# all protocols share the "same" RemoveManagee() implementation
pvar = ExprVar('aProtocolId')
listenervar = ExprVar('aListener')
removemanagee = MethodDefn(MethodDecl(
p.removeManageeMethod().name,
params=[ Decl(_protocolIdType(), pvar.name),
Decl(protocolbase, listenervar.name) ],
virtual=1))
if not len(p.managesStmts):
removemanagee.addstmts([ _runtimeAbort('unreached'), StmtReturn() ])
else:
switchontype = StmtSwitch(pvar)
for managee in p.managesStmts:
case = StmtBlock()
actorvar = ExprVar('actor')
manageeipdltype = managee.decl.type
manageecxxtype = _cxxBareType(ipdl.type.ActorType(manageeipdltype),
self.side)
manageearray = p.managedVar(manageeipdltype, self.side)
case.addstmts([
StmtDecl(Decl(manageecxxtype, actorvar.name),
ExprCast(listenervar, manageecxxtype, static=1)),
_abortIfFalse(
_callHasManagedActor(manageearray, actorvar),
"actor not managed by this!"),
Whitespace.NL,
StmtExpr(_callRemoveManagedActor(manageearray, actorvar)),
StmtExpr(ExprCall(_deallocMethod(manageeipdltype, self.side),
args=[ actorvar ])),
StmtReturn()
])
switchontype.addcase(CaseLabel(_protocolId(manageeipdltype).name),
case)
default = StmtBlock()
default.addstmts([ _runtimeAbort('unreached'), StmtReturn() ])
switchontype.addcase(DefaultLabel(), default)
removemanagee.addstmt(switchontype)
return [ register,
registerid,
lookup,
unregister,
removemanagee,
createshmem,
adoptshmem,
lookupshmem,
istracking,
destroyshmem,
otherpid,
getchannel,
clonemanagees,
cloneprotocol,
Whitespace.NL ]
def makeShmemIface(self):
p = self.protocol
idvar = ExprVar('id')
sizevar = ExprVar('aSize')
typevar = ExprVar('aType')
memvar = ExprVar('aMem')
outmemvar = ExprVar('aOutMem')
rawvar = ExprVar('rawmem')
def allocShmemMethod(name, unsafe):
# bool Alloc*Shmem(size_t aSize, Type aType, Shmem* aOutMem):
# id_t id;
# SharedMemory* rawmem(CreateSharedMemory(aSize, aType, false, &id));
# if (!rawmem)
# return false;
# *aOutMem = Shmem(rawmem, id)
# return true;
method = MethodDefn(MethodDecl(
name,
params=[ Decl(Type.SIZE, sizevar.name),
Decl(_shmemTypeType(), typevar.name),
Decl(_shmemType(ptr=1), outmemvar.name) ],
ret=Type.BOOL))
ifallocfails = StmtIf(ExprNot(rawvar))
ifallocfails.addifstmt(StmtReturn.FALSE)
if unsafe:
unsafe = ExprLiteral.TRUE
else:
unsafe = ExprLiteral.FALSE
method.addstmts([
StmtDecl(Decl(_shmemIdType(), idvar.name)),
StmtDecl(Decl(_rawShmemType(ptr=1), rawvar.name),
initargs=[ ExprCall(p.createSharedMemory(),
args=[ sizevar,
typevar,
unsafe,
ExprAddrOf(idvar) ]) ]),
ifallocfails,
Whitespace.NL,
StmtExpr(ExprAssn(
ExprDeref(outmemvar), _shmemCtor(rawvar, idvar))),
StmtReturn.TRUE
])
return method
# bool AllocShmem(size_t size, Type type, Shmem* outmem):
allocShmem = allocShmemMethod('AllocShmem', False)
# bool AllocUnsafeShmem(size_t size, Type type, Shmem* outmem):
allocUnsafeShmem = allocShmemMethod('AllocUnsafeShmem', True)
# bool AdoptShmem(Shmem& aMem, Shmem* aOutmem):
# SharedMemory* rawmem = aMem.Segment();
# if (!rawmem || IsTrackingSharedMemory(rawmem)) {
# NS_WARNING("bad Shmem"); // or NS_RUNTIMEABORT on child side
# return false;
# }
# id_t id
# if (!AdoptSharedMemory(rawmem, &id))
# return false
# *aOutmem = Shmem(rawmem, id);
# return true;
adoptShmem = MethodDefn(MethodDecl(
'AdoptShmem',
params=[ Decl(_shmemType(const=1, ref=1), memvar.name),
Decl(_shmemType(ptr=1), outmemvar.name) ],
ret=Type.BOOL))
adoptShmem.addstmt(StmtDecl(Decl(_rawShmemType(ptr=1), rawvar.name),
init=_shmemSegment(memvar)))
ifbad = StmtIf(ExprBinary(
ExprNot(rawvar), '||',
ExprCall(ExprVar('IsTrackingSharedMemory'), args=[ rawvar ])))
badShmemActions = []
if (self.side == 'child'):
badShmemActions.append(_runtimeAbort('bad Shmem'));
else:
badShmemActions.append(_printWarningMessage('bad Shmem'));
badShmemActions.append(StmtReturn.FALSE);
ifbad.addifstmts(badShmemActions)
adoptShmem.addstmt(ifbad)
ifadoptfails = StmtIf(ExprNot(ExprCall(
p.adoptSharedMemory(), args=[ rawvar, ExprAddrOf(idvar) ])))
ifadoptfails.addifstmt(StmtReturn.FALSE)
adoptShmem.addstmts([
Whitespace.NL,
StmtDecl(Decl(_shmemIdType(), idvar.name)),
ifadoptfails,
Whitespace.NL,
StmtExpr(ExprAssn(ExprDeref(outmemvar),
_shmemCtor(rawvar, idvar))),
StmtReturn.TRUE
])
# bool DeallocShmem(Shmem& mem):
# bool ok = DestroySharedMemory(mem);
##ifdef DEBUG
# if (!ok) {
# NS_WARNING("bad Shmem"); // or NS_RUNTIMEABORT on child side
# return false;
# }
##endif // DEBUG
# mem.forget();
# return ok;
deallocShmem = MethodDefn(MethodDecl(
'DeallocShmem',
params=[ Decl(_shmemType(ref=1), memvar.name) ],
ret=Type.BOOL))
okvar = ExprVar('ok')
ifbad = StmtIf(ExprNot(okvar))
ifbad.addifstmts(badShmemActions)
deallocShmem.addstmts([
StmtDecl(Decl(Type.BOOL, okvar.name),
init=ExprCall(p.destroySharedMemory(),
args=[ memvar ])),
CppDirective('ifdef', 'DEBUG'),
ifbad,
CppDirective('endif', '// DEBUG'),
StmtExpr(_shmemForget(memvar)),
StmtReturn(okvar)
])
return [ Whitespace('// Methods for managing shmem\n', indent=1),
allocShmem,
Whitespace.NL,
allocUnsafeShmem,
Whitespace.NL,
adoptShmem,
Whitespace.NL,
deallocShmem,
Whitespace.NL ]
def genShmemCreatedHandler(self):
p = self.protocol
assert p.decl.type.isToplevel()
case = StmtBlock()
rawvar = ExprVar('rawmem')
idvar = ExprVar('id')
case.addstmts([
StmtDecl(Decl(_shmemIdType(), idvar.name)),
StmtDecl(Decl(_refptr(_rawShmemType()), rawvar.name),
initargs=[ _shmemOpenExisting(self.msgvar,
ExprAddrOf(idvar)) ])
])
failif = StmtIf(ExprNot(rawvar))
failif.addifstmt(StmtReturn(_Result.PayloadError))
case.addstmts([
failif,
StmtExpr(ExprCall(
ExprSelect(p.shmemMapVar(), '.', 'AddWithID'),
args=[ _refptrTake(_refptrForget(rawvar)), idvar ])),
Whitespace.NL,
StmtReturn(_Result.Processed)
])
return case
def genShmemDestroyedHandler(self):
p = self.protocol
assert p.decl.type.isToplevel()
case = StmtBlock()
rawvar = ExprVar('rawmem')
idvar = ExprVar('id')
itervar = ExprVar('iter')
case.addstmts([
StmtDecl(Decl(_shmemIdType(), idvar.name)),
StmtDecl(Decl(Type.VOIDPTR, itervar.name), init=ExprLiteral.NULL)
])
failif = StmtIf(ExprNot(
ExprCall(ExprVar('IPC::ReadParam'),
args=[ ExprAddrOf(self.msgvar), ExprAddrOf(itervar),
ExprAddrOf(idvar) ])))
failif.addifstmt(StmtReturn(_Result.PayloadError))
case.addstmts([
failif,
StmtExpr(ExprCall(ExprSelect(self.msgvar, '.', 'EndRead'),
args=[ itervar ])),
Whitespace.NL,
StmtDecl(Decl(_rawShmemType(ptr=1), rawvar.name),
init=ExprCall(p.lookupSharedMemory(), args=[ idvar ]))
])
# Here we don't return an error if we failed to look the shmem up. This
# is because we don't have a way to know if it is because we failed to
# map the shmem or if the id is wrong. In the latter case it would be
# better to catch the error but the former case is legit...
lookupif = StmtIf(rawvar)
lookupif.addifstmt(StmtExpr(p.removeShmemId(idvar)))
lookupif.addifstmt(StmtExpr(_shmemDealloc(rawvar)))
case.addstmts([
lookupif,
StmtReturn(_Result.Processed)
])
return case
def makeChannelOpenedHandlers(self, actors):
handlers = StmtBlock()
# unpack the transport descriptor et al.
msgvar = self.msgvar
tdvar = ExprVar('td')
pidvar = ExprVar('pid')
pvar = ExprVar('protocolid')
iffail = StmtIf(ExprNot(ExprCall(
ExprVar('mozilla::ipc::UnpackChannelOpened'),
args=[ _backstagePass(),
msgvar,
ExprAddrOf(tdvar), ExprAddrOf(pidvar), ExprAddrOf(pvar) ])))
iffail.addifstmt(StmtReturn(_Result.PayloadError))
handlers.addstmts([
StmtDecl(Decl(Type('TransportDescriptor'), tdvar.name)),
StmtDecl(Decl(Type('ProcessId'), pidvar.name)),
StmtDecl(Decl(Type('ProtocolId'), pvar.name)),
iffail,
Whitespace.NL
])
def makeHandlerCase(actor):
self.protocolCxxIncludes.append(_protocolHeaderName(actor.ptype._ast,
actor.side))
case = StmtBlock()
modevar = _sideToTransportMode(actor.side)
tvar = ExprVar('t')
iffailopen = StmtIf(ExprNot(ExprAssn(
tvar,
ExprCall(ExprVar('mozilla::ipc::OpenDescriptor'),
args=[ tdvar, modevar ]))))
iffailopen.addifstmt(StmtReturn(_Result.ValuError))
pvar = ExprVar('p')
iffailalloc = StmtIf(ExprNot(ExprAssn(
pvar,
ExprCall(
_allocMethod(actor.ptype, actor.side),
args=[ tvar, pidvar ]))))
iffailalloc.addifstmt(StmtReturn(_Result.ProcessingError))
settrans = StmtExpr(ExprCall(
ExprSelect(pvar, '->', 'IToplevelProtocol::SetTransport'),
args=[tvar]))
addopened = StmtExpr(ExprCall(
ExprVar('IToplevelProtocol::AddOpenedActor'),
args=[pvar]))
case.addstmts([
StmtDecl(Decl(Type('Transport', ptr=1), tvar.name)),
StmtDecl(Decl(Type(_actorName(actor.ptype.name(), actor.side),
ptr=1), pvar.name)),
iffailopen,
iffailalloc,
settrans,
addopened,
StmtBreak()
])
label = _messageStartName(actor.ptype)
if actor.side == 'child':
label += 'Child'
return CaseLabel(label), case
pswitch = StmtSwitch(pvar)
for actor in actors:
label, case = makeHandlerCase(actor)
pswitch.addcase(label, case)
die = Block()
die.addstmts([ _runtimeAbort('Invalid protocol'),
StmtReturn(_Result.ValuError) ])
pswitch.addcase(DefaultLabel(), die)
handlers.addstmts([
pswitch,
StmtReturn(_Result.Processed)
])
self.asyncSwitch.addcase(CaseLabel('CHANNEL_OPENED_MESSAGE_TYPE'),
handlers)
##-------------------------------------------------------------------------
## The next few functions are the crux of the IPDL code generator.
## They generate code for all the nasty work of message
## serialization/deserialization and dispatching handlers for
## received messages.
##
def implementPickling(self):
# pickling of "normal", non-IPDL types
self.implementGenericPickling()
# pickling for IPDL types
specialtypes = set()
class findSpecialTypes(TypeVisitor):
def visitActorType(self, a): specialtypes.add(a)
def visitShmemType(self, s): specialtypes.add(s)
def visitFDType(self, s): specialtypes.add(s)
def visitStructType(self, s):
specialtypes.add(s)
return TypeVisitor.visitStructType(self, s)
def visitUnionType(self, u):
specialtypes.add(u)
return TypeVisitor.visitUnionType(self, u)
def visitArrayType(self, a):
if a.basetype.isIPDL():
specialtypes.add(a)
return a.basetype.accept(self)
for md in self.protocol.messageDecls:
for param in md.params:
mtype = md.decl.type
# special case for top-level __delete__(), which isn't
# understood yet
if mtype.isDtor() and mtype.constructedType().isToplevel():
continue
param.ipdltype.accept(findSpecialTypes())
for ret in md.returns:
ret.ipdltype.accept(findSpecialTypes())
for t in specialtypes:
if t.isActor(): self.implementActorPickling(t)
elif t.isArray(): self.implementSpecialArrayPickling(t)
elif t.isShmem(): self.implementShmemPickling(t)
elif t.isFD(): self.implementFDPickling(t)
elif t.isStruct(): self.implementStructPickling(t)
elif t.isUnion(): self.implementUnionPickling(t)
else:
assert 0 and 'unknown special type'
def implementGenericPickling(self):
var = self.var
msgvar = self.msgvar
itervar = self.itervar
write = MethodDefn(self.writeMethodDecl(
Type('T', const=1, ref=1), var, template=Type('T')))
write.addstmt(StmtExpr(ExprCall(ExprVar('IPC::WriteParam'),
args=[ msgvar, var ])))
read = MethodDefn(self.readMethodDecl(
Type('T', ptr=1), var, template=Type('T')))
read.addstmt(StmtReturn(ExprCall(ExprVar('IPC::ReadParam'),
args=[ msgvar, itervar, var ])))
self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
def implementActorPickling(self, actortype):
# Note that we pickle based on *protocol* type and *not* actor
# type. The actor type includes a |nullable| qualifier, but
# this method is not specialized based on nullability. The
# |actortype| nullability is ignored in this method.
var = self.var
idvar = ExprVar('id')
intype = _cxxConstRefType(actortype, self.side)
cxxtype = _cxxBareType(actortype, self.side)
outtype = _cxxPtrToType(actortype, self.side)
## Write([const] PFoo* var)
write = MethodDefn(self.writeMethodDecl(intype, var))
nullablevar = ExprVar('nullable__')
write.decl.params.append(Decl(Type.BOOL, nullablevar.name))
# id_t id;
# if (!var)
# if(!nullable)
# abort()
# id = NULL_ID
write.addstmt(StmtDecl(Decl(_actorIdType(), idvar.name)))
ifnull = StmtIf(ExprNot(var))
ifnotnullable = StmtIf(ExprNot(nullablevar))
ifnotnullable.addifstmt(
_runtimeAbort("NULL actor value passed to non-nullable param"))
ifnull.addifstmt(ifnotnullable)
ifnull.addifstmt(StmtExpr(ExprAssn(idvar, _NULL_ACTOR_ID)))
# else
# id = var->mId
# if (id == FREED_ID)
# abort()
# Write(msg, id)
ifnull.addelsestmt(StmtExpr(ExprAssn(idvar, _actorId(var))))
iffreed = StmtIf(ExprBinary(_FREED_ACTOR_ID, '==', idvar))
# this is always a hard-abort, because it means that some C++
# code has a live pointer to a freed actor, so we're playing
# Russian roulette with invalid memory
iffreed.addifstmt(_runtimeAbort("actor has been |delete|d"))
ifnull.addelsestmt(iffreed)
write.addstmts([
ifnull,
Whitespace.NL,
StmtExpr(self.write(None, idvar, self.msgvar))
])
## Read(PFoo** var)
read = MethodDefn(self.readMethodDecl(outtype, var))
read.decl.params.append(Decl(Type.BOOL, nullablevar.name))
# if (!Read(id, msg))
# return false
# if (FREED_ID == id
# || NULL_ID == id && !nullable)
# return false
read.addstmts([
StmtDecl(Decl(_actorIdType(), idvar.name)),
self.checkedRead(None, ExprAddrOf(idvar),
self.msgvar, self.itervar, errfnRead,
'id\' for \'' + cxxtype.name),
])
ifbadid = StmtIf(ExprBinary(
ExprBinary(_FREED_ACTOR_ID, '==', idvar),
'||',
ExprBinary(ExprBinary(_NULL_ACTOR_ID, '==', idvar),
'&&',
ExprNot(nullablevar))))
ifbadid.addifstmts([
_protocolErrorBreakpoint('bad ID for '+ self.protocol.name),
StmtReturn.FALSE
])
read.addstmts([ ifbadid, Whitespace.NL ])
# if (NULL_ID == id)
# *var = null
# return true
outactor = ExprDeref(var)
ifnull = StmtIf(ExprBinary(_NULL_ACTOR_ID, '==', idvar))
ifnull.addifstmts([ StmtExpr(ExprAssn(outactor, ExprLiteral.NULL)),
StmtReturn.TRUE ])
read.addstmts([ ifnull, Whitespace.NL ])
# Listener* listener = Lookup(id)
# if (!listener)
# return false
listenervar = ExprVar('listener')
read.addstmt(StmtDecl(Decl(Type('ChannelListener', ptr=1),
listenervar.name),
init=_lookupListener(idvar)))
ifnotfound = StmtIf(ExprNot(listenervar))
ifnotfound.addifstmts([
_protocolErrorBreakpoint('could not look up '+ actortype.name()),
StmtReturn.FALSE
])
read.addstmts([ ifnotfound, Whitespace.NL ])
# if listener->GetProtocolTypeId() != [expected protocol type]
# return false
ifbadtype = StmtIf(ExprBinary(
_protocolId(actortype), '!=',
ExprCall(ExprSelect(listenervar, '->', 'GetProtocolTypeId'))))
ifbadtype.addifstmts([
_protocolErrorBreakpoint('actor that should be of type '+ actortype.name() +' has different type'),
StmtReturn.FALSE
])
read.addstmts([ ifbadtype, Whitespace.NL ])
# *outactor = static_cast<ExpectedType>(listener)
# return true
read.addstmts([
StmtExpr(ExprAssn(outactor,
ExprCast(listenervar, cxxtype, static=1))),
StmtReturn.TRUE
])
self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
def implementSpecialArrayPickling(self, arraytype):
var = self.var
msgvar = self.msgvar
itervar = self.itervar
lenvar = ExprVar('length')
ivar = ExprVar('i')
eltipdltype = arraytype.basetype
intype = _cxxConstRefType(arraytype, self.side)
outtype = _cxxPtrToType(arraytype, self.side)
write = MethodDefn(self.writeMethodDecl(intype, var))
forwrite = StmtFor(init=ExprAssn(Decl(Type.UINT32, ivar.name),
ExprLiteral.ZERO),
cond=ExprBinary(ivar, '<', lenvar),
update=ExprPrefixUnop(ivar, '++'))
forwrite.addstmt(StmtExpr(
self.write(eltipdltype, ExprIndex(var, ivar), msgvar)))
write.addstmts([
StmtDecl(Decl(Type.UINT32, lenvar.name),
init=_callCxxArrayLength(var)),
StmtExpr(self.write(None, lenvar, msgvar)),
Whitespace.NL,
forwrite
])
read = MethodDefn(self.readMethodDecl(outtype, var))
favar = ExprVar('fa')
forread = StmtFor(init=ExprAssn(Decl(Type.UINT32, ivar.name),
ExprLiteral.ZERO),
cond=ExprBinary(ivar, '<', lenvar),
update=ExprPrefixUnop(ivar, '++'))
forread.addstmt(
self.checkedRead(eltipdltype, ExprAddrOf(ExprIndex(favar, ivar)),
msgvar, itervar, errfnRead,
eltipdltype.name() + '[i]'))
read.addstmts([
StmtDecl(Decl(_cxxFallibleArrayType(_cxxBareType(arraytype.basetype, self.side)), favar.name)),
StmtDecl(Decl(Type.UINT32, lenvar.name)),
self.checkedRead(None, ExprAddrOf(lenvar),
msgvar, itervar, errfnRead,
'length\' (' + Type.UINT32.name + ') of \'' +
arraytype.name()),
Whitespace.NL,
_callCxxCheckedArraySetLength(favar, lenvar),
forread,
StmtExpr(_callCxxSwapArrayElements(var, favar, '->')),
StmtReturn.TRUE
])
self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
def implementShmemPickling(self, shmemtype):
msgvar = self.msgvar
itervar = self.itervar
var = self.var
tmpvar = ExprVar('tmp')
idvar = ExprVar('shmemid')
rawvar = ExprVar('rawmem')
baretype = _cxxBareType(shmemtype, self.side)
intype = _cxxConstRefType(shmemtype, self.side)
outtype = _cxxPtrToType(shmemtype, self.side)
write = MethodDefn(self.writeMethodDecl(intype, var))
write.addstmts([
StmtExpr(ExprCall(ExprVar('IPC::WriteParam'),
args=[ msgvar, var ])),
StmtExpr(_shmemRevokeRights(var)),
StmtExpr(_shmemForget(var))
])
read = MethodDefn(self.readMethodDecl(outtype, var))
ifread = StmtIf(ExprNot(ExprCall(ExprVar('IPC::ReadParam'),
args=[ msgvar, itervar,
ExprAddrOf(tmpvar) ])))
ifread.addifstmt(StmtReturn.FALSE)
iffound = StmtIf(rawvar)
iffound.addifstmt(StmtExpr(ExprAssn(
ExprDeref(var), _shmemCtor(rawvar, idvar))))
iffound.addifstmt(StmtReturn.TRUE)
read.addstmts([
StmtDecl(Decl(_shmemType(), tmpvar.name)),
ifread,
Whitespace.NL,
StmtDecl(Decl(_shmemIdType(), idvar.name),
init=_shmemId(tmpvar)),
StmtDecl(Decl(_rawShmemType(ptr=1), rawvar.name),
init=_lookupShmem(idvar)),
iffound,
# This is ugly: we failed to look the shmem up, most likely because
# we failed to map it the first time it was deserialized. we create
# an empty shmem and let the user of the shmem deal with it.
# if we returned false here we would crash.
StmtExpr(ExprAssn(ExprDeref(var), ExprCall(ExprVar('Shmem'), args=[]) )),
StmtReturn.TRUE
])
self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
def implementFDPickling(self, fdtype):
msgvar = self.msgvar
itervar = self.itervar
var = self.var
tmpvar = ExprVar('fd')
picklevar = ExprVar('pfd')
intype = _cxxConstRefType(fdtype, self.side)
outtype = _cxxPtrToType(fdtype, self.side)
def _fdType():
return Type('FileDescriptor')
def _fdPickleType():
return Type('FileDescriptor::PickleType')
def _fdBackstagePass():
return ExprCall(ExprVar('FileDescriptor::IPDLPrivate'))
write = MethodDefn(self.writeMethodDecl(intype, var))
write.addstmts([
StmtDecl(Decl(_fdPickleType(), picklevar.name),
init=ExprCall(ExprSelect(var, '.', 'ShareTo'),
args=[ _fdBackstagePass(),
self.protocol.callOtherPid() ])),
StmtExpr(ExprCall(ExprVar('IPC::WriteParam'),
args=[ msgvar, picklevar ])),
])
read = MethodDefn(self.readMethodDecl(outtype, var))
ifread = StmtIf(ExprNot(ExprCall(ExprVar('IPC::ReadParam'),
args=[ msgvar, itervar,
ExprAddrOf(picklevar) ])))
ifread.addifstmt(StmtReturn.FALSE)
ifnvalid = StmtIf(ExprNot(ExprCall(ExprSelect(tmpvar, '.', 'IsValid'))))
ifnvalid.addifstmt(
_protocolErrorBreakpoint('[' +
_actorName(self.protocol.name, self.side) +
'] Received an invalid file descriptor!'))
read.addstmts([
StmtDecl(Decl(_fdPickleType(), picklevar.name)),
ifread,
Whitespace.NL,
StmtDecl(Decl(_fdType(), tmpvar.name),
init=ExprCall(ExprVar('FileDescriptor'),
args=[ _fdBackstagePass(), picklevar ])),
ifnvalid,
Whitespace.NL,
StmtExpr(ExprAssn(ExprDeref(var), tmpvar)),
StmtReturn.TRUE
])
self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
def implementStructPickling(self, structtype):
msgvar = self.msgvar
itervar = self.itervar
var = self.var
intype = _cxxConstRefType(structtype, self.side)
outtype = _cxxPtrToType(structtype, self.side)
sd = structtype._ast
write = MethodDefn(self.writeMethodDecl(intype, var))
read = MethodDefn(self.readMethodDecl(outtype, var))
def get(sel, f):
return ExprCall(f.getMethod(thisexpr=var, sel=sel))
for f in sd.fields:
desc = f.getMethod().name + '\' (' + f.ipdltype.name() + \
') member of \'' + intype.name
writefield = StmtExpr(self.write(f.ipdltype, get('.', f), msgvar))
readfield = self.checkedRead(f.ipdltype,
ExprAddrOf(get('->', f)),
msgvar, itervar, errfnRead, desc)
if f.special and f.side != self.side:
writefield = Whitespace(
"// skipping actor field that's meaningless on this side\n", indent=1)
readfield = Whitespace(
"// skipping actor field that's meaningless on this side\n", indent=1)
write.addstmt(writefield)
read.addstmt(readfield)
read.addstmt(StmtReturn.TRUE)
self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
def implementUnionPickling(self, uniontype):
msgvar = self.msgvar
itervar = self.itervar
var = self.var
intype = _cxxConstRefType(uniontype, self.side)
outtype = _cxxPtrToType(uniontype, self.side)
ud = uniontype._ast
typename = 'type__'
uniontdef = Typedef(_cxxBareType(uniontype, typename), typename)
typevar = ExprVar('type')
writeswitch = StmtSwitch(ud.callType(var))
readswitch = StmtSwitch(typevar)
for c in ud.components:
ct = c.ipdltype
isactor = (ct.isIPDL() and ct.isActor())
caselabel = CaseLabel(typename +'::'+ c.enum())
writecase = StmtBlock()
if c.special and c.side != self.side:
writecase.addstmt(_runtimeAbort('wrong side!'))
else:
wexpr = ExprCall(ExprSelect(var, '.', c.getTypeName()))
writecase.addstmt(StmtExpr(self.write(ct, wexpr, msgvar)))
writecase.addstmt(StmtReturn())
writeswitch.addcase(caselabel, writecase)
readcase = StmtBlock()
if c.special and c.side == self.side:
# the type comes across flipped from what the actor
# will be on this side; i.e. child->parent messages
# have type PFooChild when received on the parent side
# XXX: better error message
readcase.addstmt(StmtReturn.FALSE)
else:
if c.special:
c = c.other # see above
tmpvar = ExprVar('tmp')
ct = c.bareType()
readcase.addstmts([
StmtDecl(Decl(ct, tmpvar.name), init=c.defaultValue()),
StmtExpr(ExprAssn(ExprDeref(var), tmpvar)),
StmtReturn(self.read(
c.ipdltype,
ExprAddrOf(ExprCall(ExprSelect(var, '->',
c.getTypeName()))),
msgvar, itervar))
])
readswitch.addcase(caselabel, readcase)
unknowntype = 'unknown union type'
writeswitch.addcase(DefaultLabel(),
StmtBlock([ _runtimeAbort(unknowntype),
StmtReturn() ]))
readswitch.addcase(DefaultLabel(), StmtBlock(errfnRead(unknowntype)))
write = MethodDefn(self.writeMethodDecl(intype, var))
write.addstmts([
uniontdef,
StmtExpr(self.write(
None, ExprCall(Type.INT, args=[ ud.callType(var) ]), msgvar)),
Whitespace.NL,
writeswitch
])
read = MethodDefn(self.readMethodDecl(outtype, var))
read.addstmts([
uniontdef,
StmtDecl(Decl(Type.INT, typevar.name)),
self.checkedRead(
None, ExprAddrOf(typevar), msgvar, itervar, errfnRead,
typevar.name + '\' (' + Type.INT.name + ') of union \'' + uniontype.name()),
Whitespace.NL,
readswitch,
])
self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ])
def writeMethodDecl(self, intype, var, template=None):
return MethodDecl(
'Write',
params=[ Decl(intype, var.name),
Decl(Type('Message', ptr=1), self.msgvar.name) ],
T=template)
def readMethodDecl(self, outtype, var, template=None):
return MethodDecl(
'Read',
params=[ Decl(outtype, var.name),
Decl(Type('Message', ptr=1, const=1),
self.msgvar.name),
Decl(Type('void', ptrptr=1), self.itervar.name)],
warn_unused=not template,
T=template,
ret=Type.BOOL)
def maybeAddNullabilityArg(self, ipdltype, call):
if ipdltype and ipdltype.isIPDL() and ipdltype.isActor():
if ipdltype.nullable:
call.args.append(ExprLiteral.TRUE)
else:
call.args.append(ExprLiteral.FALSE)
return call
def write(self, ipdltype, expr, to, this=None):
write = ExprVar('Write')
if this: write = ExprSelect(this, '->', write.name)
return self.maybeAddNullabilityArg(ipdltype,
ExprCall(write, args=[ expr, to ]))
def read(self, ipdltype, expr, from_, iterexpr, this=None):
read = ExprVar('Read')
if this: read = ExprSelect(this, '->', read.name)
return self.maybeAddNullabilityArg(
ipdltype, ExprCall(read, args=[ expr, from_, iterexpr ]))
def visitMessageDecl(self, md):
isctor = md.decl.type.isCtor()
isdtor = md.decl.type.isDtor()
decltype = md.decl.type
sendmethod = None
helpermethod = None
recvlbl, recvcase = None, None
def addRecvCase(lbl, case):
if decltype.isAsync():
self.asyncSwitch.addcase(lbl, case)
elif decltype.isSync():
self.syncSwitch.addcase(lbl, case)
elif decltype.isInterrupt():
self.interruptSwitch.addcase(lbl, case)
else: assert 0
if self.sendsMessage(md):
isasync = decltype.isAsync()
if isctor:
self.cls.addstmts([ self.genHelperCtor(md), Whitespace.NL ])
if isctor and isasync:
sendmethod, (recvlbl, recvcase) = self.genAsyncCtor(md)
elif isctor:
sendmethod = self.genBlockingCtorMethod(md)
elif isdtor and isasync:
sendmethod, (recvlbl, recvcase) = self.genAsyncDtor(md)
elif isdtor:
sendmethod = self.genBlockingDtorMethod(md)
elif isasync:
sendmethod = self.genAsyncSendMethod(md)
else:
sendmethod = self.genBlockingSendMethod(md)
# XXX figure out what to do here
if isdtor and md.decl.type.constructedType().isToplevel():
sendmethod = None
if sendmethod is not None:
self.cls.addstmts([ sendmethod, Whitespace.NL ])
if recvcase is not None:
addRecvCase(recvlbl, recvcase)
recvlbl, recvcase = None, None
if self.receivesMessage(md):
if isctor:
recvlbl, recvcase = self.genCtorRecvCase(md)
elif isdtor:
recvlbl, recvcase = self.genDtorRecvCase(md)
else:
recvlbl, recvcase = self.genRecvCase(md)
# XXX figure out what to do here
if isdtor and md.decl.type.constructedType().isToplevel():
return
addRecvCase(recvlbl, recvcase)
def genAsyncCtor(self, md):
actor = md.actorDecl()
method = MethodDefn(self.makeSendMethodDecl(md))
method.addstmts(self.ctorPrologue(md) + [ Whitespace.NL ])
msgvar, stmts = self.makeMessage(md, errfnSendCtor)
sendok, sendstmts = self.sendAsync(md, msgvar)
method.addstmts(
stmts
+ sendstmts
+ self.failCtorIf(md, ExprNot(sendok))
+ [ StmtReturn(actor.var()) ])
lbl = CaseLabel(md.pqReplyId())
case = StmtBlock()
case.addstmt(StmtReturn(_Result.Processed))
# TODO not really sure what to do with async ctor "replies" yet.
# destroy actor if there was an error? tricky ...
return method, (lbl, case)
def genBlockingCtorMethod(self, md):
actor = md.actorDecl()
method = MethodDefn(self.makeSendMethodDecl(md))
method.addstmts(self.ctorPrologue(md) + [ Whitespace.NL ])
msgvar, stmts = self.makeMessage(md, errfnSendCtor)
replyvar = self.replyvar
sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar)
method.addstmts(
stmts
+ [ Whitespace.NL,
StmtDecl(Decl(Type('Message'), replyvar.name)) ]
+ sendstmts
+ self.failCtorIf(md, ExprNot(sendok)))
def errfnCleanupCtor(msg):
return self.failCtorIf(md, ExprLiteral.TRUE)
stmts = self.deserializeReply(
md, ExprAddrOf(replyvar), self.side, errfnCleanupCtor)
method.addstmts(stmts + [ StmtReturn(actor.var()) ])
return method
def ctorPrologue(self, md, errfn=ExprLiteral.NULL, idexpr=None):
actordecl = md.actorDecl()
actorvar = actordecl.var()
actorproto = actordecl.ipdltype.protocol
actortype = ipdl.type.ActorType(actorproto)
if idexpr is None:
idexpr = ExprCall(self.protocol.registerMethod(),
args=[ actorvar ])
else:
idexpr = ExprCall(self.protocol.registerIDMethod(),
args=[ actorvar, idexpr ])
return [
self.failIfNullActor(actorvar, errfn, msg="Error constructing actor %s" % actortype.name() + self.side.capitalize()),
StmtExpr(ExprAssn(_actorId(actorvar), idexpr)),
StmtExpr(ExprAssn(_actorManager(actorvar), ExprVar.THIS)),
StmtExpr(ExprAssn(_actorChannel(actorvar),
self.protocol.channelForSubactor())),
StmtExpr(_callInsertManagedActor(
self.protocol.managedVar(md.decl.type.constructedType(),
self.side),
actorvar)),
StmtExpr(ExprAssn(_actorState(actorvar),
_startState(actorproto, fq=1)))
]
def failCtorIf(self, md, cond):
actorvar = md.actorDecl().var()
type = md.decl.type.constructedType()
failif = StmtIf(cond)
if self.side=='child':
# in the child process this should not fail
failif.addifstmt(_runtimeAbort('constructor for actor failed'))
else:
failif.addifstmts(self.destroyActor(md, actorvar,
why=_DestroyReason.FailedConstructor))
failif.addifstmt(StmtReturn(ExprLiteral.NULL))
return [ failif ]
def genHelperCtor(self, md):
helperdecl = self.makeSendMethodDecl(md)
helperdecl.params = helperdecl.params[1:]
helper = MethodDefn(helperdecl)
callctor = self.callAllocActor(md, retsems='out', side=self.side)
helper.addstmt(StmtReturn(ExprCall(
ExprVar(helperdecl.name), args=[ callctor ] + callctor.args)))
return helper
def genAsyncDtor(self, md):
actor = md.actorDecl()
actorvar = actor.var()
method = MethodDefn(self.makeDtorMethodDecl(md))
method.addstmts(self.dtorPrologue(actorvar))
msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar)
sendok, sendstmts = self.sendAsync(md, msgvar, actorvar)
method.addstmts(
stmts
+ sendstmts
+ [ Whitespace.NL ]
+ self.dtorEpilogue(md, actor.var())
+ [ StmtReturn(sendok) ])
lbl = CaseLabel(md.pqReplyId())
case = StmtBlock()
case.addstmt(StmtReturn(_Result.Processed))
# TODO if the dtor is "inherently racy", keep the actor alive
# until the other side acks
return method, (lbl, case)
def genBlockingDtorMethod(self, md):
actor = md.actorDecl()
actorvar = actor.var()
method = MethodDefn(self.makeDtorMethodDecl(md))
method.addstmts(self.dtorPrologue(actorvar))
msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar)
replyvar = self.replyvar
sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar, actorvar)
method.addstmts(
stmts
+ [ Whitespace.NL,
StmtDecl(Decl(Type('Message'), replyvar.name)) ]
+ sendstmts)
destmts = self.deserializeReply(
md, ExprAddrOf(replyvar), self.side, errfnSend, actorvar)
ifsendok = StmtIf(ExprLiteral.FALSE)
ifsendok.addifstmts(destmts)
ifsendok.addifstmts([ Whitespace.NL,
StmtExpr(ExprAssn(sendok, ExprLiteral.FALSE, '&=')) ])
method.addstmt(ifsendok)
if self.protocol.decl.type.hasReentrantDelete:
method.addstmts(self.transition(md, 'in', actor.var(), reply=True))
method.addstmts(
self.dtorEpilogue(md, actor.var())
+ [ Whitespace.NL, StmtReturn(sendok) ])
return method
def destroyActor(self, md, actorexpr, why=_DestroyReason.Deletion):
if md.decl.type.isCtor():
destroyedType = md.decl.type.constructedType()
else:
destroyedType = self.protocol.decl.type
return ([ StmtExpr(self.callActorDestroy(actorexpr, why)),
StmtExpr(self.callDeallocSubtree(md, actorexpr)),
StmtExpr(self.callRemoveActor(
actorexpr,
manager=self.protocol.managerVar(actorexpr),
ipdltype=destroyedType))
])
def dtorPrologue(self, actorexpr):
return [ self.failIfNullActor(actorexpr), Whitespace.NL ]
def dtorEpilogue(self, md, actorexpr):
return self.destroyActor(md, actorexpr)
def genAsyncSendMethod(self, md):
method = MethodDefn(self.makeSendMethodDecl(md))
msgvar, stmts = self.makeMessage(md, errfnSend)
sendok, sendstmts = self.sendAsync(md, msgvar)
method.addstmts(stmts
+[ Whitespace.NL ]
+ sendstmts
+[ StmtReturn(sendok) ])
return method
def genBlockingSendMethod(self, md, fromActor=None):
method = MethodDefn(self.makeSendMethodDecl(md))
msgvar, serstmts = self.makeMessage(md, errfnSend, fromActor)
replyvar = self.replyvar
sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar)
failif = StmtIf(ExprNot(sendok))
failif.addifstmt(StmtReturn.FALSE)
desstmts = self.deserializeReply(
md, ExprAddrOf(replyvar), self.side, errfnSend)
method.addstmts(
serstmts
+ [ Whitespace.NL,
StmtDecl(Decl(Type('Message'), replyvar.name)) ]
+ sendstmts
+ [ failif ]
+ desstmts
+ [ Whitespace.NL,
StmtReturn.TRUE ])
return method
def genCtorRecvCase(self, md):
lbl = CaseLabel(md.pqMsgId())
case = StmtBlock()
actorvar = md.actorDecl().var()
actorhandle = self.handlevar
stmts = self.deserializeMessage(md, self.side, errfnRecv)
idvar, saveIdStmts = self.saveActorId(md)
case.addstmts(
stmts
+ self.transition(md, 'in')
+ [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
for r in md.returns ]
# alloc the actor, register it under the foreign ID
+ [ StmtExpr(ExprAssn(
actorvar,
self.callAllocActor(md, retsems='in', side=self.side))) ]
+ self.ctorPrologue(md, errfn=_Result.ValuError,
idexpr=_actorHId(actorhandle))
+ [ Whitespace.NL ]
+ saveIdStmts
+ self.invokeRecvHandler(md)
+ self.makeReply(md, errfnRecv, idvar)
+ [ Whitespace.NL,
StmtReturn(_Result.Processed) ])
return lbl, case
def genDtorRecvCase(self, md):
lbl = CaseLabel(md.pqMsgId())
case = StmtBlock()
stmts = self.deserializeMessage(md, self.side, errfnRecv)
idvar, saveIdStmts = self.saveActorId(md)
case.addstmts(
stmts
+ self.transition(md, 'in')
+ [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
for r in md.returns ]
+ self.invokeRecvHandler(md, implicit=0)
+ [ Whitespace.NL ]
+ saveIdStmts
+ self.makeReply(md, errfnRecv, routingId=idvar)
+ [ Whitespace.NL ]
+ self.dtorEpilogue(md, md.actorDecl().var())
+ [ Whitespace.NL,
StmtReturn(_Result.Processed) ])
return lbl, case
def genRecvCase(self, md):
lbl = CaseLabel(md.pqMsgId())
case = StmtBlock()
stmts = self.deserializeMessage(md, self.side, errfn=errfnRecv)
idvar, saveIdStmts = self.saveActorId(md)
case.addstmts(
stmts
+ self.transition(md, 'in')
+ [ StmtDecl(Decl(r.bareType(self.side), r.var().name))
for r in md.returns ]
+ saveIdStmts
+ self.invokeRecvHandler(md)
+ [ Whitespace.NL ]
+ self.makeReply(md, errfnRecv, routingId=idvar)
+ [ StmtReturn(_Result.Processed) ])
return lbl, case
# helper methods
def failIfNullActor(self, actorExpr, retOnNull=ExprLiteral.FALSE, msg=None):
failif = StmtIf(ExprNot(actorExpr))
if msg:
failif.addifstmt(_printWarningMessage(msg))
failif.addifstmt(StmtReturn(retOnNull))
return failif
def unregisterActor(self, actorexpr=None):
return [ StmtExpr(ExprCall(self.protocol.unregisterMethod(actorexpr),
args=[ _actorId(actorexpr) ])),
StmtExpr(ExprAssn(_actorId(actorexpr), _FREED_ACTOR_ID)) ]
def makeMessage(self, md, errfn, fromActor=None):
msgvar = self.msgvar
routingId = self.protocol.routingId(fromActor)
this = None
if md.decl.type.isDtor(): this = md.actorDecl().var()
stmts = ([ StmtDecl(Decl(Type(md.pqMsgClass(), ptr=1), msgvar.name),
init=ExprNew(Type(md.pqMsgClass()),
args=[ routingId ])) ]
+ [ Whitespace.NL ]
+ [ StmtExpr(self.write(p.ipdltype, p.var(), msgvar, this))
for p in md.params ]
+ [ Whitespace.NL ]
+ self.setMessageFlags(md, msgvar, reply=0))
return msgvar, stmts
def makeReply(self, md, errfn, routingId):
if routingId is None:
routingId = self.protocol.routingId()
# TODO special cases for async ctor/dtor replies
if not md.decl.type.hasReply():
return [ ]
replyvar = self.replyvar
return (
[ StmtExpr(ExprAssn(
replyvar, ExprNew(Type(md.pqReplyClass()), args=[ routingId ]))),
Whitespace.NL ]
+ [ StmtExpr(self.write(r.ipdltype, r.var(), replyvar))
for r in md.returns ]
+ self.setMessageFlags(md, replyvar, reply=1)
+ [ self.logMessage(md, md.replyCast(replyvar), 'Sending reply ') ])
def setMessageFlags(self, md, var, reply):
stmts = [ ]
if md.decl.type.isSync():
stmts.append(StmtExpr(ExprCall(
ExprSelect(var, '->', 'set_sync'))))
elif md.decl.type.isInterrupt():
stmts.append(StmtExpr(ExprCall(
ExprSelect(var, '->', 'set_interrupt'))))
if reply:
stmts.append(StmtExpr(ExprCall(
ExprSelect(var, '->', 'set_reply'))))
return stmts + [ Whitespace.NL ]
def deserializeMessage(self, md, side, errfn):
msgvar = self.msgvar
itervar = self.itervar
msgexpr = ExprAddrOf(msgvar)
isctor = md.decl.type.isCtor()
stmts = ([
# this is kind of naughty, but the only two other options
# are forwarding the message name (yuck) or making the
# IPDL|*Channel abstraction leak more
StmtExpr(ExprCall(
ExprSelect(
ExprCast(msgvar, Type('Message', ref=1), const=1),
'.', 'set_name'),
args=[ ExprLiteral.String(md.prettyMsgName(self.protocol.name
+'::')) ])),
self.logMessage(md, md.msgCast(msgexpr), 'Received ',
receiving=True),
self.profilerLabel('Recv', md.decl.progname),
Whitespace.NL
])
if 0 == len(md.params):
return stmts
start, decls, reads = 0, [], []
if isctor:
# return the raw actor handle so that its ID can be used
# to construct the "real" actor
handlevar = self.handlevar
handletype = Type('ActorHandle')
decls = [ StmtDecl(Decl(handletype, handlevar.name)) ]
reads = [ self.checkedRead(None, ExprAddrOf(handlevar), msgexpr,
ExprAddrOf(self.itervar),
errfn, handletype.name) ]
start = 1
stmts.extend((
[ StmtDecl(Decl(Type.VOIDPTR, self.itervar.name),
init=ExprLiteral.NULL) ]
+ decls + [ StmtDecl(Decl(p.bareType(side), p.var().name))
for p in md.params ]
+ [ Whitespace.NL ]
+ reads + [ self.checkedRead(p.ipdltype, ExprAddrOf(p.var()),
msgexpr, ExprAddrOf(itervar),
errfn, p.bareType(side).name)
for p in md.params[start:] ]
+ [ self.endRead(msgvar, itervar) ]))
return stmts
def deserializeReply(self, md, replyexpr, side, errfn, actor=None):
stmts = [ Whitespace.NL,
self.logMessage(md, md.replyCast(replyexpr),
'Received reply ', actor, receiving=True) ]
if 0 == len(md.returns):
return stmts
itervar = self.itervar
stmts.extend(
[ Whitespace.NL,
StmtDecl(Decl(Type.VOIDPTR, itervar.name),
init=ExprLiteral.NULL) ]
+ [ self.checkedRead(r.ipdltype, r.var(),
ExprAddrOf(self.replyvar),
ExprAddrOf(self.itervar),
errfn, r.bareType(side).name)
for r in md.returns ]
+ [ self.endRead(self.replyvar, itervar) ])
return stmts
def sendAsync(self, md, msgexpr, actor=None):
sendok = ExprVar('sendok__')
return (
sendok,
([ Whitespace.NL,
self.logMessage(md, msgexpr, 'Sending ', actor),
self.profilerLabel('AsyncSend', md.decl.progname) ]
+ self.transition(md, 'out', actor)
+ [ Whitespace.NL,
StmtDecl(Decl(Type.BOOL, sendok.name),
init=ExprCall(
ExprSelect(self.protocol.channelVar(actor),
self.protocol.channelSel(), 'Send'),
args=[ msgexpr ]))
])
)
def sendBlocking(self, md, msgexpr, replyexpr, actor=None):
sendok = ExprVar('sendok__')
return (
sendok,
([ Whitespace.NL,
self.logMessage(md, msgexpr, 'Sending ', actor),
self.profilerLabel('Send', md.decl.progname) ]
+ self.transition(md, 'out', actor)
+ [ Whitespace.NL,
StmtDecl(
Decl(Type.BOOL, sendok.name),
init=ExprCall(ExprSelect(self.protocol.channelVar(actor),
self.protocol.channelSel(),
_sendPrefix(md.decl.type)),
args=[ msgexpr, ExprAddrOf(replyexpr) ]))
])
)
def callAllocActor(self, md, retsems, side):
return ExprCall(
_allocMethod(md.decl.type.constructedType(), side),
args=md.makeCxxArgs(params=1, retsems=retsems, retcallsems='out',
implicit=0))
def callActorDestroy(self, actorexpr, why=_DestroyReason.Deletion):
return ExprCall(ExprSelect(actorexpr, '->', 'DestroySubtree'),
args=[ why ])
def callRemoveActor(self, actorexpr, manager=None, ipdltype=None):
if ipdltype is None: ipdltype = self.protocol.decl.type
if not ipdltype.isManaged():
return Whitespace('// unmanaged protocol')
removefunc = self.protocol.removeManageeMethod()
if manager is not None:
removefunc = ExprSelect(manager, '->', removefunc.name)
return ExprCall(removefunc,
args=[ _protocolId(ipdltype),
actorexpr ])
def callDeallocSubtree(self, md, actorexpr):
return ExprCall(ExprSelect(actorexpr, '->', 'DeallocSubtree'))
def invokeRecvHandler(self, md, implicit=1):
failif = StmtIf(ExprNot(
ExprCall(md.recvMethod(),
args=md.makeCxxArgs(params=1,
retsems='in', retcallsems='out',
implicit=implicit))))
failif.addifstmts([
_protocolErrorBreakpoint('Handler for '+ md.name +' returned error code'),
StmtReturn(_Result.ProcessingError)
])
return [ failif ]
def makeDtorMethodDecl(self, md):
decl = self.makeSendMethodDecl(md)
decl.static = 1
return decl
def makeSendMethodDecl(self, md):
implicit = md.decl.type.hasImplicitActorParam()
decl = MethodDecl(
md.sendMethod().name,
params=md.makeCxxParams(paramsems='in', returnsems='out',
side=self.side, implicit=implicit),
warn_unused=(self.side == 'parent'),
ret=Type.BOOL)
if md.decl.type.isCtor():
decl.ret = md.actorDecl().bareType(self.side)
return decl
def logMessage(self, md, msgptr, pfx, actor=None, receiving=False):
actorname = _actorName(self.protocol.name, self.side)
topLevel = self.protocol.decl.type.toplevel().name()
return _ifLogging(ExprLiteral.String(topLevel), [ StmtExpr(ExprCall(
ExprSelect(msgptr, '->', 'Log'),
args=[ ExprLiteral.String('['+ actorname +'] '+ pfx),
self.protocol.callOtherPid(actor),
ExprLiteral.TRUE if receiving else ExprLiteral.FALSE ])) ])
def profilerLabel(self, tag, msgname):
return StmtExpr(ExprCall(ExprVar('PROFILER_LABEL'),
[ ExprLiteral.String('IPDL::' + self.protocol.name),
ExprLiteral.String(tag + msgname),
ExprVar('js::ProfileEntry::Category::OTHER') ]))
def saveActorId(self, md):
idvar = ExprVar('id__')
if md.decl.type.hasReply():
# only save the ID if we're actually going to use it, to
# avoid unused-variable warnings
saveIdStmts = [ StmtDecl(Decl(_actorIdType(), idvar.name),
self.protocol.routingId()) ]
else:
saveIdStmts = [ ]
return idvar, saveIdStmts
def transition(self, md, direction, actor=None, reply=False):
if actor is not None: stateexpr = _actorState(actor)
else: stateexpr = self.protocol.stateVar()
if (self.side is 'parent' and direction is 'out'
or self.side is 'child' and direction is 'in'):
action = ExprVar('Trigger::Send')
elif (self.side is 'parent' and direction is 'in'
or self.side is 'child' and direction is 'out'):
action = ExprVar('Trigger::Recv')
else: assert 0 and 'unknown combo %s/%s'% (self.side, direction)
msgid = md.pqMsgId() if not reply else md.pqReplyId()
ifbad = StmtIf(ExprNot(
ExprCall(
ExprVar(self.protocol.name +'::Transition'),
args=[ stateexpr,
ExprCall(ExprVar('Trigger'),
args=[ action, ExprVar(msgid) ]),
ExprAddrOf(stateexpr) ])))
ifbad.addifstmts(_badTransition())
return [ ifbad ]
def checkedRead(self, ipdltype, expr, msgexpr, iterexpr, errfn, paramtype):
ifbad = StmtIf(ExprNot(self.read(ipdltype, expr, msgexpr, iterexpr)))
ifbad.addifstmts(errfn('Error deserializing \'' + paramtype + '\''))
return ifbad
def endRead(self, msgexpr, iterexpr):
return StmtExpr(ExprCall(ExprSelect(msgexpr, '.', 'EndRead'),
args=[ iterexpr ]))
class _GenerateProtocolParentCode(_GenerateProtocolActorCode):
def __init__(self):
_GenerateProtocolActorCode.__init__(self, 'parent')
def sendsMessage(self, md):
return not md.decl.type.isIn()
def receivesMessage(self, md):
return md.decl.type.isInout() or md.decl.type.isIn()
class _GenerateProtocolChildCode(_GenerateProtocolActorCode):
def __init__(self):
_GenerateProtocolActorCode.__init__(self, 'child')
def sendsMessage(self, md):
return not md.decl.type.isOut()
def receivesMessage(self, md):
return md.decl.type.isInout() or md.decl.type.isOut()
##-----------------------------------------------------------------------------
## Utility passes
##
def _splitClassDeclDefn(cls):
"""Destructively split |cls| methods into declarations and
definitions (if |not methodDecl.force_inline|). Return classDecl,
methodDefns."""
defns = Block()
for i, stmt in enumerate(cls.stmts):
if isinstance(stmt, MethodDefn) and not stmt.decl.force_inline:
decl, defn = _splitMethodDefn(stmt, cls.name)
cls.stmts[i] = StmtDecl(decl)
defns.addstmts([ defn, Whitespace.NL ])
return cls, defns
def _splitMethodDefn(md, clsname):
saveddecl = deepcopy(md.decl)
md.decl.name = (clsname +'::'+ md.decl.name)
md.decl.virtual = 0
md.decl.static = 0
md.decl.warn_unused = 0
md.decl.never_inline = 0
md.decl.only_for_definition = True
for param in md.decl.params:
if isinstance(param, Param):
param.default = None
return saveddecl, md
def _splitFuncDeclDefn(fun):
assert not fun.decl.inline
return StmtDecl(fun.decl), fun
# XXX this is tantalizingly similar to _splitClassDeclDefn, but just
# different enough that I don't see the need to define
# _GenerateSkeleton in terms of that
class _GenerateSkeletonImpl(Visitor):
def __init__(self, name, namespaces):
self.name = name
self.cls = None
self.namespaces = namespaces
self.methodimpls = Block()
def fromclass(self, cls):
cls.accept(self)
nsclass = _putInNamespaces(self.cls, self.namespaces)
nsmethodimpls = _putInNamespaces(self.methodimpls, self.namespaces)
return [
Whitespace('''
//-----------------------------------------------------------------------------
// Skeleton implementation of abstract actor class
'''),
Whitespace('// Header file contents\n'),
nsclass,
Whitespace.NL,
Whitespace('\n// C++ file contents\n'),
nsmethodimpls
]
def visitClass(self, cls):
self.cls = Class(self.name, inherits=[ Inherit(Type(cls.name)) ])
Visitor.visitClass(self, cls)
def visitMethodDecl(self, md):
if not md.pure:
return
decl = deepcopy(md)
decl.pure = 0
impl = MethodDefn(MethodDecl(self.implname(md.name),
params=md.params,
ret=md.ret))
if md.ret.ptr:
impl.addstmt(StmtReturn(ExprLiteral.ZERO))
elif md.ret == Type.BOOL:
impl.addstmt(StmtReturn(ExprVar('false')))
self.cls.addstmts([ StmtDecl(decl), Whitespace.NL ])
self.addmethodimpl(impl)
def visitConstructorDecl(self, cd):
self.cls.addstmt(StmtDecl(ConstructorDecl(self.name)))
ctor = ConstructorDefn(ConstructorDecl(self.implname(self.name)))
ctor.addstmt(StmtExpr(ExprCall(ExprVar( 'MOZ_COUNT_CTOR'),
[ ExprVar(self.name) ])))
self.addmethodimpl(ctor)
def visitDestructorDecl(self, dd):
self.cls.addstmt(
StmtDecl(DestructorDecl(self.name, virtual=1)))
# FIXME/cjones: hack!
dtor = DestructorDefn(ConstructorDecl(self.implname('~' +self.name)))
dtor.addstmt(StmtExpr(ExprCall(ExprVar( 'MOZ_COUNT_DTOR'),
[ ExprVar(self.name) ])))
self.addmethodimpl(dtor)
def addmethodimpl(self, impl):
self.methodimpls.addstmts([ impl, Whitespace.NL ])
def implname(self, method):
return self.name +'::'+ method