mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-25 04:29:38 +00:00
5619 lines
205 KiB
Python
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
|