diff options
Diffstat (limited to 'ipc/ipdl')
307 files changed, 25200 insertions, 0 deletions
diff --git a/ipc/ipdl/Makefile.in b/ipc/ipdl/Makefile.in new file mode 100644 index 000000000..9f1762a0d --- /dev/null +++ b/ipc/ipdl/Makefile.in @@ -0,0 +1,27 @@ +# 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/. + +GARBAGE_DIRS += _ipdlheaders +GARBAGE += ipdl_lextab.py ipdl_yacctab.py $(wildcard *.pyc $(srcdir)/ipdl/*.pyc $(srcdir)/ipdl/cxx/*.pyc) + +ifdef COMPILE_ENVIRONMENT + +# This file is generated by the moz.build backend. +include ipdlsrcs.mk + +include $(topsrcdir)/config/rules.mk + + +# NB: the IPDL compiler manages .ipdl-->.h/.cpp dependencies itself, +# which is why we don't have explicit .h/.cpp targets here +export:: $(ALL_IPDLSRCS) + $(PYTHON) $(topsrcdir)/config/pythonpath.py \ + $(PLY_INCLUDE) \ + $(srcdir)/ipdl.py \ + --outheaders-dir=_ipdlheaders \ + --outcpp-dir=. \ + $(IPDLDIRS:%=-I%) \ + $^ +endif + diff --git a/ipc/ipdl/ipdl.py b/ipc/ipdl/ipdl.py new file mode 100755 index 000000000..2220b4f83 --- /dev/null +++ b/ipc/ipdl/ipdl.py @@ -0,0 +1,232 @@ +# 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 optparse, os, re, sys +from cStringIO import StringIO +from mozbuild.pythonutil import iter_modules_in_path +import mozpack.path as mozpath +import itertools + +import ipdl + +def log(minv, fmt, *args): + if _verbosity >= minv: + print fmt % args + +# process command line + +op = optparse.OptionParser(usage='ipdl.py [options] IPDLfiles...') +op.add_option('-I', '--include', dest='includedirs', default=[ ], + action='append', + help='Additional directory to search for included protocol specifications') +op.add_option('-v', '--verbose', dest='verbosity', default=1, action='count', + help='Verbose logging (specify -vv or -vvv for very verbose logging)') +op.add_option('-q', '--quiet', dest='verbosity', action='store_const', const=0, + help="Suppress logging output") +op.add_option('-d', '--outheaders-dir', dest='headersdir', default='.', + help="""Directory into which C++ headers will be generated. +A protocol Foo in the namespace bar will cause the headers + dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h +to be generated""") +op.add_option('-o', '--outcpp-dir', dest='cppdir', default='.', + help="""Directory into which C++ sources will be generated +A protocol Foo in the namespace bar will cause the sources + cppdir/FooParent.cpp, cppdir/FooChild.cpp +to be generated""") + + +options, files = op.parse_args() +_verbosity = options.verbosity +headersdir = options.headersdir +cppdir = options.cppdir +includedirs = [ os.path.abspath(incdir) for incdir in options.includedirs ] + +if not len(files): + op.error("No IPDL files specified") + +ipcmessagestartpath = os.path.join(headersdir, 'IPCMessageStart.h') +ipc_msgtype_name_path = os.path.join(cppdir, 'IPCMessageTypeName.cpp') + +# Compiling the IPDL files can take a long time, even on a fast machine. +# Check to see whether we need to do any work. +latestipdlmod = max(os.stat(f).st_mtime + for f in itertools.chain(files, + iter_modules_in_path(mozpath.dirname(__file__)))) + +def outputModTime(f): + # A non-existant file is newer than everything. + if not os.path.exists(f): + return 0 + return os.stat(f).st_mtime + +# Because the IPDL headers are placed into directories reflecting their +# namespace, collect a list here so we can easily map output names without +# parsing the actual IPDL files themselves. +headersmap = {} +for (path, dirs, headers) in os.walk(headersdir): + for h in headers: + base = os.path.basename(h) + if base in headersmap: + root, ext = os.path.splitext(base) + print >>sys.stderr, 'A protocol named', root, 'exists in multiple namespaces' + sys.exit(1) + headersmap[base] = os.path.join(path, h) + +def outputfiles(f): + base = os.path.basename(f) + root, ext = os.path.splitext(base) + + suffixes = [''] + if ext == '.ipdl': + suffixes += ['Child', 'Parent'] + + for suffix in suffixes: + yield os.path.join(cppdir, "%s%s.cpp" % (root, suffix)) + header = "%s%s.h" % (root, suffix) + # If the header already exists on disk, use that. Otherwise, + # just claim that the header is found in headersdir. + if header in headersmap: + yield headersmap[header] + else: + yield os.path.join(headersdir, header) + +def alloutputfiles(): + for f in files: + for s in outputfiles(f): + yield s + yield ipcmessagestartpath + +earliestoutputmod = min(outputModTime(f) for f in alloutputfiles()) + +if latestipdlmod < earliestoutputmod: + sys.exit(0) + +log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir) +log(2, 'Generated C++ sources will be generated in "%s"', cppdir) + +allmessages = {} +allprotocols = [] + +def normalizedFilename(f): + if f == '-': + return '<stdin>' + return f + +# First pass: parse and type-check all protocols +for f in files: + log(2, os.path.basename(f)) + filename = normalizedFilename(f) + if f == '-': + fd = sys.stdin + else: + fd = open(f) + + specstring = fd.read() + fd.close() + + ast = ipdl.parse(specstring, filename, includedirs=includedirs) + if ast is None: + print >>sys.stderr, 'Specification could not be parsed.' + sys.exit(1) + + log(2, 'checking types') + if not ipdl.typecheck(ast): + print >>sys.stderr, 'Specification is not well typed.' + sys.exit(1) + + if _verbosity > 2: + log(3, ' pretty printed code:') + ipdl.genipdl(ast, codedir) + +# Second pass: generate code +for f in files: + # Read from parser cache + filename = normalizedFilename(f) + ast = ipdl.parse(None, filename, includedirs=includedirs) + ipdl.gencxx(filename, ast, headersdir, cppdir) + + if ast.protocol: + allmessages[ast.protocol.name] = ipdl.genmsgenum(ast) + allprotocols.append('%sMsgStart' % ast.protocol.name) + +allprotocols.sort() + +ipcmsgstart = StringIO() + +print >>ipcmsgstart, """ +// CODE GENERATED by ipdl.py. Do not edit. + +#ifndef IPCMessageStart_h +#define IPCMessageStart_h + +enum IPCMessageStart { +""" + +for name in allprotocols: + print >>ipcmsgstart, " %s," % name + print >>ipcmsgstart, " %sChild," % name + +print >>ipcmsgstart, """ + LastMsgIndex +}; + +static_assert(LastMsgIndex <= 65536, "need to update IPC_MESSAGE_MACRO"); + +#endif // ifndef IPCMessageStart_h +""" + +ipc_msgtype_name = StringIO() +print >>ipc_msgtype_name, """ +// CODE GENERATED by ipdl.py. Do not edit. +#include <cstdint> + +#include "IPCMessageStart.h" + +using std::uint32_t; + +namespace { + +enum IPCMessages { +""" + +for protocol in sorted(allmessages.keys()): + for (msg, num) in allmessages[protocol].idnums: + if num: + print >>ipc_msgtype_name, " %s = %s," % (msg, num) + elif not msg.endswith('End'): + print >>ipc_msgtype_name, " %s__%s," % (protocol, msg) + +print >>ipc_msgtype_name, """ +}; + +} // anonymous namespace + +namespace mozilla { +namespace ipc { + +const char* StringFromIPCMessageType(uint32_t aMessageType) +{ + switch (aMessageType) { +""" + +for protocol in sorted(allmessages.keys()): + for (msg, num) in allmessages[protocol].idnums: + if num or msg.endswith('End'): + continue + print >>ipc_msgtype_name, """ + case %s__%s: + return "%s::%s";""" % (protocol, msg, protocol, msg) + +print >>ipc_msgtype_name, """ + default: + return "???"; + } +} + +} // namespace ipc +} // namespace mozilla +""" + +ipdl.writeifmodified(ipcmsgstart.getvalue(), ipcmessagestartpath) +ipdl.writeifmodified(ipc_msgtype_name.getvalue(), ipc_msgtype_name_path) diff --git a/ipc/ipdl/ipdl/__init__.py b/ipc/ipdl/ipdl/__init__.py new file mode 100644 index 000000000..d2d883f86 --- /dev/null +++ b/ipc/ipdl/ipdl/__init__.py @@ -0,0 +1,77 @@ +# 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/. + +__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck', 'writeifmodified' ] + +import os, sys +from cStringIO import StringIO + +from ipdl.cgen import IPDLCodeGen +from ipdl.lower import LowerToCxx, msgenums +from ipdl.parser import Parser +from ipdl.type import TypeCheck + +from ipdl.cxx.cgen import CxxCodeGen + + +def parse(specstring, filename='/stdin', includedirs=[ ], errout=sys.stderr): + '''Return an IPDL AST if parsing was successful. Print errors to |errout| + if it is not.''' + # The file type and name are later enforced by the type checker. + # This is just a hint to the parser. + prefix, ext = os.path.splitext(filename) + name = os.path.basename(prefix) + if ext == '.ipdlh': + type = 'header' + else: + type = 'protocol' + return Parser(type, name).parse(specstring, os.path.abspath(filename), includedirs, errout) + + +def typecheck(ast, errout=sys.stderr): + '''Return True iff |ast| is well typed. Print errors to |errout| if + it is not.''' + return TypeCheck().check(ast, errout) + + +def gencxx(ipdlfilename, ast, outheadersdir, outcppdir): + headers, cpps = LowerToCxx().lower(ast) + + def resolveHeader(hdr): + return [ + hdr, + os.path.join( + outheadersdir, + *([ns.name for ns in ast.namespaces] + [hdr.name])) + ] + def resolveCpp(cpp): + return [ cpp, os.path.join(outcppdir, cpp.name) ] + + for ast, filename in ([ resolveHeader(hdr) for hdr in headers ] + + [ resolveCpp(cpp) for cpp in cpps ]): + tempfile = StringIO() + CxxCodeGen(tempfile).cgen(ast) + writeifmodified(tempfile.getvalue(), filename) + + +def genipdl(ast, outdir): + return IPDLCodeGen().cgen(ast) + + +def genmsgenum(ast): + return msgenums(ast.protocol, pretty=True) + +def writeifmodified(contents, file): + dir = os.path.dirname(file) + os.path.exists(dir) or os.makedirs(dir) + + oldcontents = None + if os.path.exists(file): + fd = open(file, 'rb') + oldcontents = fd.read() + fd.close() + if oldcontents != contents: + fd = open(file, 'wb') + fd.write(contents) + fd.close() diff --git a/ipc/ipdl/ipdl/ast.py b/ipc/ipdl/ipdl/ast.py new file mode 100644 index 000000000..a8bd1e41f --- /dev/null +++ b/ipc/ipdl/ipdl/ast.py @@ -0,0 +1,454 @@ +# 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 sys + +NOT_NESTED = 1 +INSIDE_SYNC_NESTED = 2 +INSIDE_CPOW_NESTED = 3 + +NORMAL_PRIORITY = 1 +HIGH_PRIORITY = 2 + +class Visitor: + def defaultVisit(self, node): + raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% ( + node.__class__.__name__) + + def visitTranslationUnit(self, tu): + for cxxInc in tu.cxxIncludes: + cxxInc.accept(self) + for inc in tu.includes: + inc.accept(self) + for su in tu.structsAndUnions: + su.accept(self) + for using in tu.builtinUsing: + using.accept(self) + for using in tu.using: + using.accept(self) + if tu.protocol: + tu.protocol.accept(self) + + + def visitCxxInclude(self, inc): + pass + + def visitInclude(self, inc): + # Note: we don't visit the child AST here, because that needs delicate + # and pass-specific handling + pass + + def visitStructDecl(self, struct): + for f in struct.fields: + f.accept(self) + + def visitStructField(self, field): + field.typespec.accept(self) + + def visitUnionDecl(self, union): + for t in union.components: + t.accept(self) + + def visitUsingStmt(self, using): + pass + + def visitProtocol(self, p): + for namespace in p.namespaces: + namespace.accept(self) + for spawns in p.spawnsStmts: + spawns.accept(self) + for bridges in p.bridgesStmts: + bridges.accept(self) + for opens in p.opensStmts: + opens.accept(self) + for mgr in p.managers: + mgr.accept(self) + for managed in p.managesStmts: + managed.accept(self) + for msgDecl in p.messageDecls: + msgDecl.accept(self) + for transitionStmt in p.transitionStmts: + transitionStmt.accept(self) + + def visitNamespace(self, ns): + pass + + def visitSpawnsStmt(self, spawns): + pass + + def visitBridgesStmt(self, bridges): + pass + + def visitOpensStmt(self, opens): + pass + + def visitManager(self, mgr): + pass + + def visitManagesStmt(self, mgs): + pass + + def visitMessageDecl(self, md): + for inParam in md.inParams: + inParam.accept(self) + for outParam in md.outParams: + outParam.accept(self) + + def visitTransitionStmt(self, ts): + ts.state.accept(self) + for trans in ts.transitions: + trans.accept(self) + + def visitTransition(self, t): + for toState in t.toStates: + toState.accept(self) + + def visitState(self, s): + pass + + def visitParam(self, decl): + pass + + def visitTypeSpec(self, ts): + pass + + def visitDecl(self, d): + pass + +class Loc: + def __init__(self, filename='<??>', lineno=0): + assert filename + self.filename = filename + self.lineno = lineno + def __repr__(self): + return '%r:%r'% (self.filename, self.lineno) + def __str__(self): + return '%s:%s'% (self.filename, self.lineno) + +Loc.NONE = Loc(filename='<??>', lineno=0) + +class _struct: + pass + +class Node: + def __init__(self, loc=Loc.NONE): + self.loc = loc + + def accept(self, visitor): + visit = getattr(visitor, 'visit'+ self.__class__.__name__, None) + if visit is None: + return getattr(visitor, 'defaultVisit')(self) + return visit(self) + + def addAttrs(self, attrsName): + if not hasattr(self, attrsName): + setattr(self, attrsName, _struct()) + + +class NamespacedNode(Node): + def __init__(self, loc=Loc.NONE, name=None): + Node.__init__(self, loc) + self.name = name + self.namespaces = [ ] + + def addOuterNamespace(self, namespace): + self.namespaces.insert(0, namespace) + + def qname(self): + return QualifiedId(self.loc, self.name, + [ ns.name for ns in self.namespaces ]) + +class TranslationUnit(NamespacedNode): + def __init__(self, type, name): + NamespacedNode.__init__(self, name=name) + self.filetype = type + self.filename = None + self.cxxIncludes = [ ] + self.includes = [ ] + self.builtinUsing = [ ] + self.using = [ ] + self.structsAndUnions = [ ] + self.protocol = None + + def addCxxInclude(self, cxxInclude): self.cxxIncludes.append(cxxInclude) + def addInclude(self, inc): self.includes.append(inc) + def addStructDecl(self, struct): self.structsAndUnions.append(struct) + def addUnionDecl(self, union): self.structsAndUnions.append(union) + def addUsingStmt(self, using): self.using.append(using) + + def setProtocol(self, protocol): self.protocol = protocol + +class CxxInclude(Node): + def __init__(self, loc, cxxFile): + Node.__init__(self, loc) + self.file = cxxFile + +class Include(Node): + def __init__(self, loc, type, name): + Node.__init__(self, loc) + suffix = 'ipdl' + if type == 'header': + suffix += 'h' + self.file = "%s.%s" % (name, suffix) + +class UsingStmt(Node): + def __init__(self, loc, cxxTypeSpec, cxxHeader=None, kind=None): + Node.__init__(self, loc) + assert not isinstance(cxxTypeSpec, str) + assert cxxHeader is None or isinstance(cxxHeader, str); + assert kind is None or kind == 'class' or kind == 'struct' + self.type = cxxTypeSpec + self.header = cxxHeader + self.kind = kind + def canBeForwardDeclared(self): + return self.isClass() or self.isStruct() + def isClass(self): + return self.kind == 'class' + def isStruct(self): + return self.kind == 'struct' + +# "singletons" +class PrettyPrinted: + @classmethod + def __hash__(cls): return hash(cls.pretty) + @classmethod + def __str__(cls): return cls.pretty + +class ASYNC(PrettyPrinted): + pretty = 'async' +class INTR(PrettyPrinted): + pretty = 'intr' +class SYNC(PrettyPrinted): + pretty = 'sync' + +class INOUT(PrettyPrinted): + pretty = 'inout' +class IN(PrettyPrinted): + pretty = 'in' +class OUT(PrettyPrinted): + pretty = 'out' + + +class Namespace(Node): + def __init__(self, loc, namespace): + Node.__init__(self, loc) + self.name = namespace + +class Protocol(NamespacedNode): + def __init__(self, loc): + NamespacedNode.__init__(self, loc) + self.sendSemantics = ASYNC + self.nested = NOT_NESTED + self.spawnsStmts = [ ] + self.bridgesStmts = [ ] + self.opensStmts = [ ] + self.managers = [ ] + self.managesStmts = [ ] + self.messageDecls = [ ] + self.transitionStmts = [ ] + self.startStates = [ ] + +class StructField(Node): + def __init__(self, loc, type, name): + Node.__init__(self, loc) + self.typespec = type + self.name = name + +class StructDecl(NamespacedNode): + def __init__(self, loc, name, fields): + NamespacedNode.__init__(self, loc, name) + self.fields = fields + +class UnionDecl(NamespacedNode): + def __init__(self, loc, name, components): + NamespacedNode.__init__(self, loc, name) + self.components = components + +class SpawnsStmt(Node): + def __init__(self, loc, side, proto, spawnedAs): + Node.__init__(self, loc) + self.side = side + self.proto = proto + self.spawnedAs = spawnedAs + +class BridgesStmt(Node): + def __init__(self, loc, parentSide, childSide): + Node.__init__(self, loc) + self.parentSide = parentSide + self.childSide = childSide + +class OpensStmt(Node): + def __init__(self, loc, side, proto): + Node.__init__(self, loc) + self.side = side + self.proto = proto + +class Manager(Node): + def __init__(self, loc, managerName): + Node.__init__(self, loc) + self.name = managerName + +class ManagesStmt(Node): + def __init__(self, loc, managedName): + Node.__init__(self, loc) + self.name = managedName + +class MessageDecl(Node): + def __init__(self, loc): + Node.__init__(self, loc) + self.name = None + self.sendSemantics = ASYNC + self.nested = NOT_NESTED + self.prio = NORMAL_PRIORITY + self.direction = None + self.inParams = [ ] + self.outParams = [ ] + self.compress = '' + self.verify = '' + + def addInParams(self, inParamsList): + self.inParams += inParamsList + + def addOutParams(self, outParamsList): + self.outParams += outParamsList + + def addModifiers(self, modifiers): + for modifier in modifiers: + if modifier.startswith('compress'): + self.compress = modifier + elif modifier == 'verify': + self.verify = modifier + elif modifier != '': + raise Exception, "Unexpected message modifier `%s'"% modifier + +class Transition(Node): + def __init__(self, loc, trigger, msg, toStates): + Node.__init__(self, loc) + self.trigger = trigger + self.msg = msg + self.toStates = toStates + + def __cmp__(self, o): + c = cmp(self.msg, o.msg) + if c: return c + c = cmp(self.trigger, o.trigger) + if c: return c + + def __hash__(self): return hash(str(self)) + def __str__(self): return '%s %s'% (self.trigger, self.msg) + + @staticmethod + def nameToTrigger(name): + return { 'send': SEND, 'recv': RECV, 'call': CALL, 'answer': ANSWER }[name] + +Transition.NULL = Transition(Loc.NONE, None, None, [ ]) + +class TransitionStmt(Node): + def __init__(self, loc, state, transitions): + Node.__init__(self, loc) + self.state = state + self.transitions = transitions + + @staticmethod + def makeNullStmt(state): + return TransitionStmt(Loc.NONE, state, [ Transition.NULL ]) + +class SEND: + pretty = 'send' + @classmethod + def __hash__(cls): return hash(cls.pretty) + @classmethod + def direction(cls): return OUT +class RECV: + pretty = 'recv' + @classmethod + def __hash__(cls): return hash(cls.pretty) + @classmethod + def direction(cls): return IN +class CALL: + pretty = 'call' + @classmethod + def __hash__(cls): return hash(cls.pretty) + @classmethod + def direction(cls): return OUT +class ANSWER: + pretty = 'answer' + @classmethod + def __hash__(cls): return hash(cls.pretty) + @classmethod + def direction(cls): return IN + +class State(Node): + def __init__(self, loc, name, start=False): + Node.__init__(self, loc) + self.name = name + self.start = start + def __eq__(self, o): + return (isinstance(o, State) + and o.name == self.name + and o.start == self.start) + def __hash__(self): + return hash(repr(self)) + def __ne__(self, o): + return not (self == o) + def __repr__(self): return '<State %r start=%r>'% (self.name, self.start) + def __str__(self): return '<State %s start=%s>'% (self.name, self.start) + +State.ANY = State(Loc.NONE, '[any]', start=True) +State.DEAD = State(Loc.NONE, '[dead]', start=False) +State.DYING = State(Loc.NONE, '[dying]', start=False) + +class Param(Node): + def __init__(self, loc, typespec, name): + Node.__init__(self, loc) + self.name = name + self.typespec = typespec + +class TypeSpec(Node): + def __init__(self, loc, spec, state=None, array=0, nullable=0, + myChmod=None, otherChmod=None): + Node.__init__(self, loc) + self.spec = spec # QualifiedId + self.state = state # None or State + self.array = array # bool + self.nullable = nullable # bool + self.myChmod = myChmod # None or string + self.otherChmod = otherChmod # None or string + + def basename(self): + return self.spec.baseid + + def isActor(self): + return self.state is not None + + def __str__(self): return str(self.spec) + +class QualifiedId: # FIXME inherit from node? + def __init__(self, loc, baseid, quals=[ ]): + assert isinstance(baseid, str) + for qual in quals: assert isinstance(qual, str) + + self.loc = loc + self.baseid = baseid + self.quals = quals + + def qualify(self, id): + self.quals.append(self.baseid) + self.baseid = id + + def __str__(self): + if 0 == len(self.quals): + return self.baseid + return '::'.join(self.quals) +'::'+ self.baseid + +# added by type checking passes +class Decl(Node): + def __init__(self, loc): + Node.__init__(self, loc) + self.progname = None # what the programmer typed, if relevant + self.shortname = None # shortest way to refer to this decl + self.fullname = None # full way to refer to this decl + self.loc = loc + self.type = None + self.scope = None diff --git a/ipc/ipdl/ipdl/builtin.py b/ipc/ipdl/ipdl/builtin.py new file mode 100644 index 000000000..3a49b5c7e --- /dev/null +++ b/ipc/ipdl/ipdl/builtin.py @@ -0,0 +1,59 @@ +# 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/. + +# WARNING: the syntax of the builtin types is not checked, so please +# don't add something syntactically invalid. It will not be fun to +# track down the bug. + +Types = ( + # C types + 'bool', + 'char', + 'short', + 'int', + 'long', + 'float', + 'double', + + # stdint types + 'int8_t', + 'uint8_t', + 'int16_t', + 'uint16_t', + 'int32_t', + 'uint32_t', + 'int64_t', + 'uint64_t', + 'intptr_t', + 'uintptr_t', + + # stddef types + 'size_t', + 'ssize_t', + + # Mozilla types: "less" standard things we know how serialize/deserialize + 'nsresult', + 'nsString', + 'nsCString', + 'mozilla::ipc::Shmem', + 'mozilla::ipc::FileDescriptor' +) + + +HeaderIncludes = ( + 'mozilla/Attributes.h', + 'IPCMessageStart.h', + 'ipc/IPCMessageUtils.h', + 'mozilla/RefPtr.h', + 'nsStringGlue.h', + 'nsTArray.h', + 'mozilla/ipc/ProtocolUtils.h', + 'nsTHashtable.h', + 'mozilla/OperatorNewExtensions.h', +) + +CppIncludes = ( + 'nsIFile.h', + 'GeckoProfiler.h', +) diff --git a/ipc/ipdl/ipdl/cgen.py b/ipc/ipdl/ipdl/cgen.py new file mode 100644 index 000000000..fd8951c74 --- /dev/null +++ b/ipc/ipdl/ipdl/cgen.py @@ -0,0 +1,101 @@ +# 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, sys + +from ipdl.ast import Visitor +from ipdl.ast import IN, OUT, INOUT, ASYNC, SYNC, INTR + +class CodePrinter: + def __init__(self, outf=sys.stdout, indentCols=4): + self.outf = outf + self.col = 0 + self.indentCols = indentCols + + def write(self, str): + self.outf.write(str) + + def printdent(self, str=''): + self.write((' '* self.col) + str) + + def println(self, str=''): + self.write(str +'\n') + + def printdentln(self, str): + self.write((' '* self.col) + str +'\n') + + def indent(self): self.col += self.indentCols + def dedent(self): self.col -= self.indentCols + + +##----------------------------------------------------------------------------- +class IPDLCodeGen(CodePrinter, Visitor): + '''Spits back out equivalent IPDL to the code that generated this. +Also known as pretty-printing.''' + + def __init__(self, outf=sys.stdout, indentCols=4, printed=set()): + CodePrinter.__init__(self, outf, indentCols) + self.printed = printed + + def visitTranslationUnit(self, tu): + self.printed.add(tu.filename) + self.println('//\n// Automatically generated by ipdlc\n//') + CodeGen.visitTranslationUnit(self, tu) + + def visitCxxInclude(self, inc): + self.println('include "'+ inc.file +'";') + + def visitProtocolInclude(self, inc): + self.println('include protocol "'+ inc.file +'";') + if inc.tu.filename not in self.printed: + self.println('/* Included file:') + IPDLCodeGen(outf=self.outf, indentCols=self.indentCols, + printed=self.printed).visitTranslationUnit(inc.tu) + + self.println('*/') + + def visitProtocol(self, p): + self.println() + for namespace in p.namespaces: namespace.accept(self) + + self.println('%s protocol %s\n{'% (p.sendSemantics[0], p.name)) + self.indent() + + for mgs in p.managesStmts: + mgs.accept(self) + if len(p.managesStmts): self.println() + + for msgDecl in p.messageDecls: msgDecl.accept(self) + self.println() + + for transStmt in p.transitionStmts: transStmt.accept(self) + + self.dedent() + self.println('}') + self.write('}\n'* len(p.namespaces)) + + def visitManagerStmt(self, mgr): + self.printdentln('manager '+ mgr.name +';') + + def visitManagesStmt(self, mgs): + self.printdentln('manages '+ mgs.name +';') + + def visitMessageDecl(self, msg): + self.printdent('%s %s %s('% (msg.sendSemantics[0], msg.direction[0], msg.name)) + for i, inp in enumerate(msg.inParams): + inp.accept(self) + if i != (len(msg.inParams) - 1): self.write(', ') + self.write(')') + if 0 == len(msg.outParams): + self.println(';') + return + + self.println() + self.indent() + self.printdent('returns (') + for i, outp in enumerate(msg.outParams): + outp.accept(self) + if i != (len(msg.outParams) - 1): self.write(', ') + self.println(');') + self.dedent() diff --git a/ipc/ipdl/ipdl/cxx/__init__.py b/ipc/ipdl/ipdl/cxx/__init__.py new file mode 100644 index 000000000..f43d5b0db --- /dev/null +++ b/ipc/ipdl/ipdl/cxx/__init__.py @@ -0,0 +1,6 @@ +# 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 ipdl.cxx.ast +import ipdl.cxx.cgen diff --git a/ipc/ipdl/ipdl/cxx/ast.py b/ipc/ipdl/ipdl/cxx/ast.py new file mode 100644 index 000000000..18c2b3f1d --- /dev/null +++ b/ipc/ipdl/ipdl/cxx/ast.py @@ -0,0 +1,809 @@ +# 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 copy, sys + +class Visitor: + def defaultVisit(self, node): + raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% ( + node.__class__.__name__) + + def visitWhitespace(self, ws): + pass + + def visitFile(self, f): + for thing in f.stuff: + thing.accept(self) + + def visitCppDirective(self, ppd): + pass + + def visitBlock(self, block): + for stmt in block.stmts: + stmt.accept(self) + + def visitNamespace(self, ns): + self.visitBlock(ns) + + def visitType(self, type): + pass + + def visitTypeArray(self, ta): + ta.basetype.accept(self) + ta.nmemb.accept(self) + + def visitTypeEnum(self, enum): + pass + + def visitTypeUnion(self, union): + for t, name in union.components: + t.accept(self) + + def visitTypedef(self, tdef): + tdef.fromtype.accept(self) + + def visitUsing(self, us): + us.type.accept(self) + + def visitForwardDecl(self, fd): + pass + + def visitDecl(self, decl): + decl.type.accept(self) + + def visitParam(self, param): + self.visitDecl(param) + if param.default is not None: + param.default.accept(self) + + def visitClass(self, cls): + for inherit in cls.inherits: + inherit.accept(self) + self.visitBlock(cls) + + def visitInherit(self, inh): + pass + + def visitFriendClassDecl(self, fcd): + pass + + def visitMethodDecl(self, meth): + for param in meth.params: + param.accept(self) + if meth.ret is not None: + meth.ret.accept(self) + if meth.typeop is not None: + meth.typeop.accept(self) + if meth.T is not None: + meth.T.accept(self) + + def visitMethodDefn(self, meth): + meth.decl.accept(self) + self.visitBlock(meth) + + def visitFunctionDecl(self, fun): + self.visitMethodDecl(fun) + + def visitFunctionDefn(self, fd): + self.visitMethodDefn(fd) + + def visitConstructorDecl(self, ctor): + self.visitMethodDecl(ctor) + + def visitConstructorDefn(self, cd): + cd.decl.accept(self) + for init in cd.memberinits: + init.accept(self) + self.visitBlock(cd) + + def visitDestructorDecl(self, dtor): + self.visitMethodDecl(dtor) + + def visitDestructorDefn(self, dd): + dd.decl.accept(self) + self.visitBlock(dd) + + def visitExprLiteral(self, l): + pass + + def visitExprVar(self, v): + pass + + def visitExprPrefixUnop(self, e): + e.expr.accept(self) + + def visitExprBinary(self, e): + e.left.accept(self) + e.right.accept(self) + + def visitExprConditional(self, c): + c.cond.accept(self) + c.ife.accept(self) + c.elsee.accept(self) + + def visitExprAddrOf(self, eao): + self.visitExprPrefixUnop(eao) + + def visitExprDeref(self, ed): + self.visitExprPrefixUnop(ed) + + def visitExprNot(self, en): + self.visitExprPrefixUnop(en) + + def visitExprCast(self, ec): + ec.expr.accept(self) + + def visitExprIndex(self, ei): + ei.arr.accept(self) + ei.idx.accept(self) + + def visitExprSelect(self, es): + es.obj.accept(self) + + def visitExprAssn(self, ea): + ea.lhs.accept(self) + ea.rhs.accept(self) + + def visitExprCall(self, ec): + ec.func.accept(self) + for arg in ec.args: + arg.accept(self) + + def visitExprNew(self, en): + en.ctype.accept(self) + if en.newargs is not None: + for arg in en.newargs: + arg.accept(self) + if en.args is not None: + for arg in en.args: + arg.accept(self) + + def visitExprDelete(self, ed): + ed.obj.accept(self) + + def visitExprMemberInit(self, minit): + self.visitExprCall(minit) + + def visitExprSizeof(self, es): + self.visitExprCall(es) + + def visitStmtBlock(self, sb): + self.visitBlock(sb) + + def visitStmtDecl(self, sd): + sd.decl.accept(self) + if sd.init is not None: + sd.init.accept(self) + + def visitLabel(self, label): + pass + + def visitCaseLabel(self, case): + pass + + def visitDefaultLabel(self, dl): + pass + + def visitStmtIf(self, si): + si.cond.accept(self) + si.ifb.accept(self) + if si.elseb is not None: + si.elseb.accept(self) + + def visitStmtFor(self, sf): + if sf.init is not None: + sf.init.accept(self) + if sf.cond is not None: + sf.cond.accept(self) + if sf.update is not None: + sf.update.accept(self) + + def visitStmtSwitch(self, ss): + ss.expr.accept(self) + self.visitBlock(ss) + + def visitStmtBreak(self, sb): + pass + + def visitStmtExpr(self, se): + se.expr.accept(self) + + def visitStmtReturn(self, sr): + if sr.expr is not None: + sr.expr.accept(self) + +##------------------------------ +class Node: + def __init__(self): + pass + + def accept(self, visitor): + visit = getattr(visitor, 'visit'+ self.__class__.__name__, None) + if visit is None: + return getattr(visitor, 'defaultVisit')(self) + return visit(self) + +class Whitespace(Node): + # yes, this is silly. but we need to stick comments in the + # generated code without resorting to more serious hacks + def __init__(self, ws, indent=0): + Node.__init__(self) + self.ws = ws + self.indent = indent +Whitespace.NL = Whitespace('\n') + +class File(Node): + def __init__(self, filename): + Node.__init__(self) + self.name = filename + # array of stuff in the file --- stmts and preprocessor thingies + self.stuff = [ ] + + def addthing(self, thing): + assert thing is not None + assert not isinstance(thing, list) + self.stuff.append(thing) + + def addthings(self, things): + for t in things: self.addthing(t) + + # "look like" a Block so code doesn't have to care whether they're + # in global scope or not + def addstmt(self, stmt): + assert stmt is not None + assert not isinstance(stmt, list) + self.stuff.append(stmt) + + def addstmts(self, stmts): + for s in stmts: self.addstmt(s) + +class CppDirective(Node): + '''represents |#[directive] [rest]|, where |rest| is any string''' + def __init__(self, directive, rest=None): + Node.__init__(self) + self.directive = directive + self.rest = rest + +class Block(Node): + def __init__(self): + Node.__init__(self) + self.stmts = [ ] + + def addstmt(self, stmt): + assert stmt is not None + assert not isinstance(stmt, tuple) + self.stmts.append(stmt) + + def addstmts(self, stmts): + for s in stmts: self.addstmt(s) + +##------------------------------ +# type and decl thingies +class Namespace(Block): + def __init__(self, name): + assert isinstance(name, str) + + Block.__init__(self) + self.name = name + +class Type(Node): + def __init__(self, name, const=0, + ptr=0, ptrconst=0, ptrptr=0, ptrconstptr=0, + ref=0, + hasimplicitcopyctor=True, + T=None): + """ +To avoid getting fancy with recursive types, we limit the kinds +of pointer types that can be be constructed. + + ptr => T* + ptrconst => T* const + ptrptr => T** + ptrconstptr => T* const* + +Any type, naked or pointer, can be const (const T) or ref (T&). +""" + assert isinstance(name, str) + assert not isinstance(const, str) + assert not isinstance(T, str) + + Node.__init__(self) + self.name = name + self.const = const + self.ptr = ptr + self.ptrconst = ptrconst + self.ptrptr = ptrptr + self.ptrconstptr = ptrconstptr + self.ref = ref + self.hasimplicitcopyctor = hasimplicitcopyctor + self.T = T + # XXX could get serious here with recursive types, but shouldn't + # need that for this codegen + def __deepcopy__(self, memo): + return Type(self.name, + const=self.const, + ptr=self.ptr, ptrconst=self.ptrconst, + ptrptr=self.ptrptr, ptrconstptr=self.ptrconstptr, + ref=self.ref, + T=copy.deepcopy(self.T, memo)) +Type.BOOL = Type('bool') +Type.INT = Type('int') +Type.INT32 = Type('int32_t') +Type.INTPTR = Type('intptr_t') +Type.NSRESULT = Type('nsresult') +Type.UINT32 = Type('uint32_t') +Type.UINT32PTR = Type('uint32_t', ptr=1) +Type.SIZE = Type('size_t') +Type.VOID = Type('void') +Type.VOIDPTR = Type('void', ptr=1) +Type.AUTO = Type('auto') + +class TypeArray(Node): + def __init__(self, basetype, nmemb): + '''the type |basetype DECLNAME[nmemb]|. |nmemb| is an Expr''' + self.basetype = basetype + self.nmemb = nmemb + def __deepcopy__(self, memo): + return TypeArray(deepcopy(self.basetype, memo), nmemb) + +class TypeEnum(Node): + def __init__(self, name=None): + '''name can be None''' + Node.__init__(self) + self.name = name + self.idnums = [ ] # pairs of ('Foo', [num]) or ('Foo', None) + + def addId(self, id, num=None): + self.idnums.append((id, num)) + +class TypeUnion(Node): + def __init__(self, name=None): + Node.__init__(self) + self.name = name + self.components = [ ] # [ Decl ] + + def addComponent(self, type, name): + self.components.append(Decl(type, name)) + +class Typedef(Node): + def __init__(self, fromtype, totypename, templateargs=[]): + assert isinstance(totypename, str) + + Node.__init__(self) + self.fromtype = fromtype + self.totypename = totypename + self.templateargs = templateargs + + def __cmp__(self, o): + return cmp(self.totypename, o.totypename) + def __eq__(self, o): + return (self.__class__ == o.__class__ + and 0 == cmp(self, o)) + def __hash__(self): + return hash(self.totypename) + +class Using(Node): + def __init__(self, type): + Node.__init__(self) + self.type = type + +class ForwardDecl(Node): + def __init__(self, pqname, cls=0, struct=0): + assert (not cls and struct) or (cls and not struct) + + self.pqname = pqname + self.cls = cls + self.struct = struct + +class Decl(Node): + '''represents |Foo bar|, e.g. in a function signature''' + def __init__(self, type, name): + assert type is not None + assert not isinstance(type, str) + assert isinstance(name, str) + + Node.__init__(self) + self.type = type + self.name = name + def __deepcopy__(self, memo): + return Decl(copy.deepcopy(self.type, memo), self.name) + +class Param(Decl): + def __init__(self, type, name, default=None): + Decl.__init__(self, type, name) + self.default = default + def __deepcopy__(self, memo): + return Param(copy.deepcopy(self.type, memo), self.name, + copy.deepcopy(self.default, memo)) + +##------------------------------ +# class stuff +class Class(Block): + def __init__(self, name, inherits=[ ], + interface=0, abstract=0, final=0, + specializes=None, struct=0): + assert not (interface and abstract) + assert not (abstract and final) + assert not (interface and final) + assert not (inherits and specializes) + + Block.__init__(self) + self.name = name + self.inherits = inherits # [ Type ] + self.interface = interface # bool + self.abstract = abstract # bool + self.final = final # bool + self.specializes = specializes # Type or None + self.struct = struct # bool + +class Inherit(Node): + def __init__(self, type, viz='public'): + assert isinstance(viz, str) + Node.__init__(self) + self.type = type + self.viz = viz + +class FriendClassDecl(Node): + def __init__(self, friend): + Node.__init__(self) + self.friend = friend + +class MethodDecl(Node): + def __init__(self, name, params=[ ], ret=Type('void'), + virtual=0, const=0, pure=0, static=0, warn_unused=0, + inline=0, force_inline=0, never_inline=0, + typeop=None, + T=None): + assert not (virtual and static) + assert not pure or virtual # pure => virtual + assert not (static and typeop) + assert not (name and typeop) + assert name is None or isinstance(name, str) + assert not isinstance(ret, list) + for decl in params: assert not isinstance(decl, str) + assert not isinstance(T, int) + assert not (inline and never_inline) + assert not (force_inline and never_inline) + + if typeop is not None: + ret = None + + Node.__init__(self) + self.name = name + self.params = params # [ Param ] + self.ret = ret # Type or None + self.virtual = virtual # bool + self.const = const # bool + self.pure = pure # bool + self.static = static # bool + self.warn_unused = warn_unused # bool + self.force_inline = (force_inline or T) # bool + self.inline = inline # bool + self.never_inline = never_inline # bool + self.typeop = typeop # Type or None + self.T = T # Type or None + self.only_for_definition = False + + def __deepcopy__(self, memo): + return MethodDecl( + self.name, + params=copy.deepcopy(self.params, memo), + ret=copy.deepcopy(self.ret, memo), + virtual=self.virtual, + const=self.const, + pure=self.pure, + static=self.static, + warn_unused=self.warn_unused, + inline=self.inline, + force_inline=self.force_inline, + never_inline=self.never_inline, + typeop=copy.deepcopy(self.typeop, memo), + T=copy.deepcopy(self.T, memo)) + +class MethodDefn(Block): + def __init__(self, decl): + Block.__init__(self) + self.decl = decl + +class FunctionDecl(MethodDecl): + def __init__(self, name, params=[ ], ret=Type('void'), + static=0, warn_unused=0, + inline=0, force_inline=0, + T=None): + MethodDecl.__init__(self, name, params=params, ret=ret, + static=static, warn_unused=warn_unused, + inline=inline, force_inline=force_inline, + T=T) + +class FunctionDefn(MethodDefn): + def __init__(self, decl): + MethodDefn.__init__(self, decl) + +class ConstructorDecl(MethodDecl): + def __init__(self, name, params=[ ], explicit=0, force_inline=0): + MethodDecl.__init__(self, name, params=params, ret=None, + force_inline=force_inline) + self.explicit = explicit + + def __deepcopy__(self, memo): + return ConstructorDecl(self.name, + copy.deepcopy(self.params, memo), + self.explicit) + +class ConstructorDefn(MethodDefn): + def __init__(self, decl, memberinits=[ ]): + MethodDefn.__init__(self, decl) + self.memberinits = memberinits + +class DestructorDecl(MethodDecl): + def __init__(self, name, virtual=0, force_inline=0, inline=0): + MethodDecl.__init__(self, name, params=[ ], ret=None, + virtual=virtual, + force_inline=force_inline, inline=inline) + + def __deepcopy__(self, memo): + return DestructorDecl(self.name, + virtual=self.virtual, + force_inline=self.force_inline, + inline=self.inline) + + +class DestructorDefn(MethodDefn): + def __init__(self, decl): MethodDefn.__init__(self, decl) + +##------------------------------ +# expressions +class ExprVar(Node): + def __init__(self, name): + assert isinstance(name, str) + + Node.__init__(self) + self.name = name +ExprVar.THIS = ExprVar('this') + +class ExprLiteral(Node): + def __init__(self, value, type): + '''|type| is a Python format specifier; 'd' for example''' + Node.__init__(self) + self.value = value + self.type = type + + @staticmethod + def Int(i): return ExprLiteral(i, 'd') + + @staticmethod + def String(s): return ExprLiteral('"'+ s +'"', 's') + + @staticmethod + def WString(s): return ExprLiteral('L"'+ s +'"', 's') + + def __str__(self): + return ('%'+ self.type)% (self.value) +ExprLiteral.ZERO = ExprLiteral.Int(0) +ExprLiteral.ONE = ExprLiteral.Int(1) +ExprLiteral.NULL = ExprVar('nullptr') +ExprLiteral.TRUE = ExprVar('true') +ExprLiteral.FALSE = ExprVar('false') + +class ExprPrefixUnop(Node): + def __init__(self, expr, op): + assert not isinstance(expr, tuple) + self.expr = expr + self.op = op + +class ExprNot(ExprPrefixUnop): + def __init__(self, expr): + ExprPrefixUnop.__init__(self, expr, '!') + +class ExprAddrOf(ExprPrefixUnop): + def __init__(self, expr): + ExprPrefixUnop.__init__(self, expr, '&') + +class ExprDeref(ExprPrefixUnop): + def __init__(self, expr): + ExprPrefixUnop.__init__(self, expr, '*') + +class ExprCast(Node): + def __init__(self, expr, type, + dynamic=0, static=0, reinterpret=0, const=0, C=0): + assert 1 == reduce(lambda a, x: a+x, [ dynamic, static, reinterpret, const, C ]) + + Node.__init__(self) + self.expr = expr + self.type = type + self.dynamic = dynamic + self.static = static + self.reinterpret = reinterpret + self.const = const + self.C = C + +class ExprBinary(Node): + def __init__(self, left, op, right): + Node.__init__(self) + self.left = left + self.op = op + self.right = right + +class ExprConditional(Node): + def __init__(self, cond, ife, elsee): + Node.__init__(self) + self.cond = cond + self.ife = ife + self.elsee = elsee + +class ExprIndex(Node): + def __init__(self, arr, idx): + Node.__init__(self) + self.arr = arr + self.idx = idx + +class ExprSelect(Node): + def __init__(self, obj, op, field): + assert obj and op and field + assert not isinstance(obj, str) + assert isinstance(field, str) + + Node.__init__(self) + self.obj = obj + self.op = op + self.field = field + +class ExprAssn(Node): + def __init__(self, lhs, rhs, op='='): + Node.__init__(self) + self.lhs = lhs + self.op = op + self.rhs = rhs + +class ExprCall(Node): + def __init__(self, func, args=[ ]): + assert hasattr(func, 'accept') + assert isinstance(args, list) + for arg in args: assert arg and not isinstance(arg, str) + + Node.__init__(self) + self.func = func + self.args = args + +class ExprMove(ExprCall): + def __init__(self, arg): + ExprCall.__init__(self, ExprVar("mozilla::Move"), args=[arg]) + +class ExprNew(Node): + # XXX taking some poetic license ... + def __init__(self, ctype, args=[ ], newargs=None): + assert not (ctype.const or ctype.ref) + + Node.__init__(self) + self.ctype = ctype + self.args = args + self.newargs = newargs + +class ExprDelete(Node): + def __init__(self, obj): + Node.__init__(self) + self.obj = obj + +class ExprMemberInit(ExprCall): + def __init__(self, member, args=[ ]): + ExprCall.__init__(self, member, args) + +class ExprSizeof(ExprCall): + def __init__(self, t): + ExprCall.__init__(self, ExprVar('sizeof'), [ t ]) + +##------------------------------ +# statements etc. +class StmtBlock(Block): + def __init__(self, stmts=[ ]): + Block.__init__(self) + self.addstmts(stmts) + +class StmtDecl(Node): + def __init__(self, decl, init=None, initargs=None): + assert not (init and initargs) + assert not isinstance(init, str) # easy to confuse with Decl + assert not isinstance(init, list) + assert not isinstance(decl, tuple) + + Node.__init__(self) + self.decl = decl + self.init = init + self.initargs = initargs + +class Label(Node): + def __init__(self, name): + Node.__init__(self) + self.name = name +Label.PUBLIC = Label('public') +Label.PROTECTED = Label('protected') +Label.PRIVATE = Label('private') + +class CaseLabel(Node): + def __init__(self, name): + Node.__init__(self) + self.name = name + +class DefaultLabel(Node): + def __init__(self): + Node.__init__(self) + +class StmtIf(Node): + def __init__(self, cond): + Node.__init__(self) + self.cond = cond + self.ifb = Block() + self.elseb = None + + def addifstmt(self, stmt): + self.ifb.addstmt(stmt) + + def addifstmts(self, stmts): + self.ifb.addstmts(stmts) + + def addelsestmt(self, stmt): + if self.elseb is None: self.elseb = Block() + self.elseb.addstmt(stmt) + + def addelsestmts(self, stmts): + if self.elseb is None: self.elseb = Block() + self.elseb.addstmts(stmts) + +class StmtFor(Block): + def __init__(self, init=None, cond=None, update=None): + Block.__init__(self) + self.init = init + self.cond = cond + self.update = update + +class StmtRangedFor(Block): + def __init__(self, var, iteree): + assert isinstance(var, ExprVar) + assert iteree + + Block.__init__(self) + self.var = var + self.iteree = iteree + +class StmtSwitch(Block): + def __init__(self, expr): + Block.__init__(self) + self.expr = expr + self.nr_cases = 0 + + def addcase(self, case, block): + '''NOTE: |case| is not checked for uniqueness''' + assert not isinstance(case, str) + assert (isinstance(block, StmtBreak) + or isinstance(block, StmtReturn) + or isinstance(block, StmtSwitch) + or (hasattr(block, 'stmts') + and (isinstance(block.stmts[-1], StmtBreak) + or isinstance(block.stmts[-1], StmtReturn)))) + self.addstmt(case) + self.addstmt(block) + self.nr_cases += 1 + + def addfallthrough(self, case): + self.addstmt(case) + self.nr_cases += 1 + +class StmtBreak(Node): + def __init__(self): + Node.__init__(self) + +class StmtExpr(Node): + def __init__(self, expr): + assert expr is not None + + Node.__init__(self) + self.expr = expr + +class StmtReturn(Node): + def __init__(self, expr=None): + Node.__init__(self) + self.expr = expr + +StmtReturn.TRUE = StmtReturn(ExprLiteral.TRUE) +StmtReturn.FALSE = StmtReturn(ExprLiteral.FALSE) diff --git a/ipc/ipdl/ipdl/cxx/cgen.py b/ipc/ipdl/ipdl/cxx/cgen.py new file mode 100644 index 000000000..30f2f2bca --- /dev/null +++ b/ipc/ipdl/ipdl/cxx/cgen.py @@ -0,0 +1,520 @@ +# 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 sys + +from ipdl.cgen import CodePrinter +from ipdl.cxx.ast import TypeArray, Visitor + +class CxxCodeGen(CodePrinter, Visitor): + def __init__(self, outf=sys.stdout, indentCols=4): + CodePrinter.__init__(self, outf, indentCols) + + def cgen(self, cxxfile): + cxxfile.accept(self) + + def visitWhitespace(self, ws): + if ws.indent: + self.printdent('') + self.write(ws.ws) + + def visitCppDirective(self, cd): + if cd.rest: + self.println('#%s %s'% (cd.directive, cd.rest)) + else: + self.println('#%s'% (cd.directive)) + + def visitNamespace(self, ns): + self.println('namespace '+ ns.name +' {') + self.visitBlock(ns) + self.println('} // namespace '+ ns.name) + + def visitType(self, t): + if t.const: + self.write('const ') + + self.write(t.name) + + if t.T is not None: + self.write('<') + t.T.accept(self) + self.write('>') + + ts = '' + if t.ptr: ts += '*' + elif t.ptrconst: ts += '* const' + elif t.ptrptr: ts += '**' + elif t.ptrconstptr: ts += '* const*' + + ts += '&' * t.ref + + self.write(ts) + + def visitTypeEnum(self, te): + self.write('enum') + if te.name: + self.write(' '+ te.name) + self.println(' {') + + self.indent() + nids = len(te.idnums) + for i, (id, num) in enumerate(te.idnums): + self.printdent(id) + if num: + self.write(' = '+ str(num)) + if i != (nids-1): + self.write(',') + self.println() + self.dedent() + self.printdent('}') + + def visitTypeUnion(self, u): + self.write('union') + if u.name: + self.write(' '+ u.name) + self.println(' {') + + self.indent() + for decl in u.components: + self.printdent() + decl.accept(self) + self.println(';') + self.dedent() + + self.printdent('}') + + + def visitTypedef(self, td): + if td.templateargs: + formals = ', '.join([ 'class ' + T for T in td.templateargs ]) + args = ', '.join(td.templateargs) + self.printdent('template<' + formals + '> using ' + td.totypename + ' = ') + td.fromtype.accept(self) + self.println('<' + args + '>;') + else: + self.printdent('typedef ') + td.fromtype.accept(self) + self.println(' '+ td.totypename +';') + + def visitUsing(self, us): + self.printdent('using ') + us.type.accept(self) + self.println(';') + + def visitForwardDecl(self, fd): + if fd.cls: self.printdent('class ') + elif fd.struct: self.printdent('struct ') + self.write(str(fd.pqname)) + self.println(';') + + def visitDecl(self, d): + # C-syntax arrays make code generation much more annoying + if isinstance(d.type, TypeArray): + d.type.basetype.accept(self) + else: + d.type.accept(self) + + if d.name: + self.write(' '+ d.name) + + if isinstance(d.type, TypeArray): + self.write('[') + d.type.nmemb.accept(self) + self.write(']') + + def visitParam(self, p): + self.visitDecl(p) + if p.default is not None: + self.write(' = ') + p.default.accept(self) + + def visitClass(self, c): + if c.specializes is not None: + self.printdentln('template<>') + + if c.struct: + self.printdent('struct') + else: + self.printdent('class') + self.write(' '+ c.name) + if c.final: + self.write(' final') + + if c.specializes is not None: + self.write(' <') + c.specializes.accept(self) + self.write('>') + + ninh = len(c.inherits) + if 0 < ninh: + self.println(' :') + self.indent() + for i, inherit in enumerate(c.inherits): + self.printdent() + inherit.accept(self) + if i != (ninh - 1): + self.println(',') + self.dedent() + self.println() + + self.printdentln('{') + self.indent() + + self.visitBlock(c) + + self.dedent() + self.printdentln('};') + + def visitInherit(self, inh): + self.write(inh.viz +' ') + inh.type.accept(self) + + def visitFriendClassDecl(self, fcd): + self.printdentln('friend class '+ fcd.friend +';') + + + def visitMethodDecl(self, md): + assert not (md.static and md.virtual) + + if md.T: + self.write('template<') + self.write('typename ') + md.T.accept(self) + self.println('>') + self.printdent() + + if md.warn_unused: + self.write('MOZ_MUST_USE ') + if md.inline: + self.write('inline ') + if md.never_inline: + self.write('MOZ_NEVER_INLINE ') + if md.static: + self.write('static ') + if md.virtual: + self.write('virtual ') + if md.ret: + if md.only_for_definition: + self.write('auto ') + else: + md.ret.accept(self) + self.println() + self.printdent() + if md.typeop is not None: + self.write('operator ') + md.typeop.accept(self) + else: + self.write(md.name) + + self.write('(') + self.writeDeclList(md.params) + self.write(')') + + if md.const: + self.write(' const') + if md.ret and md.only_for_definition: + self.write(' -> ') + md.ret.accept(self) + if md.pure: + self.write(' = 0') + + + def visitMethodDefn(self, md): + if md.decl.pure: + return + + self.printdent() + md.decl.accept(self) + self.println() + + self.printdentln('{') + self.indent() + self.visitBlock(md) + self.dedent() + self.printdentln('}') + + + def visitConstructorDecl(self, cd): + if cd.explicit: + self.write('explicit ') + else: + self.write('MOZ_IMPLICIT ') + self.visitMethodDecl(cd) + + def visitConstructorDefn(self, cd): + self.printdent() + cd.decl.accept(self) + if len(cd.memberinits): + self.println(' :') + self.indent() + ninits = len(cd.memberinits) + for i, init in enumerate(cd.memberinits): + self.printdent() + init.accept(self) + if i != (ninits-1): + self.println(',') + self.dedent() + self.println() + + self.printdentln('{') + self.indent() + + self.visitBlock(cd) + + self.dedent() + self.printdentln('}') + + + def visitDestructorDecl(self, dd): + if dd.inline: + self.write('inline ') + if dd.virtual: + self.write('virtual ') + + # hack alert + parts = dd.name.split('::') + parts[-1] = '~'+ parts[-1] + + self.write('::'.join(parts) +'()') + + def visitDestructorDefn(self, dd): + self.printdent() + dd.decl.accept(self) + self.println() + + self.printdentln('{') + self.indent() + + self.visitBlock(dd) + + self.dedent() + self.printdentln('}') + + + def visitExprLiteral(self, el): + self.write(str(el)) + + def visitExprVar(self, ev): + self.write(ev.name) + + def visitExprPrefixUnop(self, e): + self.write('(') + self.write(e.op) + self.write('(') + e.expr.accept(self) + self.write(')') + self.write(')') + + def visitExprCast(self, c): + pfx, sfx = '', '' + if c.dynamic: pfx, sfx = 'dynamic_cast<', '>' + elif c.static: pfx, sfx = 'static_cast<', '>' + elif c.reinterpret: pfx, sfx = 'reinterpret_cast<', '>' + elif c.const: pfx, sfx = 'const_cast<', '>' + elif c.C: pfx, sfx = '(', ')' + self.write(pfx) + c.type.accept(self) + self.write(sfx +'(') + c.expr.accept(self) + self.write(')') + + def visitExprBinary(self, e): + self.write('(') + e.left.accept(self) + self.write(') '+ e.op +' (') + e.right.accept(self) + self.write(')') + + def visitExprConditional(self, c): + self.write('(') + c.cond.accept(self) + self.write(' ? ') + c.ife.accept(self) + self.write(' : ') + c.elsee.accept(self) + self.write(')') + + def visitExprIndex(self, ei): + ei.arr.accept(self) + self.write('[') + ei.idx.accept(self) + self.write(']') + + def visitExprSelect(self, es): + self.write('(') + es.obj.accept(self) + self.write(')') + self.write(es.op + es.field) + + def visitExprAssn(self, ea): + ea.lhs.accept(self) + self.write(' '+ ea.op +' ') + ea.rhs.accept(self) + + def visitExprCall(self, ec): + ec.func.accept(self) + self.write('(') + self.writeExprList(ec.args) + self.write(')') + + def visitExprMove(self, em): + self.visitExprCall(em) + + def visitExprNew(self, en): + self.write('new ') + if en.newargs is not None: + self.write('(') + self.writeExprList(en.newargs) + self.write(') ') + en.ctype.accept(self) + if en.args is not None: + self.write('(') + self.writeExprList(en.args) + self.write(')') + + def visitExprDelete(self, ed): + self.write('delete ') + ed.obj.accept(self) + + + def visitStmtBlock(self, b): + self.printdentln('{') + self.indent() + self.visitBlock(b) + self.dedent() + self.printdentln('}') + + def visitLabel(self, label): + self.dedent() # better not be at global scope ... + self.printdentln(label.name +':') + self.indent() + + def visitCaseLabel(self, cl): + self.dedent() + self.printdentln('case '+ cl.name +':') + self.indent() + + def visitDefaultLabel(self, dl): + self.dedent() + self.printdentln('default:') + self.indent() + + + def visitStmtIf(self, si): + self.printdent('if (') + si.cond.accept(self) + self.println(') {') + self.indent() + si.ifb.accept(self) + self.dedent() + self.printdentln('}') + + if si.elseb is not None: + self.printdentln('else {') + self.indent() + si.elseb.accept(self) + self.dedent() + self.printdentln('}') + + + def visitStmtFor(self, sf): + self.printdent('for (') + if sf.init is not None: + sf.init.accept(self) + self.write('; ') + if sf.cond is not None: + sf.cond.accept(self) + self.write('; ') + if sf.update is not None: + sf.update.accept(self) + self.println(') {') + + self.indent() + self.visitBlock(sf) + self.dedent() + self.printdentln('}') + + + def visitStmtRangedFor(self, rf): + self.printdent('for (auto& ') + rf.var.accept(self) + self.write(' : ') + rf.iteree.accept(self) + self.println(') {') + + self.indent() + self.visitBlock(rf) + self.dedent() + self.printdentln('}') + + + def visitStmtSwitch(self, sw): + self.printdent('switch (') + sw.expr.accept(self) + self.println(') {') + self.indent() + self.visitBlock(sw) + self.dedent() + self.printdentln('}') + + def visitStmtBreak(self, sb): + self.printdentln('break;') + + + def visitStmtDecl(self, sd): + self.printdent() + sd.decl.accept(self) + if sd.initargs is not None: + self.write('(') + self.writeDeclList(sd.initargs) + self.write(')') + if sd.init is not None: + self.write(' = ') + sd.init.accept(self) + self.println(';') + + + def visitStmtExpr(self, se): + self.printdent() + se.expr.accept(self) + self.println(';') + + + def visitStmtReturn(self, sr): + self.printdent('return') + if sr.expr: + self.write (' ') + sr.expr.accept(self) + self.println(';') + + + def writeDeclList(self, decls): + # FIXME/cjones: try to do nice formatting of these guys + + ndecls = len(decls) + if 0 == ndecls: + return + elif 1 == ndecls: + decls[0].accept(self) + return + + self.indent() + self.indent() + for i, decl in enumerate(decls): + self.println() + self.printdent() + decl.accept(self) + if i != (ndecls-1): + self.write(',') + self.dedent() + self.dedent() + + def writeExprList(self, exprs): + # FIXME/cjones: try to do nice formatting and share code with + # writeDeclList() + nexprs = len(exprs) + for i, expr in enumerate(exprs): + expr.accept(self) + if i != (nexprs-1): + self.write(', ') diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py new file mode 100644 index 000000000..f810cccb0 --- /dev/null +++ b/ipc/ipdl/ipdl/lower.py @@ -0,0 +1,4822 @@ +# 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 + +##----------------------------------------------------------------------------- +## "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 +## + +def hashfunc(value): + h = hash(value) % 2**32 + if h < 0: h += 2**32 + return h + +_NULL_ACTOR_ID = ExprLiteral.ZERO +_FREED_ACTOR_ID = ExprLiteral.ONE + +_DISCLAIMER = Whitespace('''// +// Automatically generated by ipdlc. +// Edit at your own risk +// + +''') + + +class _struct: pass + +def _namespacedHeaderName(name, namespaces): + pfx = '/'.join([ ns.name for ns in namespaces ]) + if pfx: + return pfx +'/'+ name + else: + return name + +def _ipdlhHeaderName(tu): + assert tu.filetype == 'header' + return _namespacedHeaderName(tu.name, tu.namespaces) + +def _protocolHeaderName(p, side=''): + if side: side = side.title() + base = p.name + side + return _namespacedHeaderName(base, p.namespaces) + +def _includeGuardMacroName(headerfile): + return re.sub(r'[./]', '_', headerfile.name) + +def _includeGuardStart(headerfile): + guard = _includeGuardMacroName(headerfile) + return [ CppDirective('ifndef', guard), + CppDirective('define', guard) ] + +def _includeGuardEnd(headerfile): + guard = _includeGuardMacroName(headerfile) + return [ CppDirective('endif', '// ifndef '+ guard) ] + +def _messageStartName(ptype): + return ptype.name() +'MsgStart' + +def _protocolId(ptype): + return ExprVar(_messageStartName(ptype)) + +def _protocolIdType(): + return Type.INT32 + +def _actorName(pname, side): + """|pname| is the protocol name. |side| is 'Parent' or 'Child'.""" + tag = side + if not tag[0].isupper(): tag = side.title() + return pname + tag + +def _actorIdType(): + return Type.INT32 + +def _actorTypeTagType(): + return Type.INT32 + +def _actorId(actor=None): + if actor is not None: + return ExprCall(ExprSelect(actor, '->', 'Id')) + return ExprCall(ExprVar('Id')) + +def _actorHId(actorhandle): + return ExprSelect(actorhandle, '.', 'mId') + +def _actorManager(actor): + return ExprCall(ExprSelect(actor, '->', 'Manager'), args=[]) + +def _actorState(actor): + return ExprSelect(actor, '->', 'mState') + +def _backstagePass(): + return ExprCall(ExprVar('mozilla::ipc::PrivateIPDLInterface')) + +def _iterType(ptr): + return Type('PickleIterator', ptr=ptr) + +def _nullState(proto=None): + pfx = '' + if proto is not None: pfx = proto.name() +'::' + return ExprVar(pfx +'__Null') + +def _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_RELEASE_ASSERT'), + [ cond, ExprLiteral.String(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 _uniqueptr(T): + return Type('UniquePtr', T=T) + +def _uniqueptrGet(expr): + return ExprCall(ExprSelect(expr, '.', 'get')) + +def _cxxArrayType(basetype, const=0, ref=0): + return Type('nsTArray', T=basetype, const=const, ref=ref, hasimplicitcopyctor=False) + +def _cxxManagedContainerType(basetype, const=0, ref=0): + return Type('ManagedContainer', T=basetype, + const=const, ref=ref, hasimplicitcopyctor=False) + +def _callCxxArrayLength(arr): + return ExprCall(ExprSelect(arr, '.', 'Length')) + +def _callCxxArraySetLength(arr, lenexpr, sel='.'): + return ExprCall(ExprSelect(arr, sel, 'SetLength'), + args=[ lenexpr ]) + +def _callCxxSwapArrayElements(arr1, arr2, sel='.'): + return ExprCall(ExprSelect(arr1, sel, 'SwapElements'), + args=[ arr2 ]) + +def _callInsertManagedActor(managees, actor): + return ExprCall(ExprSelect(managees, '.', 'PutEntry'), + args=[ actor ]) + +def _callRemoveManagedActor(managees, actor): + return ExprCall(ExprSelect(managees, '.', 'RemoveEntry'), + args=[ actor ]) + +def _callClearManagedActors(managees): + return ExprCall(ExprSelect(managees, '.', 'Clear')) + +def _callHasManagedActor(managees, actor): + return ExprCall(ExprSelect(managees, '.', 'Contains'), args=[ actor ]) + +def _otherSide(side): + if side == 'child': return 'parent' + if side == 'parent': return 'child' + assert 0 + +def _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 _printWarningMessage(msg): + if isinstance(msg, str): + msg = ExprLiteral.String(msg) + return StmtExpr( + ExprCall(ExprVar('NS_WARNING'), args=[ msg ])) + +def _fatalError(msg): + return StmtExpr( + ExprCall(ExprVar('FatalError'), args=[ ExprLiteral.String(msg) ])) + +def _logicError(msg): + return StmtExpr( + ExprCall(ExprVar('mozilla::ipc::LogicError'), args=[ ExprLiteral.String(msg) ])) + +def _arrayLengthReadError(elementname): + return StmtExpr( + ExprCall(ExprVar('mozilla::ipc::ArrayLengthReadError'), + args=[ ExprLiteral.String(elementname) ])) + +def _unionTypeReadError(unionname): + return StmtExpr( + ExprCall(ExprVar('mozilla::ipc::UnionTypeReadError'), + args=[ ExprLiteral.String(unionname) ])) + +def _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 errfnArrayLength(elementname): + return [ _arrayLengthReadError(elementname), StmtReturn.FALSE ] + +def errfnUnionType(unionname): + return [ _unionTypeReadError(unionname), 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 visitEndpointType(self, s): + return Type(self.typename(s)) + + def visitProtocolType(self, p): assert 0 + def visitMessageType(self, m): assert 0 + def visitVoidType(self, v): assert 0 + def visitStateType(self, st): assert 0 + +def _cxxBareType(ipdltype, side, fq=0): + return ipdltype.accept(_ConvertToCxxType(side, fq)) + +def _cxxRefType(ipdltype, side): + t = _cxxBareType(ipdltype, side) + t.ref = 1 + return t + +def _cxxConstRefType(ipdltype, side): + t = _cxxBareType(ipdltype, side) + if ipdltype.isIPDL() and ipdltype.isActor(): + return t + if ipdltype.isIPDL() and ipdltype.isShmem(): + t.ref = 1 + return t + t.const = 1 + t.ref = 1 + return t + +def _cxxMoveRefType(ipdltype, side): + t = _cxxBareType(ipdltype, side) + if ipdltype.isIPDL() and (ipdltype.isArray() or ipdltype.isShmem() or ipdltype.isEndpoint()): + t.ref = 2 + return t + return _cxxConstRefType(ipdltype, side) + +def _cxxPtrToType(ipdltype, side): + t = _cxxBareType(ipdltype, side) + if ipdltype.isIPDL() and ipdltype.isActor(): + t.ptr = 0 + t.ptrptr = 1 + return t + t.ptr = 1 + return t + +def _cxxConstPtrToType(ipdltype, side): + t = _cxxBareType(ipdltype, side) + if ipdltype.isIPDL() and ipdltype.isActor(): + t.ptr = 0 + t.ptrconstptr = 1 + return t + t.const = 1 + t.ptr = 1 + return t + +def _allocMethod(ptype, side): + return ExprVar('Alloc'+ 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): + self.basename = name + fname = name + special = _hasVisibleActor(ipdltype) + if special: + fname += side.title() + + _CompoundTypeComponent.__init__(self, ipdltype, fname, side, sd) + + def getMethod(self, thisexpr=None, sel='.'): + meth = self.var() + if thisexpr is not None: + return ExprSelect(thisexpr, sel, meth.name) + return meth + + def initExpr(self, thisexpr): + expr = ExprCall(self.getMethod(thisexpr=thisexpr)) + if self.ipdltype.isIPDL() and self.ipdltype.isActor(): + expr = ExprCast(expr, self.bareType(), const=1) + return expr + + def refExpr(self, thisexpr=None): + ref = self.memberVar() + if thisexpr is not None: + ref = ExprSelect(thisexpr, '.', ref.name) + if self.recursive: + ref = ExprDeref(ref) + return ref + + def constRefExpr(self, thisexpr=None): + # sigh, gross hack + refexpr = self.refExpr(thisexpr) + if 'Shmem' == self.ipdltype.name(): + refexpr = ExprCast(refexpr, Type('Shmem', ref=1), const=1) + if 'FileDescriptor' == self.ipdltype.name(): + refexpr = ExprCast(refexpr, Type('FileDescriptor', ref=1), const=1) + return refexpr + + def argVar(self): + return ExprVar('_'+ self.name) + + def memberVar(self): + return ExprVar(self.name + '_') + + def initStmts(self): + if self.recursive: + return [ StmtExpr(ExprAssn(self.memberVar(), + ExprNew(self.bareType()))) ] + elif self.ipdltype.isIPDL() and self.ipdltype.isActor(): + return [ StmtExpr(ExprAssn(self.memberVar(), + ExprLiteral.NULL)) ] + else: + return [] + + def destructStmts(self): + if self.recursive: + return [ StmtExpr(ExprDelete(self.memberVar())) ] + else: + return [] + + +class UnionDecl(ipdl.ast.UnionDecl, HasFQName): + def callType(self, var=None): + func = ExprVar('type') + if var is not None: + func = ExprSelect(var, '.', func.name) + return ExprCall(func) + + @staticmethod + def upgrade(unionDecl): + assert isinstance(unionDecl, ipdl.ast.UnionDecl) + unionDecl.__class__ = UnionDecl + return unionDecl + + +class _UnionMember(_CompoundTypeComponent): + """Not in the AFL sense, but rather a member (e.g. |int;|) of an +IPDL union type.""" + def __init__(self, ipdltype, ud, side=None, other=None): + flatname = _flatTypeName(ipdltype) + special = _hasVisibleActor(ipdltype) + if special: + flatname += side.title() + + _CompoundTypeComponent.__init__(self, ipdltype, 'V'+ flatname, side, ud) + self.flattypename = flatname + if special: + if other is not None: + self.other = other + else: + self.other = _UnionMember(ipdltype, ud, _otherSide(side), self) + + def enum(self): + return 'T' + self.flattypename + + def pqEnum(self): + return self.ud.name +'::'+ self.enum() + + def enumvar(self): + return ExprVar(self.enum()) + + def unionType(self): + """Type used for storage in generated C union decl.""" + if self.recursive: + return self.ptrToType() + else: + return Type('mozilla::AlignedStorage2', T=self.internalType()) + + def unionValue(self): + # NB: knows that Union's storage C union is named |mValue| + return ExprSelect(ExprVar('mValue'), '.', self.name) + + def typedef(self): + return self.flattypename +'__tdef' + + def callGetConstPtr(self): + """Return an expression of type self.constptrToSelfType()""" + return ExprCall(ExprVar(self.getConstPtrName())) + + def callGetPtr(self): + """Return an expression of type self.ptrToSelfType()""" + return ExprCall(ExprVar(self.getPtrName())) + + def callOperatorEq(self, rhs): + if self.ipdltype.isIPDL() and self.ipdltype.isActor(): + rhs = ExprCast(rhs, self.bareType(), const=1) + return ExprAssn(ExprDeref(self.callGetPtr()), rhs) + + def callCtor(self, expr=None): + assert not isinstance(expr, list) + + if expr is None: + args = None + elif self.ipdltype.isIPDL() and self.ipdltype.isActor(): + args = [ ExprCast(expr, self.bareType(), const=1) ] + else: + args = [ expr ] + + if self.recursive: + return ExprAssn(self.callGetPtr(), + ExprNew(self.bareType(self.side), + args=args)) + else: + return ExprNew(self.bareType(self.side), + args=args, + newargs=[ ExprVar('mozilla::KnownNotNull'), self.callGetPtr() ]) + + def callDtor(self): + if self.recursive: + return ExprDelete(self.callGetPtr()) + else: + return ExprCall( + ExprSelect(self.callGetPtr(), '->', '~'+ self.typedef())) + + def getTypeName(self): return 'get_'+ self.flattypename + def getConstTypeName(self): return 'get_'+ self.flattypename + + def getOtherTypeName(self): return 'get_'+ self.otherflattypename + + def getPtrName(self): return 'ptr_'+ self.flattypename + def getConstPtrName(self): return 'constptr_'+ self.flattypename + + def ptrToSelfExpr(self): + """|*ptrToSelfExpr()| has type |self.bareType()|""" + v = self.unionValue() + if self.recursive: + return v + else: + return ExprCall(ExprSelect(v, '.', 'addr')) + + def constptrToSelfExpr(self): + """|*constptrToSelfExpr()| has type |self.constType()|""" + v = self.unionValue() + if self.recursive: + return v + return ExprCall(ExprSelect(v, '.', 'addr')) + + def ptrToInternalType(self): + t = self.ptrToType() + if self.recursive: + t.ref = 1 + return t + + def defaultValue(self): + # Use the default constructor for any class that does not have an + # implicit copy constructor. + if not self.bareType().hasimplicitcopyctor: + return None + + if self.ipdltype.isIPDL() and self.ipdltype.isActor(): + return ExprLiteral.NULL + # XXX sneaky here, maybe need ExprCtor()? + return ExprCall(self.bareType()) + + def getConstValue(self): + v = ExprDeref(self.callGetConstPtr()) + # sigh + if 'Shmem' == self.ipdltype.name(): + v = ExprCast(v, Type('Shmem', ref=1), const=1) + if 'FileDescriptor' == self.ipdltype.name(): + v = ExprCast(v, Type('FileDescriptor', ref=1), const=1) + return v + +##-------------------------------------------------- + +class MessageDecl(ipdl.ast.MessageDecl): + def baseName(self): + return self.name + + def recvMethod(self): + name = _recvPrefix(self.decl.type) + self.baseName() + if self.decl.type.isCtor(): + name += 'Constructor' + return ExprVar(name) + + def sendMethod(self): + name = _sendPrefix(self.decl.type) + self.baseName() + if self.decl.type.isCtor(): + name += 'Constructor' + return ExprVar(name) + + def hasReply(self): + return (self.decl.type.hasReply() + or self.decl.type.isCtor() + or self.decl.type.isDtor()) + + def msgCtorFunc(self): + return 'Msg_%s'% (self.decl.progname) + + def prettyMsgName(self, pfx=''): + return pfx + self.msgCtorFunc() + + def pqMsgCtorFunc(self): + return '%s::%s'% (self.namespace, self.msgCtorFunc()) + + def msgId(self): return self.msgCtorFunc()+ '__ID' + def pqMsgId(self): + return '%s::%s'% (self.namespace, self.msgId()) + + def replyCtorFunc(self): + return 'Reply_%s'% (self.decl.progname) + + def pqReplyCtorFunc(self): + return '%s::%s'% (self.namespace, self.replyCtorFunc()) + + def replyId(self): return self.replyCtorFunc()+ '__ID' + def pqReplyId(self): + return '%s::%s'% (self.namespace, self.replyId()) + + def prettyReplyName(self, pfx=''): + return pfx + self.replyCtorFunc() + + def 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, paramsems='in', retsems='out', retcallsems='out', + implicit=1): + assert not retcallsems or retsems # retcallsems => returnsems + cxxargs = [ ] + + if paramsems is 'move': + cxxargs.extend([ ExprMove(p.var()) for p in self.params ]) + elif paramsems is 'in': + cxxargs.extend([ p.var() for p in self.params ]) + else: + assert False + + for ret in self.returns: + if retsems is 'in': + if retcallsems is 'in': + cxxargs.append(ret.var()) + elif retcallsems is 'out': + cxxargs.append(ExprAddrOf(ret.var())) + else: assert 0 + elif retsems is 'out': + if retcallsems is 'in': + cxxargs.append(ExprDeref(ret.var())) + elif retcallsems is 'out': + cxxargs.append(ret.var()) + else: assert 0 + + if not implicit: + assert self.decl.type.hasImplicitActorParam() + cxxargs = cxxargs[1:] + + return cxxargs + + + @staticmethod + def upgrade(messageDecl): + assert isinstance(messageDecl, ipdl.ast.MessageDecl) + if messageDecl.decl.type.hasImplicitActorParam(): + messageDecl.params.insert( + 0, + _HybridDecl( + ipdl.type.ActorType( + messageDecl.decl.type.constructedType()), + 'actor')) + messageDecl.__class__ = MessageDecl + return messageDecl + +##-------------------------------------------------- +def _semsToChannelParts(sems): + return [ 'mozilla', 'ipc', 'MessageChannel' ] + +def _usesShmem(p): + for md in p.messageDecls: + for param in md.inParams: + if ipdl.type.hasshmem(param.type): + return True + for ret in md.outParams: + if ipdl.type.hasshmem(ret.type): + return True + return False + +def _subtreeUsesShmem(p): + if _usesShmem(p): + return True + + ptype = p.decl.type + for mgd in ptype.manages: + if ptype is not mgd: + if _subtreeUsesShmem(mgd._ast): + return True + return False + +class Protocol(ipdl.ast.Protocol): + def cxxTypedefs(self): + return self.decl.cxxtypedefs + + def sendSems(self): + return self.decl.type.toplevel().sendSemantics + + def channelName(self): + return '::'.join(_semsToChannelParts(self.sendSems())) + + def channelSel(self): + if self.decl.type.isToplevel(): return '.' + return '->' + + def channelType(self): + return Type('Channel', ptr=not self.decl.type.isToplevel()) + + def channelHeaderFile(self): + return '/'.join(_semsToChannelParts(self.sendSems())) +'.h' + + def managerInterfaceType(self, ptr=0): + return Type('mozilla::ipc::IProtocol', ptr=ptr) + + def openedProtocolInterfaceType(self, ptr=0): + return Type('mozilla::ipc::IToplevelProtocol', + ptr=ptr) + + def _ipdlmgrtype(self): + assert 1 == len(self.decl.type.managers) + for mgr in self.decl.type.managers: return mgr + + def managerActorType(self, side, ptr=0): + return Type(_actorName(self._ipdlmgrtype().name(), side), + ptr=ptr) + + def 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 lookupSharedMemory(self): + return ExprVar('LookupSharedMemory') + + def isTrackingSharedMemory(self): + return ExprVar('IsTrackingSharedMemory') + + def destroySharedMemory(self): + return ExprVar('DestroySharedMemory') + + def otherPidMethod(self): + return ExprVar('OtherPid') + + def callOtherPid(self, actorThis=None): + fn = self.otherPidMethod() + if actorThis is not None: + fn = ExprSelect(actorThis, '->', fn.name) + return ExprCall(fn) + + def getChannelMethod(self): + return ExprVar('GetIPCChannel') + + def callGetChannel(self, actorThis=None): + fn = self.getChannelMethod() + if actorThis is not None: + fn = ExprSelect(actorThis, '->', fn.name) + return ExprCall(fn) + + def processingErrorVar(self): + assert self.decl.type.isToplevel() + return ExprVar('ProcessingError') + + def shouldContinueFromTimeoutVar(self): + assert self.decl.type.isToplevel() + return ExprVar('ShouldContinueFromReplyTimeout') + + def enteredCxxStackVar(self): + assert self.decl.type.isToplevel() + return ExprVar('EnteredCxxStack') + + def exitedCxxStackVar(self): + assert self.decl.type.isToplevel() + return ExprVar('ExitedCxxStack') + + def enteredCallVar(self): + assert self.decl.type.isToplevel() + return ExprVar('EnteredCall') + + def exitedCallVar(self): + assert self.decl.type.isToplevel() + return ExprVar('ExitedCall') + + def onCxxStackVar(self): + assert self.decl.type.isToplevel() + return ExprVar('IsOnCxxStack') + + # an actor's C++ private variables + def channelVar(self, actorThis=None): + if actorThis is not None: + return ExprSelect(actorThis, '->', 'mChannel') + return ExprVar('mChannel') + + def routingId(self, actorThis=None): + if self.decl.type.isToplevel(): + return ExprVar('MSG_ROUTING_CONTROL') + if actorThis is not None: + return ExprCall(ExprSelect(actorThis, '->', 'Id')) + return ExprCall(ExprVar('Id')) + + def stateVar(self, actorThis=None): + if actorThis is not None: + return ExprSelect(actorThis, '->', 'mState') + return ExprVar('mState') + + def fqStateType(self): + return Type(self.decl.type.name() +'::State') + + def startState(self): + return _startState(self.decl.type) + + def nullState(self): + return _nullState(self.decl.type) + + def deadState(self): + return _deadState(self.decl.type) + + def managerVar(self, thisexpr=None): + assert thisexpr is not None or not self.decl.type.isToplevel() + mvar = ExprCall(ExprVar('Manager'), args=[]) + if thisexpr is not None: + mvar = ExprCall(ExprSelect(thisexpr, '->', 'Manager'), args=[]) + return mvar + + def managedCxxType(self, actortype, side): + assert self.decl.type.isManagerOf(actortype) + return Type(_actorName(actortype.name(), side), ptr=1) + + def managedMethod(self, actortype, side): + assert self.decl.type.isManagerOf(actortype) + return ExprVar('Managed'+ _actorName(actortype.name(), side)) + + def managedVar(self, actortype, side): + assert self.decl.type.isManagerOf(actortype) + return ExprVar('mManaged'+ _actorName(actortype.name(), side)) + + def managedVarType(self, actortype, side, const=0, ref=0): + assert self.decl.type.isManagerOf(actortype) + return _cxxManagedContainerType(Type(_actorName(actortype.name(), side)), + const=const, ref=ref) + + # XXX this is sucky, fix + def usesShmem(self): + return _usesShmem(self) + + def subtreeUsesShmem(self): + return _subtreeUsesShmem(self) + + @staticmethod + def upgrade(protocol): + assert isinstance(protocol, ipdl.ast.Protocol) + protocol.__class__ = Protocol + return protocol + + +class TranslationUnit(ipdl.ast.TranslationUnit): + @staticmethod + def upgrade(tu): + assert isinstance(tu, ipdl.ast.TranslationUnit) + tu.__class__ = TranslationUnit + return tu + +##----------------------------------------------------------------------------- + +class _DecorateWithCxxStuff(ipdl.ast.Visitor): + """Phase 1 of lowering: decorate the IPDL AST with information +relevant to C++ code generation. + +This pass results in an AST that is a poor man's "IR"; in reality, a +"hybrid" AST mainly consisting of IPDL nodes with new C++ info along +with some new IPDL/C++ nodes that are tuned for C++ codegen.""" + + def __init__(self): + self.visitedTus = set() + # the set of typedefs that allow generated classes to + # reference known C++ types by their "short name" rather than + # fully-qualified name. e.g. |Foo| rather than |a::b::Foo|. + self.typedefs = [ ] + self.typedefSet = set([ Typedef(Type('mozilla::ipc::ActorHandle'), + 'ActorHandle'), + Typedef(Type('base::ProcessId'), + 'ProcessId'), + Typedef(Type('mozilla::ipc::ProtocolId'), + 'ProtocolId'), + Typedef(Type('mozilla::ipc::Transport'), + 'Transport'), + Typedef(Type('mozilla::ipc::Endpoint'), + 'Endpoint', ['FooSide']), + Typedef(Type('mozilla::ipc::TransportDescriptor'), + 'TransportDescriptor') ]) + 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) + +##----------------------------------------------------------------------------- + +def msgenums(protocol, pretty=False): + msgenum = TypeEnum('MessageType') + msgstart = _messageStartName(protocol.decl.type) +' << 16' + msgenum.addId(protocol.name + 'Start', msgstart) + + for md in protocol.messageDecls: + msgenum.addId(md.prettyMsgName() if pretty else md.msgId()) + if md.hasReply(): + msgenum.addId(md.prettyReplyName() if pretty else md.replyId()) + + msgenum.addId(protocol.name +'End') + return msgenum + +class _GenerateProtocolCode(ipdl.ast.Visitor): + '''Creates code common to both the parent and child actors.''' + def __init__(self): + self.protocol = None # protocol we're generating a class for + self.hdrfile = None # what will become Protocol.h + self.cppfile = None # what will become Protocol.cpp + self.cppIncludeHeaders = [] + self.structUnionDefns = [] + self.funcDefns = [] + + def lower(self, tu, cxxHeaderFile, cxxFile): + 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, '')) + + # Forward declare our own actors. + self.hdrfile.addthings([ + Whitespace.NL, + _makeForwardDeclForActor(p.decl.type, 'Parent'), + _makeForwardDeclForActor(p.decl.type, 'Child') + ]) + + 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) + + edecl, edefn = _splitFuncDeclDefn(self.genEndpointFunc()) + ns.addstmts([ edecl, Whitespace.NL ]) + self.funcDefns.append(edefn) + + # state information + stateenum = TypeEnum('State') + # NB: __Dead is the first state on purpose, so that it has + # value '0' + stateenum.addId(_deadState().name) + stateenum.addId(_nullState().name) + 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 = msgenums(self.protocol) + ns.addstmts([ StmtDecl(Decl(msgenum, '')), Whitespace.NL ]) + + tfDecl, tfDefn = _splitFuncDeclDefn(self.genTransitionFunc()) + ns.addstmts([ tfDecl, Whitespace.NL ]) + self.funcDefns.append(tfDefn) + + for md in p.messageDecls: + decls = [] + + mfDecl, mfDefn = _splitFuncDeclDefn( + _generateMessageConstructor(md.msgCtorFunc(), md.msgId(), + md.decl.type.nested, + md.decl.type.prio, + md.prettyMsgName(p.name+'::'), + md.decl.type.compress)) + decls.append(mfDecl) + self.funcDefns.append(mfDefn) + + if md.hasReply(): + rfDecl, rfDefn = _splitFuncDeclDefn( + _generateMessageConstructor( + md.replyCtorFunc(), md.replyId(), + md.decl.type.nested, + md.decl.type.prio, + md.prettyReplyName(p.name+'::'), + md.decl.type.compress)) + decls.append(rfDecl) + self.funcDefns.append(rfDefn) + + decls.append(Whitespace.NL) + ns.addstmts(decls) + + 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 + + + # Generate code for PFoo::CreateEndpoints. + def genEndpointFunc(self): + p = self.protocol.decl.type + tparent = _cxxBareType(ActorType(p), 'Parent', fq=1) + tchild = _cxxBareType(ActorType(p), 'Child', fq=1) + methodvar = ExprVar('CreateEndpoints') + rettype = Type.NSRESULT + parentpidvar = ExprVar('aParentDestPid') + childpidvar = ExprVar('aChildDestPid') + parentvar = ExprVar('aParent') + childvar = ExprVar('aChild') + + openfunc = MethodDefn(MethodDecl( + methodvar.name, + params=[ Decl(Type('base::ProcessId'), parentpidvar.name), + Decl(Type('base::ProcessId'), childpidvar.name), + Decl(Type('mozilla::ipc::Endpoint<' + tparent.name + '>', ptr=1), parentvar.name), + Decl(Type('mozilla::ipc::Endpoint<' + tchild.name + '>', ptr=1), childvar.name) ], + ret=rettype)) + openfunc.addstmt(StmtReturn(ExprCall( + ExprVar('mozilla::ipc::CreateEndpoints'), + args=[ _backstagePass(), + parentpidvar, childpidvar, + _protocolId(p), + ExprVar(_messageStartName(p) + 'Child'), + parentvar, childvar + ]))) + 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(Trigger trigger, State* next) + # The state we are transitioning from is stored in *next. + fromvar = ExprVar('from') + triggervar = ExprVar('trigger') + nextvar = ExprVar('next') + msgexpr = ExprSelect(triggervar, '.', 'mMessage') + actionexpr = ExprSelect(triggervar, '.', 'mAction') + + transitionfunc = FunctionDefn(FunctionDecl( + 'Transition', + params=[ 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([ + _logicError('__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([ + _logicError('__delete__()d (and unexpectedly dying) actor'), + StmtReturn(ExprLiteral.FALSE) ]) + fromswitch.addcase(CaseLabel(_dyingState().name), dyingblock) + + unreachedblock = Block() + unreachedblock.addstmts([ + _logicError('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(StmtDecl(Decl(Type('State'), fromvar.name), + init=ExprDeref(nextvar))) + 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 _generateMessageConstructor(clsname, msgid, nested, prio, prettyName, compress): + routingId = ExprVar('routingId') + + func = FunctionDefn(FunctionDecl( + clsname, + params=[ Decl(Type('int32_t'), routingId.name) ], + ret=Type('IPC::Message', ptr=1))) + + if compress == 'compress': + compression = ExprVar('IPC::Message::COMPRESSION_ENABLED') + elif compress: + assert compress == 'compressall' + compression = ExprVar('IPC::Message::COMPRESSION_ALL') + else: + compression = ExprVar('IPC::Message::COMPRESSION_NONE') + + if nested == ipdl.ast.NOT_NESTED: + nestedEnum = 'IPC::Message::NOT_NESTED' + elif nested == ipdl.ast.INSIDE_SYNC_NESTED: + nestedEnum = 'IPC::Message::NESTED_INSIDE_SYNC' + else: + assert nested == ipdl.ast.INSIDE_CPOW_NESTED + nestedEnum = 'IPC::Message::NESTED_INSIDE_CPOW' + + if prio == ipdl.ast.NORMAL_PRIORITY: + prioEnum = 'IPC::Message::NORMAL_PRIORITY' + else: + assert prio == ipdl.ast.HIGH_PRIORITY + prioEnum = 'IPC::Message::HIGH_PRIORITY' + + func.addstmt( + StmtReturn(ExprNew(Type('IPC::Message'), + args=[ routingId, + ExprVar(msgid), + ExprVar(nestedEnum), + ExprVar(prioEnum), + compression, + ExprLiteral.String(prettyName) ]))) + + return func + +##-------------------------------------------------- + +class _ComputeTypeDeps(TypeVisitor): + '''Pass that gathers the C++ types that a particular IPDL type +(recursively) depends on. There are two kinds of dependencies: (i) +types that need forward declaration; (ii) types that need a |using| +stmt. Some types generate both kinds.''' + + def __init__(self, fortype, unqualifiedTypedefs=False): + ipdl.type.TypeVisitor.__init__(self) + self.usingTypedefs = [ ] + self.forwardDeclStmts = [ ] + self.fortype = fortype + self.unqualifiedTypedefs = unqualifiedTypedefs + + def maybeTypedef(self, fqname, name): + if fqname != name or self.unqualifiedTypedefs: + self.usingTypedefs.append(Typedef(Type(fqname), name)) + + def 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([ _logicError("not reached"), StmtBreak() ])) + maybedtor.addstmts([ + ifnone, + ifnochange, + dtorswitch, + StmtReturn.TRUE + ]) + cls.addstmts([ maybedtor, Whitespace.NL ]) + + # add helper methods that ensure the discunion has a + # valid type + sanity = MethodDefn(MethodDecl( + assertsanityvar.name, ret=Type.VOID, const=1, force_inline=1)) + sanity.addstmts([ + _abortIfFalse(ExprBinary(tnonevar, '<=', mtypevar), + 'invalid type tag'), + _abortIfFalse(ExprBinary(mtypevar, '<=', tlastvar), + 'invalid type tag') ]) + cls.addstmt(sanity) + + atypevar = ExprVar('aType') + sanity2 = MethodDefn( + MethodDecl(assertsanityvar.name, + params=[ Decl(typetype, atypevar.name) ], + ret=Type.VOID, + const=1, force_inline=1)) + sanity2.addstmts([ + StmtExpr(ExprCall(assertsanityvar)), + _abortIfFalse(ExprBinary(mtypevar, '==', atypevar), + 'unexpected type tag') ]) + cls.addstmts([ sanity2, Whitespace.NL ]) + + ## ---- begin public methods ----- + + # Union() default ctor + cls.addstmts([ + Label.PUBLIC, + ConstructorDefn( + ConstructorDecl(ud.name, force_inline=1), + memberinits=[ ExprMemberInit(mtypevar, [ tnonevar ]) ]), + Whitespace.NL + ]) + + # Union(const T&) copy ctors + othervar = ExprVar('aOther') + for c in ud.components: + copyctor = ConstructorDefn(ConstructorDecl( + ud.name, params=[ Decl(c.inType(), othervar.name) ])) + copyctor.addstmts([ + StmtExpr(c.callCtor(othervar)), + StmtExpr(ExprAssn(mtypevar, c.enumvar())) ]) + cls.addstmts([ copyctor, Whitespace.NL ]) + + # Union(const Union&) copy ctor + copyctor = ConstructorDefn(ConstructorDecl( + ud.name, params=[ Decl(inClsType, othervar.name) ])) + othertype = ud.callType(othervar) + copyswitch = StmtSwitch(othertype) + for c in ud.components: + copyswitch.addcase( + CaseLabel(c.enum()), + StmtBlock([ + StmtExpr(c.callCtor( + ExprCall(ExprSelect(othervar, + '.', c.getConstTypeName())))), + StmtBreak() + ])) + copyswitch.addcase(CaseLabel(tnonevar.name), + StmtBlock([ StmtBreak() ])) + copyswitch.addcase( + DefaultLabel(), + StmtBlock([ _logicError('unreached'), StmtReturn() ])) + copyctor.addstmts([ + StmtExpr(callAssertSanity(uvar=othervar)), + copyswitch, + StmtExpr(ExprAssn(mtypevar, othertype)) + ]) + cls.addstmts([ copyctor, Whitespace.NL ]) + + # ~Union() + dtor = DestructorDefn(DestructorDecl(ud.name)) + # The void cast prevents Coverity from complaining about missing return + # value checks. + dtor.addstmt(StmtExpr(ExprCast(callMaybeDestroy(tnonevar), Type.VOID, + static=1))) + cls.addstmts([ dtor, Whitespace.NL ]) + + # type() + typemeth = MethodDefn(MethodDecl('type', ret=typetype, + const=1, force_inline=1)) + typemeth.addstmt(StmtReturn(mtypevar)) + cls.addstmts([ typemeth, Whitespace.NL ]) + + # Union& operator=(const T&) methods + rhsvar = ExprVar('aRhs') + for c in ud.components: + opeq = MethodDefn(MethodDecl( + 'operator=', + params=[ Decl(c.inType(), rhsvar.name) ], + ret=refClsType)) + opeq.addstmts([ + # might need to placement-delete old value first + maybeReconstruct(c, c.enumvar()), + StmtExpr(c.callOperatorEq(rhsvar)), + StmtExpr(ExprAssn(mtypevar, c.enumvar())), + StmtReturn(ExprDeref(ExprVar.THIS)) + ]) + cls.addstmts([ opeq, Whitespace.NL ]) + + # Union& operator=(const Union&) + opeq = MethodDefn(MethodDecl( + 'operator=', + params=[ Decl(inClsType, rhsvar.name) ], + ret=refClsType)) + rhstypevar = ExprVar('t') + opeqswitch = StmtSwitch(rhstypevar) + for c in ud.components: + case = StmtBlock() + case.addstmts([ + maybeReconstruct(c, rhstypevar), + StmtExpr(c.callOperatorEq( + ExprCall(ExprSelect(rhsvar, '.', c.getConstTypeName())))), + StmtBreak() + ]) + opeqswitch.addcase(CaseLabel(c.enum()), case) + opeqswitch.addcase( + CaseLabel(tnonevar.name), + # The void cast prevents Coverity from complaining about missing return + # value checks. + StmtBlock([ StmtExpr(ExprCast(callMaybeDestroy(rhstypevar), Type.VOID, + static=1)), + StmtBreak() ]) + ) + opeqswitch.addcase( + DefaultLabel(), + StmtBlock([ _logicError('unreached'), StmtBreak() ])) + opeq.addstmts([ + StmtExpr(callAssertSanity(uvar=rhsvar)), + StmtDecl(Decl(typetype, rhstypevar.name), init=ud.callType(rhsvar)), + opeqswitch, + StmtExpr(ExprAssn(mtypevar, rhstypevar)), + StmtReturn(ExprDeref(ExprVar.THIS)) + ]) + cls.addstmts([ opeq, Whitespace.NL ]) + + # bool operator==(const T&) + for c in ud.components: + opeqeq = MethodDefn(MethodDecl( + 'operator==', + params=[ Decl(c.inType(), rhsvar.name) ], + ret=Type.BOOL, + const=1)) + opeqeq.addstmt(StmtReturn(ExprBinary( + ExprCall(ExprVar(c.getTypeName())), '==', rhsvar))) + cls.addstmts([ opeqeq, Whitespace.NL ]) + + # bool operator==(const Union&) + opeqeq = MethodDefn(MethodDecl( + 'operator==', + params=[ Decl(inClsType, rhsvar.name) ], + ret=Type.BOOL, + const=1)) + iftypesmismatch = StmtIf(ExprBinary(ud.callType(), '!=', + ud.callType(rhsvar))) + iftypesmismatch.addifstmt(StmtReturn.FALSE) + opeqeq.addstmts([ iftypesmismatch, Whitespace.NL ]) + + opeqeqswitch = StmtSwitch(ud.callType()) + for c in ud.components: + case = StmtBlock() + case.addstmt(StmtReturn(ExprBinary( + ExprCall(ExprVar(c.getTypeName())), '==', + ExprCall(ExprSelect(rhsvar, '.', c.getTypeName()))))) + opeqeqswitch.addcase(CaseLabel(c.enum()), case) + opeqeqswitch.addcase( + DefaultLabel(), + StmtBlock([ _logicError('unreached'), + StmtReturn.FALSE ])) + opeqeq.addstmt(opeqeqswitch) + + cls.addstmts([ opeqeq, Whitespace.NL ]) + + # accessors for each type: operator T&, operator const T&, + # T& get(), const T& get() + for c in ud.components: + getValueVar = ExprVar(c.getTypeName()) + getConstValueVar = ExprVar(c.getConstTypeName()) + + getvalue = MethodDefn(MethodDecl(getValueVar.name, + ret=c.refType(), + force_inline=1)) + getvalue.addstmts([ + StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())), + StmtReturn(ExprDeref(c.callGetPtr())) + ]) + + getconstvalue = MethodDefn(MethodDecl( + getConstValueVar.name, ret=c.constRefType(), + const=1, force_inline=1)) + getconstvalue.addstmts([ + StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())), + StmtReturn(c.getConstValue()) + ]) + + readvalue = MethodDefn(MethodDecl( + 'get', ret=Type.VOID, const=1, + params=[Decl(c.ptrToType(), 'aOutValue')])) + readvalue.addstmts([ + StmtExpr(ExprAssn(ExprDeref(ExprVar('aOutValue')), + ExprCall(getConstValueVar))) + ]) + + optype = MethodDefn(MethodDecl('', typeop=c.refType(), force_inline=1)) + optype.addstmt(StmtReturn(ExprCall(getValueVar))) + opconsttype = MethodDefn(MethodDecl( + '', const=1, typeop=c.constRefType(), force_inline=1)) + opconsttype.addstmt(StmtReturn(ExprCall(getConstValueVar))) + + cls.addstmts([ getvalue, getconstvalue, readvalue, + optype, opconsttype, + Whitespace.NL ]) + + # private vars + cls.addstmts([ + Label.PRIVATE, + StmtDecl(Decl(valuetype, mvaluevar.name)), + StmtDecl(Decl(typetype, mtypevar.name)) + ]) + + return forwarddeclstmts, cls + +##----------------------------------------------------------------------------- + +class _FindFriends(ipdl.ast.Visitor): + def __init__(self): + self.mytype = None # ProtocolType + self.vtype = None # ProtocolType + self.friends = set() # set<ProtocolType> + + def findFriends(self, ptype): + self.mytype = ptype + for toplvl in ptype.toplevels(): + self.walkDownTheProtocolTree(toplvl); + return self.friends + + # TODO could make this into a _iterProtocolTreeHelper ... + def walkDownTheProtocolTree(self, ptype): + if ptype != self.mytype: + # don't want to |friend| ourself! + self.visit(ptype) + for mtype in ptype.manages: + if mtype is not ptype: + self.walkDownTheProtocolTree(mtype) + + def visit(self, ptype): + # |vtype| is the type currently being visited + savedptype = self.vtype + self.vtype = ptype + ptype._ast.accept(self) + self.vtype = savedptype + + def visitMessageDecl(self, md): + for it in self.iterActorParams(md): + if it.protocol == self.mytype: + self.friends.add(self.vtype) + + def iterActorParams(self, md): + for param in md.inParams: + for actor in ipdl.type.iteractortypes(param.type): + yield actor + for ret in md.outParams: + for actor in ipdl.type.iteractortypes(ret.type): + yield actor + + +class _GenerateProtocolActorCode(ipdl.ast.Visitor): + def __init__(self, myside): + self.side = myside # "parent" or "child" + self.prettyside = myside.title() + self.clsname = None + self.protocol = None + self.hdrfile = None + self.cppfile = None + self.ns = None + self.cls = None + self.includedActorTypedefs = [ ] + self.protocolCxxIncludes = [ ] + self.actorForwardDecls = [ ] + self.usingDecls = [ ] + self.externalIncludes = set() + self.nonForwardDeclaredHeaders = set() + + def lower(self, tu, clsname, cxxHeaderFile, cxxFile): + self.clsname = clsname + self.hdrfile = cxxHeaderFile + self.cppfile = cxxFile + tu.accept(self) + + def standardTypedefs(self): + return [ + Typedef(Type('mozilla::ipc::IProtocol'), 'ProtocolBase'), + Typedef(Type('IPC::Message'), 'Message'), + Typedef(Type(self.protocol.channelName()), 'Channel'), + Typedef(Type('mozilla::ipc::IProtocol'), 'ChannelListener'), + Typedef(Type('base::ProcessHandle'), 'ProcessHandle'), + Typedef(Type('mozilla::ipc::MessageChannel'), 'MessageChannel'), + Typedef(Type('mozilla::ipc::SharedMemory'), 'SharedMemory'), + Typedef(Type('mozilla::ipc::Trigger'), 'Trigger'), + ] + + + 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 ]) + + inherits = [] + if ptype.isToplevel(): + inherits.append(Inherit(p.openedProtocolInterfaceType(), + viz='public')) + else: + inherits.append(Inherit(p.managerInterfaceType(), viz='public')) + + if ptype.isToplevel() and self.side is 'parent': + self.hdrfile.addthings([ + _makeForwardDeclForQClass('nsIFile', []), + Whitespace.NL + ]) + + self.cls = Class( + self.clsname, + inherits=inherits, + 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)) + side = ExprVar('mozilla::ipc::' + self.side.title() + 'Side') + if ptype.isToplevel(): + ctor.memberinits = [ + ExprMemberInit(ExprVar('mozilla::ipc::IToplevelProtocol'), + [_protocolId(ptype), side]), + ExprMemberInit(p.channelVar(), [ + ExprCall(ExprVar('ALLOW_THIS_IN_INITIALIZER_LIST'), + [ ExprVar.THIS ]) ]), + ExprMemberInit(p.stateVar(), + [ p.startState() ]) + ] + else: + ctor.memberinits = [ + ExprMemberInit(ExprVar('mozilla::ipc::IProtocol'), [side]), + ExprMemberInit(p.stateVar(), + [ p.deadState() ]) + ] + + ctor.addstmt(StmtExpr(ExprCall(ExprVar('MOZ_COUNT_CTOR'), + [ ExprVar(self.clsname) ]))) + self.cls.addstmts([ ctor, Whitespace.NL ]) + + # ~Actor() + dtor = DestructorDefn( + DestructorDecl(self.clsname, virtual=True)) + dtor.addstmt(StmtExpr(ExprCall(ExprVar('MOZ_COUNT_DTOR'), + [ ExprVar(self.clsname) ]))) + + self.cls.addstmts([ dtor, Whitespace.NL ]) + + if not ptype.isToplevel(): + if 1 == len(p.managers): + ## manager() const + managertype = p.managerActorType(self.side, ptr=1) + managermeth = MethodDefn(MethodDecl( + 'Manager', ret=managertype, const=1)) + managerexp = ExprCall(ExprVar('IProtocol::Manager'), args=[]) + managermeth.addstmt(StmtReturn( + ExprCast(managerexp, managertype, static=1))) + + self.cls.addstmts([ managermeth, Whitespace.NL ]) + + def actorFromIter(itervar): + return ExprCall(ExprSelect(ExprCall(ExprSelect(itervar, '.', 'Get')), + '->', 'GetKey')) + def forLoopOverHashtable(hashtable, itervar, const=False): + return StmtFor( + init=Param(Type.AUTO, itervar.name, + ExprCall(ExprSelect(hashtable, '.', 'ConstIter' if const else 'Iter'))), + cond=ExprNot(ExprCall(ExprSelect(itervar, '.', 'Done'))), + update=ExprCall(ExprSelect(itervar, '.', 'Next'))) + + ## Managed[T](Array& inout) const + ## const Array<T>& Managed() const + for managed in ptype.manages: + arrvar = ExprVar('aArr') + meth = MethodDefn(MethodDecl( + p.managedMethod(managed, self.side).name, + params=[ Decl(_cxxArrayType(p.managedCxxType(managed, self.side), ref=1), + arrvar.name) ], + const=1)) + meth.addstmt(StmtExpr( + ExprCall(ExprSelect(p.managedVar(managed, self.side), + '.', 'ToArray'), + args=[ arrvar ]))) + + refmeth = MethodDefn(MethodDecl( + p.managedMethod(managed, self.side).name, + params=[ ], + ret=p.managedVarType(managed, self.side, const=1, ref=1), + const=1)) + refmeth.addstmt(StmtReturn(p.managedVar(managed, self.side))) + + self.cls.addstmts([ meth, refmeth, Whitespace.NL ]) + + 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') + deallocselfvar = ExprVar('Dealloc' + _actorName(ptype.name(), self.side)) + + # int32_t GetProtocolTypeId() { return PFoo; } + gettypetag = MethodDefn( + MethodDecl('GetProtocolTypeId', ret=_actorTypeTagType())) + gettypetag.addstmt(StmtReturn(_protocolId(ptype))) + self.cls.addstmts([ gettypetag, Whitespace.NL ]) + + if ptype.isToplevel(): + # OnChannelClose() + onclose = MethodDefn(MethodDecl('OnChannelClose')) + onclose.addstmts([ + StmtExpr(ExprCall(destroysubtreevar, + args=[ _DestroyReason.NormalShutdown ])), + StmtExpr(ExprCall(deallocsubtreevar)), + StmtExpr(ExprCall(deallocshmemvar)), + StmtExpr(ExprCall(deallocselfvar)) + ]) + self.cls.addstmts([ onclose, Whitespace.NL ]) + + # OnChannelError() + onerror = MethodDefn(MethodDecl('OnChannelError')) + onerror.addstmts([ + StmtExpr(ExprCall(destroysubtreevar, + args=[ _DestroyReason.AbnormalShutdown ])), + StmtExpr(ExprCall(deallocsubtreevar)), + StmtExpr(ExprCall(deallocshmemvar)), + StmtExpr(ExprCall(deallocselfvar)) + ]) + self.cls.addstmts([ onerror, Whitespace.NL ]) + + if (ptype.isToplevel() and ptype.isInterrupt()): + + processnative = MethodDefn( + MethodDecl('ProcessNativeEventsInInterruptCall', ret=Type.VOID)) + + processnative.addstmts([ + CppDirective('ifdef', 'OS_WIN'), + StmtExpr(ExprCall( + ExprSelect(p.channelVar(), '.', + 'ProcessNativeEventsInInterruptCall'))), + CppDirective('else'), + _fatalError('This method is Windows-only'), + CppDirective('endif'), + ]) + + self.cls.addstmts([ processnative, Whitespace.NL ]) + + ## private methods + self.cls.addstmt(Label.PRIVATE) + + ## ProtocolName() + actorname = _actorName(p.name, self.side) + protocolname = MethodDefn(MethodDecl( + 'ProtocolName', params=[], + const=1, virtual=1, ret=Type('char', const=1, ptr=1))) + protocolname.addstmts([ + StmtReturn(ExprLiteral.String(actorname)) + ]) + self.cls.addstmts([ protocolname, Whitespace.NL ]) + + ## DestroySubtree(bool normal) + whyvar = ExprVar('why') + subtreewhyvar = ExprVar('subtreewhy') + kidsvar = ExprVar('kids') + ivar = ExprVar('i') + itervar = ExprVar('iter') + ithkid = ExprIndex(kidsvar, ivar) + + destroysubtree = MethodDefn(MethodDecl( + destroysubtreevar.name, + params=[ Decl(_DestroyReason.Type(), whyvar.name) ])) + + if ptype.isManaged(): + destroysubtree.addstmt( + Whitespace('// Unregister from our manager.\n', indent=1)) + destroysubtree.addstmts(self.unregisterActor()) + destroysubtree.addstmt(Whitespace.NL) + + if ptype.isManager(): + # only declare this for managers to avoid unused var warnings + destroysubtree.addstmts([ + StmtDecl( + Decl(_DestroyReason.Type(), subtreewhyvar.name), + init=ExprConditional( + ExprBinary( + ExprBinary(whyvar, '==', + _DestroyReason.Deletion), + '||', + ExprBinary(whyvar, '==', + _DestroyReason.FailedConstructor)), + _DestroyReason.AncestorDeletion, whyvar)), + Whitespace.NL + ]) + + for managed in ptype.manages: + managedVar = p.managedVar(managed, self.side) + lenvar = ExprVar('len') + kidvar = ExprVar('kid') + + foreachdestroy = StmtRangedFor(kidvar, kidsvar) + + foreachdestroy.addstmt( + Whitespace('// Guarding against a child removing a sibling from the list during the iteration.\n', indent=1)) + ifhas = StmtIf(_callHasManagedActor(managedVar, kidvar)) + ifhas.addifstmt(StmtExpr(ExprCall( + ExprSelect(kidvar, '->', destroysubtreevar.name), + args=[ subtreewhyvar ]))) + foreachdestroy.addstmt(ifhas) + + block = StmtBlock() + block.addstmts([ + Whitespace( + '// Recursively shutting down %s kids\n'% (managed.name()), + indent=1), + StmtDecl( + Decl(_cxxArrayType(p.managedCxxType(managed, self.side)), kidsvar.name)), + Whitespace( + '// Accumulate kids into a stable structure to iterate over\n', + indent=1), + StmtExpr(ExprCall(p.managedMethod(managed, self.side), + args=[ kidsvar ])), + foreachdestroy, + ]) + destroysubtree.addstmt(block) + + if len(ptype.manages): + destroysubtree.addstmt(Whitespace.NL) + destroysubtree.addstmts([ Whitespace('// Finally, destroy "us".\n', + indent=1), + StmtExpr(ExprCall(_destroyMethod(), + args=[ whyvar ])) + ]) + + self.cls.addstmts([ destroysubtree, Whitespace.NL ]) + + ## DeallocSubtree() + deallocsubtree = MethodDefn(MethodDecl(deallocsubtreevar.name)) + for managed in ptype.manages: + managedVar = p.managedVar(managed, self.side) + + foreachrecurse = forLoopOverHashtable(managedVar, itervar) + foreachrecurse.addstmt(StmtExpr(ExprCall( + ExprSelect(actorFromIter(itervar), '->', deallocsubtreevar.name)))) + + foreachdealloc = forLoopOverHashtable(managedVar, itervar) + foreachdealloc.addstmts([ + StmtExpr(ExprCall(_deallocMethod(managed, self.side), + args=[ actorFromIter(itervar) ])) + ]) + + block = StmtBlock() + block.addstmts([ + Whitespace( + '// Recursively deleting %s kids\n'% (managed.name()), + indent=1), + foreachrecurse, + Whitespace.NL, + foreachdealloc, + StmtExpr(_callClearManagedActors(managedVar)), + + ]) + deallocsubtree.addstmt(block) + # don't delete outselves: either the manager will do it, or + # we're toplevel + self.cls.addstmts([ deallocsubtree, Whitespace.NL ]) + + if ptype.isToplevel(): + deallocself = MethodDefn(MethodDecl(deallocselfvar.name, virtual=1)) + self.cls.addstmts([ deallocself, Whitespace.NL ]) + + self.implementPickling() + + ## private members + if ptype.isToplevel(): + self.cls.addstmt(StmtDecl(Decl(p.channelType(), 'mChannel'))) + + self.cls.addstmt(StmtDecl(Decl(Type('State'), p.stateVar().name))) + + for managed in ptype.manages: + self.cls.addstmts([ + StmtDecl(Decl( + p.managedVarType(managed, self.side), + p.managedVar(managed, self.side).name)) ]) + + def implementManagerIface(self): + p = self.protocol + routedvar = ExprVar('aRouted') + idvar = ExprVar('aId') + shmemvar = ExprVar('shmem') + rawvar = ExprVar('segment') + sizevar = ExprVar('aSize') + typevar = ExprVar('aType') + unsafevar = ExprVar('aUnsafe') + protocolbase = Type('ProtocolBase', ptr=1) + sourcevar = ExprVar('aSource') + ivar = ExprVar('i') + kidsvar = ExprVar('kids') + ithkid = ExprIndex(kidsvar, ivar) + + methods = [] + + if p.decl.type.isToplevel(): + getchannel = MethodDefn(MethodDecl( + p.getChannelMethod().name, + ret=Type('MessageChannel', ptr=1), + virtual=1)) + getchannel.addstmt(StmtReturn(ExprAddrOf(p.channelVar()))) + + getchannelconst = MethodDefn(MethodDecl( + p.getChannelMethod().name, + ret=Type('MessageChannel', ptr=1, const=1), + virtual=1, const=1)) + getchannelconst.addstmt(StmtReturn(ExprAddrOf(p.channelVar()))) + + methods += [ getchannel, + getchannelconst ] + + if p.decl.type.isToplevel(): + tmpvar = ExprVar('tmp') + + # "private" message that passes shmem mappings from one process + # to the other + if p.subtreeUsesShmem(): + self.asyncSwitch.addcase( + CaseLabel('SHMEM_CREATED_MESSAGE_TYPE'), + self.genShmemCreatedHandler()) + self.asyncSwitch.addcase( + CaseLabel('SHMEM_DESTROYED_MESSAGE_TYPE'), + self.genShmemDestroyedHandler()) + else: + abort = StmtBlock() + abort.addstmts([ + _fatalError('this protocol tree does not use shmem'), + StmtReturn(_Result.NotKnown) + ]) + self.asyncSwitch.addcase( + CaseLabel('SHMEM_CREATED_MESSAGE_TYPE'), abort) + self.asyncSwitch.addcase( + CaseLabel('SHMEM_DESTROYED_MESSAGE_TYPE'), abort) + + othervar = ExprVar('other') + managertype = Type(_actorName(p.name, self.side), ptr=1) + + # Keep track of types created with an INOUT ctor. We need to call + # Register() or RegisterID() for them depending on the side the managee + # is created. + inoutCtorTypes = [] + for msg in p.messageDecls: + msgtype = msg.decl.type + if msgtype.isCtor() and msgtype.isInout(): + inoutCtorTypes.append(msgtype.constructedType()) + + # all protocols share the "same" RemoveManagee() implementation + pvar = ExprVar('aProtocolId') + listenervar = ExprVar('aListener') + removemanagee = MethodDefn(MethodDecl( + p.removeManageeMethod().name, + params=[ Decl(_protocolIdType(), pvar.name), + Decl(protocolbase, listenervar.name) ], + virtual=1)) + + if not len(p.managesStmts): + removemanagee.addstmts([ _fatalError('unreached'), StmtReturn() ]) + else: + switchontype = StmtSwitch(pvar) + for managee in p.managesStmts: + case = StmtBlock() + actorvar = ExprVar('actor') + manageeipdltype = managee.decl.type + manageecxxtype = _cxxBareType(ipdl.type.ActorType(manageeipdltype), + self.side) + manageearray = p.managedVar(manageeipdltype, self.side) + containervar = ExprVar('container') + + case.addstmts([ + StmtDecl(Decl(manageecxxtype, actorvar.name), + ExprCast(listenervar, manageecxxtype, static=1)), + # Use a temporary variable here so all the assertion expressions + # in the _abortIfFalse call below are textually identical; the + # linker can then merge the strings from the assertion macro(s). + StmtDecl(Decl(Type('auto', ref=1), containervar.name), + manageearray), + _abortIfFalse( + _callHasManagedActor(containervar, actorvar), + "actor not managed by this!"), + Whitespace.NL, + StmtExpr(_callRemoveManagedActor(containervar, actorvar)), + StmtExpr(ExprCall(_deallocMethod(manageeipdltype, self.side), + args=[ actorvar ])), + StmtReturn() + ]) + switchontype.addcase(CaseLabel(_protocolId(manageeipdltype).name), + case) + default = StmtBlock() + default.addstmts([ _fatalError('unreached'), StmtReturn() ]) + switchontype.addcase(DefaultLabel(), default) + removemanagee.addstmt(switchontype) + + return methods + [removemanagee, Whitespace.NL] + + def genShmemCreatedHandler(self): + p = self.protocol + assert p.decl.type.isToplevel() + + case = StmtBlock() + + ifstmt = StmtIf(ExprNot(ExprCall(ExprVar('ShmemCreated'), args=[self.msgvar]))) + case.addstmts([ + ifstmt, + StmtReturn(_Result.Processed) + ]) + ifstmt.addifstmt(StmtReturn(_Result.PayloadError)) + + return case + + def genShmemDestroyedHandler(self): + p = self.protocol + assert p.decl.type.isToplevel() + + case = StmtBlock() + + ifstmt = StmtIf(ExprNot(ExprCall(ExprVar('ShmemDestroyed'), args=[self.msgvar]))) + case.addstmts([ + ifstmt, + StmtReturn(_Result.Processed) + ]) + ifstmt.addifstmt(StmtReturn(_Result.PayloadError)) + + return case + + + 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=[ _uniqueptrGet(tvar), pidvar ])))) + iffailalloc.addifstmt(StmtReturn(_Result.ProcessingError)) + + settrans = StmtExpr(ExprCall( + ExprSelect(pvar, '->', 'IToplevelProtocol::SetTransport'), + args=[ExprMove(tvar)])) + + case.addstmts([ + StmtDecl(Decl(_uniqueptr(Type('Transport')), tvar.name)), + StmtDecl(Decl(Type(_actorName(actor.ptype.name(), actor.side), + ptr=1), pvar.name)), + iffailopen, + iffailalloc, + settrans, + 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([ _fatalError('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) + # XXX the writer code can treat the actor as logically const; many + # other places that call _cxxConstRefType cannot treat the actor + # as logically const, particularly callers that can leak out to + # Gecko directly. + intype.const = 1 + cxxtype = _cxxBareType(actortype, self.side) + outtype = _cxxPtrToType(actortype, self.side) + + ## Write([const] PFoo* var) + write = MethodDefn(self.writeMethodDecl(intype, var)) + nullablevar = ExprVar('nullable__') + write.decl.params.append(Decl(Type.BOOL, nullablevar.name)) + # id_t id; + # if (!var) + # if(!nullable) + # abort() + # id = NULL_ID + write.addstmt(StmtDecl(Decl(_actorIdType(), idvar.name))) + + ifnull = StmtIf(ExprNot(var)) + ifnotnullable = StmtIf(ExprNot(nullablevar)) + ifnotnullable.addifstmt( + _fatalError("NULL actor value passed to non-nullable param")) + ifnull.addifstmt(ifnotnullable) + ifnull.addifstmt(StmtExpr(ExprAssn(idvar, _NULL_ACTOR_ID))) + # else + # id = var->mId + # if (id == FREED_ID) + # abort() + # Write(msg, id) + ifnull.addelsestmt(StmtExpr(ExprAssn(idvar, _actorId(var)))) + iffreed = StmtIf(ExprBinary(_FREED_ACTOR_ID, '==', idvar)) + # this is always a hard-abort, because it means that some C++ + # code has a live pointer to a freed actor, so we're playing + # Russian roulette with invalid memory + iffreed.addifstmt(_fatalError("actor has been |delete|d")) + ifnull.addelsestmt(iffreed) + + write.addstmts([ + ifnull, + Whitespace.NL, + StmtExpr(self.write(None, idvar, self.msgvar)) + ]) + + ## Read(PFoo** var) + read = MethodDefn(self.readMethodDecl(outtype, var)) + read.decl.params.append(Decl(Type.BOOL, nullablevar.name)) + + actorvar = ExprVar('actor') + read.addstmts([ + StmtDecl(Decl(Type('Maybe', T=Type('mozilla::ipc::IProtocol', ptr=1)), actorvar.name), + init=ExprCall(ExprVar('ReadActor'), + args=[ self.msgvar, self.itervar, nullablevar, + ExprLiteral.String(actortype.name()), + _protocolId(actortype) ])), + ]) + + # if (actor.isNothing()) + # return false + # + # Reading the actor failed in some way, and the appropriate error was raised. + ifnothing = StmtIf(ExprCall(ExprSelect(actorvar, '.', 'isNothing'))) + ifnothing.addifstmts([ + StmtReturn.FALSE, + ]) + + read.addstmts([ ifnothing, Whitespace.NL ]) + + read.addstmts([ + StmtExpr(ExprAssn(ExprDeref(var), + ExprCast(ExprCall(ExprSelect(actorvar, '.', 'value')), cxxtype, static=1))), + StmtReturn.TRUE + ]) + + self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ]) + + + def implementSpecialArrayPickling(self, arraytype): + var = self.var + msgvar = self.msgvar + itervar = self.itervar + lenvar = ExprVar('length') + ivar = ExprVar('i') + eltipdltype = arraytype.basetype + intype = _cxxConstRefType(arraytype, self.side) + outtype = _cxxPtrToType(arraytype, self.side) + + # We access elements directly in Read and Write to avoid array bounds + # checking. + directtype = _cxxBareType(arraytype.basetype, self.side) + if directtype.ptr: + typeinit = { 'ptrptr': 1 } + else: + typeinit = { 'ptr': 1 } + directtype = Type(directtype.name, **typeinit) + elemsvar = ExprVar('elems') + elemvar = ExprVar('elem') + + write = MethodDefn(self.writeMethodDecl(intype, var)) + forwrite = StmtRangedFor(elemvar, var) + forwrite.addstmt( + self.checkedWrite(eltipdltype, elemvar, msgvar, + sentinelKey=arraytype.name())) + write.addstmts([ + StmtDecl(Decl(Type.UINT32, lenvar.name), + init=_callCxxArrayLength(var)), + self.checkedWrite(None, lenvar, msgvar, sentinelKey=('length', arraytype.name())), + Whitespace.NL, + forwrite + ]) + + read = MethodDefn(self.readMethodDecl(outtype, var)) + favar = ExprVar('fa') + forread = StmtFor(init=ExprAssn(Decl(Type.UINT32, ivar.name), + ExprLiteral.ZERO), + cond=ExprBinary(ivar, '<', lenvar), + update=ExprPrefixUnop(ivar, '++')) + forread.addstmt( + self.checkedRead(eltipdltype, ExprAddrOf(ExprIndex(elemsvar, ivar)), + msgvar, itervar, errfnRead, + '\'' + eltipdltype.name() + '[i]\'', + sentinelKey=arraytype.name())) + appendstmt = StmtDecl(Decl(directtype, elemsvar.name), + init=ExprCall(ExprSelect(favar, '.', 'AppendElements'), + args=[ lenvar ])) + read.addstmts([ + StmtDecl(Decl(_cxxArrayType(_cxxBareType(arraytype.basetype, self.side)), favar.name)), + StmtDecl(Decl(Type.UINT32, lenvar.name)), + self.checkedRead(None, ExprAddrOf(lenvar), + msgvar, itervar, errfnArrayLength, + [ arraytype.name() ], + sentinelKey=('length', arraytype.name())), + Whitespace.NL, + appendstmt, + forread, + StmtExpr(_callCxxSwapArrayElements(var, favar, '->')), + StmtReturn.TRUE + ]) + + self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ]) + + + def implementShmemPickling(self, shmemtype): + msgvar = self.msgvar + itervar = self.itervar + var = self.var + tmpvar = ExprVar('tmp') + idvar = ExprVar('shmemid') + rawvar = ExprVar('rawmem') + baretype = _cxxBareType(shmemtype, self.side) + intype = _cxxConstRefType(shmemtype, self.side) + outtype = _cxxPtrToType(shmemtype, self.side) + + write = MethodDefn(self.writeMethodDecl(intype, var)) + write.addstmts([ + StmtExpr(ExprCall(ExprVar('IPC::WriteParam'), + args=[ msgvar, var ])), + StmtExpr(_shmemRevokeRights(var)), + StmtExpr(_shmemForget(var)) + ]) + + read = MethodDefn(self.readMethodDecl(outtype, var)) + ifread = StmtIf(ExprNot(ExprCall(ExprVar('IPC::ReadParam'), + args=[ msgvar, itervar, + ExprAddrOf(tmpvar) ]))) + ifread.addifstmt(StmtReturn.FALSE) + + iffound = StmtIf(rawvar) + iffound.addifstmt(StmtExpr(ExprAssn( + ExprDeref(var), _shmemCtor(rawvar, idvar)))) + iffound.addifstmt(StmtReturn.TRUE) + + read.addstmts([ + StmtDecl(Decl(_shmemType(), tmpvar.name)), + ifread, + Whitespace.NL, + StmtDecl(Decl(_shmemIdType(), idvar.name), + init=_shmemId(tmpvar)), + StmtDecl(Decl(_rawShmemType(ptr=1), rawvar.name), + init=_lookupShmem(idvar)), + iffound, + # This is ugly: we failed to look the shmem up, most likely because + # we failed to map it the first time it was deserialized. we create + # an empty shmem and let the user of the shmem deal with it. + # if we returned false here we would crash. + StmtExpr(ExprAssn(ExprDeref(var), ExprCall(ExprVar('Shmem'), args=[]) )), + StmtReturn.TRUE + ]) + + self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ]) + + def implementFDPickling(self, fdtype): + msgvar = self.msgvar + itervar = self.itervar + var = self.var + tmpvar = ExprVar('fd') + picklevar = ExprVar('pfd') + intype = _cxxConstRefType(fdtype, self.side) + outtype = _cxxPtrToType(fdtype, self.side) + + def _fdType(): + return Type('FileDescriptor') + + def _fdPickleType(): + return Type('FileDescriptor::PickleType') + + def _fdBackstagePass(): + return ExprCall(ExprVar('FileDescriptor::IPDLPrivate')) + + write = MethodDefn(self.writeMethodDecl(intype, var)) + write.addstmts([ + StmtDecl(Decl(_fdPickleType(), picklevar.name), + init=ExprCall(ExprSelect(var, '.', 'ShareTo'), + args=[ _fdBackstagePass(), + self.protocol.callOtherPid() ])), + StmtExpr(ExprCall(ExprVar('IPC::WriteParam'), + args=[ msgvar, picklevar ])), + ]) + + read = MethodDefn(self.readMethodDecl(outtype, var)) + ifread = StmtIf(ExprNot(ExprCall(ExprVar('IPC::ReadParam'), + args=[ msgvar, itervar, + ExprAddrOf(picklevar) ]))) + ifread.addifstmt(StmtReturn.FALSE) + + ifnvalid = StmtIf(ExprNot(ExprCall(ExprSelect(tmpvar, '.', 'IsValid')))) + ifnvalid.addifstmt( + _protocolErrorBreakpoint('[' + + _actorName(self.protocol.name, self.side) + + '] Received an invalid file descriptor!')) + + read.addstmts([ + StmtDecl(Decl(_fdPickleType(), picklevar.name)), + ifread, + Whitespace.NL, + StmtDecl(Decl(_fdType(), tmpvar.name), + init=ExprCall(ExprVar('FileDescriptor'), + args=[ _fdBackstagePass(), picklevar ])), + ifnvalid, + Whitespace.NL, + StmtExpr(ExprAssn(ExprDeref(var), tmpvar)), + StmtReturn.TRUE + ]) + + self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ]) + + def implementStructPickling(self, structtype): + msgvar = self.msgvar + itervar = self.itervar + var = self.var + intype = _cxxConstRefType(structtype, self.side) + outtype = _cxxPtrToType(structtype, self.side) + sd = structtype._ast + + write = MethodDefn(self.writeMethodDecl(intype, var)) + read = MethodDefn(self.readMethodDecl(outtype, var)) + + def get(sel, f): + return ExprCall(f.getMethod(thisexpr=var, sel=sel)) + + for f in sd.fields: + desc = '\'' + f.getMethod().name + '\' (' + f.ipdltype.name() + \ + ') member of \'' + intype.name + '\'' + writefield = self.checkedWrite(f.ipdltype, get('.', f), msgvar, sentinelKey=f.basename) + readfield = self.checkedRead(f.ipdltype, + ExprAddrOf(get('->', f)), + msgvar, itervar, errfnRead, desc, sentinelKey=f.basename) + if f.special and f.side != self.side: + writefield = Whitespace( + "// skipping actor field that's meaningless on this side\n", indent=1) + readfield = Whitespace( + "// skipping actor field that's meaningless on this side\n", indent=1) + write.addstmt(writefield) + read.addstmt(readfield) + + read.addstmt(StmtReturn.TRUE) + + self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ]) + + + def implementUnionPickling(self, uniontype): + msgvar = self.msgvar + itervar = self.itervar + var = self.var + intype = _cxxConstRefType(uniontype, self.side) + outtype = _cxxPtrToType(uniontype, self.side) + ud = uniontype._ast + + typename = 'type__' + uniontdef = Typedef(_cxxBareType(uniontype, typename), typename) + + typevar = ExprVar('type') + writeswitch = StmtSwitch(ud.callType(var)) + readswitch = StmtSwitch(typevar) + + for c in ud.components: + ct = c.ipdltype + isactor = (ct.isIPDL() and ct.isActor()) + caselabel = CaseLabel(typename +'::'+ c.enum()) + origenum = c.enum() + + writecase = StmtBlock() + if c.special and c.side != self.side: + writecase.addstmt(_fatalError('wrong side!')) + else: + wexpr = ExprCall(ExprSelect(var, '.', c.getTypeName())) + writecase.addstmt(self.checkedWrite(ct, wexpr, msgvar, sentinelKey=c.enum())) + + writecase.addstmt(StmtReturn()) + writeswitch.addcase(caselabel, writecase) + + readcase = StmtBlock() + if c.special and c.side == self.side: + # the type comes across flipped from what the actor + # will be on this side; i.e. child->parent messages + # have type PFooChild when received on the parent side + # XXX: better error message + readcase.addstmt(StmtReturn.FALSE) + else: + if c.special: + c = c.other # see above + tmpvar = ExprVar('tmp') + ct = c.bareType() + readcase.addstmts([ + StmtDecl(Decl(ct, tmpvar.name), init=c.defaultValue()), + StmtExpr(ExprAssn(ExprDeref(var), tmpvar)), + self.checkedRead( + c.ipdltype, + ExprAddrOf(ExprCall(ExprSelect(var, '->', + c.getTypeName()))), + msgvar, itervar, errfnRead, 'Union type', sentinelKey=origenum), + StmtReturn(ExprLiteral.TRUE) + ]) + + readswitch.addcase(caselabel, readcase) + + unknowntype = 'unknown union type' + writeswitch.addcase(DefaultLabel(), + StmtBlock([ _fatalError(unknowntype), + StmtReturn() ])) + readswitch.addcase(DefaultLabel(), StmtBlock(errfnRead(unknowntype))) + + write = MethodDefn(self.writeMethodDecl(intype, var)) + write.addstmts([ + uniontdef, + self.checkedWrite( + None, ExprCall(Type.INT, args=[ ud.callType(var) ]), msgvar, + sentinelKey=uniontype.name()), + Whitespace.NL, + writeswitch + ]) + + read = MethodDefn(self.readMethodDecl(outtype, var)) + read.addstmts([ + uniontdef, + StmtDecl(Decl(Type.INT, typevar.name)), + self.checkedRead( + None, ExprAddrOf(typevar), msgvar, itervar, errfnUnionType, + [ uniontype.name() ], + sentinelKey=uniontype.name()), + Whitespace.NL, + readswitch, + ]) + + self.cls.addstmts([ write, Whitespace.NL, read, Whitespace.NL ]) + + + def writeMethodDecl(self, intype, var, template=None): + return MethodDecl( + 'Write', + params=[ Decl(intype, var.name), + Decl(Type('Message', ptr=1), self.msgvar.name) ], + T=template) + + def readMethodDecl(self, outtype, var, template=None): + return MethodDecl( + 'Read', + params=[ Decl(outtype, var.name), + Decl(Type('Message', ptr=1, const=1), + self.msgvar.name), + Decl(_iterType(ptr=1), self.itervar.name)], + warn_unused=not template, + T=template, + ret=Type.BOOL) + + def maybeAddNullabilityArg(self, ipdltype, call): + if ipdltype and ipdltype.isIPDL() and ipdltype.isActor(): + if ipdltype.nullable: + call.args.append(ExprLiteral.TRUE) + else: + call.args.append(ExprLiteral.FALSE) + return call + + def write(self, ipdltype, expr, to, this=None): + write = ExprVar('Write') + if this: write = ExprSelect(this, '->', write.name) + return self.maybeAddNullabilityArg(ipdltype, + ExprCall(write, args=[ expr, to ])) + + def read(self, ipdltype, expr, from_, iterexpr, this=None): + read = ExprVar('Read') + if this: read = ExprSelect(this, '->', read.name) + return self.maybeAddNullabilityArg( + ipdltype, ExprCall(read, args=[ expr, from_, iterexpr ])) + + def checkedWrite(self, ipdltype, expr, msgvar, sentinelKey, this=None): + assert sentinelKey + + write = StmtExpr(self.write(ipdltype, expr, msgvar, this)) + + sentinel = StmtExpr(ExprCall(ExprSelect(msgvar, '->', 'WriteSentinel'), + args=[ ExprLiteral.Int(hashfunc(sentinelKey)) ])) + block = Block() + block.addstmts([ + write, + Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n', indent=1), + sentinel ]) + return block + + + def visitMessageDecl(self, md): + isctor = md.decl.type.isCtor() + isdtor = md.decl.type.isDtor() + decltype = md.decl.type + sendmethod = None + helpermethod = None + recvlbl, recvcase = None, None + + def addRecvCase(lbl, case): + if decltype.isAsync(): + self.asyncSwitch.addcase(lbl, case) + elif decltype.isSync(): + self.syncSwitch.addcase(lbl, case) + elif decltype.isInterrupt(): + self.interruptSwitch.addcase(lbl, case) + else: assert 0 + + if self.sendsMessage(md): + isasync = decltype.isAsync() + + if isctor: + self.cls.addstmts([ self.genHelperCtor(md), Whitespace.NL ]) + + if isctor and isasync: + sendmethod, (recvlbl, recvcase) = self.genAsyncCtor(md) + elif isctor: + sendmethod = self.genBlockingCtorMethod(md) + elif isdtor and isasync: + sendmethod, (recvlbl, recvcase) = self.genAsyncDtor(md) + elif isdtor: + sendmethod = self.genBlockingDtorMethod(md) + elif isasync: + sendmethod = self.genAsyncSendMethod(md) + else: + sendmethod = self.genBlockingSendMethod(md) + + # XXX figure out what to do here + if isdtor and md.decl.type.constructedType().isToplevel(): + sendmethod = None + + if sendmethod is not None: + self.cls.addstmts([ sendmethod, Whitespace.NL ]) + if recvcase is not None: + addRecvCase(recvlbl, recvcase) + recvlbl, recvcase = None, None + + if self.receivesMessage(md): + if isctor: + recvlbl, recvcase = self.genCtorRecvCase(md) + elif isdtor: + recvlbl, recvcase = self.genDtorRecvCase(md) + else: + recvlbl, recvcase = self.genRecvCase(md) + + # XXX figure out what to do here + if isdtor and md.decl.type.constructedType().isToplevel(): + return + + addRecvCase(recvlbl, recvcase) + + + def genAsyncCtor(self, md): + actor = md.actorDecl() + method = MethodDefn(self.makeSendMethodDecl(md)) + method.addstmts(self.ctorPrologue(md) + [ Whitespace.NL ]) + + msgvar, stmts = self.makeMessage(md, errfnSendCtor) + sendok, sendstmts = self.sendAsync(md, msgvar) + method.addstmts( + stmts + + self.genVerifyMessage(md.decl.type.verify, md.params, + errfnSendCtor, ExprVar('msg__')) + + sendstmts + + self.failCtorIf(md, ExprNot(sendok)) + + [ StmtReturn(actor.var()) ]) + + lbl = CaseLabel(md.pqReplyId()) + case = StmtBlock() + case.addstmt(StmtReturn(_Result.Processed)) + # TODO not really sure what to do with async ctor "replies" yet. + # destroy actor if there was an error? tricky ... + + return method, (lbl, case) + + + def genBlockingCtorMethod(self, md): + actor = md.actorDecl() + method = MethodDefn(self.makeSendMethodDecl(md)) + method.addstmts(self.ctorPrologue(md) + [ Whitespace.NL ]) + + msgvar, stmts = self.makeMessage(md, errfnSendCtor) + + replyvar = self.replyvar + sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar) + method.addstmts( + stmts + + [ Whitespace.NL, + StmtDecl(Decl(Type('Message'), replyvar.name)) ] + + self.genVerifyMessage(md.decl.type.verify, md.params, + errfnSendCtor, ExprVar('msg__')) + + sendstmts + + self.failCtorIf(md, ExprNot(sendok))) + + def errfnCleanupCtor(msg): + return self.failCtorIf(md, ExprLiteral.TRUE) + stmts = self.deserializeReply( + md, ExprAddrOf(replyvar), self.side, errfnCleanupCtor) + 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(ExprCall(ExprSelect(actorvar, '->', 'SetId'), args=[idexpr])), + StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetManager'), args=[ExprVar.THIS])), + StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetIPCChannel'), + args=[self.protocol.callGetChannel()])), + StmtExpr(_callInsertManagedActor( + self.protocol.managedVar(md.decl.type.constructedType(), + self.side), + actorvar)), + StmtExpr(ExprAssn(_actorState(actorvar), + _startState(actorproto, fq=1))) + ] + + def failCtorIf(self, md, cond): + actorvar = md.actorDecl().var() + type = md.decl.type.constructedType() + failif = StmtIf(cond) + + if self.side=='child': + # in the child process this should not fail + failif.addifstmt(_fatalError('constructor for actor failed')) + else: + failif.addifstmts(self.destroyActor(md, actorvar, + why=_DestroyReason.FailedConstructor)) + + failif.addifstmt(StmtReturn(ExprLiteral.NULL)) + return [ failif ] + + def genHelperCtor(self, md): + helperdecl = self.makeSendMethodDecl(md) + helperdecl.params = helperdecl.params[1:] + helper = MethodDefn(helperdecl) + + callctor = self.callAllocActor(md, retsems='out', side=self.side) + helper.addstmt(StmtReturn(ExprCall( + ExprVar(helperdecl.name), args=[ callctor ] + callctor.args))) + return helper + + + def genAsyncDtor(self, md): + actor = md.actorDecl() + actorvar = actor.var() + method = MethodDefn(self.makeDtorMethodDecl(md)) + + method.addstmts(self.dtorPrologue(actorvar)) + + msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar) + sendok, sendstmts = self.sendAsync(md, msgvar, actorvar) + method.addstmts( + stmts + + self.genVerifyMessage(md.decl.type.verify, md.params, + errfnSendDtor, ExprVar('msg__')) + + sendstmts + + [ Whitespace.NL ] + + self.dtorEpilogue(md, actor.var()) + + [ StmtReturn(sendok) ]) + + lbl = CaseLabel(md.pqReplyId()) + case = StmtBlock() + case.addstmt(StmtReturn(_Result.Processed)) + # TODO if the dtor is "inherently racy", keep the actor alive + # until the other side acks + + return method, (lbl, case) + + + def genBlockingDtorMethod(self, md): + actor = md.actorDecl() + actorvar = actor.var() + method = MethodDefn(self.makeDtorMethodDecl(md)) + + method.addstmts(self.dtorPrologue(actorvar)) + + msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar) + + replyvar = self.replyvar + sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar, actorvar) + method.addstmts( + stmts + + self.genVerifyMessage(md.decl.type.verify, md.params, + errfnSendDtor, ExprVar('msg__')) + + [ Whitespace.NL, + StmtDecl(Decl(Type('Message'), replyvar.name)) ] + + sendstmts) + + destmts = self.deserializeReply( + md, ExprAddrOf(replyvar), self.side, errfnSend, 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 ] + + self.genVerifyMessage(md.decl.type.verify, md.params, + errfnSend, ExprVar('msg__')) + + 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 + + self.genVerifyMessage(md.decl.type.verify, md.params, errfnSend, + ExprVar('msg__')) + + [ Whitespace.NL, + StmtDecl(Decl(Type('Message'), replyvar.name)) ] + + sendstmts + + [ failif ] + + desstmts + + [ Whitespace.NL, + StmtReturn.TRUE ]) + + return method + + + def genCtorRecvCase(self, md): + lbl = CaseLabel(md.pqMsgId()) + case = StmtBlock() + actorvar = md.actorDecl().var() + actorhandle = self.handlevar + + stmts = self.deserializeMessage(md, self.side, errfnRecv) + + 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) + + self.genVerifyMessage(md.decl.type.verify, md.returns, errfnRecv, + self.replyvar) + + [ Whitespace.NL, + StmtReturn(_Result.Processed) ]) + + return lbl, case + + + def genDtorRecvCase(self, md): + lbl = CaseLabel(md.pqMsgId()) + case = StmtBlock() + + stmts = self.deserializeMessage(md, self.side, errfnRecv) + + 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.genVerifyMessage(md.decl.type.verify, md.returns, errfnRecv, + self.replyvar) + + self.dtorEpilogue(md, md.actorDecl().var()) + + [ Whitespace.NL, + StmtReturn(_Result.Processed) ]) + + return lbl, case + + + def genRecvCase(self, md): + lbl = CaseLabel(md.pqMsgId()) + case = StmtBlock() + + stmts = self.deserializeMessage(md, self.side, errfn=errfnRecv) + + 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) + + self.genVerifyMessage(md.decl.type.verify, md.returns, errfnRecv, + self.replyvar) + + [ StmtReturn(_Result.Processed) ]) + + return lbl, case + + + # helper methods + + def failIfNullActor(self, actorExpr, retOnNull=ExprLiteral.FALSE, msg=None): + failif = StmtIf(ExprNot(actorExpr)) + if msg: + failif.addifstmt(_printWarningMessage(msg)) + failif.addifstmt(StmtReturn(retOnNull)) + return failif + + def unregisterActor(self): + return [ StmtExpr(ExprCall(self.protocol.unregisterMethod(), + args=[ _actorId() ])), + StmtExpr(ExprCall(ExprVar('SetId'), args=[_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('IPC::Message', ptr=1), msgvar.name), + init=ExprCall(ExprVar(md.pqMsgCtorFunc()), + args=[ routingId ])) ] + + [ Whitespace.NL ] + + [ self.checkedWrite(p.ipdltype, p.var(), msgvar, sentinelKey=p.name, this=this) + for p in md.params ] + + [ Whitespace.NL ] + + self.setMessageFlags(md, msgvar, 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, ExprCall(ExprVar(md.pqReplyCtorFunc()), args=[ routingId ]))), + Whitespace.NL ] + + [ self.checkedWrite(r.ipdltype, r.var(), replyvar, sentinelKey=r.name) + for r in md.returns ] + + self.setMessageFlags(md, replyvar, reply=1) + + [ self.logMessage(md, replyvar, 'Sending reply ') ]) + + def genVerifyMessage(self, verify, params, errfn, msgsrcVar): + stmts = [ ] + if not verify: + return stmts + if len(params) == 0: + return stmts + + msgvar = ExprVar('msgverify__') + side = self.side + + msgexpr = ExprAddrOf(msgvar) + itervar = ExprVar('msgverifyIter__') + # IPC::Message msgverify__ = Move(*(reply__)); or + # IPC::Message msgverify__ = Move(*(msg__)); + stmts.append(StmtDecl(Decl(Type('IPC::Message', ptr=0), 'msgverify__'), + init=ExprMove(ExprDeref(msgsrcVar)))) + + stmts.extend(( + # PickleIterator msgverifyIter__ = PickleIterator(msgverify__); + [ StmtDecl(Decl(_iterType(ptr=0), itervar.name), + init=ExprCall(ExprVar('PickleIterator'), + args=[ msgvar ])) ] + # declare varCopy for each variable to deserialize. + + [ StmtDecl(Decl(p.bareType(side), p.var().name + 'Copy')) + for p in params ] + + [ Whitespace.NL ] + # checked Read(&(varCopy), &(msgverify__), &(msgverifyIter__)) + + [ self.checkedRead(p.ipdltype, + ExprAddrOf(ExprVar(p.var().name + 'Copy')), + msgexpr, ExprAddrOf(itervar), + errfn, p.bareType(side).name, + p.name) + for p in params ] + + [ self.endRead(msgvar, itervar) ] + # Move the message back to its source before sending. + + [ StmtExpr(ExprAssn(ExprDeref(msgsrcVar), ExprMove(msgvar))) ] + )) + + return stmts + + def setMessageFlags(self, md, var, 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 = ([ + self.logMessage(md, msgexpr, 'Received ', + receiving=True), + self.profilerLabel(md), + Whitespace.NL + ]) + + if 0 == len(md.params): + return stmts + + start, decls, reads = 0, [], [] + if isctor: + # return the raw actor handle so that its ID can be used + # to construct the "real" actor + handlevar = self.handlevar + handletype = Type('ActorHandle') + decls = [ StmtDecl(Decl(handletype, handlevar.name)) ] + reads = [ self.checkedRead(None, ExprAddrOf(handlevar), msgexpr, + ExprAddrOf(self.itervar), + errfn, "'%s'" % handletype.name, + sentinelKey='actor') ] + start = 1 + + stmts.extend(( + [ StmtDecl(Decl(_iterType(ptr=0), self.itervar.name), + init=ExprCall(ExprVar('PickleIterator'), + args=[ msgvar ])) ] + + decls + [ StmtDecl(Decl(p.bareType(side), p.var().name)) + for p in md.params ] + + [ Whitespace.NL ] + + reads + [ self.checkedRead(p.ipdltype, ExprAddrOf(p.var()), + msgexpr, ExprAddrOf(itervar), + errfn, "'%s'" % p.bareType(side).name, + sentinelKey=p.name) + 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, replyexpr, + 'Received reply ', actor, receiving=True) ] + if 0 == len(md.returns): + return stmts + + itervar = self.itervar + stmts.extend( + [ Whitespace.NL, + StmtDecl(Decl(_iterType(ptr=0), itervar.name), + init=ExprCall(ExprVar('PickleIterator'), + args=[ self.replyvar ])) ] + + [ self.checkedRead(r.ipdltype, r.var(), + ExprAddrOf(self.replyvar), + ExprAddrOf(self.itervar), + errfn, "'%s'" % r.bareType(side).name, + sentinelKey=r.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(md) ] + + self.transition(md, 'out', actor) + + [ Whitespace.NL, + StmtDecl(Decl(Type.BOOL, sendok.name), + init=ExprCall( + ExprSelect(self.protocol.callGetChannel(actor), + '->', '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(md) ] + + self.transition(md, 'out', actor) + + [ Whitespace.NL, + StmtDecl( + Decl(Type.BOOL, sendok.name), + init=ExprCall(ExprSelect(self.protocol.callGetChannel(actor), + '->', + _sendPrefix(md.decl.type)), + args=[ msgexpr, ExprAddrOf(replyexpr) ])) + ]) + ) + + def callAllocActor(self, md, retsems, side): + return ExprCall( + _allocMethod(md.decl.type.constructedType(), side), + args=md.makeCxxArgs(retsems=retsems, retcallsems='out', + implicit=0)) + + def callActorDestroy(self, actorexpr, why=_DestroyReason.Deletion): + return ExprCall(ExprSelect(actorexpr, '->', 'DestroySubtree'), + args=[ why ]) + + def callRemoveActor(self, actorexpr, manager=None, ipdltype=None): + if ipdltype is None: ipdltype = self.protocol.decl.type + + if not ipdltype.isManaged(): + return Whitespace('// unmanaged protocol') + + removefunc = self.protocol.removeManageeMethod() + if manager is not None: + removefunc = ExprSelect(manager, '->', removefunc.name) + + return ExprCall(removefunc, + args=[ _protocolId(ipdltype), + actorexpr ]) + + def callDeallocSubtree(self, md, actorexpr): + return ExprCall(ExprSelect(actorexpr, '->', 'DeallocSubtree')) + + def invokeRecvHandler(self, md, implicit=1): + failif = StmtIf(ExprNot( + ExprCall(md.recvMethod(), + args=md.makeCxxArgs(paramsems='move', retsems='in', + retcallsems='out', + implicit=implicit)))) + failif.addifstmts([ + _protocolErrorBreakpoint('Handler 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) + + return _ifLogging(ExprLiteral.String(actorname), + [ StmtExpr(ExprCall( + ExprVar('mozilla::ipc::LogMessageForProtocol'), + args=[ ExprLiteral.String(actorname), + self.protocol.callOtherPid(actor), + ExprLiteral.String(pfx), + ExprCall(ExprSelect(msgptr, '->', 'type')), + ExprVar('mozilla::ipc::MessageDirection::eReceiving' + if receiving + else 'mozilla::ipc::MessageDirection::eSending') ])) ]) + + def profilerLabel(self, md): + return StmtExpr(ExprCall(ExprVar('PROFILER_LABEL'), + [ ExprLiteral.String(self.protocol.name), + ExprLiteral.String(md.prettyMsgName()), + 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=[ ExprCall(ExprVar('Trigger'), + args=[ action, ExprVar(msgid) ]), + ExprAddrOf(stateexpr) ]))) + ifbad.addifstmts(_badTransition()) + return [ ifbad ] + + def checkedRead(self, ipdltype, expr, msgexpr, iterexpr, errfn, paramtype, sentinelKey, sentinel=True): + ifbad = StmtIf(ExprNot(self.read(ipdltype, expr, msgexpr, iterexpr))) + if isinstance(paramtype, list): + errorcall = errfn(*paramtype) + else: + errorcall = errfn('Error deserializing ' + paramtype) + ifbad.addifstmts(errorcall) + + block = Block() + block.addstmt(ifbad) + + if sentinel: + assert sentinelKey + + block.addstmt(Whitespace('// Sentinel = ' + repr(sentinelKey) + '\n', indent=1)) + read = ExprCall(ExprSelect(msgexpr, '->', 'ReadSentinel'), + args=[ iterexpr, ExprLiteral.Int(hashfunc(sentinelKey)) ]) + ifsentinel = StmtIf(ExprNot(read)) + ifsentinel.addifstmts(errorcall) + block.addstmt(ifsentinel) + + return block + + 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 diff --git a/ipc/ipdl/ipdl/parser.py b/ipc/ipdl/ipdl/parser.py new file mode 100644 index 000000000..38c46dc73 --- /dev/null +++ b/ipc/ipdl/ipdl/parser.py @@ -0,0 +1,807 @@ +# 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, sys +from ply import lex, yacc + +from ipdl.ast import * + +def _getcallerpath(): + '''Return the absolute path of the file containing the code that +**CALLED** this function.''' + return os.path.abspath(sys._getframe(1).f_code.co_filename) + +##----------------------------------------------------------------------------- + +class ParseError(Exception): + def __init__(self, loc, fmt, *args): + self.loc = loc + self.error = ('%s%s: error: %s'% ( + Parser.includeStackString(), loc, fmt)) % args + def __str__(self): + return self.error + +def _safeLinenoValue(t): + lineno, value = 0, '???' + if hasattr(t, 'lineno'): lineno = t.lineno + if hasattr(t, 'value'): value = t.value + return lineno, value + +def _error(loc, fmt, *args): + raise ParseError(loc, fmt, *args) + +class Parser: + # when we reach an |include [protocol] foo;| statement, we need to + # save the current parser state and create a new one. this "stack" is + # where that state is saved + # + # there is one Parser per file + current = None + parseStack = [ ] + parsed = { } + + def __init__(self, type, name, debug=0): + assert type and name + self.type = type + self.debug = debug + self.filename = None + self.includedirs = None + self.loc = None # not always up to date + self.lexer = None + self.parser = None + self.tu = TranslationUnit(type, name) + self.direction = None + self.errout = None + + def parse(self, input, filename, includedirs, errout): + assert os.path.isabs(filename) + + if filename in Parser.parsed: + return Parser.parsed[filename].tu + + self.lexer = lex.lex(debug=self.debug, + optimize=not self.debug, + lextab="ipdl_lextab") + self.parser = yacc.yacc(debug=self.debug, + optimize=not self.debug, + tabmodule="ipdl_yacctab") + self.filename = filename + self.includedirs = includedirs + self.tu.filename = filename + self.errout = errout + + Parser.parsed[filename] = self + Parser.parseStack.append(Parser.current) + Parser.current = self + + try: + ast = self.parser.parse(input=input, lexer=self.lexer, + debug=self.debug) + except ParseError, p: + print >>errout, p + return None + + Parser.current = Parser.parseStack.pop() + return ast + + def resolveIncludePath(self, filepath): + '''Return the absolute path from which the possibly partial +|filepath| should be read, or |None| if |filepath| cannot be located.''' + for incdir in self.includedirs +[ '' ]: + realpath = os.path.join(incdir, filepath) + if os.path.isfile(realpath): + return os.path.abspath(realpath) + return None + + # returns a GCC-style string representation of the include stack. + # e.g., + # in file included from 'foo.ipdl', line 120: + # in file included from 'bar.ipd', line 12: + # which can be printed above a proper error message or warning + @staticmethod + def includeStackString(): + s = '' + for parse in Parser.parseStack[1:]: + s += " in file included from `%s', line %d:\n"% ( + parse.loc.filename, parse.loc.lineno) + return s + +def locFromTok(p, num): + return Loc(Parser.current.filename, p.lineno(num)) + + +##----------------------------------------------------------------------------- + +reserved = set(( + 'answer', + 'as', + 'async', + 'both', + 'bridges', + 'call', + 'child', + 'class', + 'compress', + 'compressall', + '__delete__', + 'delete', # reserve 'delete' to prevent its use + 'from', + 'goto', + 'include', + 'intr', + 'manager', + 'manages', + 'namespace', + 'nested', + 'nullable', + 'opens', + 'or', + 'parent', + 'prio', + 'protocol', + 'recv', + 'returns', + 'send', + 'spawns', + 'start', + 'state', + 'struct', + 'sync', + 'union', + 'upto', + 'using', + 'verify')) +tokens = [ + 'COLONCOLON', 'ID', 'STRING', +] + [ r.upper() for r in reserved ] + +t_COLONCOLON = '::' + +literals = '(){}[]<>;:,~' +t_ignore = ' \f\t\v' + +def t_linecomment(t): + r'//[^\n]*' + +def t_multilinecomment(t): + r'/\*(\n|.)*?\*/' + t.lexer.lineno += t.value.count('\n') + +def t_NL(t): + r'(?:\r\n|\n|\n)+' + t.lexer.lineno += len(t.value) + +def t_ID(t): + r'[a-zA-Z_][a-zA-Z0-9_]*' + if t.value in reserved: + t.type = t.value.upper() + return t + +def t_STRING(t): + r'"[^"\n]*"' + t.value = t.value[1:-1] + return t + +def t_error(t): + _error(Loc(Parser.current.filename, t.lineno), + 'lexically invalid characters `%s', t.value) + +##----------------------------------------------------------------------------- + +def p_TranslationUnit(p): + """TranslationUnit : Preamble NamespacedStuff""" + tu = Parser.current.tu + tu.loc = Loc(tu.filename) + for stmt in p[1]: + if isinstance(stmt, CxxInclude): + tu.addCxxInclude(stmt) + elif isinstance(stmt, Include): + tu.addInclude(stmt) + elif isinstance(stmt, UsingStmt): + tu.addUsingStmt(stmt) + else: + assert 0 + + for thing in p[2]: + if isinstance(thing, StructDecl): + tu.addStructDecl(thing) + elif isinstance(thing, UnionDecl): + tu.addUnionDecl(thing) + elif isinstance(thing, Protocol): + if tu.protocol is not None: + _error(thing.loc, "only one protocol definition per file") + tu.protocol = thing + else: + assert(0) + + # The "canonical" namespace of the tu, what it's considered to be + # in for the purposes of C++: |#include "foo/bar/TU.h"| + if tu.protocol: + assert tu.filetype == 'protocol' + tu.namespaces = tu.protocol.namespaces + tu.name = tu.protocol.name + else: + assert tu.filetype == 'header' + # There's not really a canonical "thing" in headers. So + # somewhat arbitrarily use the namespace of the last + # interesting thing that was declared. + for thing in reversed(tu.structsAndUnions): + tu.namespaces = thing.namespaces + break + + p[0] = tu + +##-------------------- +## Preamble +def p_Preamble(p): + """Preamble : Preamble PreambleStmt ';' + |""" + if 1 == len(p): + p[0] = [ ] + else: + p[1].append(p[2]) + p[0] = p[1] + +def p_PreambleStmt(p): + """PreambleStmt : CxxIncludeStmt + | IncludeStmt + | UsingStmt""" + p[0] = p[1] + +def p_CxxIncludeStmt(p): + """CxxIncludeStmt : INCLUDE STRING""" + p[0] = CxxInclude(locFromTok(p, 1), p[2]) + +def p_IncludeStmt(p): + """IncludeStmt : INCLUDE PROTOCOL ID + | INCLUDE ID""" + loc = locFromTok(p, 1) + + Parser.current.loc = loc + if 4 == len(p): + id = p[3] + type = 'protocol' + else: + id = p[2] + type = 'header' + inc = Include(loc, type, id) + + path = Parser.current.resolveIncludePath(inc.file) + if path is None: + raise ParseError(loc, "can't locate include file `%s'"% ( + inc.file)) + + inc.tu = Parser(type, id).parse(open(path).read(), path, Parser.current.includedirs, Parser.current.errout) + p[0] = inc + +def p_UsingStmt(p): + """UsingStmt : USING CxxType FROM STRING + | USING CLASS CxxType FROM STRING + | USING STRUCT CxxType FROM STRING""" + if 6 == len(p): + header = p[5] + elif 5 == len(p): + header = p[4] + else: + header = None + if 6 == len(p): + kind = p[2] + else: + kind = None + if 6 == len(p): + cxxtype = p[3] + else: + cxxtype = p[2] + p[0] = UsingStmt(locFromTok(p, 1), cxxtype, header, kind) + +##-------------------- +## Namespaced stuff +def p_NamespacedStuff(p): + """NamespacedStuff : NamespacedStuff NamespaceThing + | NamespaceThing""" + if 2 == len(p): + p[0] = p[1] + else: + p[1].extend(p[2]) + p[0] = p[1] + +def p_NamespaceThing(p): + """NamespaceThing : NAMESPACE ID '{' NamespacedStuff '}' + | StructDecl + | UnionDecl + | ProtocolDefn""" + if 2 == len(p): + p[0] = [ p[1] ] + else: + for thing in p[4]: + thing.addOuterNamespace(Namespace(locFromTok(p, 1), p[2])) + p[0] = p[4] + +def p_StructDecl(p): + """StructDecl : STRUCT ID '{' StructFields '}' ';' + | STRUCT ID '{' '}' ';'""" + if 7 == len(p): + p[0] = StructDecl(locFromTok(p, 1), p[2], p[4]) + else: + p[0] = StructDecl(locFromTok(p, 1), p[2], [ ]) + +def p_StructFields(p): + """StructFields : StructFields StructField ';' + | StructField ';'""" + if 3 == len(p): + p[0] = [ p[1] ] + else: + p[1].append(p[2]) + p[0] = p[1] + +def p_StructField(p): + """StructField : Type ID""" + p[0] = StructField(locFromTok(p, 1), p[1], p[2]) + +def p_UnionDecl(p): + """UnionDecl : UNION ID '{' ComponentTypes '}' ';'""" + p[0] = UnionDecl(locFromTok(p, 1), p[2], p[4]) + +def p_ComponentTypes(p): + """ComponentTypes : ComponentTypes Type ';' + | Type ';'""" + if 3 == len(p): + p[0] = [ p[1] ] + else: + p[1].append(p[2]) + p[0] = p[1] + +def p_ProtocolDefn(p): + """ProtocolDefn : OptionalProtocolSendSemanticsQual PROTOCOL ID '{' ProtocolBody '}' ';'""" + protocol = p[5] + protocol.loc = locFromTok(p, 2) + protocol.name = p[3] + protocol.nestedRange = p[1][0] + protocol.sendSemantics = p[1][1] + p[0] = protocol + + if Parser.current.type == 'header': + _error(protocol.loc, 'can\'t define a protocol in a header. Do it in a protocol spec instead.') + + +def p_ProtocolBody(p): + """ProtocolBody : SpawnsStmtsOpt""" + p[0] = p[1] + +##-------------------- +## spawns/bridges/opens stmts + +def p_SpawnsStmtsOpt(p): + """SpawnsStmtsOpt : SpawnsStmt SpawnsStmtsOpt + | BridgesStmtsOpt""" + if 2 == len(p): + p[0] = p[1] + else: + p[2].spawnsStmts.insert(0, p[1]) + p[0] = p[2] + +def p_SpawnsStmt(p): + """SpawnsStmt : PARENT SPAWNS ID AsOpt ';' + | CHILD SPAWNS ID AsOpt ';'""" + p[0] = SpawnsStmt(locFromTok(p, 1), p[1], p[3], p[4]) + +def p_AsOpt(p): + """AsOpt : AS PARENT + | AS CHILD + | """ + if 3 == len(p): + p[0] = p[2] + else: + p[0] = 'child' + +def p_BridgesStmtsOpt(p): + """BridgesStmtsOpt : BridgesStmt BridgesStmtsOpt + | OpensStmtsOpt""" + if 2 == len(p): + p[0] = p[1] + else: + p[2].bridgesStmts.insert(0, p[1]) + p[0] = p[2] + +def p_BridgesStmt(p): + """BridgesStmt : BRIDGES ID ',' ID ';'""" + p[0] = BridgesStmt(locFromTok(p, 1), p[2], p[4]) + +def p_OpensStmtsOpt(p): + """OpensStmtsOpt : OpensStmt OpensStmtsOpt + | ManagersStmtOpt""" + if 2 == len(p): + p[0] = p[1] + else: + p[2].opensStmts.insert(0, p[1]) + p[0] = p[2] + +def p_OpensStmt(p): + """OpensStmt : PARENT OPENS ID ';' + | CHILD OPENS ID ';'""" + p[0] = OpensStmt(locFromTok(p, 1), p[1], p[3]) + +##-------------------- +## manager/manages stmts + +def p_ManagersStmtOpt(p): + """ManagersStmtOpt : ManagersStmt ManagesStmtsOpt + | ManagesStmtsOpt""" + if 2 == len(p): + p[0] = p[1] + else: + p[2].managers = p[1] + p[0] = p[2] + +def p_ManagersStmt(p): + """ManagersStmt : MANAGER ManagerList ';'""" + if 1 == len(p): + p[0] = [ ] + else: + p[0] = p[2] + +def p_ManagerList(p): + """ManagerList : ID + | ManagerList OR ID""" + if 2 == len(p): + p[0] = [ Manager(locFromTok(p, 1), p[1]) ] + else: + p[1].append(Manager(locFromTok(p, 3), p[3])) + p[0] = p[1] + +def p_ManagesStmtsOpt(p): + """ManagesStmtsOpt : ManagesStmt ManagesStmtsOpt + | MessageDeclsOpt""" + if 2 == len(p): + p[0] = p[1] + else: + p[2].managesStmts.insert(0, p[1]) + p[0] = p[2] + +def p_ManagesStmt(p): + """ManagesStmt : MANAGES ID ';'""" + p[0] = ManagesStmt(locFromTok(p, 1), p[2]) + + +##-------------------- +## Message decls + +def p_MessageDeclsOpt(p): + """MessageDeclsOpt : MessageDeclThing MessageDeclsOpt + | TransitionStmtsOpt""" + if 2 == len(p): + p[0] = p[1] + else: + p[2].messageDecls.insert(0, p[1]) + p[0] = p[2] + +def p_MessageDeclThing(p): + """MessageDeclThing : MessageDirectionLabel ':' MessageDecl ';' + | MessageDecl ';'""" + if 3 == len(p): + p[0] = p[1] + else: + p[0] = p[3] + +def p_MessageDirectionLabel(p): + """MessageDirectionLabel : PARENT + | CHILD + | BOTH""" + if p[1] == 'parent': + Parser.current.direction = IN + elif p[1] == 'child': + Parser.current.direction = OUT + elif p[1] == 'both': + Parser.current.direction = INOUT + else: + assert 0 + +def p_MessageDecl(p): + """MessageDecl : SendSemanticsQual MessageBody""" + msg = p[2] + msg.nested = p[1][0] + msg.prio = p[1][1] + msg.sendSemantics = p[1][2] + + if Parser.current.direction is None: + _error(msg.loc, 'missing message direction') + msg.direction = Parser.current.direction + + p[0] = msg + +def p_MessageBody(p): + """MessageBody : MessageId MessageInParams MessageOutParams OptionalMessageModifiers""" + # FIXME/cjones: need better loc info: use one of the quals + loc, name = p[1] + msg = MessageDecl(loc) + msg.name = name + msg.addInParams(p[2]) + msg.addOutParams(p[3]) + msg.addModifiers(p[4]) + + p[0] = msg + +def p_MessageId(p): + """MessageId : ID + | __DELETE__ + | DELETE + | '~' ID""" + loc = locFromTok(p, 1) + if 3 == len(p): + _error(loc, "sorry, `%s()' destructor syntax is a relic from a bygone era. Declare `__delete__()' in the `%s' protocol instead", p[1]+p[2], p[2]) + elif 'delete' == p[1]: + _error(loc, "`delete' is a reserved identifier") + p[0] = [ loc, p[1] ] + +def p_MessageInParams(p): + """MessageInParams : '(' ParamList ')'""" + p[0] = p[2] + +def p_MessageOutParams(p): + """MessageOutParams : RETURNS '(' ParamList ')' + | """ + if 1 == len(p): + p[0] = [ ] + else: + p[0] = p[3] + +def p_OptionalMessageModifiers(p): + """OptionalMessageModifiers : OptionalMessageModifiers MessageModifier + | MessageModifier + | """ + if 1 == len(p): + p[0] = [ ] + elif 2 == len(p): + p[0] = [ p[1] ] + else: + p[1].append(p[2]) + p[0] = p[1] + +def p_MessageModifier(p): + """ MessageModifier : MessageVerify + | MessageCompress """ + p[0] = p[1] + +def p_MessageVerify(p): + """MessageVerify : VERIFY""" + p[0] = p[1] + +def p_MessageCompress(p): + """MessageCompress : COMPRESS + | COMPRESSALL""" + p[0] = p[1] + +##-------------------- +## State machine + +def p_TransitionStmtsOpt(p): + """TransitionStmtsOpt : TransitionStmt TransitionStmtsOpt + |""" + if 1 == len(p): + # we fill in |loc| in the Protocol rule + p[0] = Protocol(None) + else: + p[2].transitionStmts.insert(0, p[1]) + p[0] = p[2] + +def p_TransitionStmt(p): + """TransitionStmt : OptionalStart STATE State ':' Transitions""" + p[3].start = p[1] + p[0] = TransitionStmt(locFromTok(p, 2), p[3], p[5]) + +def p_OptionalStart(p): + """OptionalStart : START + | """ + p[0] = (len(p) == 2) # True iff 'start' specified + +def p_Transitions(p): + """Transitions : Transitions Transition + | Transition""" + if 3 == len(p): + p[1].append(p[2]) + p[0] = p[1] + else: + p[0] = [ p[1] ] + +def p_Transition(p): + """Transition : Trigger ID GOTO StateList ';' + | Trigger __DELETE__ ';' + | Trigger DELETE ';'""" + if 'delete' == p[2]: + _error(locFromTok(p, 1), "`delete' is a reserved identifier") + + loc, trigger = p[1] + if 6 == len(p): + nextstates = p[4] + else: + nextstates = [ State.DEAD ] + p[0] = Transition(loc, trigger, p[2], nextstates) + +def p_Trigger(p): + """Trigger : SEND + | RECV + | CALL + | ANSWER""" + p[0] = [ locFromTok(p, 1), Transition.nameToTrigger(p[1]) ] + +def p_StateList(p): + """StateList : StateList OR State + | State""" + if 2 == len(p): + p[0] = [ p[1] ] + else: + p[1].append(p[3]) + p[0] = p[1] + +def p_State(p): + """State : ID""" + p[0] = State(locFromTok(p, 1), p[1]) + +##-------------------- +## Minor stuff +def p_Nested(p): + """Nested : ID""" + kinds = {'not': 1, + 'inside_sync': 2, + 'inside_cpow': 3} + if p[1] not in kinds: + _error(locFromTok(p, 1), "Expected not, inside_sync, or inside_cpow for nested()") + + p[0] = { 'nested': kinds[p[1]] } + +def p_Priority(p): + """Priority : ID""" + kinds = {'normal': 1, + 'high': 2} + if p[1] not in kinds: + _error(locFromTok(p, 1), "Expected normal or high for prio()") + + p[0] = { 'prio': kinds[p[1]] } + +def p_SendQualifier(p): + """SendQualifier : NESTED '(' Nested ')' + | PRIO '(' Priority ')'""" + p[0] = p[3] + +def p_SendQualifierList(p): + """SendQualifierList : SendQualifier SendQualifierList + | """ + if len(p) > 1: + p[0] = p[1] + p[0].update(p[2]) + else: + p[0] = {} + +def p_SendSemanticsQual(p): + """SendSemanticsQual : SendQualifierList ASYNC + | SendQualifierList SYNC + | INTR""" + quals = {} + if len(p) == 3: + quals = p[1] + mtype = p[2] + else: + mtype = 'intr' + + if mtype == 'async': mtype = ASYNC + elif mtype == 'sync': mtype = SYNC + elif mtype == 'intr': mtype = INTR + else: assert 0 + + p[0] = [ quals.get('nested', NOT_NESTED), quals.get('prio', NORMAL_PRIORITY), mtype ] + +def p_OptionalProtocolSendSemanticsQual(p): + """OptionalProtocolSendSemanticsQual : ProtocolSendSemanticsQual + | """ + if 2 == len(p): p[0] = p[1] + else: p[0] = [ (NOT_NESTED, NOT_NESTED), ASYNC ] + +def p_ProtocolSendSemanticsQual(p): + """ProtocolSendSemanticsQual : ASYNC + | SYNC + | NESTED '(' UPTO Nested ')' ASYNC + | NESTED '(' UPTO Nested ')' SYNC + | INTR""" + if p[1] == 'nested': + mtype = p[6] + nested = (NOT_NESTED, p[4]) + else: + mtype = p[1] + nested = (NOT_NESTED, NOT_NESTED) + + if mtype == 'async': mtype = ASYNC + elif mtype == 'sync': mtype = SYNC + elif mtype == 'intr': mtype = INTR + else: assert 0 + + p[0] = [ nested, mtype ] + +def p_ParamList(p): + """ParamList : ParamList ',' Param + | Param + | """ + if 1 == len(p): + p[0] = [ ] + elif 2 == len(p): + p[0] = [ p[1] ] + else: + p[1].append(p[3]) + p[0] = p[1] + +def p_Param(p): + """Param : Type ID""" + p[0] = Param(locFromTok(p, 1), p[1], p[2]) + +def p_Type(p): + """Type : MaybeNullable BasicType""" + # only actor types are nullable; we check this in the type checker + p[2].nullable = p[1] + p[0] = p[2] + +def p_BasicType(p): + """BasicType : ScalarType + | ScalarType '[' ']'""" + if 4 == len(p): + p[1].array = 1 + p[0] = p[1] + +def p_ScalarType(p): + """ScalarType : ActorType + | CxxID""" # ID == CxxType; we forbid qnames here, + # in favor of the |using| declaration + if isinstance(p[1], TypeSpec): + p[0] = p[1] + else: + loc, id = p[1] + p[0] = TypeSpec(loc, QualifiedId(loc, id)) + +def p_ActorType(p): + """ActorType : ID ':' State""" + loc = locFromTok(p, 1) + p[0] = TypeSpec(loc, QualifiedId(loc, p[1]), state=p[3]) + +def p_MaybeNullable(p): + """MaybeNullable : NULLABLE + | """ + p[0] = (2 == len(p)) + +##-------------------- +## C++ stuff +def p_CxxType(p): + """CxxType : QualifiedID + | CxxID""" + if isinstance(p[1], QualifiedId): + p[0] = TypeSpec(p[1].loc, p[1]) + else: + loc, id = p[1] + p[0] = TypeSpec(loc, QualifiedId(loc, id)) + +def p_QualifiedID(p): + """QualifiedID : QualifiedID COLONCOLON CxxID + | CxxID COLONCOLON CxxID""" + if isinstance(p[1], QualifiedId): + loc, id = p[3] + p[1].qualify(id) + p[0] = p[1] + else: + loc1, id1 = p[1] + _, id2 = p[3] + p[0] = QualifiedId(loc1, id2, [ id1 ]) + +def p_CxxID(p): + """CxxID : ID + | CxxTemplateInst""" + if isinstance(p[1], tuple): + p[0] = p[1] + else: + p[0] = (locFromTok(p, 1), str(p[1])) + +def p_CxxTemplateInst(p): + """CxxTemplateInst : ID '<' ID '>'""" + p[0] = (locFromTok(p, 1), str(p[1]) +'<'+ str(p[3]) +'>') + +def p_error(t): + lineno, value = _safeLinenoValue(t) + _error(Loc(Parser.current.filename, lineno), + "bad syntax near `%s'", value) diff --git a/ipc/ipdl/ipdl/type.py b/ipc/ipdl/ipdl/type.py new file mode 100644 index 000000000..68a10cd01 --- /dev/null +++ b/ipc/ipdl/ipdl/type.py @@ -0,0 +1,2200 @@ +# vim: set ts=4 sw=4 tw=99 et: +# 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, sys + +from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, State, StructDecl, TransitionStmt +from ipdl.ast import TypeSpec, UnionDecl, UsingStmt, Visitor +from ipdl.ast import ASYNC, SYNC, INTR +from ipdl.ast import IN, OUT, INOUT, ANSWER, CALL, RECV, SEND +from ipdl.ast import NOT_NESTED, INSIDE_SYNC_NESTED, INSIDE_CPOW_NESTED +import ipdl.builtin as builtin + +_DELETE_MSG = '__delete__' + + +def _otherside(side): + if side == 'parent': return 'child' + elif side == 'child': return 'parent' + else: assert 0 and 'unknown side "%s"'% (side) + +def unique_pairs(s): + n = len(s) + for i, e1 in enumerate(s): + for j in xrange(i+1, n): + yield (e1, s[j]) + +def cartesian_product(s1, s2): + for e1 in s1: + for e2 in s2: + yield (e1, e2) + + +class TypeVisitor: + def __init__(self): + self.visited = set() + + def defaultVisit(self, node, *args): + raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% ( + node.__class__.__name__) + + def visitVoidType(self, v, *args): + pass + + def visitBuiltinCxxType(self, t, *args): + pass + + def visitImportedCxxType(self, t, *args): + pass + + def visitStateType(self, s, *args): + pass + + def visitMessageType(self, m, *args): + for param in m.params: + param.accept(self, *args) + for ret in m.returns: + ret.accept(self, *args) + if m.cdtype is not None: + m.cdtype.accept(self, *args) + + def visitProtocolType(self, p, *args): + # NB: don't visit manager and manages. a naive default impl + # could result in an infinite loop + pass + + def visitActorType(self, a, *args): + a.protocol.accept(self, *args) + a.state.accept(self, *args) + + def visitStructType(self, s, *args): + if s in self.visited: + return + + self.visited.add(s) + for field in s.fields: + field.accept(self, *args) + + def visitUnionType(self, u, *args): + if u in self.visited: + return + + self.visited.add(u) + for component in u.components: + component.accept(self, *args) + + def visitArrayType(self, a, *args): + a.basetype.accept(self, *args) + + def visitShmemType(self, s, *args): + pass + + def visitShmemChmodType(self, c, *args): + c.shmem.accept(self) + + def visitFDType(self, s, *args): + pass + + def visitEndpointType(self, s, *args): + pass + +class Type: + def __cmp__(self, o): + return cmp(self.fullname(), o.fullname()) + def __eq__(self, o): + return (self.__class__ == o.__class__ + and self.fullname() == o.fullname()) + def __hash__(self): + return hash(self.fullname()) + + # Is this a C++ type? + def isCxx(self): + return False + # Is this an IPDL type? + def isIPDL(self): + return False + # Is this type neither compound nor an array? + def isAtom(self): + return False + # Can this type appear in IPDL programs? + def isVisible(self): + return False + def isVoid(self): + return False + def typename(self): + return self.__class__.__name__ + + def name(self): raise Exception, 'NYI' + def fullname(self): raise Exception, 'NYI' + + def accept(self, visitor, *args): + visit = getattr(visitor, 'visit'+ self.__class__.__name__, None) + if visit is None: + return getattr(visitor, 'defaultVisit')(self, *args) + return visit(self, *args) + +class VoidType(Type): + def isCxx(self): + return True + def isIPDL(self): + return False + def isAtom(self): + return True + def isVisible(self): + return False + def isVoid(self): + return True + + def name(self): return 'void' + def fullname(self): return 'void' + +VOID = VoidType() + +##-------------------- +class CxxType(Type): + def isCxx(self): + return True + def isAtom(self): + return True + def isBuiltin(self): + return False + def isImported(self): + return False + def isGenerated(self): + return False + def isVisible(self): + return True + +class BuiltinCxxType(CxxType): + def __init__(self, qname): + assert isinstance(qname, QualifiedId) + self.loc = qname.loc + self.qname = qname + def isBuiltin(self): return True + + def name(self): + return self.qname.baseid + def fullname(self): + return str(self.qname) + +class ImportedCxxType(CxxType): + def __init__(self, qname): + assert isinstance(qname, QualifiedId) + self.loc = qname.loc + self.qname = qname + def isImported(self): return True + + def name(self): + return self.qname.baseid + def fullname(self): + return str(self.qname) + +##-------------------- +class IPDLType(Type): + def isIPDL(self): return True + def isVisible(self): return True + def isState(self): return False + def isMessage(self): return False + def isProtocol(self): return False + def isActor(self): return False + def isStruct(self): return False + def isUnion(self): return False + def isArray(self): return False + def isAtom(self): return True + def isCompound(self): return False + def isShmem(self): return False + def isChmod(self): return False + def isFD(self): return False + def isEndpoint(self): return False + + def isAsync(self): return self.sendSemantics == ASYNC + def isSync(self): return self.sendSemantics == SYNC + def isInterrupt(self): return self.sendSemantics is INTR + + def hasReply(self): return (self.isSync() or self.isInterrupt()) + + @classmethod + def convertsTo(cls, lesser, greater): + if (lesser.nestedRange[0] < greater.nestedRange[0] or + lesser.nestedRange[1] > greater.nestedRange[1]): + return False + + # Protocols that use intr semantics are not allowed to use + # message nesting. + if (greater.isInterrupt() and + lesser.nestedRange != (NOT_NESTED, NOT_NESTED)): + return False + + if lesser.isAsync(): + return True + elif lesser.isSync() and not greater.isAsync(): + return True + elif greater.isInterrupt(): + return True + + return False + + def needsMoreJuiceThan(self, o): + return not IPDLType.convertsTo(self, o) + +class StateType(IPDLType): + def __init__(self, protocol, name, start=False): + self.protocol = protocol + self.name = name + self.start = start + def isState(self): return True + def name(self): + return self.name + def fullname(self): + return self.name() + +class MessageType(IPDLType): + def __init__(self, nested, prio, sendSemantics, direction, + ctor=False, dtor=False, cdtype=None, compress=False, + verify=False): + assert not (ctor and dtor) + assert not (ctor or dtor) or type is not None + + self.nested = nested + self.prio = prio + self.nestedRange = (nested, nested) + self.sendSemantics = sendSemantics + self.direction = direction + self.params = [ ] + self.returns = [ ] + self.ctor = ctor + self.dtor = dtor + self.cdtype = cdtype + self.compress = compress + self.verify = verify + def isMessage(self): return True + + def isCtor(self): return self.ctor + def isDtor(self): return self.dtor + def constructedType(self): return self.cdtype + + def isIn(self): return self.direction is IN + def isOut(self): return self.direction is OUT + def isInout(self): return self.direction is INOUT + + def hasImplicitActorParam(self): + return self.isCtor() or self.isDtor() + +class Bridge: + def __init__(self, parentPtype, childPtype): + assert parentPtype.isToplevel() and childPtype.isToplevel() + self.parent = parentPtype + self.child = childPtype + + def __cmp__(self, o): + return cmp(self.parent, o.parent) or cmp(self.child, o.child) + def __eq__(self, o): + return self.parent == o.parent and self.child == o.child + def __hash__(self): + return hash(self.parent) + hash(self.child) + +class ProtocolType(IPDLType): + def __init__(self, qname, nestedRange, sendSemantics, stateless=False): + self.qname = qname + self.nestedRange = nestedRange + self.sendSemantics = sendSemantics + self.spawns = set() # ProtocolType + self.opens = set() # ProtocolType + self.managers = [] # ProtocolType + self.manages = [ ] + self.stateless = stateless + self.hasDelete = False + self.hasReentrantDelete = False + def isProtocol(self): return True + + def name(self): + return self.qname.baseid + def fullname(self): + return str(self.qname) + + def addManager(self, mgrtype): + assert mgrtype.isIPDL() and mgrtype.isProtocol() + self.managers.append(mgrtype) + + def addSpawn(self, ptype): + assert self.isToplevel() and ptype.isToplevel() + self.spawns.add(ptype) + + def addOpen(self, ptype): + assert self.isToplevel() and ptype.isToplevel() + self.opens.add(ptype) + + def managedBy(self, mgr): + self.managers = list(mgr) + + def toplevel(self): + if self.isToplevel(): + return self + for mgr in self.managers: + if mgr is not self: + return mgr.toplevel() + + def toplevels(self): + if self.isToplevel(): + return [self] + toplevels = list() + for mgr in self.managers: + if mgr is not self: + toplevels.extend(mgr.toplevels()) + return set(toplevels) + + def isManagerOf(self, pt): + for managed in self.manages: + if pt is managed: + return True + return False + def isManagedBy(self, pt): + return pt in self.managers + + def isManager(self): + return len(self.manages) > 0 + def isManaged(self): + return 0 < len(self.managers) + def isToplevel(self): + return not self.isManaged() + + def manager(self): + assert 1 == len(self.managers) + for mgr in self.managers: return mgr + +class ActorType(IPDLType): + def __init__(self, protocol, state=None, nullable=0): + self.protocol = protocol + self.state = state + self.nullable = nullable + def isActor(self): return True + + def name(self): + return self.protocol.name() + def fullname(self): + return self.protocol.fullname() + +class _CompoundType(IPDLType): + def __init__(self): + self.defined = False # bool + self.mutualRec = set() # set(_CompoundType | ArrayType) + def isAtom(self): + return False + def isCompound(self): + return True + def itercomponents(self): + raise Exception('"pure virtual" method') + + def mutuallyRecursiveWith(self, t, exploring=None): + '''|self| is mutually recursive with |t| iff |self| and |t| +are in a cycle in the type graph rooted at |self|. This function +looks for such a cycle and returns True if found.''' + if exploring is None: + exploring = set() + + if t.isAtom(): + return False + elif t is self or t in self.mutualRec: + return True + elif t.isArray(): + isrec = self.mutuallyRecursiveWith(t.basetype, exploring) + if isrec: self.mutualRec.add(t) + return isrec + elif t in exploring: + return False + + exploring.add(t) + for c in t.itercomponents(): + if self.mutuallyRecursiveWith(c, exploring): + self.mutualRec.add(c) + return True + exploring.remove(t) + + return False + +class StructType(_CompoundType): + def __init__(self, qname, fields): + _CompoundType.__init__(self) + self.qname = qname + self.fields = fields # [ Type ] + + def isStruct(self): return True + def itercomponents(self): + for f in self.fields: + yield f + + def name(self): return self.qname.baseid + def fullname(self): return str(self.qname) + +class UnionType(_CompoundType): + def __init__(self, qname, components): + _CompoundType.__init__(self) + self.qname = qname + self.components = components # [ Type ] + + def isUnion(self): return True + def itercomponents(self): + for c in self.components: + yield c + + def name(self): return self.qname.baseid + def fullname(self): return str(self.qname) + +class ArrayType(IPDLType): + def __init__(self, basetype): + self.basetype = basetype + def isAtom(self): return False + def isArray(self): return True + + def name(self): return self.basetype.name() +'[]' + def fullname(self): return self.basetype.fullname() +'[]' + +class ShmemType(IPDLType): + def __init__(self, qname): + self.qname = qname + def isShmem(self): return True + + def name(self): + return self.qname.baseid + def fullname(self): + return str(self.qname) + +class FDType(IPDLType): + def __init__(self, qname): + self.qname = qname + def isFD(self): return True + + def name(self): + return self.qname.baseid + def fullname(self): + return str(self.qname) + +class EndpointType(IPDLType): + def __init__(self, qname): + self.qname = qname + def isEndpoint(self): return True + + def name(self): + return self.qname.baseid + def fullname(self): + return str(self.qname) + +def iteractortypes(t, visited=None): + """Iterate over any actor(s) buried in |type|.""" + if visited is None: + visited = set() + + # XXX |yield| semantics makes it hard to use TypeVisitor + if not t.isIPDL(): + return + elif t.isActor(): + yield t + elif t.isArray(): + for actor in iteractortypes(t.basetype, visited): + yield actor + elif t.isCompound() and t not in visited: + visited.add(t) + for c in t.itercomponents(): + for actor in iteractortypes(c, visited): + yield actor + +def hasactor(type): + """Return true iff |type| is an actor or has one buried within.""" + for _ in iteractortypes(type): return True + return False + +def hasshmem(type): + """Return true iff |type| is shmem or has it buried within.""" + class found: pass + class findShmem(TypeVisitor): + def visitShmemType(self, s): raise found() + try: + type.accept(findShmem()) + except found: + return True + return False + +def hasfd(type): + """Return true iff |type| is fd or has it buried within.""" + class found: pass + class findFD(TypeVisitor): + def visitFDType(self, s): raise found() + try: + type.accept(findFD()) + except found: + return True + return False + +##-------------------- +_builtinloc = Loc('<builtin>', 0) +def makeBuiltinUsing(tname): + quals = tname.split('::') + base = quals.pop() + quals = quals[0:] + return UsingStmt(_builtinloc, + TypeSpec(_builtinloc, + QualifiedId(_builtinloc, base, quals))) + +builtinUsing = [ makeBuiltinUsing(t) for t in builtin.Types ] +builtinHeaderIncludes = [ CxxInclude(_builtinloc, f) for f in builtin.HeaderIncludes ] + +def errormsg(loc, fmt, *args): + while not isinstance(loc, Loc): + if loc is None: loc = Loc.NONE + else: loc = loc.loc + return '%s: error: %s'% (str(loc), fmt % args) + +##-------------------- +class SymbolTable: + def __init__(self, errors): + self.errors = errors + self.scopes = [ { } ] # stack({}) + self.globalScope = self.scopes[0] + self.currentScope = self.globalScope + + def enterScope(self, node): + assert (isinstance(self.scopes[0], dict) + and self.globalScope is self.scopes[0]) + assert (isinstance(self.currentScope, dict)) + + if not hasattr(node, 'symtab'): + node.symtab = { } + + self.scopes.append(node.symtab) + self.currentScope = self.scopes[-1] + + def exitScope(self, node): + symtab = self.scopes.pop() + assert self.currentScope is symtab + + self.currentScope = self.scopes[-1] + + assert (isinstance(self.scopes[0], dict) + and self.globalScope is self.scopes[0]) + assert isinstance(self.currentScope, dict) + + def lookup(self, sym): + # NB: since IPDL doesn't allow any aliased names of different types, + # it doesn't matter in which order we walk the scope chain to resolve + # |sym| + for scope in self.scopes: + decl = scope.get(sym, None) + if decl is not None: return decl + return None + + def declare(self, decl): + assert decl.progname or decl.shortname or decl.fullname + assert decl.loc + assert decl.type + + def tryadd(name): + olddecl = self.lookup(name) + if olddecl is not None: + self.errors.append(errormsg( + decl.loc, + "redeclaration of symbol `%s', first declared at %s", + name, olddecl.loc)) + return + self.currentScope[name] = decl + decl.scope = self.currentScope + + if decl.progname: tryadd(decl.progname) + if decl.shortname: tryadd(decl.shortname) + if decl.fullname: tryadd(decl.fullname) + + +class TypeCheck: + '''This pass sets the .type attribute of every AST node. For some +nodes, the type is meaningless and it is set to "VOID." This pass +also sets the .decl attribute of AST nodes for which that is relevant; +a decl says where, with what type, and under what name(s) a node was +declared. + +With this information, it finally type checks the AST.''' + + def __init__(self): + # NB: no IPDL compile will EVER print a warning. A program has + # one of two attributes: it is either well typed, or not well typed. + self.errors = [ ] # [ string ] + + def check(self, tu, errout=sys.stderr): + def runpass(tcheckpass): + tu.accept(tcheckpass) + if len(self.errors): + self.reportErrors(errout) + return False + return True + + # tag each relevant node with "decl" information, giving type, name, + # and location of declaration + if not runpass(GatherDecls(builtinUsing, self.errors)): + return False + + # now that the nodes have decls, type checking is much easier. + if not runpass(CheckTypes(self.errors)): + return False + + if not (runpass(BuildProcessGraph(self.errors)) + and runpass(CheckProcessGraph(self.errors))): + return False + + if (tu.protocol + and len(tu.protocol.startStates) + and not runpass(CheckStateMachine(self.errors))): + return False + return True + + def reportErrors(self, errout): + for error in self.errors: + print >>errout, error + + +class TcheckVisitor(Visitor): + def __init__(self, symtab, errors): + self.symtab = symtab + self.errors = errors + + def error(self, loc, fmt, *args): + self.errors.append(errormsg(loc, fmt, *args)) + + def declare(self, loc, type, shortname=None, fullname=None, progname=None): + d = Decl(loc) + d.type = type + d.progname = progname + d.shortname = shortname + d.fullname = fullname + self.symtab.declare(d) + return d + +class GatherDecls(TcheckVisitor): + def __init__(self, builtinUsing, errors): + # |self.symtab| is the symbol table for the translation unit + # currently being visited + TcheckVisitor.__init__(self, None, errors) + self.builtinUsing = builtinUsing + + def visitTranslationUnit(self, tu): + # all TranslationUnits declare symbols in global scope + if hasattr(tu, 'symtab'): + return + tu.symtab = SymbolTable(self.errors) + savedSymtab = self.symtab + self.symtab = tu.symtab + + # pretend like the translation unit "using"-ed these for the + # sake of type checking and C++ code generation + tu.builtinUsing = self.builtinUsing + + # for everyone's sanity, enforce that the filename and tu name + # match + basefilename = os.path.basename(tu.filename) + expectedfilename = '%s.ipdl'% (tu.name) + if not tu.protocol: + # header + expectedfilename += 'h' + if basefilename != expectedfilename: + self.error(tu.loc, + "expected file for translation unit `%s' to be named `%s'; instead it's named `%s'", + tu.name, expectedfilename, basefilename) + + if tu.protocol: + assert tu.name == tu.protocol.name + + p = tu.protocol + + # FIXME/cjones: it's a little weird and counterintuitive + # to put both the namespace and non-namespaced name in the + # global scope. try to figure out something better; maybe + # a type-neutral |using| that works for C++ and protocol + # types? + qname = p.qname() + if 0 == len(qname.quals): + fullname = None + else: + fullname = str(qname) + p.decl = self.declare( + loc=p.loc, + type=ProtocolType(qname, p.nestedRange, p.sendSemantics, + stateless=(0 == len(p.transitionStmts))), + shortname=p.name, + fullname=fullname) + + p.parentEndpointDecl = self.declare( + loc=p.loc, + type=EndpointType(QualifiedId(p.loc, 'Endpoint<' + fullname + 'Parent>', ['mozilla', 'ipc'])), + shortname='Endpoint<' + p.name + 'Parent>') + p.childEndpointDecl = self.declare( + loc=p.loc, + type=EndpointType(QualifiedId(p.loc, 'Endpoint<' + fullname + 'Child>', ['mozilla', 'ipc'])), + shortname='Endpoint<' + p.name + 'Child>') + + # XXX ugh, this sucks. but we need this information to compute + # what friend decls we need in generated C++ + p.decl.type._ast = p + + # make sure we have decls for all dependent protocols + for pinc in tu.includes: + pinc.accept(self) + + # declare imported (and builtin) C++ types + for using in tu.builtinUsing: + using.accept(self) + for using in tu.using: + using.accept(self) + + # first pass to "forward-declare" all structs and unions in + # order to support recursive definitions + for su in tu.structsAndUnions: + self.declareStructOrUnion(su) + + # second pass to check each definition + for su in tu.structsAndUnions: + su.accept(self) + for inc in tu.includes: + if inc.tu.filetype == 'header': + for su in inc.tu.structsAndUnions: + su.accept(self) + + if tu.protocol: + # grab symbols in the protocol itself + p.accept(self) + + + tu.type = VOID + + self.symtab = savedSymtab + + def declareStructOrUnion(self, su): + if hasattr(su, 'decl'): + self.symtab.declare(su.decl) + return + + qname = su.qname() + if 0 == len(qname.quals): + fullname = None + else: + fullname = str(qname) + + if isinstance(su, StructDecl): + sutype = StructType(qname, [ ]) + elif isinstance(su, UnionDecl): + sutype = UnionType(qname, [ ]) + else: assert 0 and 'unknown type' + + # XXX more suckage. this time for pickling structs/unions + # declared in headers. + sutype._ast = su + + su.decl = self.declare( + loc=su.loc, + type=sutype, + shortname=su.name, + fullname=fullname) + + + def visitInclude(self, inc): + if inc.tu is None: + self.error( + inc.loc, + "(type checking here will be unreliable because of an earlier error)") + return + inc.tu.accept(self) + if inc.tu.protocol: + self.symtab.declare(inc.tu.protocol.decl) + self.symtab.declare(inc.tu.protocol.parentEndpointDecl) + self.symtab.declare(inc.tu.protocol.childEndpointDecl) + else: + # This is a header. Import its "exported" globals into + # our scope. + for using in inc.tu.using: + using.accept(self) + for su in inc.tu.structsAndUnions: + self.declareStructOrUnion(su) + + def visitStructDecl(self, sd): + # If we've already processed this struct, don't do it again. + if hasattr(sd, 'symtab'): + return + + stype = sd.decl.type + + self.symtab.enterScope(sd) + + for f in sd.fields: + ftypedecl = self.symtab.lookup(str(f.typespec)) + if ftypedecl is None: + self.error(f.loc, "field `%s' of struct `%s' has unknown type `%s'", + f.name, sd.name, str(f.typespec)) + continue + + f.decl = self.declare( + loc=f.loc, + type=self._canonicalType(ftypedecl.type, f.typespec), + shortname=f.name, + fullname=None) + stype.fields.append(f.decl.type) + + self.symtab.exitScope(sd) + + def visitUnionDecl(self, ud): + utype = ud.decl.type + + # If we've already processed this union, don't do it again. + if len(utype.components): + return + + for c in ud.components: + cdecl = self.symtab.lookup(str(c)) + if cdecl is None: + self.error(c.loc, "unknown component type `%s' of union `%s'", + str(c), ud.name) + continue + utype.components.append(self._canonicalType(cdecl.type, c)) + + def visitUsingStmt(self, using): + fullname = str(using.type) + if using.type.basename() == fullname: + fullname = None + if fullname == 'mozilla::ipc::Shmem': + ipdltype = ShmemType(using.type.spec) + elif fullname == 'mozilla::ipc::FileDescriptor': + ipdltype = FDType(using.type.spec) + else: + ipdltype = ImportedCxxType(using.type.spec) + existingType = self.symtab.lookup(ipdltype.fullname()) + if existingType and existingType.fullname == ipdltype.fullname(): + using.decl = existingType + return + using.decl = self.declare( + loc=using.loc, + type=ipdltype, + shortname=using.type.basename(), + fullname=fullname) + + def visitProtocol(self, p): + # protocol scope + self.symtab.enterScope(p) + + for spawns in p.spawnsStmts: + spawns.accept(self) + + for bridges in p.bridgesStmts: + bridges.accept(self) + + for opens in p.opensStmts: + opens.accept(self) + + seenmgrs = set() + for mgr in p.managers: + if mgr.name in seenmgrs: + self.error(mgr.loc, "manager `%s' appears multiple times", + mgr.name) + continue + + seenmgrs.add(mgr.name) + mgr.of = p + mgr.accept(self) + + for managed in p.managesStmts: + managed.manager = p + managed.accept(self) + + if 0 == len(p.managers) and 0 == len(p.messageDecls): + self.error(p.loc, + "top-level protocol `%s' cannot be empty", + p.name) + + setattr(self, 'currentProtocolDecl', p.decl) + for msg in p.messageDecls: + msg.accept(self) + del self.currentProtocolDecl + + p.decl.type.hasDelete = (not not self.symtab.lookup(_DELETE_MSG)) + if not (p.decl.type.hasDelete or p.decl.type.isToplevel()): + self.error( + p.loc, + "destructor declaration `%s(...)' required for managed protocol `%s'", + _DELETE_MSG, p.name) + + p.decl.type.hasReentrantDelete = p.decl.type.hasDelete and self.symtab.lookup(_DELETE_MSG).type.isInterrupt() + + for managed in p.managesStmts: + mgdname = managed.name + ctordecl = self.symtab.lookup(mgdname +'Constructor') + + if not (ctordecl and ctordecl.type.isCtor()): + self.error( + managed.loc, + "constructor declaration required for managed protocol `%s' (managed by protocol `%s')", + mgdname, p.name) + + p.states = { } + + if len(p.transitionStmts): + p.startStates = [ ts for ts in p.transitionStmts + if ts.state.start ] + if 0 == len(p.startStates): + p.startStates = [ p.transitionStmts[0] ] + + # declare implicit "any", "dead", and "dying" states + self.declare(loc=State.ANY.loc, + type=StateType(p.decl.type, State.ANY.name, start=False), + progname=State.ANY.name) + self.declare(loc=State.DEAD.loc, + type=StateType(p.decl.type, State.DEAD.name, start=False), + progname=State.DEAD.name) + if p.decl.type.hasReentrantDelete: + self.declare(loc=State.DYING.loc, + type=StateType(p.decl.type, State.DYING.name, start=False), + progname=State.DYING.name) + + # declare each state before decorating their mention + for trans in p.transitionStmts: + p.states[trans.state] = trans + trans.state.decl = self.declare( + loc=trans.state.loc, + type=StateType(p.decl.type, trans.state, trans.state.start), + progname=trans.state.name) + + for trans in p.transitionStmts: + self.seentriggers = set() + trans.accept(self) + + if not (p.decl.type.stateless + or (p.decl.type.isToplevel() + and None is self.symtab.lookup(_DELETE_MSG))): + # add a special state |state DEAD: null goto DEAD;| + deadtrans = TransitionStmt.makeNullStmt(State.DEAD) + p.states[State.DEAD] = deadtrans + if p.decl.type.hasReentrantDelete: + dyingtrans = TransitionStmt.makeNullStmt(State.DYING) + p.states[State.DYING] = dyingtrans + + # visit the message decls once more and resolve the state names + # attached to actor params and returns + def resolvestate(loc, actortype): + assert actortype.isIPDL() and actortype.isActor() + + # already resolved this guy's state + if isinstance(actortype.state, Decl): + return + + if actortype.state is None: + # we thought this was a C++ type until type checking, + # when we realized it was an IPDL actor type. But + # that means that the actor wasn't specified to be in + # any particular state + actortype.state = State.ANY + + statename = actortype.state.name + # FIXME/cjones: this is just wrong. we need the symbol table + # of the protocol this actor refers to. low priority bug + # since nobody's using this feature yet + statedecl = self.symtab.lookup(statename) + if statedecl is None: + self.error( + loc, + "protocol `%s' does not have the state `%s'", + actortype.protocol.name(), + statename) + elif not statedecl.type.isState(): + self.error( + loc, + "tag `%s' is supposed to be of state type, but is instead of type `%s'", + statename, + statedecl.type.typename()) + else: + actortype.state = statedecl.type + + for msg in p.messageDecls: + for iparam in msg.inParams: + loc = iparam.loc + for actortype in iteractortypes(iparam.type): + resolvestate(loc, actortype) + for oparam in msg.outParams: + loc = oparam.loc + for actortype in iteractortypes(oparam.type): + resolvestate(loc, actortype) + + # FIXME/cjones declare all the little C++ thingies that will + # be generated. they're not relevant to IPDL itself, but + # those ("invisible") symbols can clash with others in the + # IPDL spec, and we'd like to catch those before C++ compilers + # are allowed to obfuscate the error + + self.symtab.exitScope(p) + + + def visitSpawnsStmt(self, spawns): + pname = spawns.proto + spawns.proto = self.symtab.lookup(pname) + if spawns.proto is None: + self.error(spawns.loc, + "spawned protocol `%s' has not been declared", + pname) + + def visitBridgesStmt(self, bridges): + def lookup(p): + decl = self.symtab.lookup(p) + if decl is None: + self.error(bridges.loc, + "bridged protocol `%s' has not been declared", p) + return decl + bridges.parentSide = lookup(bridges.parentSide) + bridges.childSide = lookup(bridges.childSide) + + def visitOpensStmt(self, opens): + pname = opens.proto + opens.proto = self.symtab.lookup(pname) + if opens.proto is None: + self.error(opens.loc, + "opened protocol `%s' has not been declared", + pname) + + + def visitManager(self, mgr): + mgrdecl = self.symtab.lookup(mgr.name) + pdecl = mgr.of.decl + assert pdecl + + pname, mgrname = pdecl.shortname, mgr.name + loc = mgr.loc + + if mgrdecl is None: + self.error( + loc, + "protocol `%s' referenced as |manager| of `%s' has not been declared", + mgrname, pname) + elif not isinstance(mgrdecl.type, ProtocolType): + self.error( + loc, + "entity `%s' referenced as |manager| of `%s' is not of `protocol' type; instead it is of type `%s'", + mgrname, pname, mgrdecl.type.typename()) + else: + mgr.decl = mgrdecl + pdecl.type.addManager(mgrdecl.type) + + + def visitManagesStmt(self, mgs): + mgsdecl = self.symtab.lookup(mgs.name) + pdecl = mgs.manager.decl + assert pdecl + + pname, mgsname = pdecl.shortname, mgs.name + loc = mgs.loc + + if mgsdecl is None: + self.error(loc, + "protocol `%s', managed by `%s', has not been declared", + mgsname, pname) + elif not isinstance(mgsdecl.type, ProtocolType): + self.error( + loc, + "%s declares itself managing a non-`protocol' entity `%s' of type `%s'", + pname, mgsname, mgsdecl.type.typename()) + else: + mgs.decl = mgsdecl + pdecl.type.manages.append(mgsdecl.type) + + + def visitMessageDecl(self, md): + msgname = md.name + loc = md.loc + + isctor = False + isdtor = False + cdtype = None + + decl = self.symtab.lookup(msgname) + if decl is not None and decl.type.isProtocol(): + # probably a ctor. we'll check validity later. + msgname += 'Constructor' + isctor = True + cdtype = decl.type + elif decl is not None: + self.error(loc, "message name `%s' already declared as `%s'", + msgname, decl.type.typename()) + # if we error here, no big deal; move on to find more + + if _DELETE_MSG == msgname: + isdtor = True + cdtype = self.currentProtocolDecl.type + + + # enter message scope + self.symtab.enterScope(md) + + msgtype = MessageType(md.nested, md.prio, md.sendSemantics, md.direction, + ctor=isctor, dtor=isdtor, cdtype=cdtype, + compress=md.compress, verify=md.verify) + + # replace inparam Param nodes with proper Decls + def paramToDecl(param): + ptname = param.typespec.basename() + ploc = param.typespec.loc + + ptdecl = self.symtab.lookup(ptname) + if ptdecl is None: + self.error( + ploc, + "argument typename `%s' of message `%s' has not been declared", + ptname, msgname) + ptype = VOID + else: + ptype = self._canonicalType(ptdecl.type, param.typespec, + chmodallowed=1) + return self.declare(loc=ploc, + type=ptype, + progname=param.name) + + for i, inparam in enumerate(md.inParams): + pdecl = paramToDecl(inparam) + msgtype.params.append(pdecl.type) + md.inParams[i] = pdecl + for i, outparam in enumerate(md.outParams): + pdecl = paramToDecl(outparam) + msgtype.returns.append(pdecl.type) + md.outParams[i] = pdecl + + self.symtab.exitScope(md) + + md.decl = self.declare( + loc=loc, + type=msgtype, + progname=msgname) + md.protocolDecl = self.currentProtocolDecl + md.decl._md = md + + + def visitTransitionStmt(self, ts): + self.seentriggers = set() + TcheckVisitor.visitTransitionStmt(self, ts) + + def visitTransition(self, t): + loc = t.loc + + # check the trigger message + mname = t.msg + if t in self.seentriggers: + self.error(loc, "trigger `%s' appears multiple times", t.msg) + self.seentriggers.add(t) + + mdecl = self.symtab.lookup(mname) + if mdecl is not None and mdecl.type.isIPDL() and mdecl.type.isProtocol(): + mdecl = self.symtab.lookup(mname +'Constructor') + + if mdecl is None: + self.error(loc, "message `%s' has not been declared", mname) + elif not mdecl.type.isMessage(): + self.error( + loc, + "`%s' should have message type, but instead has type `%s'", + mname, mdecl.type.typename()) + else: + t.msg = mdecl + + # check the to-states + seenstates = set() + for toState in t.toStates: + sname = toState.name + sdecl = self.symtab.lookup(sname) + + if sname in seenstates: + self.error(loc, "to-state `%s' appears multiple times", sname) + seenstates.add(sname) + + if sdecl is None: + self.error(loc, "state `%s' has not been declared", sname) + elif not sdecl.type.isState(): + self.error( + loc, "`%s' should have state type, but instead has type `%s'", + sname, sdecl.type.typename()) + else: + toState.decl = sdecl + toState.start = sdecl.type.start + + t.toStates = set(t.toStates) + + + def _canonicalType(self, itype, typespec, chmodallowed=0): + loc = typespec.loc + + if itype.isIPDL(): + if itype.isProtocol(): + itype = ActorType(itype, + state=typespec.state, + nullable=typespec.nullable) + # FIXME/cjones: ShmemChmod is disabled until bug 524193 + if 0 and chmodallowed and itype.isShmem(): + itype = ShmemChmodType( + itype, + myChmod=typespec.myChmod, + otherChmod=typespec.otherChmod) + + if ((typespec.myChmod or typespec.otherChmod) + and not (itype.isIPDL() and (itype.isShmem() or itype.isChmod()))): + self.error( + loc, + "fine-grained access controls make no sense for type `%s'", + itype.name()) + + if not chmodallowed and (typespec.myChmod or typespec.otherChmod): + self.error(loc, "fine-grained access controls not allowed here") + + if typespec.nullable and not (itype.isIPDL() and itype.isActor()): + self.error( + loc, + "`nullable' qualifier for type `%s' makes no sense", + itype.name()) + + if typespec.array: + itype = ArrayType(itype) + + return itype + + +##----------------------------------------------------------------------------- + +def checkcycles(p, stack=None): + cycles = [] + + if stack is None: + stack = [] + + for cp in p.manages: + # special case for self-managed protocols + if cp is p: + continue + + if cp in stack: + return [stack + [p, cp]] + cycles += checkcycles(cp, stack + [p]) + + return cycles + +def formatcycles(cycles): + r = [] + for cycle in cycles: + s = " -> ".join([ptype.name() for ptype in cycle]) + r.append("`%s'" % s) + return ", ".join(r) + + +def fullyDefined(t, exploring=None): + '''The rules for "full definition" of a type are + defined(atom) := true + defined(array basetype) := defined(basetype) + defined(struct f1 f2...) := defined(f1) and defined(f2) and ... + defined(union c1 c2 ...) := defined(c1) or defined(c2) or ... +''' + if exploring is None: + exploring = set() + + if t.isAtom(): + return True + elif t.isArray(): + return fullyDefined(t.basetype, exploring) + elif t.defined: + return True + assert t.isCompound() + + if t in exploring: + return False + + exploring.add(t) + for c in t.itercomponents(): + cdefined = fullyDefined(c, exploring) + if t.isStruct() and not cdefined: + t.defined = False + break + elif t.isUnion() and cdefined: + t.defined = True + break + else: + if t.isStruct(): t.defined = True + elif t.isUnion(): t.defined = False + exploring.remove(t) + + return t.defined + + +class CheckTypes(TcheckVisitor): + def __init__(self, errors): + # don't need the symbol table, we just want the error reporting + TcheckVisitor.__init__(self, None, errors) + self.visited = set() + self.ptype = None + + def visitInclude(self, inc): + if inc.tu.filename in self.visited: + return + self.visited.add(inc.tu.filename) + if inc.tu.protocol: + inc.tu.protocol.accept(self) + + + def visitStructDecl(self, sd): + if not fullyDefined(sd.decl.type): + self.error(sd.decl.loc, + "struct `%s' is only partially defined", sd.name) + + def visitUnionDecl(self, ud): + if not fullyDefined(ud.decl.type): + self.error(ud.decl.loc, + "union `%s' is only partially defined", ud.name) + + + def visitProtocol(self, p): + self.ptype = p.decl.type + + # check that we require no more "power" than our manager protocols + ptype, pname = p.decl.type, p.decl.shortname + + if len(p.spawnsStmts) and not ptype.isToplevel(): + self.error(p.decl.loc, + "protocol `%s' is not top-level and so cannot declare |spawns|", + pname) + + if len(p.bridgesStmts) and not ptype.isToplevel(): + self.error(p.decl.loc, + "protocol `%s' is not top-level and so cannot declare |bridges|", + pname) + + if len(p.opensStmts) and not ptype.isToplevel(): + self.error(p.decl.loc, + "protocol `%s' is not top-level and so cannot declare |opens|", + pname) + + for mgrtype in ptype.managers: + if mgrtype is not None and ptype.needsMoreJuiceThan(mgrtype): + self.error( + p.decl.loc, + "protocol `%s' requires more powerful send semantics than its manager `%s' provides", + pname, mgrtype.name()) + + # XXX currently we don't require a delete() message of top-level + # actors. need to let experience guide this decision + if not ptype.isToplevel(): + for md in p.messageDecls: + if _DELETE_MSG == md.name: break + else: + self.error( + p.decl.loc, + "managed protocol `%s' requires a `delete()' message to be declared", + p.name) + else: + cycles = checkcycles(p.decl.type) + if cycles: + self.error( + p.decl.loc, + "cycle(s) detected in manager/manages heirarchy: %s", + formatcycles(cycles)) + + if 1 == len(ptype.managers) and ptype is ptype.manager(): + self.error( + p.decl.loc, + "top-level protocol `%s' cannot manage itself", + p.name) + + return Visitor.visitProtocol(self, p) + + + def visitSpawnsStmt(self, spawns): + if not self.ptype.isToplevel(): + self.error(spawns.loc, + "only top-level protocols can have |spawns| statements; `%s' cannot", + self.ptype.name()) + return + + spawnedType = spawns.proto.type + if not (spawnedType.isIPDL() and spawnedType.isProtocol() + and spawnedType.isToplevel()): + self.error(spawns.loc, + "cannot spawn non-top-level-protocol `%s'", + spawnedType.name()) + else: + self.ptype.addSpawn(spawnedType) + + + def visitBridgesStmt(self, bridges): + if not self.ptype.isToplevel(): + self.error(bridges.loc, + "only top-level protocols can have |bridges| statements; `%s' cannot", + self.ptype.name()) + return + + parentType = bridges.parentSide.type + childType = bridges.childSide.type + if not (parentType.isIPDL() and parentType.isProtocol() + and childType.isIPDL() and childType.isProtocol() + and parentType.isToplevel() and childType.isToplevel()): + self.error(bridges.loc, + "cannot bridge non-top-level-protocol(s) `%s' and `%s'", + parentType.name(), childType.name()) + + + def visitOpensStmt(self, opens): + if not self.ptype.isToplevel(): + self.error(opens.loc, + "only top-level protocols can have |opens| statements; `%s' cannot", + self.ptype.name()) + return + + openedType = opens.proto.type + if not (openedType.isIPDL() and openedType.isProtocol() + and openedType.isToplevel()): + self.error(opens.loc, + "cannot open non-top-level-protocol `%s'", + openedType.name()) + else: + self.ptype.addOpen(openedType) + + + def visitManagesStmt(self, mgs): + pdecl = mgs.manager.decl + ptype, pname = pdecl.type, pdecl.shortname + + mgsdecl = mgs.decl + mgstype, mgsname = mgsdecl.type, mgsdecl.shortname + + loc = mgs.loc + + # we added this information; sanity check it + assert ptype.isManagerOf(mgstype) + + # check that the "managed" protocol agrees + if not mgstype.isManagedBy(ptype): + self.error( + loc, + "|manages| declaration in protocol `%s' does not match any |manager| declaration in protocol `%s'", + pname, mgsname) + + + def visitManager(self, mgr): + # FIXME/bug 541126: check that the protocol graph is acyclic + + pdecl = mgr.of.decl + ptype, pname = pdecl.type, pdecl.shortname + + mgrdecl = mgr.decl + mgrtype, mgrname = mgrdecl.type, mgrdecl.shortname + + # we added this information; sanity check it + assert ptype.isManagedBy(mgrtype) + + loc = mgr.loc + + # check that the "manager" protocol agrees + if not mgrtype.isManagerOf(ptype): + self.error( + loc, + "|manager| declaration in protocol `%s' does not match any |manages| declaration in protocol `%s'", + pname, mgrname) + + + def visitMessageDecl(self, md): + mtype, mname = md.decl.type, md.decl.progname + ptype, pname = md.protocolDecl.type, md.protocolDecl.shortname + + loc = md.decl.loc + + if mtype.nested == INSIDE_SYNC_NESTED and not mtype.isSync(): + self.error( + loc, + "inside_sync nested messages must be sync (here, message `%s' in protocol `%s')", + mname, pname) + + if mtype.nested == INSIDE_CPOW_NESTED and (mtype.isOut() or mtype.isInout()): + self.error( + loc, + "inside_cpow nested parent-to-child messages are verboten (here, message `%s' in protocol `%s')", + mname, pname) + + # We allow inside_sync messages that are themselves sync to be sent from the + # parent. Normal and inside_cpow nested messages that are sync can only come from + # the child. + if mtype.isSync() and mtype.nested == NOT_NESTED and (mtype.isOut() or mtype.isInout()): + self.error( + loc, + "sync parent-to-child messages are verboten (here, message `%s' in protocol `%s')", + mname, pname) + + if mtype.needsMoreJuiceThan(ptype): + self.error( + loc, + "message `%s' requires more powerful send semantics than its protocol `%s' provides", + mname, pname) + + if mtype.isAsync() and len(mtype.returns): + # XXX/cjones could modify grammar to disallow this ... + self.error(loc, + "asynchronous message `%s' declares return values", + mname) + + if (mtype.compress and + (not mtype.isAsync() or mtype.isCtor() or mtype.isDtor())): + self.error( + loc, + "message `%s' in protocol `%s' requests compression but is not async or is special (ctor or dtor)", + mname[:-len('constructor')], pname) + + if mtype.isCtor() and not ptype.isManagerOf(mtype.constructedType()): + self.error( + loc, + "ctor for protocol `%s', which is not managed by protocol `%s'", + mname[:-len('constructor')], pname) + + + def visitTransition(self, t): + _YNC = [ ASYNC, SYNC ] + + loc = t.loc + impliedDirection, impliedSems = { + SEND: [ OUT, _YNC ], RECV: [ IN, _YNC ], + CALL: [ OUT, INTR ], ANSWER: [ IN, INTR ], + } [t.trigger] + + if (OUT is impliedDirection and t.msg.type.isIn() + or IN is impliedDirection and t.msg.type.isOut() + or _YNC is impliedSems and t.msg.type.isInterrupt() + or INTR is impliedSems and (not t.msg.type.isInterrupt())): + mtype = t.msg.type + + self.error( + loc, "%s %s message `%s' is not `%s'd", + mtype.sendSemantics.pretty, mtype.direction.pretty, + t.msg.progname, + t.trigger.pretty) + +##----------------------------------------------------------------------------- + +class Process: + def __init__(self): + self.actors = set() # set(Actor) + self.edges = { } # Actor -> [ SpawnsEdge ] + self.spawn = set() # set(Actor) + + def edge(self, spawner, spawn): + if spawner not in self.edges: self.edges[spawner] = [ ] + self.edges[spawner].append(SpawnsEdge(spawner, spawn)) + self.spawn.add(spawn) + + def iteredges(self): + for edgelist in self.edges.itervalues(): + for edge in edgelist: + yield edge + + def merge(self, o): + 'Merge the Process |o| into this Process' + if self == o: + return + for actor in o.actors: + ProcessGraph.actorToProcess[actor] = self + self.actors.update(o.actors) + self.edges.update(o.edges) + self.spawn.update(o.spawn) + ProcessGraph.processes.remove(o) + + def spawns(self, actor): + return actor in self.spawn + + def __cmp__(self, o): return cmp(self.actors, o.actors) + def __eq__(self, o): return self.actors == o.actors + def __hash__(self): return hash(id(self)) + def __repr__(self): + return reduce(lambda a, x: str(a) + str(x) +'|', self.actors, '|') + def __str__(self): return repr(self) + +class Actor: + def __init__(self, ptype, side): + self.ptype = ptype + self.side = side + + def asType(self): + return ActorType(self.ptype) + def other(self): + return Actor(self.ptype, _otherside(self.side)) + + def __cmp__(self, o): + return cmp(self.ptype, o.ptype) or cmp(self.side, o.side) + def __eq__(self, o): + return self.ptype == o.ptype and self.side == o.side + def __hash__(self): return hash(repr(self)) + def __repr__(self): return '%s%s'% (self.ptype.name(), self.side.title()) + def __str__(self): return repr(self) + +class SpawnsEdge: + def __init__(self, spawner, spawn): + self.spawner = spawner # Actor + self.spawn = spawn # Actor + def __repr__(self): + return '(%r)--spawns-->(%r)'% (self.spawner, self.spawn) + def __str__(self): return repr(self) + +class BridgeEdge: + def __init__(self, bridgeProto, parent, child): + self.bridgeProto = bridgeProto # ProtocolType + self.parent = parent # Actor + self.child = child # Actor + def __repr__(self): + return '(%r)--%s bridge-->(%r)'% ( + self.parent, self.bridgeProto.name(), self.child) + def __str__(self): return repr(self) + +class OpensEdge: + def __init__(self, opener, openedProto): + self.opener = opener # Actor + self.openedProto = openedProto # ProtocolType + def __repr__(self): + return '(%r)--opens-->(%s)'% (self.opener, self.openedProto.name()) + def __str__(self): return repr(self) + +# "singleton" class with state that persists across type checking of +# all protocols +class ProcessGraph: + processes = set() # set(Process) + bridges = { } # ProtocolType -> [ BridgeEdge ] + opens = { } # ProtocolType -> [ OpensEdge ] + actorToProcess = { } # Actor -> Process + visitedSpawns = set() # set(ActorType) + visitedBridges = set() # set(ActorType) + + @classmethod + def findProcess(cls, actor): + return cls.actorToProcess.get(actor, None) + + @classmethod + def getProcess(cls, actor): + if actor not in cls.actorToProcess: + p = Process() + p.actors.add(actor) + cls.processes.add(p) + cls.actorToProcess[actor] = p + return cls.actorToProcess[actor] + + @classmethod + def bridgesOf(cls, bridgeP): + return cls.bridges.get(bridgeP, []) + + @classmethod + def bridgeEndpointsOf(cls, ptype, side): + actor = Actor(ptype, side) + endpoints = [] + for b in cls.iterbridges(): + if b.parent == actor: + endpoints.append(Actor(b.bridgeProto, 'parent')) + if b.child == actor: + endpoints.append(Actor(b.bridgeProto, 'child')) + return endpoints + + @classmethod + def iterbridges(cls): + for edges in cls.bridges.itervalues(): + for bridge in edges: + yield bridge + + @classmethod + def opensOf(cls, openedP): + return cls.opens.get(openedP, []) + + @classmethod + def opensEndpointsOf(cls, ptype, side): + actor = Actor(ptype, side) + endpoints = [] + for o in cls.iteropens(): + if actor == o.opener: + endpoints.append(Actor(o.openedProto, o.opener.side)) + elif actor == o.opener.other(): + endpoints.append(Actor(o.openedProto, o.opener.other().side)) + return endpoints + + @classmethod + def iteropens(cls): + for edges in cls.opens.itervalues(): + for opens in edges: + yield opens + + @classmethod + def spawn(cls, spawner, remoteSpawn): + localSpawn = remoteSpawn.other() + spawnerProcess = ProcessGraph.getProcess(spawner) + spawnerProcess.merge(ProcessGraph.getProcess(localSpawn)) + spawnerProcess.edge(spawner, remoteSpawn) + + @classmethod + def bridge(cls, parent, child, bridgeP): + bridgeParent = Actor(bridgeP, 'parent') + parentProcess = ProcessGraph.getProcess(parent) + parentProcess.merge(ProcessGraph.getProcess(bridgeParent)) + bridgeChild = Actor(bridgeP, 'child') + childProcess = ProcessGraph.getProcess(child) + childProcess.merge(ProcessGraph.getProcess(bridgeChild)) + if bridgeP not in cls.bridges: + cls.bridges[bridgeP] = [ ] + cls.bridges[bridgeP].append(BridgeEdge(bridgeP, parent, child)) + + @classmethod + def open(cls, opener, opened, openedP): + remoteOpener, remoteOpened, = opener.other(), opened.other() + openerProcess = ProcessGraph.getProcess(opener) + openerProcess.merge(ProcessGraph.getProcess(opened)) + remoteOpenerProcess = ProcessGraph.getProcess(remoteOpener) + remoteOpenerProcess.merge(ProcessGraph.getProcess(remoteOpened)) + if openedP not in cls.opens: + cls.opens[openedP] = [ ] + cls.opens[openedP].append(OpensEdge(opener, openedP)) + + +class BuildProcessGraph(TcheckVisitor): + class findSpawns(TcheckVisitor): + def __init__(self, errors): + TcheckVisitor.__init__(self, None, errors) + + def visitTranslationUnit(self, tu): + TcheckVisitor.visitTranslationUnit(self, tu) + + def visitInclude(self, inc): + if inc.tu.protocol: + inc.tu.protocol.accept(self) + + def visitProtocol(self, p): + ptype = p.decl.type + # non-top-level protocols don't add any information + if not ptype.isToplevel() or ptype in ProcessGraph.visitedSpawns: + return + + ProcessGraph.visitedSpawns.add(ptype) + self.visiting = ptype + ProcessGraph.getProcess(Actor(ptype, 'parent')) + ProcessGraph.getProcess(Actor(ptype, 'child')) + return TcheckVisitor.visitProtocol(self, p) + + def visitSpawnsStmt(self, spawns): + # The picture here is: + # [ spawner | localSpawn | ??? ] (process 1) + # | + # | + # [ remoteSpawn | ???] (process 2) + # + # A spawns stmt tells us that |spawner| and |localSpawn| + # are in the same process. + spawner = Actor(self.visiting, spawns.side) + remoteSpawn = Actor(spawns.proto.type, spawns.spawnedAs) + ProcessGraph.spawn(spawner, remoteSpawn) + + def __init__(self, errors): + TcheckVisitor.__init__(self, None, errors) + self.visiting = None # ActorType + self.visited = set() # set(ActorType) + + def visitTranslationUnit(self, tu): + tu.accept(self.findSpawns(self.errors)) + TcheckVisitor.visitTranslationUnit(self, tu) + + def visitInclude(self, inc): + if inc.tu.protocol: + inc.tu.protocol.accept(self) + + def visitProtocol(self, p): + ptype = p.decl.type + # non-top-level protocols don't add any information + if not ptype.isToplevel() or ptype in ProcessGraph.visitedBridges: + return + + ProcessGraph.visitedBridges.add(ptype) + self.visiting = ptype + return TcheckVisitor.visitProtocol(self, p) + + def visitBridgesStmt(self, bridges): + bridgeProto = self.visiting + parentSideProto = bridges.parentSide.type + childSideProto = bridges.childSide.type + + # the picture here is: + # (process 1| + # [ parentSide(Parent|Child) | childSide(Parent|Child) | ... ] + # | | + # | (process 2| | + # [ parentSide(Child|Parent) | bridgeParent ] | + # | | + # | | (process 3| + # [ bridgeChild | childSide(Child|Parent) ] + # + # First we have to figure out which parentSide/childSide + # actors live in the same process. The possibilities are { + # parent, child } x { parent, child }. (Multiple matches + # aren't allowed yet.) Then we make ProcessGraph aware of the + # new bridge. + parentSideActor, childSideActor = None, None + pc = ( 'parent', 'child' ) + for parentSide, childSide in cartesian_product(pc, pc): + pactor = Actor(parentSideProto, parentSide) + pproc = ProcessGraph.findProcess(pactor) + cactor = Actor(childSideProto, childSide) + cproc = ProcessGraph.findProcess(cactor) + assert pproc and cproc + + if pproc == cproc: + if parentSideActor is not None: + if parentSideProto != childSideProto: + self.error(bridges.loc, + "ambiguous bridge `%s' between `%s' and `%s'", + bridgeProto.name(), + parentSideProto.name(), + childSideProto.name()) + else: + parentSideActor, childSideActor = pactor.other(), cactor.other() + + if parentSideActor is None: + self.error(bridges.loc, + "`%s' and `%s' cannot be bridged by `%s' ", + parentSideProto.name(), childSideProto.name(), + bridgeProto.name()) + + ProcessGraph.bridge(parentSideActor, childSideActor, bridgeProto) + + def visitOpensStmt(self, opens): + openedP = opens.proto.type + opener = Actor(self.visiting, opens.side) + opened = Actor(openedP, opens.side) + + # The picture here is: + # [ opener | opened ] (process 1) + # | | + # | | + # [ remoteOpener | remoteOpened ] (process 2) + # + # An opens stmt tells us that the pairs |opener|/|opened| + # and |remoteOpener|/|remoteOpened| are each in the same + # process. + ProcessGraph.open(opener, opened, openedP) + + +class CheckProcessGraph(TcheckVisitor): + def __init__(self, errors): + TcheckVisitor.__init__(self, None, errors) + + # TODO: verify spawns-per-process assumption and check that graph + # is a dag + def visitTranslationUnit(self, tu): + if 0: + print 'Processes' + for process in ProcessGraph.processes: + print ' ', process + for edge in process.iteredges(): + print ' ', edge + print 'Bridges' + for bridgeList in ProcessGraph.bridges.itervalues(): + for bridge in bridgeList: + print ' ', bridge + print 'Opens' + for opensList in ProcessGraph.opens.itervalues(): + for opens in opensList: + print ' ', opens + +##----------------------------------------------------------------------------- + +class CheckStateMachine(TcheckVisitor): + def __init__(self, errors): + # don't need the symbol table, we just want the error reporting + TcheckVisitor.__init__(self, None, errors) + self.p = None + + def visitProtocol(self, p): + self.p = p + self.checkReachability(p) + for ts in p.transitionStmts: + ts.accept(self) + + def visitTransitionStmt(self, ts): + # We want to disallow "race conditions" in protocols. These + # can occur when a protocol state machine has a state that + # allows triggers of opposite direction. That declaration + # allows the parent to send the child a message at the + # exact instance the child sends the parent a message. One of + # those messages would (probably) violate the state machine + # and cause the child to be terminated. It's obviously very + # nice if we can forbid this at the level of IPDL state + # machines, rather than resorting to static or dynamic + # checking of C++ implementation code. + # + # An easy way to avoid this problem in IPDL is to only allow + # "unidirectional" protocol states; that is, from each state, + # only send or only recv triggers are allowed. This approach + # is taken by the Singularity project's "contract-based + # message channels." However, this can be something of a + # notational burden for stateful protocols. + # + # If two messages race, the effect is that the parent's and + # child's states get temporarily out of sync. Informally, + # IPDL allows this *only if* the state machines get out of + # sync for only *one* step (state machine transition), then + # sync back up. This is a design decision: the states could + # be allowd to get out of sync for any constant k number of + # steps. (If k is unbounded, there's no point in presenting + # the abstraction of parent and child actor states being + # "entangled".) The working hypothesis is that the more steps + # the states are allowed to be out of sync, the harder it is + # to reason about the protocol. + # + # Slightly less informally, two messages are allowed to race + # only if processing them in either order leaves the protocol + # in the same state. That is, messages A and B are allowed to + # race only if processing A then B leaves the protocol in + # state S, *and* processing B then A also leaves the protocol + # in state S. Technically, if this holds, then messages A and + # B could be called "commutative" wrt to actor state. + # + # "Formally", state machine definitions must adhere to two + # rules. + # + # *Rule 1*: from a state S, all sync triggers must be of the same + # "direction," i.e. only |send| or only |recv| + # + # (Pairs of sync messages can't commute, because otherwise + # deadlock can occur from simultaneously in-flight sync + # requests.) + # + # *Rule 2*: the "Diamond Rule". + # from a state S, + # for any pair of triggers t1 and t2, + # where t1 and t2 have opposite direction, + # and t1 transitions to state T1 and t2 to T2, + # then the following must be true: + # (T2 allows the trigger t1, transitioning to state U) + # and + # (T1 allows the trigger t2, transitioning to state U) + # and + # ( + # ( + # (all of T1's triggers have the same direction as t2) + # and + # (all of T2's triggers have the same direction as t1) + # ) + # or + # (T1, T2, and U are the same "terminal state") + # ) + # + # A "terminal state" S is one from which all triggers + # transition back to S itself. + # + # The presence of triggers with multiple out states complicates + # this check slightly, but doesn't fundamentally change it. + # + # from a state S, + # for any pair of triggers t1 and t2, + # where t1 and t2 have opposite direction, + # for each pair of states (T1, T2) \in t1_out x t2_out, + # where t1_out is the set of outstates from t1 + # t2_out is the set of outstates from t2 + # t1_out x t2_out is their Cartesian product + # and t1 transitions to state T1 and t2 to T2, + # then the following must be true: + # (T2 allows the trigger t1, with out-state set { U }) + # and + # (T1 allows the trigger t2, with out-state set { U }) + # and + # ( + # ( + # (all of T1's triggers have the same direction as t2) + # and + # (all of T2's triggers have the same direction as t1) + # ) + # or + # (T1, T2, and U are the same "terminal state") + # ) + + # check Rule 1 + syncdirection = None + syncok = True + for trans in ts.transitions: + if not trans.msg.type.isSync(): continue + if syncdirection is None: + syncdirection = trans.trigger.direction() + elif syncdirection is not trans.trigger.direction(): + self.error( + trans.loc, + "sync trigger at state `%s' in protocol `%s' has different direction from earlier sync trigger at same state", + ts.state.name, self.p.name) + syncok = False + # don't check the Diamond Rule if Rule 1 doesn't hold + if not syncok: + return + + # helper functions + def triggerTargets(S, t): + '''Return the set of states transitioned to from state |S| +upon trigger |t|, or { } if |t| is not a trigger in |S|.''' + for trans in self.p.states[S].transitions: + if t.trigger is trans.trigger and t.msg is trans.msg: + return trans.toStates + return set() + + def allTriggersSameDirectionAs(S, t): + '''Return true iff all the triggers from state |S| have the same +direction as trigger |t|''' + direction = t.direction() + for trans in self.p.states[S].transitions: + if direction != trans.trigger.direction(): + return False + return True + + def terminalState(S): + '''Return true iff |S| is a "terminal state".''' + for trans in self.p.states[S].transitions: + for S_ in trans.toStates: + if S_ != S: return False + return True + + def sameTerminalState(S1, S2, S3): + '''Return true iff states |S1|, |S2|, and |S3| are all the same +"terminal state".''' + if isinstance(S3, set): + assert len(S3) == 1 + for S3_ in S3: pass + S3 = S3_ + + return (S1 == S2 == S3) and terminalState(S1) + + S = ts.state.name + + # check the Diamond Rule + for (t1, t2) in unique_pairs(ts.transitions): + # if the triggers have the same direction, they can't race, + # since only one endpoint can initiate either (and delivery + # is in-order) + if t1.trigger.direction() == t2.trigger.direction(): + continue + + loc = t1.loc + t1_out = t1.toStates + t2_out = t2.toStates + + for (T1, T2) in cartesian_product(t1_out, t2_out): + # U1 <- { u | T1 --t2--> u } + U1 = triggerTargets(T1, t2) + # U2 <- { u | T2 --t1--> u } + U2 = triggerTargets(T2, t1) + + # don't report more than one Diamond Rule violation + # per state. there may be O(n^4) total, way too many + # for a human to parse + # + # XXX/cjones: could set a limit on #printed and stop + # after that limit ... + raceError = False + errT1 = None + errT2 = None + + if 0 == len(U1) or 0 == len(U2): + print "******* case 1" + raceError = True + elif 1 < len(U1) or 1 < len(U2): + raceError = True + # there are potentially many unpaired states; just + # pick two + print "******* case 2" + for u1, u2 in cartesian_product(U1, U2): + if u1 != u2: + errT1, errT2 = u1, u2 + break + elif U1 != U2: + print "******* case 3" + raceError = True + for errT1 in U1: pass + for errT2 in U2: pass + + if raceError: + self.reportRaceError(loc, S, + [ T1, t1, errT1 ], + [ T2, t2, errT2 ]) + return + + if not ((allTriggersSameDirectionAs(T1, t2.trigger) + and allTriggersSameDirectionAs(T2, t1.trigger)) + or sameTerminalState(T1, T2, U1)): + self.reportRunawayError(loc, S, [ T1, t1, None ], [ T2, t2, None ]) + return + + def checkReachability(self, p): + def explore(ts, visited): + if ts.state in visited: + return + visited.add(ts.state) + for outedge in ts.transitions: + for toState in outedge.toStates: + explore(p.states[toState], visited) + + checkfordelete = (State.DEAD in p.states) + + allvisited = set() # set(State) + for root in p.startStates: + visited = set() + + explore(root, visited) + allvisited.update(visited) + + if checkfordelete and State.DEAD not in visited: + self.error( + root.loc, + "when starting from state `%s', actors of protocol `%s' cannot be deleted", root.state.name, p.name) + + for ts in p.states.itervalues(): + if ts.state is not State.DEAD and ts.state not in allvisited: + self.error(ts.loc, + "unreachable state `%s' in protocol `%s'", + ts.state.name, p.name) + + + def _normalizeTransitionSequences(self, t1Seq, t2Seq): + T1, M1, U1 = t1Seq + T2, M2, U2 = t2Seq + assert M1 is not None and M2 is not None + + # make sure that T1/M1/U1 is the parent side of the race + if M1.trigger is RECV or M1.trigger is ANSWER: + T1, M1, U1, T2, M2, U2 = T2, M2, U2, T1, M1, U1 + + def stateName(S): + if S: return S.name + return '[error]' + + T1 = stateName(T1) + T2 = stateName(T2) + U1 = stateName(U1) + U2 = stateName(U2) + + return T1, M1.msg.progname, U1, T2, M2.msg.progname, U2 + + + def reportRaceError(self, loc, S, t1Seq, t2Seq): + T1, M1, U1, T2, M2, U2 = self._normalizeTransitionSequences(t1Seq, t2Seq) + self.error( + loc, +"""in protocol `%(P)s', the sequence of events + parent: +--`send %(M1)s'-->( state `%(T1)s' )--`recv %(M2)s'-->( state %(U1)s ) + / + ( state `%(S)s' ) + \\ + child: +--`send %(M2)s'-->( state `%(T2)s' )--`recv %(M1)s'-->( state %(U2)s ) +results in error(s) or leaves parent/child state out of sync for more than one step and is thus a race hazard; i.e., triggers `%(M1)s' and `%(M2)s' fail to commute in state `%(S)s'"""% { + 'P': self.p.name, 'S': S, 'M1': M1, 'M2': M2, + 'T1': T1, 'T2': T2, 'U1': U1, 'U2': U2 + }) + + + def reportRunawayError(self, loc, S, t1Seq, t2Seq): + T1, M1, _, T2, M2, __ = self._normalizeTransitionSequences(t1Seq, t2Seq) + self.error( + loc, + """in protocol `%(P)s', the sequence of events + parent: +--`send %(M1)s'-->( state `%(T1)s' ) + / + ( state `%(S)s' ) + \\ + child: +--`send %(M2)s'-->( state `%(T2)s' ) +lead to parent/child states in which parent/child state can become more than one step out of sync (though this divergence might not lead to error conditions)"""% { + 'P': self.p.name, 'S': S, 'M1': M1, 'M2': M2, 'T1': T1, 'T2': T2 + }) diff --git a/ipc/ipdl/moz.build b/ipc/ipdl/moz.build new file mode 100644 index 000000000..47d840603 --- /dev/null +++ b/ipc/ipdl/moz.build @@ -0,0 +1,19 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=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/. + +if CONFIG['MOZ_IPDL_TESTS']: + DIRS += ['test'] + +include('/ipc/chromium/chromium-config.mozbuild') + +# Generated by ipdl.py +SOURCES += ['!IPCMessageTypeName.cpp'] + +FINAL_LIBRARY = 'xul' + +# We #include some things in the dom/plugins/ directory that rely on +# toolkit libraries. +CXXFLAGS += CONFIG['TK_CFLAGS'] diff --git a/ipc/ipdl/msgtype-components b/ipc/ipdl/msgtype-components new file mode 100644 index 000000000..e411234f9 --- /dev/null +++ b/ipc/ipdl/msgtype-components @@ -0,0 +1,13 @@ +#!/usr/bin/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 sys + +msgid = int(sys.argv[1]) +protocol = (msgid >> 16) +msg = (msgid - (protocol << 16)) + +print 'protocol', protocol, 'message', msg diff --git a/ipc/ipdl/test/README.txt b/ipc/ipdl/test/README.txt new file mode 100644 index 000000000..8c5eb0411 --- /dev/null +++ b/ipc/ipdl/test/README.txt @@ -0,0 +1,10 @@ +There are two major categories of tests, segregated into different +top-level directories under test/. + +The first category (ipdl/) is IPDL-compiler tests. These tests check +that the IPDL compiler is successfully compiling correct +specifications, and successfully rejecting erroneous specifications. + +The second category (cxx/) is C++ tests of IPDL semantics. These +tests check that async/sync/rpc semantics are implemented correctly, +ctors/dtors behave as they should, etc. diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp new file mode 100644 index 000000000..7123e3db8 --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp @@ -0,0 +1,32 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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/. */ + +#include "mozilla/ipc/IOThreadChild.h" + +#include "IPDLUnitTestProcessChild.h" +#include "IPDLUnitTests.h" + +#include "nsRegion.h" + +using mozilla::ipc::IOThreadChild; + +namespace mozilla { +namespace _ipdltest { + +bool +IPDLUnitTestProcessChild::Init() +{ + IPDLUnitTestChildInit(IOThreadChild::channel(), + ParentPid(), + IOThreadChild::message_loop()); + + if (NS_FAILED(nsRegion::InitStatic())) + return false; + + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h new file mode 100644 index 000000000..693cc7360 --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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/. */ + +#ifndef mozilla__ipdltest_IPDLUnitTestThreadChild_h +#define mozilla__ipdltest_IPDLUnitTestThreadChild_h 1 + +#include "mozilla/ipc/ProcessChild.h" + +namespace mozilla { +namespace _ipdltest { + +class IPDLUnitTestProcessChild : public mozilla::ipc::ProcessChild +{ + typedef mozilla::ipc::ProcessChild ProcessChild; + +public: + explicit IPDLUnitTestProcessChild(ProcessId aParentPid) : + ProcessChild(aParentPid) + { } + + ~IPDLUnitTestProcessChild() + { } + + virtual bool Init(); +}; + +} // namespace _ipdltest +} // namespace mozilla + +#endif // ifndef mozilla__ipdltest_IPDLUnitTestThreadChild_h diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp new file mode 100644 index 000000000..87fe0ddc7 --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp @@ -0,0 +1,23 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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/. */ + +#include "IPDLUnitTestSubprocess.h" + +using mozilla::ipc::GeckoChildProcessHost; + +namespace mozilla { +namespace _ipdltest { + +IPDLUnitTestSubprocess::IPDLUnitTestSubprocess() : + GeckoChildProcessHost(GeckoProcessType_IPDLUnitTest) +{ +} + +IPDLUnitTestSubprocess::~IPDLUnitTestSubprocess() +{ +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h new file mode 100644 index 000000000..cdb212df2 --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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/. */ + +#ifndef mozilla__ipdltest_IPDLUnitTestTestSubprocess_h +#define mozilla__ipdltest_IPDLUnitTestTestSubprocess_h 1 + + +#include "mozilla/ipc/GeckoChildProcessHost.h" + +namespace mozilla { +namespace _ipdltest { +//----------------------------------------------------------------------------- + +class IPDLUnitTestSubprocess : public mozilla::ipc::GeckoChildProcessHost +{ +public: + IPDLUnitTestSubprocess(); + ~IPDLUnitTestSubprocess(); + + /** + * Asynchronously launch the plugin process. + */ + // Could override parent Launch, but don't need to here + //bool Launch(); + +private: + DISALLOW_EVIL_CONSTRUCTORS(IPDLUnitTestSubprocess); +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_IPDLUnitTestTestSubprocess_h diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h b/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h new file mode 100644 index 000000000..6da135a0a --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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/. */ + +#ifndef mozilla__ipdltest_IPDLUnitTestTypes_h +#define mozilla__ipdltest_IPDLUnitTestTypes_h + +#include "mozilla/ipc/ProtocolUtils.h" // ActorDestroyReason + +namespace mozilla { +namespace _ipdltest { + +struct DirtyRect +{ + int x; int y; int w; int h; +}; + +} +} + +namespace IPC { +template<> +struct ParamTraits<mozilla::_ipdltest::DirtyRect> +{ + typedef mozilla::_ipdltest::DirtyRect paramType; + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.x); + WriteParam(aMsg, aParam.y); + WriteParam(aMsg, aParam.w); + WriteParam(aMsg, aParam.h); + } + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->x) && + ReadParam(aMsg, aIter, &aResult->y) && + ReadParam(aMsg, aIter, &aResult->w) && + ReadParam(aMsg, aIter, &aResult->h)); + } +}; +} + + +#endif // ifndef mozilla__ipdltest_IPDLUnitTestTypes_h diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestUtils.h b/ipc/ipdl/test/cxx/IPDLUnitTestUtils.h new file mode 100644 index 000000000..ad9da374d --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestUtils.h @@ -0,0 +1,27 @@ + +#ifndef mozilla__ipdltest_IPDLUnitTestUtils +#define mozilla__ipdltest_IPDLUnitTestUtils 1 + +namespace mozilla { +namespace _ipdltest { + +struct Bad {}; + +} // namespace _ipdltest +} // namespace mozilla + +namespace IPC { + +template<> +struct ParamTraits<mozilla::_ipdltest::Bad> +{ + typedef mozilla::_ipdltest::Bad paramType; + + // Defined in TestActorPunning.cpp. + static void Write(Message* aMsg, const paramType& aParam); + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult); +}; + +} // namespace IPC + +#endif // mozilla__ipdltest_IPDLUnitTestUtils diff --git a/ipc/ipdl/test/cxx/IPDLUnitTests.h b/ipc/ipdl/test/cxx/IPDLUnitTests.h new file mode 100644 index 000000000..544bc0ffb --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTests.h @@ -0,0 +1,93 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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/. */ + +#ifndef mozilla__ipdltest_IPDLUnitTests_h +#define mozilla__ipdltest_IPDLUnitTests_h 1 + +#include "base/message_loop.h" +#include "base/process.h" +#include "chrome/common/ipc_channel.h" + +#include "nsIAppShell.h" + +#include "nsCOMPtr.h" +#include "nsDebug.h" +#include "nsServiceManagerUtils.h" // do_GetService() +#include "nsWidgetsCID.h" // NS_APPSHELL_CID +#include "nsXULAppAPI.h" + + +#define MOZ_IPDL_TESTFAIL_LABEL "TEST-UNEXPECTED-FAIL" +#define MOZ_IPDL_TESTPASS_LABEL "TEST-PASS" +#define MOZ_IPDL_TESTINFO_LABEL "TEST-INFO" + + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// both processes +const char* IPDLUnitTestName(); + +// NB: these are named like the similar functions in +// xpcom/test/TestHarness.h. The names should nominally be kept in +// sync. + +inline void fail(const char* fmt, ...) +{ + va_list ap; + + fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL " | %s | ", IPDLUnitTestName()); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fputc('\n', stderr); + + NS_RUNTIMEABORT("failed test"); +} + +inline void passed(const char* fmt, ...) +{ + va_list ap; + + printf(MOZ_IPDL_TESTPASS_LABEL " | %s | ", IPDLUnitTestName()); + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + fputc('\n', stdout); +} + +//----------------------------------------------------------------------------- +// parent process only + +class IPDLUnitTestSubprocess; + +extern void* gParentActor; +extern IPDLUnitTestSubprocess* gSubprocess; + +void IPDLUnitTestMain(void* aData); + +void QuitParent(); + +//----------------------------------------------------------------------------- +// child process only + +extern void* gChildActor; + +void IPDLUnitTestChildInit(IPC::Channel* transport, + base::ProcessId parentPid, + MessageLoop* worker); + +void QuitChild(); + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_IPDLUnitTests_h diff --git a/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp new file mode 100644 index 000000000..6a91033bf --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp @@ -0,0 +1,390 @@ +// +// Autogenerated from Python template. Hands off. +// + +#include <stdlib.h> +#include <string.h> + +#include "IPDLUnitTests.h" + +#include "base/command_line.h" +#include "base/string_util.h" +#include "base/task.h" +#include "base/thread.h" + +#include "nsRegion.h" + +#include "IPDLUnitTestSubprocess.h" + +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${INCLUDES} +//----------------------------------------------------------------------------- + +using namespace std; + +using base::Thread; + +namespace mozilla { +namespace _ipdltest { + +void* gParentActor; +IPDLUnitTestSubprocess* gSubprocess; + +void* gChildActor; + +// Note: in threaded mode, this will be non-null (for both parent and +// child, since they share one set of globals). +Thread* gChildThread; +MessageLoop *gParentMessageLoop; +bool gParentDone; +bool gChildDone; + +void +DeleteChildActor(); + +//----------------------------------------------------------------------------- +// data/functions accessed by both parent and child processes + +char* gIPDLUnitTestName = nullptr; + +const char* +IPDLUnitTestName() +{ + if (!gIPDLUnitTestName) { +#if defined(OS_WIN) + vector<wstring> args = + CommandLine::ForCurrentProcess()->GetLooseValues(); + gIPDLUnitTestName = ::strdup(WideToUTF8(args[0]).c_str()); +#elif defined(OS_POSIX) + vector<string> argv = CommandLine::ForCurrentProcess()->argv(); + gIPDLUnitTestName = ::moz_xstrdup(argv[1].c_str()); +#else +# error Sorry +#endif + } + return gIPDLUnitTestName; +} + +} // namespace _ipdltest +} // namespace mozilla + + +namespace { + +enum IPDLUnitTestType { + NoneTest = 0, + +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${ENUM_VALUES} + + LastTest = ${LAST_ENUM} +//----------------------------------------------------------------------------- +}; + + +IPDLUnitTestType +IPDLUnitTestFromString(const char* const aString) +{ + if (!aString) + return static_cast<IPDLUnitTestType>(0); +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${STRING_TO_ENUMS} +//----------------------------------------------------------------------------- + else + return static_cast<IPDLUnitTestType>(0); +} + + +const char* +IPDLUnitTestToString(IPDLUnitTestType aTest) +{ + switch (aTest) { +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${ENUM_TO_STRINGS} +//----------------------------------------------------------------------------- + + default: + return nullptr; + } +} + + +IPDLUnitTestType +IPDLUnitTest() +{ + return IPDLUnitTestFromString(::mozilla::_ipdltest::IPDLUnitTestName()); +} + + +} // namespace <anon> + + +//----------------------------------------------------------------------------- +// parent process only + +namespace mozilla { +namespace _ipdltest { + +void +DeferredParentShutdown(); + +void +IPDLUnitTestThreadMain(char *testString); + +void +IPDLUnitTestMain(void* aData) +{ + char* testString = reinterpret_cast<char*>(aData); + + // Check if we are to run the test using threads instead: + const char *prefix = "thread:"; + const int prefixLen = strlen(prefix); + if (!strncmp(testString, prefix, prefixLen)) { + IPDLUnitTestThreadMain(testString + prefixLen); + return; + } + + IPDLUnitTestType test = IPDLUnitTestFromString(testString); + if (!test) { + // use this instead of |fail()| because we don't know what the test is + fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL "| %s | unknown unit test %s\n", + "<--->", testString); + NS_RUNTIMEABORT("can't continue"); + } + gIPDLUnitTestName = testString; + + // Check whether this test is enabled for processes: + switch (test) { +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${PARENT_ENABLED_CASES_PROC} +//----------------------------------------------------------------------------- + + default: + fail("not reached"); + return; // unreached + } + + printf(MOZ_IPDL_TESTINFO_LABEL "| running test | %s\n", gIPDLUnitTestName); + + std::vector<std::string> testCaseArgs; + testCaseArgs.push_back(testString); + + gSubprocess = new IPDLUnitTestSubprocess(); + if (!gSubprocess->SyncLaunch(testCaseArgs)) + fail("problem launching subprocess"); + + IPC::Channel* transport = gSubprocess->GetChannel(); + if (!transport) + fail("no transport"); + + base::ProcessId child = base::GetProcId(gSubprocess->GetChildProcessHandle()); + + switch (test) { +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${PARENT_MAIN_CASES_PROC} +//----------------------------------------------------------------------------- + + default: + fail("not reached"); + return; // unreached + } +} + +void +IPDLUnitTestThreadMain(char *testString) +{ + IPDLUnitTestType test = IPDLUnitTestFromString(testString); + if (!test) { + // use this instead of |fail()| because we don't know what the test is + fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL "| %s | unknown unit test %s\n", + "<--->", testString); + NS_RUNTIMEABORT("can't continue"); + } + gIPDLUnitTestName = testString; + + // Check whether this test is enabled for threads: + switch (test) { +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${PARENT_ENABLED_CASES_THREAD} +//----------------------------------------------------------------------------- + + default: + fail("not reached"); + return; // unreached + } + + printf(MOZ_IPDL_TESTINFO_LABEL "| running test | %s\n", gIPDLUnitTestName); + + std::vector<std::string> testCaseArgs; + testCaseArgs.push_back(testString); + + gChildThread = new Thread("ParentThread"); + if (!gChildThread->Start()) + fail("starting parent thread"); + + gParentMessageLoop = MessageLoop::current(); + MessageLoop *childMessageLoop = gChildThread->message_loop(); + + switch (test) { +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${PARENT_MAIN_CASES_THREAD} +//----------------------------------------------------------------------------- + + default: + fail("not reached"); + return; // unreached + } +} + +void +DeleteParentActor() +{ + if (!gParentActor) + return; + + switch (IPDLUnitTest()) { +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${PARENT_DELETE_CASES} +//----------------------------------------------------------------------------- + default: ::mozilla::_ipdltest::fail("???"); + } +} + +void +QuitXPCOM() +{ + DeleteParentActor(); + + static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID)); + appShell->Exit(); +} + +void +DeleteSubprocess(MessageLoop* uiLoop) +{ + // pong to QuitXPCOM + delete gSubprocess; + uiLoop->PostTask(NewRunnableFunction(QuitXPCOM)); +} + +void +DeferredParentShutdown() +{ + // ping to DeleteSubprocess + XRE_GetIOMessageLoop()->PostTask( + NewRunnableFunction(DeleteSubprocess, MessageLoop::current())); +} + +void +TryThreadedShutdown() +{ + // Stop if either: + // - the child has not finished, + // - the parent has not finished, + // - or this code has already executed. + // Remember: this TryThreadedShutdown() task is enqueued + // by both parent and child (though always on parent's msg loop). + if (!gChildDone || !gParentDone || !gChildThread) + return; + + delete gChildThread; + gChildThread = 0; + DeferredParentShutdown(); +} + +void +ChildCompleted() +{ + // Executes on the parent message loop once child has completed. + gChildDone = true; + TryThreadedShutdown(); +} + +void +QuitParent() +{ + if (gChildThread) { + gParentDone = true; + MessageLoop::current()->PostTask( + NewRunnableFunction(TryThreadedShutdown)); + } else { + // defer "real" shutdown to avoid *Channel::Close() racing with the + // deletion of the subprocess + MessageLoop::current()->PostTask( + NewRunnableFunction(DeferredParentShutdown)); + } +} + +static void +ChildDie() +{ + DeleteChildActor(); + XRE_ShutdownChildProcess(); +} + +void +QuitChild() +{ + if (gChildThread) { // Threaded-mode test + gParentMessageLoop->PostTask( + NewRunnableFunction(ChildCompleted)); + } else { // Process-mode test + MessageLoop::current()->PostTask( + NewRunnableFunction(ChildDie)); + } +} + +} // namespace _ipdltest +} // namespace mozilla + + +//----------------------------------------------------------------------------- +// child process only + +namespace mozilla { +namespace _ipdltest { + +void +DeleteChildActor() +{ + if (!gChildActor) + return; + + switch (IPDLUnitTest()) { +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${CHILD_DELETE_CASES} +//----------------------------------------------------------------------------- + default: ::mozilla::_ipdltest::fail("???"); + } +} + +void +IPDLUnitTestChildInit(IPC::Channel* transport, + base::ProcessId parentPid, + MessageLoop* worker) +{ + switch (IPDLUnitTest()) { +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${CHILD_INIT_CASES} +//----------------------------------------------------------------------------- + + default: + fail("not reached"); + return; // unreached + } +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/Makefile.in b/ipc/ipdl/test/cxx/Makefile.in new file mode 100644 index 000000000..8c9cdaa99 --- /dev/null +++ b/ipc/ipdl/test/cxx/Makefile.in @@ -0,0 +1,46 @@ +# 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/. + +IPDLTESTSRCS = $(filter Test%,$(CPPSRCS)) +IPDLTESTS = $(IPDLTESTSRCS:.cpp=) + +EXTRA_PROTOCOLS = \ + TestBridgeSub \ + TestEndpointBridgeSub \ + $(NULL) + +IPDLTESTHDRS = $(addprefix $(srcdir)/,$(addsuffix .h,$(IPDLTESTS))) + +TESTER_TEMPLATE := $(srcdir)/IPDLUnitTests.template.cpp +GENTESTER := $(srcdir)/genIPDLUnitTests.py + +include $(topsrcdir)/config/rules.mk + + +IPDLUNITTEST_BIN = $(DEPTH)/dist/bin/ipdlunittest$(BIN_SUFFIX) + +IPDLUnitTests.cpp : Makefile.in moz.build $(GENTESTER) $(TESTER_TEMPLATE) $(IPDLTESTHDRS) + $(PYTHON) $(GENTESTER) $(TESTER_TEMPLATE) -t $(IPDLTESTS) -e $(EXTRA_PROTOCOLS) > $@ + +check-proc:: + @$(EXIT_ON_ERROR) \ + for test in $(IPDLTESTS); do \ + $(RUN_TEST_PROGRAM) $(IPDLUNITTEST_BIN) $$test ; \ + done + +check-thread:: + @$(EXIT_ON_ERROR) \ + for test in $(IPDLTESTS); do \ + $(RUN_TEST_PROGRAM) $(IPDLUNITTEST_BIN) thread:$$test ; \ + done + +check:: check-proc check-thread + +check-valgrind:: + @$(EXIT_ON_ERROR) \ + for test in $(IPDLTESTS); do \ + $(RUN_TEST_PROGRAM) -g -d \ + valgrind -a '--leak-check=full --trace-children=yes -q' \ + $(IPDLUNITTEST_BIN) $$test ; \ + done diff --git a/ipc/ipdl/test/cxx/PTestActorPunning.ipdl b/ipc/ipdl/test/cxx/PTestActorPunning.ipdl new file mode 100644 index 000000000..54deb277a --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestActorPunning.ipdl @@ -0,0 +1,39 @@ + +include protocol PTestActorPunningPunned; +include protocol PTestActorPunningSub; +include "mozilla/_ipdltest/IPDLUnitTestUtils.h"; + +using struct mozilla::_ipdltest::Bad from "mozilla/_ipdltest/IPDLUnitTestUtils.h"; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestActorPunning { + manages PTestActorPunningPunned; + manages PTestActorPunningSub; + +child: + async Start(); + +parent: + async PTestActorPunningPunned(); + async PTestActorPunningSub(); + async Pun(PTestActorPunningSub a, Bad bad); + async __delete__(); + + +state PING: + send Start goto CONSTRUCTING; + +state CONSTRUCTING: + recv PTestActorPunningPunned goto CONSTRUCTING; + recv PTestActorPunningSub goto CONSTRUCTING; + recv Pun goto DEAD; + // We never make it past this transition, --> error. + +state DEAD: + recv __delete__; +}; + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestActorPunningPunned.ipdl b/ipc/ipdl/test/cxx/PTestActorPunningPunned.ipdl new file mode 100644 index 000000000..a6b875920 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestActorPunningPunned.ipdl @@ -0,0 +1,15 @@ + +include protocol PTestActorPunning; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestActorPunningPunned { + manager PTestActorPunning; + +child: + async __delete__(); +}; + +} // namespace mozilla +} // namespace _ipdltes diff --git a/ipc/ipdl/test/cxx/PTestActorPunningSub.ipdl b/ipc/ipdl/test/cxx/PTestActorPunningSub.ipdl new file mode 100644 index 000000000..1441219c3 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestActorPunningSub.ipdl @@ -0,0 +1,16 @@ + +include protocol PTestActorPunning; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestActorPunningSub { + manager PTestActorPunning; + +child: + async Bad(); + async __delete__(); +}; + +} // namespace mozilla +} // namespace _ipdltes diff --git a/ipc/ipdl/test/cxx/PTestBadActor.ipdl b/ipc/ipdl/test/cxx/PTestBadActor.ipdl new file mode 100644 index 000000000..841d89ff6 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestBadActor.ipdl @@ -0,0 +1,18 @@ +include protocol PTestBadActorSub; + +namespace mozilla { +namespace _ipdltest { + +// Test that a parent sending a reentrant __delete__ message +// is not killed if a child's message races with the reply. + +intr protocol PTestBadActor { + manages PTestBadActorSub; + +child: + async PTestBadActorSub(); + async __delete__(); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestBadActorSub.ipdl b/ipc/ipdl/test/cxx/PTestBadActorSub.ipdl new file mode 100644 index 000000000..99c78f4ac --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestBadActorSub.ipdl @@ -0,0 +1,17 @@ +include protocol PTestBadActor; + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestBadActorSub { + manager PTestBadActor; + +child: + intr __delete__(); + +parent: + async Ping(); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl b/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl new file mode 100644 index 000000000..229820b17 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl @@ -0,0 +1,26 @@ +include protocol PTestBridgeMainSub; +include protocol PTestBridgeSub; + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestBridgeMain { + child spawns PTestBridgeSub; + child opens PTestBridgeMainSub; + +child: + async Start(); + +parent: + async __delete__(); + +state START: + send Start goto DEAD; +state DEAD: + recv __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl b/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl new file mode 100644 index 000000000..67e50fdd4 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl @@ -0,0 +1,33 @@ +include protocol PTestBridgeMain; +include protocol PTestBridgeSub; + +namespace mozilla { +namespace _ipdltest { + +// (Bridge protocols can have different semantics than the endpoints +// they bridge) +intr protocol PTestBridgeMainSub { + bridges PTestBridgeMain, PTestBridgeSub; + +child: + async Hi(); + intr HiRpc(); + +parent: + async Hello(); + sync HelloSync(); + intr HelloRpc(); + async __delete__(); + +state START: recv Hello goto HI; +state HI: send Hi goto HELLO_SYNC; +state HELLO_SYNC: recv HelloSync goto HELLO_RPC; +state HELLO_RPC: answer HelloRpc goto HI_RPC; +state HI_RPC: call HiRpc goto DEAD; +state DEAD: + recv __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl b/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl new file mode 100644 index 000000000..6c798c84e --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl @@ -0,0 +1,25 @@ +include protocol PTestBridgeMainSub; + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestBridgeSub { +child: + async Ping(); + +parent: + async BridgeEm(); + async __delete__(); + +state START: + send Ping goto BRIDGEEM; +state BRIDGEEM: + recv BridgeEm goto DEAD; +state DEAD: + recv __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestCancel.ipdl b/ipc/ipdl/test/cxx/PTestCancel.ipdl new file mode 100644 index 000000000..3a6b46b43 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestCancel.ipdl @@ -0,0 +1,36 @@ +namespace mozilla { +namespace _ipdltest { + +nested(upto inside_sync) sync protocol PTestCancel +{ +// Test1 +child: + nested(inside_sync) sync Test1_1(); +parent: + async Done1(); + +// Test2 +child: + async Start2(); + nested(inside_sync) sync Test2_2(); +parent: + sync Test2_1(); + +// Test3 +child: + nested(inside_sync) sync Test3_1(); +parent: + async Start3(); + nested(inside_sync) sync Test3_2(); + +parent: + async Done(); + +child: + nested(inside_sync) sync CheckChild() returns (uint32_t reply); +parent: + nested(inside_sync) sync CheckParent() returns (uint32_t reply); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl b/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl new file mode 100644 index 000000000..eae428211 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl @@ -0,0 +1,22 @@ +// See bug 538586: if the top-level protocol's actor is deleted before +// the "connection error" notification comes in from the IO thread, +// IPDL teardown never occurs, even if Channel::Close() is called +// after the error. + +namespace mozilla { +namespace _ipdltest { + +// NB: needs to be RPC so that the parent blocks on the child's crash. +intr protocol PTestCrashCleanup { +child: + intr DIEDIEDIE(); + async __delete__(); + +state ALIVE: + call DIEDIEDIE goto CRASH; +state CRASH: + send __delete__; +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestDataStructures.ipdl b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl new file mode 100644 index 000000000..59bf74652 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl @@ -0,0 +1,132 @@ +include protocol PTestDataStructuresSub; +include PTestDataStructuresCommon; + +include "mozilla/GfxMessageUtils.h"; + +namespace mozilla { +namespace _ipdltest { + +sync protocol PTestDataStructures { + manages PTestDataStructuresSub; + +child: + async PTestDataStructuresSub(int i); + + async Start(); + +parent: + async __delete__(); + + sync Test1(int[] i1) + returns (int[] o1); + + sync Test2(PTestDataStructuresSub[] i1) + returns (PTestDataStructuresSub[] o1); + + sync Test3(IntDouble i1, + IntDouble i2) + returns (IntDouble o1, + IntDouble o2); + + sync Test4(IntDouble[] i1) + returns (IntDouble[] o1); + + sync Test5(IntDoubleArrays i1, + IntDoubleArrays i2, + IntDoubleArrays i3) + returns (IntDoubleArrays o1, + IntDoubleArrays o2, + IntDoubleArrays o3); + + sync Test6(IntDoubleArrays[] i1) + returns (IntDoubleArrays[] o1); + + sync Test7_0(ActorWrapper a1) + returns (ActorWrapper o1); + + sync Test7(Actors i1, + Actors i2, + Actors i3) + returns (Actors o1, + Actors o2, + Actors o3); + + sync Test8(Actors[] i1) + returns (Actors[] o1); + + sync Test9(Unions i1, + Unions i2, + Unions i3, + Unions i4) + returns (Unions o1, + Unions o2, + Unions o3, + Unions o4); + + sync Test10(Unions[] i1) + returns (Unions[] o1); + + sync Test11(SIntDouble i) + returns (SIntDouble o); + + sync Test12(SIntDoubleArrays i) + returns (SIntDoubleArrays o); + + sync Test13(SActors i) + returns (SActors o); + + sync Test14(Structs i) + returns (Structs o); + + sync Test15(WithStructs i1, + WithStructs i2, + WithStructs i3, + WithStructs i4, + WithStructs i5) + returns (WithStructs o1, + WithStructs o2, + WithStructs o3, + WithStructs o4, + WithStructs o5); + + sync Test16(WithUnions i) + returns (WithUnions o); + + sync Test17(Op[] ops); + + // test that the ParamTraits<nsTArray>::Read() workaround for + // nsTArray's incorrect memmove() semantics works properly + // (nsIntRegion isn't memmove()able) + sync Test18(nsIntRegion[] ops); + + sync Dummy(ShmemUnion su) returns (ShmemUnion rsu); + +state CONSTRUCTING: + send PTestDataStructuresSub goto CONSTRUCTING; + send Start goto TEST1; +state TEST1: recv Test1 goto TEST2; +state TEST2: recv Test2 goto TEST3; +state TEST3: recv Test3 goto TEST4; +state TEST4: recv Test4 goto TEST5; +state TEST5: recv Test5 goto TEST6; +state TEST6: recv Test6 goto TEST7; +state TEST7: recv Test7 goto TEST8; +state TEST8: recv Test8 goto TEST9; +state TEST9: recv Test9 goto TEST10; +state TEST10: recv Test10 goto TEST11; +state TEST11: recv Test11 goto TEST12; +state TEST12: recv Test12 goto TEST13; +state TEST13: recv Test13 goto TEST14; +state TEST14: recv Test14 goto TEST15; +state TEST15: recv Test15 goto TEST16; +state TEST16: recv Test16 goto TEST17; +state TEST17: recv Test17 goto TEST18; +state TEST18: recv Test18 goto DEAD; + +state DEAD: + recv __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla + diff --git a/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh b/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh new file mode 100644 index 000000000..891b0c991 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh @@ -0,0 +1,107 @@ +include protocol PTestDataStructuresSub; + +using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; +using nsIntRegion from "nsRegion.h"; + +namespace mozilla { +namespace _foo { + +union IntDouble { + int; + double; +}; + +struct SIntDouble { + int i; + double d; +}; + +union IntDoubleArrays { + int; + int[]; + double[]; +}; + +struct SIntDoubleArrays { + int i; + int[] ai; + double[] ad; +}; + +struct ActorWrapper { + PTestDataStructuresSub actor; +}; + +union Actors { + int; + int[]; + PTestDataStructuresSub[]; +}; + +struct SActors { + int i; + int[] ai; + PTestDataStructuresSub[] ap; +}; + +union Unions { + int; + int[]; + PTestDataStructuresSub[]; + Actors[]; +}; + +struct Structs { + int i; + int[] ai; + PTestDataStructuresSub[] ap; + SActors[] aa; +}; + +union WithStructs { + int; + int[]; + PTestDataStructuresSub[]; + SActors[]; + Structs[]; +}; + +struct WithUnions { + int i; + int[] ai; + PTestDataStructuresSub[] ap; + Actors[] aa; + Unions[] au; +}; + +struct CommonAttrs { bool dummy; }; +struct FooAttrs { int dummy; }; +struct BarAttrs { float dummy; }; +union SpecificAttrs { + FooAttrs; + BarAttrs; +}; +struct Attrs { + CommonAttrs common; + SpecificAttrs specific; +}; +struct SetAttrs { + PTestDataStructuresSub x; + Attrs attrs; +}; +union Op { null_t; SetAttrs; }; + +struct ShmemStruct { + int i; + Shmem mem; +}; + +union ShmemUnion { + int; + Shmem; +}; + +struct Empty { }; + +} // namespace _foo +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestDataStructuresSub.ipdl b/ipc/ipdl/test/cxx/PTestDataStructuresSub.ipdl new file mode 100644 index 000000000..7a4e87d83 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestDataStructuresSub.ipdl @@ -0,0 +1,15 @@ +include PTestDataStructuresCommon; +include protocol PTestDataStructures; + +namespace mozilla { +namespace _ipdltest { + +sync protocol PTestDataStructuresSub { + manager PTestDataStructures; + +parent: + sync __delete__(); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestDemon.ipdl b/ipc/ipdl/test/cxx/PTestDemon.ipdl new file mode 100644 index 000000000..25fa5d0f9 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestDemon.ipdl @@ -0,0 +1,21 @@ +namespace mozilla { +namespace _ipdltest { + +nested(upto inside_cpow) sync protocol PTestDemon +{ +child: + async Start(); + +both: + async AsyncMessage(int n); + nested(inside_sync) sync HiPrioSyncMessage(); + +parent: + sync SyncMessage(int n); + + nested(inside_cpow) async UrgentAsyncMessage(int n); + nested(inside_cpow) sync UrgentSyncMessage(int n); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestDesc.ipdl b/ipc/ipdl/test/cxx/PTestDesc.ipdl new file mode 100644 index 000000000..835c8c62d --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestDesc.ipdl @@ -0,0 +1,31 @@ +include protocol PTestDescSub; +include protocol PTestDescSubsub; + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestDesc { + manages PTestDescSub; +child: + intr PTestDescSub(nullable PTestDescSubsub dummy); + + async Test(PTestDescSubsub a); + + async __delete__(); + +parent: + async Ok(PTestDescSubsub a); + + +state CONSTRUCT: + call PTestDescSub goto TEST; +state TEST: + send Test goto ACK; +state ACK: + recv Ok goto DEAD; +state DEAD: + send __delete__; +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestDescSub.ipdl b/ipc/ipdl/test/cxx/PTestDescSub.ipdl new file mode 100644 index 000000000..67d4fe166 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestDescSub.ipdl @@ -0,0 +1,18 @@ +include protocol PTestDesc; +include protocol PTestDescSubsub; + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestDescSub { + manager PTestDesc; + manages PTestDescSubsub; + +child: + async __delete__(); + + intr PTestDescSubsub(); +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestDescSubsub.ipdl b/ipc/ipdl/test/cxx/PTestDescSubsub.ipdl new file mode 100644 index 000000000..c449f2744 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestDescSubsub.ipdl @@ -0,0 +1,15 @@ + +include protocol PTestDescSub; + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestDescSubsub { + manager PTestDescSub; + +child: + intr __delete__(); +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestEndpointBridgeMain.ipdl b/ipc/ipdl/test/cxx/PTestEndpointBridgeMain.ipdl new file mode 100644 index 000000000..e8026a0d0 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestEndpointBridgeMain.ipdl @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ + +include protocol PTestEndpointBridgeMainSub; +include protocol PTestEndpointBridgeSub; + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestEndpointBridgeMain { + child spawns PTestEndpointBridgeSub; + +child: + async Start(); + +parent: + async Bridged(Endpoint<PTestEndpointBridgeMainSubParent> endpoint); +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestEndpointBridgeMainSub.ipdl b/ipc/ipdl/test/cxx/PTestEndpointBridgeMainSub.ipdl new file mode 100644 index 000000000..7364057ac --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestEndpointBridgeMainSub.ipdl @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ + +include protocol PTestEndpointBridgeMain; +include protocol PTestEndpointBridgeSub; + +namespace mozilla { +namespace _ipdltest { + +// (Bridge protocols can have different semantics than the endpoints +// they bridge) +intr protocol PTestEndpointBridgeMainSub { +child: + async Hi(); + intr HiRpc(); + +parent: + async Hello(); + sync HelloSync(); + intr HelloRpc(); +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestEndpointBridgeSub.ipdl b/ipc/ipdl/test/cxx/PTestEndpointBridgeSub.ipdl new file mode 100644 index 000000000..0bc09b70e --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestEndpointBridgeSub.ipdl @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ + +include protocol PTestEndpointBridgeMainSub; + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestEndpointBridgeSub { +child: + async Ping(); + + async Bridged(Endpoint<PTestEndpointBridgeMainSubChild> endpoint); + +parent: + async BridgeEm(); +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestEndpointOpens.ipdl b/ipc/ipdl/test/cxx/PTestEndpointOpens.ipdl new file mode 100644 index 000000000..7be99ddd2 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestEndpointOpens.ipdl @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +include protocol PTestEndpointOpensOpened; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestEndpointOpens { +child: + async Start(); + +parent: + async StartSubprotocol(Endpoint<PTestEndpointOpensOpenedParent> endpoint); + + async __delete__(); +}; + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl b/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl new file mode 100644 index 000000000..e14c0cb8a --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +namespace mozilla { +namespace _ipdltest2 { + +// (Opens protocols can have different semantics than the endpoints +// that opened them) +intr protocol PTestEndpointOpensOpened { +child: + async Hi(); + intr HiRpc(); + +parent: + async Hello(); + sync HelloSync(); + intr HelloRpc(); + async __delete__(); + +state START: recv Hello goto HI; +state HI: send Hi goto HELLO_SYNC; +state HELLO_SYNC: recv HelloSync goto HELLO_RPC; +state HELLO_RPC: answer HelloRpc goto HI_RPC; +state HI_RPC: call HiRpc goto DEAD; +state DEAD: + recv __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest2 diff --git a/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl b/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl new file mode 100644 index 000000000..94b9da081 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl @@ -0,0 +1,19 @@ +include protocol PTestFailedCtorSub; + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestFailedCtor { + manages PTestFailedCtorSub; +child: + intr PTestFailedCtorSub(); + async __delete__(); + +state CONSTRUCT: + call PTestFailedCtorSub goto DEAD; +state DEAD: + send __delete__; +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl b/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl new file mode 100644 index 000000000..b1d18a05f --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl @@ -0,0 +1,18 @@ +include protocol PTestFailedCtor; +include protocol PTestFailedCtorSubsub; + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestFailedCtorSub { + manager PTestFailedCtor; + manages PTestFailedCtorSubsub; + +parent: + async PTestFailedCtorSubsub(); + sync Sync(); + async __delete__(); +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestFailedCtorSubsub.ipdl b/ipc/ipdl/test/cxx/PTestFailedCtorSubsub.ipdl new file mode 100644 index 000000000..654170d97 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestFailedCtorSubsub.ipdl @@ -0,0 +1,15 @@ + +include protocol PTestFailedCtorSub; + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestFailedCtorSubsub { + manager PTestFailedCtorSub; + +parent: + async __delete__(); +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestHandle.ipdl b/ipc/ipdl/test/cxx/PTestHandle.ipdl new file mode 100644 index 000000000..aad92bae1 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestHandle.ipdl @@ -0,0 +1,14 @@ +include protocol PTestJSON; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestHandle { + manager PTestJSON; + +child: + async __delete__(); +}; + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestHangs.ipdl b/ipc/ipdl/test/cxx/PTestHangs.ipdl new file mode 100644 index 000000000..b1b43fa75 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestHangs.ipdl @@ -0,0 +1,40 @@ + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestHangs { +both: + intr StackFrame(); + +parent: + async Nonce(); + +child: + async Start(); + intr Hang(); + async __delete__(); + + +state START: + send Start goto RACE; + +state RACE: + recv Nonce goto RACE1; + call StackFrame goto RACE2; +state RACE1: + call StackFrame goto FRAME2; +state RACE2: + recv Nonce goto FRAME2; + +// So as to test unwinding the RPC stack +state FRAME2: answer StackFrame goto FRAME3; +state FRAME3: call StackFrame goto FRAME4; +state FRAME4: answer StackFrame goto HANG; +state HANG: call Hang goto DEATH; + +state DEATH: + send __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestHighestPrio.ipdl b/ipc/ipdl/test/cxx/PTestHighestPrio.ipdl new file mode 100644 index 000000000..0192f59b2 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestHighestPrio.ipdl @@ -0,0 +1,18 @@ +namespace mozilla { +namespace _ipdltest { + +nested(upto inside_cpow) sync protocol PTestHighestPrio +{ +parent: + nested(inside_cpow) async Msg1(); + nested(inside_sync) sync Msg2(); + nested(inside_cpow) async Msg3(); + nested(inside_cpow) sync Msg4(); + +child: + async Start(); + nested(inside_sync) sync StartInner(); +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParam.ipdlh b/ipc/ipdl/test/cxx/PTestIndirectProtocolParam.ipdlh new file mode 100644 index 000000000..a81fcdee4 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParam.ipdlh @@ -0,0 +1,15 @@ +include protocol PTestIndirectProtocolParamSecond;
+
+namespace mozilla {
+namespace _ipdltest {
+
+struct IndirectParamStruct {
+ PTestIndirectProtocolParamSecond actor;
+};
+
+union IndirectParamUnion {
+ IndirectParamStruct;
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParamFirst.ipdl b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamFirst.ipdl new file mode 100644 index 000000000..228ec04e8 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamFirst.ipdl @@ -0,0 +1,19 @@ +include protocol PTestIndirectProtocolParamManage;
+// FIXME/bug 792908 protocol PTestIndirectProtocolParamSecond is
+// already included in PTestIndirectProtocolParam.ipdlh
+include protocol PTestIndirectProtocolParamSecond;
+include PTestIndirectProtocolParam;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestIndirectProtocolParamFirst {
+ manager PTestIndirectProtocolParamManage;
+parent:
+ sync Test(IndirectParamUnion actor);
+both:
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParamManage.ipdl b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamManage.ipdl new file mode 100644 index 000000000..db7c828a2 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamManage.ipdl @@ -0,0 +1,17 @@ +include protocol PTestIndirectProtocolParamFirst;
+include protocol PTestIndirectProtocolParamSecond;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestIndirectProtocolParamManage {
+ manages PTestIndirectProtocolParamFirst;
+ manages PTestIndirectProtocolParamSecond;
+both:
+ async PTestIndirectProtocolParamFirst();
+ async PTestIndirectProtocolParamSecond();
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParamSecond.ipdl b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamSecond.ipdl new file mode 100644 index 000000000..ed21f58f8 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamSecond.ipdl @@ -0,0 +1,13 @@ +include protocol PTestIndirectProtocolParamManage;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestIndirectProtocolParamSecond {
+ manager PTestIndirectProtocolParamManage;
+both:
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestInterruptErrorCleanup.ipdl b/ipc/ipdl/test/cxx/PTestInterruptErrorCleanup.ipdl new file mode 100644 index 000000000..95f933bba --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestInterruptErrorCleanup.ipdl @@ -0,0 +1,11 @@ +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestInterruptErrorCleanup { +child: + intr Error(); + intr __delete__(); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl b/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl new file mode 100644 index 000000000..bf71a251c --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl @@ -0,0 +1,82 @@ +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestInterruptRaces { +both: + intr Race() returns (bool hasReply); + intr StackFrame() returns (); + intr StackFrame3() returns (); + +parent: + sync StartRace(); + intr Parent(); + sync GetAnsweredParent() returns (bool answeredParent); + +child: + async Start(); + async Wakeup(); + async Wakeup3(); + intr Child(); + async __delete__(); + +state START: + send Start goto TEST1; + +// First test: race while no other messages are on the Interrupt stack +state TEST1: + recv StartRace goto RACE1; +state RACE1: + call Race goto DUMMY1_1; + answer Race goto DUMMY1_2; +state DUMMY1_1: + answer Race goto TEST2; +state DUMMY1_2: + call Race goto TEST2; + +// Second test: race while other messages are on the Interrupt stack +state TEST2: + call StackFrame goto MORESTACK; +state MORESTACK: + answer StackFrame goto STARTRACE; +state STARTRACE: + send Wakeup goto RACE2; +state RACE2: + call Race goto DUMMY2_1; + answer Race goto DUMMY2_2; +state DUMMY2_1: + answer Race goto TEST3; +state DUMMY2_2: + call Race goto TEST3; + +// Third test: resolve race using custom policy +state TEST3: + call StackFrame3 goto MORESTACK3; +state MORESTACK3: + answer StackFrame3 goto STARTRACE3; +state STARTRACE3: + send Wakeup3 goto RACE3; +state RACE3: + call Child goto DUMMY3_1; + answer Parent goto DUMMY3_2; +state DUMMY3_1: + // the parent receives this from the child in this state + recv GetAnsweredParent goto CHECK; + // this transition is never taken (if the custom race resolution + // works correctly) + answer Parent goto CHECK; +state DUMMY3_2: + call Child goto CHECK; +state CHECK: + // the child sends this from this state + recv GetAnsweredParent goto DYING; + // because of deferred processing, the parent receives the child's + // message here + answer Parent goto DYING; + + +state DYING: + send __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl b/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl new file mode 100644 index 000000000..10b377487 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl @@ -0,0 +1,34 @@ +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestInterruptShutdownRace { +parent: + sync StartDeath(); + async Orphan(); + +child: + async Start(); + intr Exit(); + async __delete__(); + +state START: + send Start goto START_DEATH; + +state START_DEATH: + recv StartDeath goto EXITING; + +state EXITING: + recv Orphan goto QUITTING1; + call Exit goto QUITTING2; + +state QUITTING1: + call Exit goto DEAD; +state QUITTING2: + recv Orphan goto DEAD; + +state DEAD: + send __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestJSON.ipdl b/ipc/ipdl/test/cxx/PTestJSON.ipdl new file mode 100644 index 000000000..6e8f3c1de --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestJSON.ipdl @@ -0,0 +1,54 @@ +include protocol PTestHandle; + +using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; +using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; + +namespace mozilla { +namespace _ipdltest { + +union Key { +// int; +// double; + nsString; +}; + +struct KeyValue { + Key key; + JSONVariant value; +}; + +union JSONVariant { + void_t; + null_t; + bool; + int; + double; + nsString; + PTestHandle; + KeyValue[]; + JSONVariant[]; +}; + +sync protocol PTestJSON { + manages PTestHandle; + +child: + async Start(); + +parent: + async PTestHandle(); + sync Test(JSONVariant i) + returns (JSONVariant o); + async __delete__(); + +state START: + send Start goto TEST; + +state TEST: + recv PTestHandle goto TEST; + recv Test goto TEST; + recv __delete__; +}; + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestLatency.ipdl b/ipc/ipdl/test/cxx/PTestLatency.ipdl new file mode 100644 index 000000000..d0c9750d8 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestLatency.ipdl @@ -0,0 +1,75 @@ + +namespace mozilla { +namespace _ipdltest { + + +intr protocol PTestLatency { + +child: + async __delete__(); + async Ping(); + async Ping5(); + intr Rpc(); + async Spam(); + intr Synchro(); + async CompressedSpam(uint32_t seqno) compress; + intr Synchro2() returns (uint32_t lastSeqno, + uint32_t numMessagesDispatched); + +parent: + async Pong(); + async Pong5(); + +state START: + // if the timing resolution is too low, abort the test + send __delete__; + // otherwise, kick off the ping/pong trials + send Ping goto PONG; + + // Trial 1: single ping/pong latency +state PING: + send Ping goto PONG; + send Ping5 goto PING4; + +state PONG: + recv Pong goto PING; + + // Trial 2: "overlapped" ping/pong latency +state PING5: + send Ping5 goto PING4; + call Rpc goto RPC; + +state PING4: send Ping5 goto PING3; +state PING3: send Ping5 goto PING2; +state PING2: send Ping5 goto PING1; +state PING1: send Ping5 goto PONG1; + +state PONG1: recv Pong5 goto PONG2; +state PONG2: recv Pong5 goto PONG3; +state PONG3: recv Pong5 goto PONG4; +state PONG4: recv Pong5 goto PONG5; +state PONG5: recv Pong5 goto PING5; + + // Trial 3: lotsa RPC +state RPC: + call Rpc goto RPC; + send Spam goto SPAM; + + // Trial 4: lots of sequential asyn messages, which tests pipelining +state SPAM: + send Spam goto SPAM; + call Synchro goto COMPRESSED_SPAM; + + // Trial 5: lots of async spam, but compressed to cut down on + // dispatch overhead +state COMPRESSED_SPAM: // compressed spam, mmm + send CompressedSpam goto COMPRESSED_SPAM; + call Synchro2 goto DONE; + +state DONE: + send __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl b/ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl new file mode 100644 index 000000000..767af85a2 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl @@ -0,0 +1,19 @@ +include protocol PTestManyChildAllocsSub; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestManyChildAllocs { + manages PTestManyChildAllocsSub; + +child: + async Go(); // start allocating + +parent: + async Done(); + + async PTestManyChildAllocsSub(); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl b/ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl new file mode 100644 index 000000000..e3d9c98df --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl @@ -0,0 +1,19 @@ +include protocol PTestManyChildAllocs; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestManyChildAllocsSub { + manager PTestManyChildAllocs; + +child: + async __delete__(); + +parent: + async Hello(); + + // empty +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl new file mode 100644 index 000000000..6322bb1a3 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl @@ -0,0 +1,34 @@ +include protocol PTestMultiMgrsLeft; +include protocol PTestMultiMgrsRight; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestMultiMgrs { + manages PTestMultiMgrsLeft; + manages PTestMultiMgrsRight; + +parent: + async OK(); + +child: + async PTestMultiMgrsLeft(); + async PTestMultiMgrsRight(); + async Check(); + async __delete__(); + +state START: + send PTestMultiMgrsLeft goto CONSTRUCT_RIGHT; +state CONSTRUCT_RIGHT: + send PTestMultiMgrsRight goto CHILD_CHECK; +state CHILD_CHECK: + send Check goto CHILD_ACK; +state CHILD_ACK: + recv OK goto DONE; + +state DONE: + send __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl new file mode 100644 index 000000000..9c474f446 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl @@ -0,0 +1,18 @@ +include protocol PTestMultiMgrsLeft; +include protocol PTestMultiMgrsRight; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestMultiMgrsBottom { + manager PTestMultiMgrsLeft or PTestMultiMgrsRight; + +child: + async __delete__(); + +state DOA: + send __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl new file mode 100644 index 000000000..830176272 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl @@ -0,0 +1,24 @@ +include protocol PTestMultiMgrs; +include protocol PTestMultiMgrsBottom; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestMultiMgrsLeft { + manager PTestMultiMgrs; + + manages PTestMultiMgrsBottom; + +child: + async PTestMultiMgrsBottom(); + async __delete__(); + +state START: + send PTestMultiMgrsBottom goto DONE; + +state DONE: + send __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl new file mode 100644 index 000000000..36b0771b7 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl @@ -0,0 +1,24 @@ +include protocol PTestMultiMgrs; +include protocol PTestMultiMgrsBottom; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestMultiMgrsRight { + manager PTestMultiMgrs; + + manages PTestMultiMgrsBottom; + +child: + async PTestMultiMgrsBottom(); + async __delete__(); + +state START: + send PTestMultiMgrsBottom goto DONE; + +state DONE: + send __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl b/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl new file mode 100644 index 000000000..f0f067e0b --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl @@ -0,0 +1,34 @@ + +namespace mozilla { +namespace _ipdltest { + + +intr protocol PTestNestedLoops { + +child: + async Start(); + intr R(); + async __delete__(); + +parent: + async Nonce(); + + +state START: + send Start goto RACE; + +state RACE: + recv Nonce goto RACE1; + call R goto RACE2; +state RACE1: + call R goto DEAD; +state RACE2: + recv Nonce goto DEAD; + +state DEAD: + send __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestOpens.ipdl b/ipc/ipdl/test/cxx/PTestOpens.ipdl new file mode 100644 index 000000000..2717328fa --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestOpens.ipdl @@ -0,0 +1,25 @@ +include protocol PTestOpensOpened; + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestOpens { + // This channel is opened and parked on a non-main thread + child opens PTestOpensOpened; + +child: + async Start(); + +parent: + async __delete__(); + +state START: + send Start goto DEAD; +state DEAD: + recv __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl b/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl new file mode 100644 index 000000000..04b633634 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl @@ -0,0 +1,28 @@ +namespace mozilla { +namespace _ipdltest2 { + +// (Opens protocols can have different semantics than the endpoints +// that opened them) +intr protocol PTestOpensOpened { +child: + async Hi(); + intr HiRpc(); + +parent: + async Hello(); + sync HelloSync(); + intr HelloRpc(); + async __delete__(); + +state START: recv Hello goto HI; +state HI: send Hi goto HELLO_SYNC; +state HELLO_SYNC: recv HelloSync goto HELLO_RPC; +state HELLO_RPC: answer HelloRpc goto HI_RPC; +state HI_RPC: call HiRpc goto DEAD; +state DEAD: + recv __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest2 diff --git a/ipc/ipdl/test/cxx/PTestPriority.ipdl b/ipc/ipdl/test/cxx/PTestPriority.ipdl new file mode 100644 index 000000000..edb98365b --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestPriority.ipdl @@ -0,0 +1,14 @@ +namespace mozilla { +namespace _ipdltest { + +sync protocol PTestPriority { +parent: + prio(high) async Msg1(); + prio(high) sync Msg2(); + +child: + prio(high) async Msg3(); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestRPC.ipdl b/ipc/ipdl/test/cxx/PTestRPC.ipdl new file mode 100644 index 000000000..f51ee2735 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestRPC.ipdl @@ -0,0 +1,21 @@ +namespace mozilla { +namespace _ipdltest { + +nested(upto inside_sync) sync protocol PTestRPC +{ +parent: + nested(inside_sync) sync Test1_Start() returns (uint32_t result); + nested(inside_sync) sync Test1_InnerEvent() returns (uint32_t result); + async Test2_Start(); + nested(inside_sync) sync Test2_OutOfOrder(); + +child: + async Start(); + nested(inside_sync) sync Test1_InnerQuery() returns (uint32_t result); + nested(inside_sync) sync Test1_NoReenter() returns (uint32_t result); + nested(inside_sync) sync Test2_FirstUrgent(); + nested(inside_sync) sync Test2_SecondUrgent(); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestRaceDeadlock.ipdl b/ipc/ipdl/test/cxx/PTestRaceDeadlock.ipdl new file mode 100644 index 000000000..1e4f57450 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestRaceDeadlock.ipdl @@ -0,0 +1,20 @@ +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestRaceDeadlock { +both: + async StartRace(); + +parent: + intr Lose(); + +child: + intr Win(); + intr Rpc(); + async __delete__(); + +/* Tests that race resolution does not cause deadlocks */ +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl b/ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl new file mode 100644 index 000000000..ada6df4c6 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl @@ -0,0 +1,45 @@ +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestRaceDeferral { +parent: + intr Lose(); + +child: + async StartRace(); + intr Win(); + intr Rpc(); + async __delete__(); + +// Test that messages deferred due to race resolution are +// re-considered when the winner makes later RPCs + +// IPDL's type system can't express this protocol because the race +// resolution causes state to diverge for multiple steps, so we'll +// leave it "stateless" +/* +state START: + send StartRace goto DEFER; +state DEFER: + call Win goto PARENT; + answer Lose goto CHILD; + +state PARENT: + // 'Lose' is received here but deferred + call Rpc goto PARENT_LOSE; +state PARENT_LOSE: + // Calling 'Rpc' undefers 'Lose', and it wins the "race" with 'Rpc' + answer Lose goto DONE; + +state CHILD: + call Win goto CHILD_RPC; +state CHILD_RPC: + call Rpc goto DONE; + +state DONE: + send __delete__; +*/ +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl b/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl new file mode 100644 index 000000000..56e700b85 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl @@ -0,0 +1,41 @@ +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestRacyInterruptReplies { +child: + intr R_() returns (int replyNum); + async _A(); + async ChildTest(); + async __delete__(); + +parent: + intr _R() returns (int replyNum); + async A_(); + +state PARENT_START: + call R_ goto PARENT_S1; + +state PARENT_S1: + recv A_ goto PARENT_S2; + +state PARENT_S2: + call R_ goto CHILD_TEST; + +state CHILD_TEST: + send ChildTest goto CHILD_START; + +state CHILD_START: + answer _R goto CHILD_S1; + +state CHILD_S1: + send _A goto CHILD_S2; + +state CHILD_S2: + answer _R goto DYING; + +state DYING: + send __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestRacyReentry.ipdl b/ipc/ipdl/test/cxx/PTestRacyReentry.ipdl new file mode 100644 index 000000000..4dd5fe54f --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestRacyReentry.ipdl @@ -0,0 +1,21 @@ + +namespace mozilla { +namespace _ipdltest { + + +intr protocol PTestRacyReentry { + +parent: + intr E(); + async __delete__(); + +child: + async Start(); + + async N(); + intr H(); +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestRacyUndefer.ipdl b/ipc/ipdl/test/cxx/PTestRacyUndefer.ipdl new file mode 100644 index 000000000..8863d6179 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestRacyUndefer.ipdl @@ -0,0 +1,28 @@ + +namespace mozilla { +namespace _ipdltest { + + +intr protocol PTestRacyUndefer { + +child: + async Start(); + + async AwakenSpam(); + async AwakenRaceWinTwice(); + + intr Race(); + + async __delete__(); + +parent: + + intr Spam(); + intr RaceWinTwice(); + + async Done(); +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestSanity.ipdl b/ipc/ipdl/test/cxx/PTestSanity.ipdl new file mode 100644 index 000000000..50249b741 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSanity.ipdl @@ -0,0 +1,28 @@ + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestSanity { + +child: + async Ping(int zero, float zeroPtFive, int8_t dummy); + async __delete__(); + +parent: + async Pong(int one, float zeroPtTwoFive, uint8_t dummy); + + +state PING: + send Ping goto PONG; + +state PONG: + recv Pong goto DEAD; + +state DEAD: + send __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestSelfManage.ipdl b/ipc/ipdl/test/cxx/PTestSelfManage.ipdl new file mode 100644 index 000000000..69eb4f55c --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSelfManage.ipdl @@ -0,0 +1,22 @@ +include protocol PTestSelfManageRoot; + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestSelfManage { + manager PTestSelfManageRoot or PTestSelfManage; + manages PTestSelfManage; + +child: + async PTestSelfManage(); + async __delete__(); + +state LIVE: + send PTestSelfManage goto LIVE; + send __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl b/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl new file mode 100644 index 000000000..429f8bfc8 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl @@ -0,0 +1,23 @@ +include protocol PTestSelfManage; + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestSelfManageRoot { + manages PTestSelfManage; + +child: + async PTestSelfManage(); + async __delete__(); + +state LIVE: + send PTestSelfManage goto DEAD; + +state DEAD: + send __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestShmem.ipdl b/ipc/ipdl/test/cxx/PTestShmem.ipdl new file mode 100644 index 000000000..46a1958e2 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestShmem.ipdl @@ -0,0 +1,22 @@ +namespace mozilla { +namespace _ipdltest { + +protocol PTestShmem { +child: + async Give(Shmem mem, Shmem unsafe, size_t expectedSize); + +parent: + async Take(Shmem mem, Shmem unsafe, size_t expectedSize); + async __delete__(); + + +state GIVING: + send Give goto TAKING; + +state TAKING: + recv Take goto TAKING; + recv __delete__; +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestShutdown.ipdl b/ipc/ipdl/test/cxx/PTestShutdown.ipdl new file mode 100644 index 000000000..e4d8d0dbe --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestShutdown.ipdl @@ -0,0 +1,37 @@ +include protocol PTestShutdownSub; + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestShutdown { + manages PTestShutdownSub; + +child: + async Start(); + +parent: + // NB: we test deletion and crashing only, not shutdown, because + // crashing is the same code path as shutdown, and other IPDL unit + // tests check shutdown semantics + async PTestShutdownSub(bool expectCrash); + + // Used to synchronize between parent and child, to avoid races + // around flushing socket write queues + sync Sync(); + + async __delete__(); + + +state START: + send Start goto TESTING; + +state TESTING: + recv PTestShutdownSub goto TESTING; + recv Sync goto DYING; + +state DYING: + recv __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl b/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl new file mode 100644 index 000000000..39d45d9ed --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl @@ -0,0 +1,30 @@ +include protocol PTestShutdown; +include protocol PTestShutdownSubsub; + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestShutdownSub { + manager PTestShutdown; + manages PTestShutdownSubsub; + +both: + intr StackFrame(); + +parent: + async PTestShutdownSubsub(bool expectParentDeleted); + sync __delete__(); + +state CREATING: + recv PTestShutdownSubsub goto CREATING; + answer StackFrame goto DUMMYFRAME; + +state DUMMYFRAME: + call StackFrame goto DEAD; + +state DEAD: + recv __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl b/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl new file mode 100644 index 000000000..91031ab41 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl @@ -0,0 +1,17 @@ +include protocol PTestShutdownSub; + +namespace mozilla { +namespace _ipdltest { + +sync protocol PTestShutdownSubsub { + manager PTestShutdownSub; + +parent: + sync __delete__(); + +state LIVE: + recv __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestStackHooks.ipdl b/ipc/ipdl/test/cxx/PTestStackHooks.ipdl new file mode 100644 index 000000000..1f3af068f --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestStackHooks.ipdl @@ -0,0 +1,56 @@ + +namespace mozilla { +namespace _ipdltest { + + +intr protocol PTestStackHooks { +child: + async Start(); + + // These tests are more fruitful running child->parent, because + // children can send |sync| messages +parent: + async Async(); + sync Sync(); + intr Rpc(); + +both: + intr StackFrame(); + +parent: + async __delete__(); + + +state START: + send Start goto TEST1; + +state TEST1: + recv Async goto TEST2; + +state TEST2: + recv Sync goto TEST3; + +state TEST3: + answer Rpc goto TEST4; + +state TEST4: + answer StackFrame goto TEST4_2; +state TEST4_2: + call StackFrame goto TEST4_3; +state TEST4_3: + recv Async goto TEST5; + +state TEST5: + answer StackFrame goto TEST5_2; +state TEST5_2: + call StackFrame goto TEST5_3; +state TEST5_3: + recv Sync goto DEAD; + +state DEAD: + recv __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestSyncError.ipdl b/ipc/ipdl/test/cxx/PTestSyncError.ipdl new file mode 100644 index 000000000..05b68ff35 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSyncError.ipdl @@ -0,0 +1,28 @@ + +namespace mozilla { +namespace _ipdltest { + + +sync protocol PTestSyncError { + +child: + async Start(); + +parent: + sync Error(); + async __delete__(); + + +state START: + send Start goto SYNC_ERROR; + +state SYNC_ERROR: + recv Error goto DEAD; + +state DEAD: + recv __delete__; +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestSyncHang.ipdl b/ipc/ipdl/test/cxx/PTestSyncHang.ipdl new file mode 100644 index 000000000..eefcf2c5e --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSyncHang.ipdl @@ -0,0 +1,15 @@ + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestSyncHang { + +child: + async __delete__(); + +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl b/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl new file mode 100644 index 000000000..3d873cdf5 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl @@ -0,0 +1,41 @@ +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestSyncWakeup { +both: + intr StackFrame(); + +child: + async Start(); + async Note1(); + async Note2(); + +parent: + sync Sync1(); + sync Sync2(); + async __delete__(); + + +state START: + send Start goto TEST1; + +state TEST1: + recv Sync1 goto TEST1_P2; +state TEST1_P2: + send Note1 goto TEST2; + +state TEST2: + answer StackFrame goto TEST2_P2; +state TEST2_P2: + call StackFrame goto TEST2_P3; +state TEST2_P3: + recv Sync2 goto TEST2_P4; +state TEST2_P4: + send Note2 goto DONE; + +state DONE: + recv __delete__; +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestUrgency.ipdl b/ipc/ipdl/test/cxx/PTestUrgency.ipdl new file mode 100644 index 000000000..79b479ca2 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestUrgency.ipdl @@ -0,0 +1,19 @@ +namespace mozilla { +namespace _ipdltest { + +nested(upto inside_cpow) sync protocol PTestUrgency +{ +parent: + nested(inside_sync) sync Test1() returns (uint32_t result); + async Test2(); + sync Test3() returns (uint32_t result); + sync FinalTest_Begin(); + +child: + async Start(); + nested(inside_sync) sync Reply1() returns (uint32_t result); + nested(inside_sync) sync Reply2() returns (uint32_t result); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestUrgentHangs.ipdl b/ipc/ipdl/test/cxx/PTestUrgentHangs.ipdl new file mode 100644 index 000000000..8ce8d1747 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestUrgentHangs.ipdl @@ -0,0 +1,28 @@ +namespace mozilla { +namespace _ipdltest { + +nested(upto inside_cpow) sync protocol PTestUrgentHangs +{ +parent: + nested(inside_sync) sync Test1_2(); + + nested(inside_sync) sync TestInner(); + nested(inside_cpow) sync TestInnerUrgent(); + +child: + nested(inside_sync) sync Test1_1(); + nested(inside_sync) sync Test1_3(); + + nested(inside_sync) sync Test2(); + + nested(inside_sync) sync Test3(); + + async Test4(); + nested(inside_sync) sync Test4_1(); + + async Test5(); + nested(inside_sync) sync Test5_1(); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/README.txt b/ipc/ipdl/test/cxx/README.txt new file mode 100644 index 000000000..0fe6c0732 --- /dev/null +++ b/ipc/ipdl/test/cxx/README.txt @@ -0,0 +1,61 @@ +To add a new IPDL C++ unit test, you need to create (at least) the +following files (for a test "TestFoo"): + + - PTestFoo.ipdl, specifying the top-level protocol used for the test + + - TestFoo.h, declaring the top-level parent/child actors used for + the test + + - TestFoo.cpp, defining the top-level actors + + - (make sure all are in the namespace mozilla::_ipdltest) + +Next + + - add PTestFoo.ipdl to ipdl.mk + + - append TestFoo to the variable IPDLTESTS in Makefile.in + +You must define three methods in your |TestFooParent| class: + + - static methods |bool RunTestInProcesses()| and + |bool RunTestInThreads()|. These methods control whether + to execute the test using actors in separate processes and + threads respectively. Generally, both should return true. + + - an instance method |void Main()|. The test harness wil first + initialize the processes or threads, create and open both actors, + and then kick off the test using |Main()|. Make sure you define + it. + +If your test passes its criteria, please call +|MOZ_IPDL_TESTPASS("msg")| and "exit gracefully". + +If your tests fails, please call |MOZ_IPDL_TESTFAIL("msg")| and "exit +ungracefully", preferably by abort()ing. + + +If all goes well, running + + make -C $OBJDIR/ipc/ipdl/test/cxx + +will update the file IPDLUnitTests.cpp (the test launcher), and your +new code will be built automatically. + + +You can launch your new test by invoking one of + + make -C $OBJDIR/ipc/ipdl/test/cxx check-proc (test process-based tests) + make -C $OBJDIR/ipc/ipdl/test/cxx check-threads (test thread-based tests) + make -C $OBJDIR/ipc/ipdl/test/cxx check (tests both) + +If you want to launch only your test, run + + cd $OBJDIR/dist/bin + ./run-mozilla.sh ./ipdlunittest TestFoo (test in two processes, if appl.) + ./run-mozilla.sh ./ipdlunittest thread:TestFoo (test in two threads, if appl.) + + +For a bare-bones example of adding a test, take a look at +PTestSanity.ipdl, TestSanity.h, TestSanity.cpp, and how "TestSanity" +is included in ipdl.mk and Makefile.in. diff --git a/ipc/ipdl/test/cxx/TestActorPunning.cpp b/ipc/ipdl/test/cxx/TestActorPunning.cpp new file mode 100644 index 000000000..eb6fa412f --- /dev/null +++ b/ipc/ipdl/test/cxx/TestActorPunning.cpp @@ -0,0 +1,128 @@ +#include "TestActorPunning.h" + +#include "IPDLUnitTests.h" // fail etc. +#include "mozilla/Unused.h" + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +void +TestActorPunningParent::Main() +{ + if (!SendStart()) + fail("sending Start"); +} + +bool +TestActorPunningParent::RecvPun(PTestActorPunningSubParent* a, const Bad& bad) +{ + if (a->SendBad()) + fail("bad!"); + fail("shouldn't have received this message in the first place"); + return true; +} + +PTestActorPunningPunnedParent* +TestActorPunningParent::AllocPTestActorPunningPunnedParent() +{ + return new TestActorPunningPunnedParent(); +} + +bool +TestActorPunningParent::DeallocPTestActorPunningPunnedParent(PTestActorPunningPunnedParent* a) +{ + delete a; + return true; +} + +PTestActorPunningSubParent* +TestActorPunningParent::AllocPTestActorPunningSubParent() +{ + return new TestActorPunningSubParent(); +} + +bool +TestActorPunningParent::DeallocPTestActorPunningSubParent(PTestActorPunningSubParent* a) +{ + delete a; + return true; +} + +//----------------------------------------------------------------------------- +// child + +PTestActorPunningPunnedChild* +TestActorPunningChild::AllocPTestActorPunningPunnedChild() +{ + return new TestActorPunningPunnedChild(); +} + +bool +TestActorPunningChild::DeallocPTestActorPunningPunnedChild(PTestActorPunningPunnedChild*) +{ + fail("should have died by now"); + return true; +} + +PTestActorPunningSubChild* +TestActorPunningChild::AllocPTestActorPunningSubChild() +{ + return new TestActorPunningSubChild(); +} + +bool +TestActorPunningChild::DeallocPTestActorPunningSubChild(PTestActorPunningSubChild*) +{ + fail("should have died by now"); + return true; +} + +bool +TestActorPunningChild::RecvStart() +{ + SendPTestActorPunningSubConstructor(); + SendPTestActorPunningPunnedConstructor(); + PTestActorPunningSubChild* a = SendPTestActorPunningSubConstructor(); + // We can't assert whether this succeeds or fails, due to race + // conditions. + SendPun(a, Bad()); + return true; +} + +bool +TestActorPunningSubChild::RecvBad() +{ + fail("things are going really badly right now"); + return true; +} + + +} // namespace _ipdltest +} // namespace mozilla + +namespace IPC { +using namespace mozilla::_ipdltest; +using namespace mozilla::ipc; + +/*static*/ void +ParamTraits<Bad>::Write(Message* aMsg, const paramType& aParam) +{ + // Skip past the sentinel for the actor as well as the actor. + int32_t* ptr = aMsg->GetInt32PtrForTest(2 * sizeof(int32_t)); + ActorHandle* ah = reinterpret_cast<ActorHandle*>(ptr); + if (ah->mId != -3) + fail("guessed wrong offset (value is %d, should be -3)", ah->mId); + ah->mId = -2; +} + +/*static*/ bool +ParamTraits<Bad>::Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) +{ + return true; +} + +} // namespace IPC + diff --git a/ipc/ipdl/test/cxx/TestActorPunning.h b/ipc/ipdl/test/cxx/TestActorPunning.h new file mode 100644 index 000000000..5ebf2e7f4 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestActorPunning.h @@ -0,0 +1,110 @@ +#ifndef mozilla__ipdltest_TestActorPunning_h +#define mozilla__ipdltest_TestActorPunning_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestActorPunningParent.h" +#include "mozilla/_ipdltest/PTestActorPunningPunnedParent.h" +#include "mozilla/_ipdltest/PTestActorPunningSubParent.h" +#include "mozilla/_ipdltest/PTestActorPunningChild.h" +#include "mozilla/_ipdltest/PTestActorPunningPunnedChild.h" +#include "mozilla/_ipdltest/PTestActorPunningSubChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestActorPunningParent : + public PTestActorPunningParent +{ +public: + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + PTestActorPunningPunnedParent* AllocPTestActorPunningPunnedParent() override; + bool DeallocPTestActorPunningPunnedParent(PTestActorPunningPunnedParent* a) override; + + PTestActorPunningSubParent* AllocPTestActorPunningSubParent() override; + bool DeallocPTestActorPunningSubParent(PTestActorPunningSubParent* a) override; + + virtual bool RecvPun(PTestActorPunningSubParent* a, const Bad& bad) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown == why) + fail("should have died from error!"); + passed("ok"); + QuitParent(); + } +}; + +class TestActorPunningPunnedParent : + public PTestActorPunningPunnedParent +{ +public: + TestActorPunningPunnedParent() {} + virtual ~TestActorPunningPunnedParent() {} +protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} +}; + +class TestActorPunningSubParent : + public PTestActorPunningSubParent +{ +public: + TestActorPunningSubParent() {} + virtual ~TestActorPunningSubParent() {} +protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} +}; + + +class TestActorPunningChild : + public PTestActorPunningChild +{ +public: + TestActorPunningChild() {} + virtual ~TestActorPunningChild() {} + +protected: + PTestActorPunningPunnedChild* AllocPTestActorPunningPunnedChild() override; + bool DeallocPTestActorPunningPunnedChild(PTestActorPunningPunnedChild* a) override; + + PTestActorPunningSubChild* AllocPTestActorPunningSubChild() override; + bool DeallocPTestActorPunningSubChild(PTestActorPunningSubChild* a) override; + + virtual bool RecvStart() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + fail("should have been killed off!"); + } +}; + +class TestActorPunningPunnedChild : + public PTestActorPunningPunnedChild +{ +public: + TestActorPunningPunnedChild() {} + virtual ~TestActorPunningPunnedChild() {} +}; + +class TestActorPunningSubChild : + public PTestActorPunningSubChild +{ +public: + TestActorPunningSubChild() {} + virtual ~TestActorPunningSubChild() {} + + virtual bool RecvBad() override; +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestActorPunning_h diff --git a/ipc/ipdl/test/cxx/TestBadActor.cpp b/ipc/ipdl/test/cxx/TestBadActor.cpp new file mode 100644 index 000000000..bc1cf6f6d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestBadActor.cpp @@ -0,0 +1,51 @@ +#include "TestBadActor.h" +#include "IPDLUnitTests.h" +#include "mozilla/Unused.h" + +namespace mozilla { +namespace _ipdltest { + +void +TestBadActorParent::Main() +{ + // This test is designed to test a race condition where the child sends us + // a message on an actor that we've already destroyed. The child process + // should die, and the parent process should not abort. + + PTestBadActorSubParent* child = SendPTestBadActorSubConstructor(); + if (!child) + fail("Sending constructor"); + + Unused << child->Call__delete__(child); +} + +PTestBadActorSubParent* +TestBadActorParent::AllocPTestBadActorSubParent() +{ + return new TestBadActorSubParent(); +} + +bool +TestBadActorSubParent::RecvPing() +{ + fail("Shouldn't have received ping."); + return false; +} + +PTestBadActorSubChild* +TestBadActorChild::AllocPTestBadActorSubChild() +{ + return new TestBadActorSubChild(); +} + +bool +TestBadActorChild::RecvPTestBadActorSubConstructor(PTestBadActorSubChild* actor) +{ + if (!actor->SendPing()) { + fail("Couldn't send ping to an actor which supposedly isn't dead yet."); + } + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestBadActor.h b/ipc/ipdl/test/cxx/TestBadActor.h new file mode 100644 index 000000000..9b1258ff6 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestBadActor.h @@ -0,0 +1,91 @@ +#ifndef mozilla__ipdltest_TestBadActor_h +#define mozilla__ipdltest_TestBadActor_h + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestBadActorParent.h" +#include "mozilla/_ipdltest/PTestBadActorChild.h" + +#include "mozilla/_ipdltest/PTestBadActorSubParent.h" +#include "mozilla/_ipdltest/PTestBadActorSubChild.h" + +namespace mozilla { +namespace _ipdltest { + +class TestBadActorParent + : public PTestBadActorParent +{ +public: + TestBadActorParent() { } + virtual ~TestBadActorParent() { } + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (AbnormalShutdown != why) + fail("unexpected destruction"); + passed("ok"); + QuitParent(); + } + + virtual PTestBadActorSubParent* + AllocPTestBadActorSubParent(); + + virtual bool + DeallocPTestBadActorSubParent(PTestBadActorSubParent* actor) { + delete actor; + return true; + } +}; + +class TestBadActorSubParent + : public PTestBadActorSubParent +{ +public: + TestBadActorSubParent() { } + virtual ~TestBadActorSubParent() { } + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} + virtual bool RecvPing(); +}; + +class TestBadActorChild + : public PTestBadActorChild +{ +public: + TestBadActorChild() { } + virtual ~TestBadActorChild() { } + +protected: + virtual PTestBadActorSubChild* + AllocPTestBadActorSubChild(); + + virtual bool + DeallocPTestBadActorSubChild(PTestBadActorSubChild* actor) + { + delete actor; + return true; + } + + virtual bool + RecvPTestBadActorSubConstructor(PTestBadActorSubChild* actor); +}; + +class TestBadActorSubChild + : public PTestBadActorSubChild +{ +public: + TestBadActorSubChild() { } + virtual ~TestBadActorSubChild() { } +}; + +} // namespace _ipdltest +} // namespace mozilla + +#endif // mozilla__ipdltest_TestBadActor_h diff --git a/ipc/ipdl/test/cxx/TestBridgeMain.cpp b/ipc/ipdl/test/cxx/TestBridgeMain.cpp new file mode 100644 index 000000000..9b1590a46 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp @@ -0,0 +1,227 @@ +#include "TestBridgeMain.h" + +#include "base/task.h" +#include "IPDLUnitTests.h" // fail etc. +#include "IPDLUnitTestSubprocess.h" + +#include "nsAutoPtr.h" + +using namespace std; + +namespace mozilla { +namespace _ipdltest { + + +//----------------------------------------------------------------------------- +// main process +void +TestBridgeMainParent::Main() +{ + if (!SendStart()) + fail("sending Start"); +} + +PTestBridgeMainSubParent* +TestBridgeMainParent::AllocPTestBridgeMainSubParent(Transport* transport, + ProcessId otherPid) +{ + nsAutoPtr<TestBridgeMainSubParent> a(new TestBridgeMainSubParent(transport)); + if (!a->Open(transport, otherPid, XRE_GetIOMessageLoop(), ipc::ParentSide)) { + return nullptr; + } + return a.forget(); +} + +void +TestBridgeMainParent::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); +} + +bool +TestBridgeMainSubParent::RecvHello() +{ + return SendHi(); +} + +bool +TestBridgeMainSubParent::RecvHelloSync() +{ + return true; +} + +bool +TestBridgeMainSubParent::AnswerHelloRpc() +{ + return CallHiRpc(); +} + +void +TestBridgeMainSubParent::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) + fail("unexpected destruction!"); + + // ActorDestroy() is just a callback from IPDL-generated code, + // which needs the top-level actor (this) to stay alive a little + // longer so other things can be cleaned up. + MessageLoop::current()->PostTask( + do_AddRef(new DeleteTask<TestBridgeMainSubParent>(this))); +} + +//----------------------------------------------------------------------------- +// sub process --- child of main +TestBridgeMainChild* gBridgeMainChild; + +TestBridgeMainChild::TestBridgeMainChild() + : mSubprocess(nullptr) +{ + gBridgeMainChild = this; +} + +bool +TestBridgeMainChild::RecvStart() +{ + vector<string> subsubArgs; + subsubArgs.push_back("TestBridgeSub"); + + mSubprocess = new IPDLUnitTestSubprocess(); + if (!mSubprocess->SyncLaunch(subsubArgs)) + fail("problem launching subprocess"); + + IPC::Channel* transport = mSubprocess->GetChannel(); + if (!transport) + fail("no transport"); + + TestBridgeSubParent* bsp = new TestBridgeSubParent(); + bsp->Open(transport, base::GetProcId(mSubprocess->GetChildProcessHandle())); + + bsp->Main(); + return true; +} + +void +TestBridgeMainChild::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) + fail("unexpected destruction!"); + // NB: this is kosher because QuitChild() joins with the IO thread + XRE_GetIOMessageLoop()->PostTask( + do_AddRef(new DeleteTask<IPDLUnitTestSubprocess>(mSubprocess))); + QuitChild(); +} + +void +TestBridgeSubParent::Main() +{ + if (!SendPing()) + fail("sending Ping"); +} + +bool +TestBridgeSubParent::RecvBridgeEm() +{ + if (NS_FAILED(PTestBridgeMainSub::Bridge(gBridgeMainChild, this))) + fail("bridging Main and Sub"); + return true; +} + +void +TestBridgeSubParent::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) + fail("unexpected destruction!"); + gBridgeMainChild->Close(); + + // ActorDestroy() is just a callback from IPDL-generated code, + // which needs the top-level actor (this) to stay alive a little + // longer so other things can be cleaned up. + MessageLoop::current()->PostTask( + do_AddRef(new DeleteTask<TestBridgeSubParent>(this))); +} + +//----------------------------------------------------------------------------- +// subsub process --- child of sub + +static TestBridgeSubChild* gBridgeSubChild; + +TestBridgeSubChild::TestBridgeSubChild() +{ + gBridgeSubChild = this; +} + +bool +TestBridgeSubChild::RecvPing() +{ + if (!SendBridgeEm()) + fail("sending BridgeEm"); + return true; +} + +PTestBridgeMainSubChild* +TestBridgeSubChild::AllocPTestBridgeMainSubChild(Transport* transport, + ProcessId otherPid) +{ + nsAutoPtr<TestBridgeMainSubChild> a(new TestBridgeMainSubChild(transport)); + if (!a->Open(transport, otherPid, XRE_GetIOMessageLoop(), ipc::ChildSide)) { + return nullptr; + } + + if (!a->SendHello()) + fail("sending Hello"); + + return a.forget(); +} + +void +TestBridgeSubChild::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); +} + +bool +TestBridgeMainSubChild::RecvHi() +{ + if (!SendHelloSync()) + fail("sending HelloSync"); + if (!CallHelloRpc()) + fail("calling HelloRpc"); + if (!mGotHi) + fail("didn't answer HiRpc"); + + // Need to close the channel without message-processing frames on + // the C++ stack + MessageLoop::current()->PostTask( + NewNonOwningRunnableMethod(this, &TestBridgeMainSubChild::Close)); + return true; +} + +bool +TestBridgeMainSubChild::AnswerHiRpc() +{ + mGotHi = true; // d00d + return true; +} + +void +TestBridgeMainSubChild::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) + fail("unexpected destruction!"); + + gBridgeSubChild->Close(); + + // ActorDestroy() is just a callback from IPDL-generated code, + // which needs the top-level actor (this) to stay alive a little + // longer so other things can be cleaned up. + MessageLoop::current()->PostTask( + do_AddRef(new DeleteTask<TestBridgeMainSubChild>(this))); +} + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/TestBridgeMain.h b/ipc/ipdl/test/cxx/TestBridgeMain.h new file mode 100644 index 000000000..a5c7c10cb --- /dev/null +++ b/ipc/ipdl/test/cxx/TestBridgeMain.h @@ -0,0 +1,149 @@ +#ifndef mozilla__ipdltest_TestBridgeMain_h +#define mozilla__ipdltest_TestBridgeMain_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestBridgeMainParent.h" +#include "mozilla/_ipdltest/PTestBridgeMainChild.h" + +#include "mozilla/_ipdltest/PTestBridgeSubParent.h" +#include "mozilla/_ipdltest/PTestBridgeSubChild.h" + +#include "mozilla/_ipdltest/PTestBridgeMainSubParent.h" +#include "mozilla/_ipdltest/PTestBridgeMainSubChild.h" + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// "Main" process +// +class TestBridgeMainParent : + public PTestBridgeMainParent +{ +public: + TestBridgeMainParent() {} + virtual ~TestBridgeMainParent() {} + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + virtual PTestBridgeMainSubParent* + AllocPTestBridgeMainSubParent(Transport* transport, + ProcessId otherProcess) override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +class TestBridgeMainSubParent : + public PTestBridgeMainSubParent +{ +public: + explicit TestBridgeMainSubParent(Transport* aTransport) + : mTransport(aTransport) + {} + virtual ~TestBridgeMainSubParent() {} + +protected: + virtual bool RecvHello() override; + virtual bool RecvHelloSync() override; + virtual bool AnswerHelloRpc() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + Transport* mTransport; +}; + +//----------------------------------------------------------------------------- +// "Sub" process --- child of "main" +// +class TestBridgeSubParent; + +class TestBridgeMainChild : + public PTestBridgeMainChild +{ +public: + TestBridgeMainChild(); + virtual ~TestBridgeMainChild() {} + +protected: + virtual bool RecvStart() override; + + virtual PTestBridgeMainSubChild* + AllocPTestBridgeMainSubChild(Transport* transport, + ProcessId otherProcess) override + { + // This shouldn't be called. It's just a byproduct of testing that + // the right code is generated for a bridged protocol that's also + // opened, but we only test bridging here. + MOZ_CRASH(); + } + + virtual void ActorDestroy(ActorDestroyReason why) override; + + IPDLUnitTestSubprocess* mSubprocess; +}; + +class TestBridgeSubParent : + public PTestBridgeSubParent +{ +public: + TestBridgeSubParent() {} + virtual ~TestBridgeSubParent() {} + + void Main(); + +protected: + virtual bool RecvBridgeEm() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +//----------------------------------------------------------------------------- +// "Subsub" process --- child of "sub" +// +class TestBridgeSubChild : + public PTestBridgeSubChild +{ +public: + TestBridgeSubChild(); + virtual ~TestBridgeSubChild() {} + +protected: + virtual bool RecvPing() override; + + virtual PTestBridgeMainSubChild* + AllocPTestBridgeMainSubChild(Transport* transport, + ProcessId otherProcess) override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +class TestBridgeMainSubChild : + public PTestBridgeMainSubChild +{ +public: + explicit TestBridgeMainSubChild(Transport* aTransport) + : mGotHi(false) + , mTransport(aTransport) + {} + virtual ~TestBridgeMainSubChild() {} + +protected: + virtual bool RecvHi() override; + virtual bool AnswerHiRpc() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + bool mGotHi; + Transport* mTransport; +}; + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestBridgeMain_h diff --git a/ipc/ipdl/test/cxx/TestCancel.cpp b/ipc/ipdl/test/cxx/TestCancel.cpp new file mode 100644 index 000000000..8f983b3de --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCancel.cpp @@ -0,0 +1,168 @@ +#include "TestCancel.h" + +#include "IPDLUnitTests.h" // fail etc. + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestCancelParent::TestCancelParent() +{ + MOZ_COUNT_CTOR(TestCancelParent); +} + +TestCancelParent::~TestCancelParent() +{ + MOZ_COUNT_DTOR(TestCancelParent); +} + +void +TestCancelParent::Main() +{ + if (SendTest1_1()) + fail("sending Test1_1"); + + uint32_t value = 0; + if (!SendCheckChild(&value)) + fail("Test1 CheckChild"); + + if (value != 12) + fail("Test1 CheckChild reply"); +} + +bool +TestCancelParent::RecvDone1() +{ + if (!SendStart2()) + fail("sending Start2"); + + return true; +} + +bool +TestCancelParent::RecvTest2_1() +{ + if (SendTest2_2()) + fail("sending Test2_2"); + + return true; +} + +bool +TestCancelParent::RecvStart3() +{ + if (SendTest3_1()) + fail("sending Test3_1"); + + uint32_t value = 0; + if (!SendCheckChild(&value)) + fail("Test1 CheckChild"); + + if (value != 12) + fail("Test1 CheckChild reply"); + + return true; +} + +bool +TestCancelParent::RecvTest3_2() +{ + GetIPCChannel()->CancelCurrentTransaction(); + return true; +} + +bool +TestCancelParent::RecvDone() +{ + MessageLoop::current()->PostTask( + NewNonOwningRunnableMethod(this, &TestCancelParent::Close)); + return true; +} + +bool +TestCancelParent::RecvCheckParent(uint32_t *reply) +{ + *reply = 12; + return true; +} + +//----------------------------------------------------------------------------- +// child + +bool +TestCancelChild::RecvTest1_1() +{ + GetIPCChannel()->CancelCurrentTransaction(); + + uint32_t value = 0; + if (!SendCheckParent(&value)) + fail("Test1 CheckParent"); + + if (value != 12) + fail("Test1 CheckParent reply"); + + if (!SendDone1()) + fail("Test1 CheckParent"); + + return true; +} + +bool +TestCancelChild::RecvStart2() +{ + if (!SendTest2_1()) + fail("sending Test2_1"); + + if (!SendStart3()) + fail("sending Start3"); + + return true; +} + +bool +TestCancelChild::RecvTest2_2() +{ + GetIPCChannel()->CancelCurrentTransaction(); + return true; +} + +bool +TestCancelChild::RecvTest3_1() +{ + if (SendTest3_2()) + fail("sending Test3_2"); + + uint32_t value = 0; + if (!SendCheckParent(&value)) + fail("Test1 CheckParent"); + + if (value != 12) + fail("Test1 CheckParent reply"); + + if (!SendDone()) + fail("sending Done"); + + return true; +} + +bool +TestCancelChild::RecvCheckChild(uint32_t *reply) +{ + *reply = 12; + return true; +} + +TestCancelChild::TestCancelChild() +{ + MOZ_COUNT_CTOR(TestCancelChild); +} + +TestCancelChild::~TestCancelChild() +{ + MOZ_COUNT_DTOR(TestCancelChild); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestCancel.h b/ipc/ipdl/test/cxx/TestCancel.h new file mode 100644 index 000000000..8cd51acd1 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCancel.h @@ -0,0 +1,66 @@ +#ifndef mozilla__ipdltest_TestCancel_h +#define mozilla__ipdltest_TestCancel_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestCancelParent.h" +#include "mozilla/_ipdltest/PTestCancelChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestCancelParent : + public PTestCancelParent +{ +public: + TestCancelParent(); + virtual ~TestCancelParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + + virtual bool RecvDone1() override; + virtual bool RecvTest2_1() override; + virtual bool RecvStart3() override; + virtual bool RecvTest3_2() override; + virtual bool RecvDone() override; + + virtual bool RecvCheckParent(uint32_t *reply) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + passed("ok"); + QuitParent(); + } +}; + + +class TestCancelChild : + public PTestCancelChild +{ +public: + TestCancelChild(); + virtual ~TestCancelChild(); + + virtual bool RecvTest1_1() override; + virtual bool RecvStart2() override; + virtual bool RecvTest2_2() override; + virtual bool RecvTest3_1() override; + + virtual bool RecvCheckChild(uint32_t *reply) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestCancel_h diff --git a/ipc/ipdl/test/cxx/TestCrashCleanup.cpp b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp new file mode 100644 index 000000000..2f335daae --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp @@ -0,0 +1,116 @@ +#include "TestCrashCleanup.h" + +#include "base/task.h" +#include "mozilla/CondVar.h" +#include "mozilla/Mutex.h" + +#include "IPDLUnitTests.h" // fail etc. +#include "IPDLUnitTestSubprocess.h" + +using mozilla::CondVar; +using mozilla::Mutex; +using mozilla::MutexAutoLock; + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +namespace { + +// NB: this test does its own shutdown, rather than going through +// QuitParent(), because it's testing degenerate edge cases + +void DeleteSubprocess(Mutex* mutex, CondVar* cvar) +{ + MutexAutoLock lock(*mutex); + + delete gSubprocess; + gSubprocess = nullptr; + + cvar->Notify(); +} + +void DeleteTheWorld() +{ + delete static_cast<TestCrashCleanupParent*>(gParentActor); + gParentActor = nullptr; + + // needs to be synchronous to avoid affecting event ordering on + // the main thread + Mutex mutex("TestCrashCleanup.DeleteTheWorld.mutex"); + CondVar cvar(mutex, "TestCrashCleanup.DeleteTheWorld.cvar"); + + MutexAutoLock lock(mutex); + + XRE_GetIOMessageLoop()->PostTask( + NewRunnableFunction(DeleteSubprocess, &mutex, &cvar)); + + cvar.Wait(); +} + +void Done() +{ + static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID)); + appShell->Exit(); + + passed(__FILE__); +} + +} // namespace <anon> + +TestCrashCleanupParent::TestCrashCleanupParent() : mCleanedUp(false) +{ + MOZ_COUNT_CTOR(TestCrashCleanupParent); +} + +TestCrashCleanupParent::~TestCrashCleanupParent() +{ + MOZ_COUNT_DTOR(TestCrashCleanupParent); + + if (!mCleanedUp) + fail("should have been ActorDestroy()d!"); +} + +void +TestCrashCleanupParent::Main() +{ + // NB: has to be enqueued before IO thread's error notification + MessageLoop::current()->PostTask( + NewRunnableFunction(DeleteTheWorld)); + + if (CallDIEDIEDIE()) + fail("expected an error!"); + + Close(); + + MessageLoop::current()->PostTask(NewRunnableFunction(Done)); +} + + +//----------------------------------------------------------------------------- +// child + +TestCrashCleanupChild::TestCrashCleanupChild() +{ + MOZ_COUNT_CTOR(TestCrashCleanupChild); +} + +TestCrashCleanupChild::~TestCrashCleanupChild() +{ + MOZ_COUNT_DTOR(TestCrashCleanupChild); +} + +bool +TestCrashCleanupChild::AnswerDIEDIEDIE() +{ + _exit(0); + NS_RUNTIMEABORT("unreached"); + return false; +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestCrashCleanup.h b/ipc/ipdl/test/cxx/TestCrashCleanup.h new file mode 100644 index 000000000..b64b2c628 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCrashCleanup.h @@ -0,0 +1,58 @@ +#ifndef mozilla__ipdltest_TestCrashCleanup_h +#define mozilla__ipdltest_TestCrashCleanup_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestCrashCleanupParent.h" +#include "mozilla/_ipdltest/PTestCrashCleanupChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestCrashCleanupParent : + public PTestCrashCleanupParent +{ +public: + TestCrashCleanupParent(); + virtual ~TestCrashCleanupParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (AbnormalShutdown != why) + fail("unexpected destruction!"); + mCleanedUp = true; + } + + bool mCleanedUp; +}; + + +class TestCrashCleanupChild : + public PTestCrashCleanupChild +{ +public: + TestCrashCleanupChild(); + virtual ~TestCrashCleanupChild(); + +protected: + virtual bool AnswerDIEDIEDIE() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + fail("should have 'crashed'!"); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestCrashCleanup_h diff --git a/ipc/ipdl/test/cxx/TestDataStructures.cpp b/ipc/ipdl/test/cxx/TestDataStructures.cpp new file mode 100644 index 000000000..fe0d5918a --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDataStructures.cpp @@ -0,0 +1,984 @@ +#include "TestDataStructures.h" + +#include "mozilla/Unused.h" + +#include "IPDLUnitTests.h" // fail etc. + +typedef InfallibleTArray<nsIntRegion> RegionArray; + +namespace mozilla { +namespace _ipdltest { + +static const uint32_t nactors = 10; + +#define test_assert(_cond, _msg) \ + if (!(_cond)) fail(_msg) + +template<typename T> +static void +assert_arrays_equal(const InfallibleTArray<T>& a, const InfallibleTArray<T>& b) +{ + test_assert(a == b, "arrays equal"); +} + +inline static TestDataStructuresSub& +Cast(PTestDataStructuresSubParent* a) +{ + return *static_cast<TestDataStructuresSub*>(a); +} + +inline static TestDataStructuresSub& +Cast(PTestDataStructuresSubChild* a) +{ + return *static_cast<TestDataStructuresSub*>(a); +} + +//----------------------------------------------------------------------------- +// parent + +TestDataStructuresParent::TestDataStructuresParent() +{ + MOZ_COUNT_CTOR(TestDataStructuresParent); +} + +TestDataStructuresParent::~TestDataStructuresParent() +{ + MOZ_COUNT_DTOR(TestDataStructuresParent); +} + +void +TestDataStructuresParent::Main() +{ + for (uint32_t i = 0; i < nactors; ++i) + if (!SendPTestDataStructuresSubConstructor(i)) + fail("can't alloc actor"); + + if (!SendStart()) + fail("can't send Start()"); +} + +bool +TestDataStructuresParent::DeallocPTestDataStructuresSubParent(PTestDataStructuresSubParent* actor) +{ + test_assert(Cast(actor).mI == Cast(mKids[0]).mI, + "dtor sent to wrong actor"); + mKids.RemoveElementAt(0); + delete actor; + if (mKids.Length() > 0) + return true; + + return true; +} + +bool TestDataStructuresParent::RecvTest1( + InfallibleTArray<int>&& ia, + InfallibleTArray<int>* oa) +{ + test_assert(5 == ia.Length(), "wrong length"); + for (int i = 0; i < 5; ++i) + test_assert(i == ia[i], "wrong value"); + + *oa = ia; + + return true; +} + +bool TestDataStructuresParent::RecvTest2( + InfallibleTArray<PTestDataStructuresSubParent*>&& i1, + InfallibleTArray<PTestDataStructuresSubParent*>* o1) +{ + test_assert(nactors == i1.Length(), "wrong #actors"); + for (uint32_t i = 0; i < i1.Length(); ++i) + test_assert(i == Cast(i1[i]).mI, "wrong mI value"); + *o1 = i1; + return true; +} + +bool TestDataStructuresParent::RecvTest3( + const IntDouble& i1, + const IntDouble& i2, + IntDouble* o1, + IntDouble* o2) +{ + test_assert(42 == i1.get_int(), "wrong value"); + test_assert(4.0 == i2.get_double(), "wrong value"); + + *o1 = i1; + *o2 = i2; + + return true; +} + +bool TestDataStructuresParent::RecvTest4( + InfallibleTArray<IntDouble>&& i1, + InfallibleTArray<IntDouble>* o1) +{ + test_assert(4 == i1.Length(), "wrong length"); + test_assert(1 == i1[0].get_int(), "wrong value"); + test_assert(2.0 == i1[1].get_double(), "wrong value"); + test_assert(3 == i1[2].get_int(), "wrong value"); + test_assert(4.0 == i1[3].get_double(), "wrong value"); + + *o1 = i1; + + return true; +} + +bool TestDataStructuresParent::RecvTest5( + const IntDoubleArrays& i1, + const IntDoubleArrays& i2, + const IntDoubleArrays& i3, + IntDoubleArrays* o1, + IntDoubleArrays* o2, + IntDoubleArrays* o3) +{ + test_assert(42 == i1.get_int(), "wrong value"); + + const InfallibleTArray<int>& i2a = i2.get_ArrayOfint(); + test_assert(3 == i2a.Length(), "wrong length"); + test_assert(1 == i2a[0], "wrong value"); + test_assert(2 == i2a[1], "wrong value"); + test_assert(3 == i2a[2], "wrong value"); + + const InfallibleTArray<double>& i3a = i3.get_ArrayOfdouble(); + test_assert(3 == i3a.Length(), "wrong length"); + test_assert(1.0 == i3a[0], "wrong value"); + test_assert(2.0 == i3a[1], "wrong value"); + test_assert(3.0 == i3a[2], "wrong value"); + + *o1 = i1; + *o2 = i2a; + *o3 = i3a; + + return true; +} + +bool +TestDataStructuresParent::RecvTest7_0(const ActorWrapper& i1, + ActorWrapper* o1) +{ + if (i1.actorChild() != nullptr) + fail("child side actor should always be null"); + + if (i1.actorParent() != mKids[0]) + fail("should have got back same actor on parent side"); + + o1->actorParent() = mKids[0]; + // malicious behavior + o1->actorChild() = + reinterpret_cast<PTestDataStructuresSubChild*>(uintptr_t(0xdeadbeef)); + return true; +} + +bool TestDataStructuresParent::RecvTest6( + InfallibleTArray<IntDoubleArrays>&& i1, + InfallibleTArray<IntDoubleArrays>* o1) +{ + test_assert(3 == i1.Length(), "wrong length"); + + IntDoubleArrays id1(i1[0]); + test_assert(42 == id1.get_int(), "wrong value"); + + InfallibleTArray<int> i2a(i1[1].get_ArrayOfint()); + test_assert(3 == i2a.Length(), "wrong length"); + test_assert(1 == i2a[0], "wrong value"); + test_assert(2 == i2a[1], "wrong value"); + test_assert(3 == i2a[2], "wrong value"); + + InfallibleTArray<double> i3a(i1[2].get_ArrayOfdouble()); + test_assert(3 == i3a.Length(), "wrong length"); + test_assert(1.0 == i3a[0], "wrong value"); + test_assert(2.0 == i3a[1], "wrong value"); + test_assert(3.0 == i3a[2], "wrong value"); + + o1->AppendElement(id1); + o1->AppendElement(IntDoubleArrays(i2a)); + o1->AppendElement(IntDoubleArrays(i3a)); + + return true; +} + +bool TestDataStructuresParent::RecvTest7( + const Actors& i1, + const Actors& i2, + const Actors& i3, + Actors* o1, + Actors* o2, + Actors* o3) +{ + test_assert(42 == i1.get_int(), "wrong value"); + + InfallibleTArray<int> i2a(i2.get_ArrayOfint()); + test_assert(3 == i2a.Length(), "wrong length"); + test_assert(1 == i2a[0], "wrong value"); + test_assert(2 == i2a[1], "wrong value"); + test_assert(3 == i2a[2], "wrong value"); + + assert_arrays_equal(mKids, i3.get_ArrayOfPTestDataStructuresSubParent()); + + *o1 = 42; + *o2 = i2a; + *o3 = mKids; + + return true; +} + +bool TestDataStructuresParent::RecvTest8( + InfallibleTArray<Actors>&& i1, + InfallibleTArray<Actors>* o1) +{ + test_assert(3 == i1.Length(), "wrong length"); + test_assert(42 == i1[0].get_int(), "wrong value"); + + const InfallibleTArray<int>& i2a = i1[1].get_ArrayOfint(); + test_assert(3 == i2a.Length(), "wrong length"); + test_assert(1 == i2a[0], "wrong value"); + test_assert(2 == i2a[1], "wrong value"); + test_assert(3 == i2a[2], "wrong value"); + + assert_arrays_equal(mKids, i1[2].get_ArrayOfPTestDataStructuresSubParent()); + + *o1 = i1; + + return true; +} + +bool TestDataStructuresParent::RecvTest9( + const Unions& i1, + const Unions& i2, + const Unions& i3, + const Unions& i4, + Unions* o1, + Unions* o2, + Unions* o3, + Unions* o4) +{ + test_assert(42 == i1.get_int(), "wrong value"); + + const InfallibleTArray<int>& i2a = i2.get_ArrayOfint(); + test_assert(3 == i2a.Length(), "wrong length"); + test_assert(1 == i2a[0], "wrong value"); + test_assert(2 == i2a[1], "wrong value"); + test_assert(3 == i2a[2], "wrong value"); + + assert_arrays_equal(mKids, i3.get_ArrayOfPTestDataStructuresSubParent()); + + const InfallibleTArray<PTestDataStructuresSubParent*>& i4a = + i4.get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubParent(); + assert_arrays_equal(mKids, i4a); + + *o1 = i1; + *o2 = i2; + *o3 = i3; + *o4 = i4; + + return true; +} + +bool TestDataStructuresParent::RecvTest10( + InfallibleTArray<Unions>&& i1, + InfallibleTArray<Unions>* o1) +{ + test_assert(42 == i1[0].get_int(), "wrong value"); + + const InfallibleTArray<int>& i2a = i1[1].get_ArrayOfint(); + test_assert(3 == i2a.Length(), "wrong length"); + test_assert(1 == i2a[0], "wrong value"); + test_assert(2 == i2a[1], "wrong value"); + test_assert(3 == i2a[2], "wrong value"); + + assert_arrays_equal(mKids, i1[2].get_ArrayOfPTestDataStructuresSubParent()); + + const InfallibleTArray<PTestDataStructuresSubParent*>& i4a = + i1[3].get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubParent(); + assert_arrays_equal(mKids, i4a); + + *o1 = i1; + + return true; +} + +bool TestDataStructuresParent::RecvTest11( + const SIntDouble& i, + SIntDouble* o) +{ + test_assert(1 == i.i(), "wrong value"); + test_assert(2.0 == i.d(), "wrong value"); + *o = i; + return true; +} + +bool TestDataStructuresParent::RecvTest12( + const SIntDoubleArrays& i, + SIntDoubleArrays* o) +{ + InfallibleTArray<int> ai; + ai.AppendElement(1); + ai.AppendElement(2); + ai.AppendElement(3); + + InfallibleTArray<double> ad; + ad.AppendElement(.5); + ad.AppendElement(1.0); + ad.AppendElement(2.0); + + test_assert(42 == i.i(), "wrong value"); + assert_arrays_equal(ai, i.ai()); + assert_arrays_equal(ad, i.ad()); + + *o = i; + + return true; +} + +bool TestDataStructuresParent::RecvTest13( + const SActors& i, + SActors* o) +{ + InfallibleTArray<int> ai; + ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3); + + test_assert(42 == i.i(), "wrong value"); + assert_arrays_equal(ai, i.ai()); + assert_arrays_equal(mKids, i.apParent()); + + *o = i; + + return true; +} + +bool TestDataStructuresParent::RecvTest14( + const Structs& i, + Structs* o) +{ + InfallibleTArray<int> ai; + ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3); + + test_assert(42 == i.i(), "wrong value"); + assert_arrays_equal(ai, i.ai()); + assert_arrays_equal(mKids, i.apParent()); + + const SActors& ia = i.aa()[0]; + test_assert(42 == ia.i(), "wrong value"); + assert_arrays_equal(ai, ia.ai()); + assert_arrays_equal(mKids, ia.apParent()); + + *o = i; + + return true; +} + +bool TestDataStructuresParent::RecvTest15( + const WithStructs& i1, + const WithStructs& i2, + const WithStructs& i3, + const WithStructs& i4, + const WithStructs& i5, + WithStructs* o1, + WithStructs* o2, + WithStructs* o3, + WithStructs* o4, + WithStructs* o5) +{ + InfallibleTArray<int> ai; + ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3); + + test_assert(i1 == int(42), "wrong value"); + assert_arrays_equal(i2.get_ArrayOfint(), ai); + assert_arrays_equal(i3.get_ArrayOfPTestDataStructuresSubParent(), mKids); + + const SActors& ia = i4.get_ArrayOfSActors()[0]; + test_assert(42 == ia.i(), "wrong value"); + assert_arrays_equal(ai, ia.ai()); + assert_arrays_equal(mKids, ia.apParent()); + + const Structs& is = i5.get_ArrayOfStructs()[0]; + test_assert(42 == is.i(), "wrong value"); + assert_arrays_equal(ai, is.ai()); + assert_arrays_equal(mKids, is.apParent()); + + const SActors& isa = is.aa()[0]; + test_assert(42 == isa.i(), "wrong value"); + assert_arrays_equal(ai, isa.ai()); + assert_arrays_equal(mKids, isa.apParent()); + + *o1 = i1; + *o2 = i2; + *o3 = i3; + *o4 = i4; + *o5 = i5; + + return true; +} + +bool TestDataStructuresParent::RecvTest16( + const WithUnions& i, + WithUnions* o) +{ + test_assert(i.i() == 42, "wrong value"); + + InfallibleTArray<int> ai; + ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3); + assert_arrays_equal(ai, i.ai()); + + assert_arrays_equal(i.apParent(), mKids); + + assert_arrays_equal(mKids, i.aa()[0].get_ArrayOfPTestDataStructuresSubParent()); + + const InfallibleTArray<Unions>& iau = i.au(); + test_assert(iau[0] == 42, "wrong value"); + assert_arrays_equal(ai, iau[1].get_ArrayOfint()); + assert_arrays_equal(mKids, iau[2].get_ArrayOfPTestDataStructuresSubParent()); + assert_arrays_equal(mKids, + iau[3].get_ArrayOfActors()[0] + .get_ArrayOfPTestDataStructuresSubParent()); + + *o = i; + + return true; +} + +bool TestDataStructuresParent::RecvTest17(InfallibleTArray<Op>&& sa) +{ + test_assert(sa.Length() == 1 && Op::TSetAttrs == sa[0].type(), + "wrong value"); + return true; +} + +bool TestDataStructuresParent::RecvTest18(RegionArray&& ra) +{ + for (RegionArray::index_type i = 0; i < ra.Length(); ++i) { + // if |ra| has been realloc()d and given a different allocator + // chunk, this loop will nondeterministically crash or iloop. + for (auto iter = ra[i].RectIter(); !iter.Done(); iter.Next()) { + Unused << iter.Get(); + } + } + return true; +} + +//----------------------------------------------------------------------------- +// child + +TestDataStructuresChild::TestDataStructuresChild() +{ + MOZ_COUNT_CTOR(TestDataStructuresChild); +} + +TestDataStructuresChild::~TestDataStructuresChild() +{ + MOZ_COUNT_DTOR(TestDataStructuresChild); +} + +bool +TestDataStructuresChild::RecvStart() +{ + puts("[TestDataStructuresChild] starting"); + + Test1(); + Test2(); + Test3(); + Test4(); + Test5(); + Test6(); + Test7_0(); + Test7(); + Test8(); + Test9(); + Test10(); + Test11(); + Test12(); + Test13(); + Test14(); + Test15(); + Test16(); + Test17(); + if (OtherPid() != base::GetCurrentProcId()) { + //FIXME/bug 703317 allocation of nsIntRegion uses a global + //region pool which breaks threads + Test18(); + } + + for (uint32_t i = 0; i < nactors; ++i) + if (!PTestDataStructuresSubChild::Send__delete__(mKids[i])) + fail("can't send dtor"); + + Close(); + + return true; +} + +void +TestDataStructuresChild::Test1() +{ + InfallibleTArray<int> ia; + + for (int i = 0; i < 5; ++i) + ia.AppendElement(i); + + InfallibleTArray<int> oa; + if (!SendTest1(ia, &oa)) + fail("can't send Test1"); + + assert_arrays_equal(ia, oa); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test2() +{ + InfallibleTArray<PTestDataStructuresSubChild*> oa; + if (!SendTest2(mKids, &oa)) + fail("can't send Test2"); + assert_arrays_equal(mKids, oa); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test3() +{ + int i1i = 42; + double i2d = 4.0; + IntDouble i1(i1i); + IntDouble i2(i2d); + IntDouble o1, o2; + + SendTest3(i1, i2, &o1, &o2); + + test_assert(i1i == o1.get_int(), "wrong value"); + test_assert(i2d == o2.get_double(), "wrong value"); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test4() +{ + InfallibleTArray<IntDouble> i1; + i1.AppendElement(IntDouble(int(1))); + i1.AppendElement(IntDouble(2.0)); + i1.AppendElement(IntDouble(int(3))); + i1.AppendElement(IntDouble(4.0)); + + InfallibleTArray<IntDouble> o1; + if (!SendTest4(i1, &o1)) + fail("can't send Test4"); + + // TODO Union::operator==() + test_assert(i1.Length() == o1.Length(), "wrong length"); + test_assert(1 == o1[0].get_int(), "wrong value"); + test_assert(2.0 == o1[1].get_double(), "wrong value"); + test_assert(3 == o1[2].get_int(), "wrong value"); + test_assert(4.0 == o1[3].get_double(), "wrong value"); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test5() +{ + IntDoubleArrays i1(int(42)); + InfallibleTArray<int> i2; + i2.AppendElement(1); i2.AppendElement(2); i2.AppendElement(3); + InfallibleTArray<double> i3; + i3.AppendElement(1.0); i3.AppendElement(2.0); i3.AppendElement(3.0); + + IntDoubleArrays o1, o2, o3; + if (!SendTest5(i1, IntDoubleArrays(i2), IntDoubleArrays(i3), + &o1, &o2, &o3)) + fail("can't send Test5"); + + test_assert(42 == o1.get_int(), "wrong value"); + assert_arrays_equal(i2, o2.get_ArrayOfint()); + assert_arrays_equal(i3, o3.get_ArrayOfdouble()); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test6() +{ + IntDoubleArrays id1(int(42)); + InfallibleTArray<int> id2; + id2.AppendElement(1); id2.AppendElement(2); id2.AppendElement(3); + InfallibleTArray<double> id3; + id3.AppendElement(1.0); id3.AppendElement(2.0); id3.AppendElement(3.0); + + InfallibleTArray<IntDoubleArrays> i1; + i1.AppendElement(id1); + i1.AppendElement(IntDoubleArrays(id2)); + i1.AppendElement(IntDoubleArrays(id3)); + + InfallibleTArray<IntDoubleArrays> o1; + if (!SendTest6(i1, &o1)) + fail("can't send Test6"); + + test_assert(3 == o1.Length(), "wrong length"); + IntDoubleArrays od1(o1[0]); + InfallibleTArray<int> od2(o1[1].get_ArrayOfint()); + InfallibleTArray<double> od3(o1[2].get_ArrayOfdouble()); + + test_assert(42 == od1.get_int(), "wrong value"); + assert_arrays_equal(id2, od2); + assert_arrays_equal(id3, od3); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test7_0() +{ + ActorWrapper iaw; + if (iaw.actorChild() != nullptr || iaw.actorParent() != nullptr) + fail("actor members should be null initially"); + + iaw.actorChild() = mKids[0]; + if (iaw.actorParent() != nullptr) + fail("parent should be null on child side after set"); + + ActorWrapper oaw; + if (!SendTest7_0(iaw, &oaw)) + fail("sending Test7_0"); + + if (oaw.actorParent() != nullptr) + fail("parent accessor on actor-struct members should always be null in child"); + + if (oaw.actorChild() != mKids[0]) + fail("should have got back same child-side actor"); +} + +void +TestDataStructuresChild::Test7() +{ + Actors i1(42); + InfallibleTArray<int> i2a; + i2a.AppendElement(1); i2a.AppendElement(2); i2a.AppendElement(3); + + Actors o1, o2, o3; + if (!SendTest7(i1, Actors(i2a), Actors(mKids), &o1, &o2, &o3)) + fail("can't send Test7"); + + test_assert(42 == o1.get_int(), "wrong value"); + assert_arrays_equal(i2a, o2.get_ArrayOfint()); + assert_arrays_equal(mKids, o3.get_ArrayOfPTestDataStructuresSubChild()); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test8() +{ + Actors i1e(42); + InfallibleTArray<int> i2a; + i2a.AppendElement(1); i2a.AppendElement(2); i2a.AppendElement(3); + + InfallibleTArray<Actors> i1; + i1.AppendElement(i1e); + i1.AppendElement(i2a); + i1.AppendElement(mKids); + + InfallibleTArray<Actors> o1; + if (!SendTest8(i1, &o1)) + fail("can't send Test8"); + + test_assert(3 == o1.Length(), "wrong length"); + test_assert(42 == o1[0].get_int(), "wrong value"); + assert_arrays_equal(i2a, o1[1].get_ArrayOfint()); + assert_arrays_equal(mKids, o1[2].get_ArrayOfPTestDataStructuresSubChild()); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test9() +{ + Unions i1(int(42)); + + InfallibleTArray<int> i2a; + i2a.AppendElement(1); + i2a.AppendElement(2); + i2a.AppendElement(3); + + InfallibleTArray<Actors> i4a; + i4a.AppendElement(mKids); + + Unions o1, o2, o3, o4; + if (!SendTest9(i1, Unions(i2a), Unions(mKids), Unions(i4a), + &o1, &o2, &o3, &o4)) + fail("can't send Test9"); + + test_assert(42 == o1.get_int(), "wrong value"); + assert_arrays_equal(i2a, o2.get_ArrayOfint()); + assert_arrays_equal(mKids, o3.get_ArrayOfPTestDataStructuresSubChild()); + assert_arrays_equal( + mKids, + o4.get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubChild()); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test10() +{ + Unions i1a(int(42)); + + InfallibleTArray<int> i2a; + i2a.AppendElement(1); + i2a.AppendElement(2); + i2a.AppendElement(3); + + InfallibleTArray<Actors> i4a; + i4a.AppendElement(mKids); + + InfallibleTArray<Unions> i1; + i1.AppendElement(i1a); + i1.AppendElement(Unions(i2a)); + i1.AppendElement(Unions(mKids)); + i1.AppendElement(Unions(i4a)); + + InfallibleTArray<Unions> o1; + if (!SendTest10(i1, &o1)) + fail("can't send Test10"); + + test_assert(4 == o1.Length(), "wrong length"); + test_assert(42 == o1[0].get_int(), "wrong value"); + assert_arrays_equal(i2a, o1[1].get_ArrayOfint()); + assert_arrays_equal(mKids, o1[2].get_ArrayOfPTestDataStructuresSubChild()); + assert_arrays_equal( + mKids, + o1[3].get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubChild()); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test11() +{ + SIntDouble i(1, 2.0); + SIntDouble o; + + if (!SendTest11(i, &o)) + fail("sending Test11"); + + test_assert(1 == o.i() && 2.0 == o.d(), "wrong values"); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test12() +{ + InfallibleTArray<int> ai; + ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3); + + InfallibleTArray<double> ad; + ad.AppendElement(.5); ad.AppendElement(1.0); ad.AppendElement(2.0); + + SIntDoubleArrays i(42, ai, ad); + SIntDoubleArrays o; + + if (!SendTest12(i, &o)) + fail("sending Test12"); + + test_assert(42 == o.i(), "wrong value"); + assert_arrays_equal(ai, o.ai()); + assert_arrays_equal(ad, o.ad()); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test13() +{ + InfallibleTArray<int> ai; + ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3); + + SActors i; + i.i() = 42; + i.ai() = ai; + i.apChild() = mKids; + + SActors o; + if (!SendTest13(i, &o)) + fail("can't send Test13"); + + test_assert(42 == o.i(), "wrong value"); + assert_arrays_equal(ai, o.ai()); + assert_arrays_equal(mKids, o.apChild()); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test14() +{ + InfallibleTArray<int> ai; + ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3); + + SActors ia; + ia.i() = 42; + ia.ai() = ai; + ia.apChild() = mKids; + InfallibleTArray<SActors> aa; aa.AppendElement(ia); + + Structs i; + i.i() = 42; + i.ai() = ai; + i.apChild() = mKids; + i.aa() = aa; + + Structs o; + if (!SendTest14(i, &o)) + fail("can't send Test14"); + + test_assert(42 == o.i(), "wrong value"); + assert_arrays_equal(ai, o.ai()); + assert_arrays_equal(mKids, o.apChild()); + + const SActors& os = o.aa()[0]; + test_assert(42 == os.i(), "wrong value"); + assert_arrays_equal(ai, os.ai()); + assert_arrays_equal(mKids, os.apChild()); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test15() +{ + InfallibleTArray<int> ai; + ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3); + + SActors ia; + ia.i() = 42; + ia.ai() = ai; + ia.apChild() = mKids; + InfallibleTArray<SActors> iaa; iaa.AppendElement(ia); + + Structs is; + is.i() = 42; + is.ai() = ai; + is.apChild() = mKids; + is.aa() = iaa; + InfallibleTArray<Structs> isa; isa.AppendElement(is); + + WithStructs o1, o2, o3, o4, o5; + if (!SendTest15(WithStructs(42), + WithStructs(ai), + WithStructs(mKids), + WithStructs(iaa), + WithStructs(isa), + &o1, &o2, &o3, &o4, &o5)) + fail("sending Test15"); + + test_assert(o1 == int(42), "wrong value"); + assert_arrays_equal(o2.get_ArrayOfint(), ai); + assert_arrays_equal(o3.get_ArrayOfPTestDataStructuresSubChild(), mKids); + + const SActors& oa = o4.get_ArrayOfSActors()[0]; + test_assert(42 == oa.i(), "wrong value"); + assert_arrays_equal(ai, oa.ai()); + assert_arrays_equal(mKids, oa.apChild()); + + const Structs& os = o5.get_ArrayOfStructs()[0]; + test_assert(42 == os.i(), "wrong value"); + assert_arrays_equal(ai, os.ai()); + assert_arrays_equal(mKids, os.apChild()); + + const SActors& osa = os.aa()[0]; + test_assert(42 == osa.i(), "wrong value"); + assert_arrays_equal(ai, osa.ai()); + assert_arrays_equal(mKids, osa.apChild()); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test16() +{ + WithUnions i; + + i.i() = 42; + + InfallibleTArray<int> ai; + ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3); + i.ai() = ai; + + i.apChild() = mKids; + + InfallibleTArray<Actors> iaa; + iaa.AppendElement(mKids); + i.aa() = iaa; + + InfallibleTArray<Unions> iau; + iau.AppendElement(int(42)); + iau.AppendElement(ai); + iau.AppendElement(mKids); + iau.AppendElement(iaa); + i.au() = iau; + + WithUnions o; + if (!SendTest16(i, &o)) + fail("sending Test16"); + + test_assert(42 == o.i(), "wrong value"); + assert_arrays_equal(o.ai(), ai); + assert_arrays_equal(o.apChild(), mKids); + + const Actors& oaa = o.aa()[0]; + assert_arrays_equal(oaa.get_ArrayOfPTestDataStructuresSubChild(), mKids); + + const InfallibleTArray<Unions>& oau = o.au(); + test_assert(oau[0] == 42, "wrong value"); + assert_arrays_equal(oau[1].get_ArrayOfint(), ai); + assert_arrays_equal(oau[2].get_ArrayOfPTestDataStructuresSubChild(), + mKids); + assert_arrays_equal(oau[3].get_ArrayOfActors()[0] + .get_ArrayOfPTestDataStructuresSubChild(), + mKids); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test17() +{ + Attrs attrs; + attrs.common() = CommonAttrs(true); + attrs.specific() = BarAttrs(1.0f); + + InfallibleTArray<Op> ops; + ops.AppendElement(SetAttrs(nullptr, mKids[0], attrs)); + + if (!SendTest17(ops)) + fail("sending Test17"); + + printf(" passed %s\n", __FUNCTION__); +} + +void +TestDataStructuresChild::Test18() +{ + const int nelements = 1000; + RegionArray ra; + // big enough to hopefully force a realloc to a different chunk of + // memory on the receiving side, if the workaround isn't working + // correctly. But SetCapacity() here because we don't want to + // crash on the sending side. + ra.SetCapacity(nelements); + for (int i = 0; i < nelements; ++i) { + nsIntRegion r; + r.Or(nsIntRect(0, 0, 10, 10), nsIntRect(10, 10, 10, 10)); + ra.AppendElement(r); + } + + if (!SendTest18(ra)) + fail("sending Test18"); + + printf(" passed %s\n", __FUNCTION__); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestDataStructures.h b/ipc/ipdl/test/cxx/TestDataStructures.h new file mode 100644 index 000000000..f77cfa100 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDataStructures.h @@ -0,0 +1,234 @@ +#ifndef mozilla__ipdltest_TestDataStructures_h +#define mozilla__ipdltest_TestDataStructures_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestDataStructuresParent.h" +#include "mozilla/_ipdltest/PTestDataStructuresChild.h" + +#include "mozilla/_ipdltest/PTestDataStructuresSubParent.h" +#include "mozilla/_ipdltest/PTestDataStructuresSubChild.h" + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// Subprotocol actors + +class TestDataStructuresSub : + public PTestDataStructuresSubParent, + public PTestDataStructuresSubChild +{ +public: + explicit TestDataStructuresSub(uint32_t i) : mI(i) + { } + virtual ~TestDataStructuresSub() + { } + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (Deletion != why) + fail("unexpected destruction!"); + } + uint32_t mI; +}; + +//----------------------------------------------------------------------------- +// Main actors + +class TestDataStructuresParent : + public PTestDataStructuresParent +{ +public: + TestDataStructuresParent(); + virtual ~TestDataStructuresParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual PTestDataStructuresSubParent* AllocPTestDataStructuresSubParent(const int& i) override + { + PTestDataStructuresSubParent* actor = new TestDataStructuresSub(i); + mKids.AppendElement(actor); + return actor; + } + + virtual bool DeallocPTestDataStructuresSubParent(PTestDataStructuresSubParent* actor) override; + + virtual bool RecvTest1( + InfallibleTArray<int>&& i1, + InfallibleTArray<int>* o1) override; + + virtual bool RecvTest2( + InfallibleTArray<PTestDataStructuresSubParent*>&& i1, + InfallibleTArray<PTestDataStructuresSubParent*>* o1) override; + + virtual bool RecvTest3( + const IntDouble& i1, + const IntDouble& i2, + IntDouble* o1, + IntDouble* o2) override; + + virtual bool RecvTest4( + InfallibleTArray<IntDouble>&& i1, + InfallibleTArray<IntDouble>* o1) override; + + virtual bool RecvTest5( + const IntDoubleArrays& i1, + const IntDoubleArrays& i2, + const IntDoubleArrays& i3, + IntDoubleArrays* o1, + IntDoubleArrays* o2, + IntDoubleArrays* o3) override; + + virtual bool RecvTest6( + InfallibleTArray<IntDoubleArrays>&& i1, + InfallibleTArray<IntDoubleArrays>* o1) override; + + + virtual bool RecvTest7_0(const ActorWrapper& i1, + ActorWrapper* o1) override; + + virtual bool RecvTest7( + const Actors& i1, + const Actors& i2, + const Actors& i3, + Actors* o1, + Actors* o2, + Actors* o3) override; + + virtual bool RecvTest8( + InfallibleTArray<Actors>&& i1, + InfallibleTArray<Actors>* o1) override; + + virtual bool RecvTest9( + const Unions& i1, + const Unions& i2, + const Unions& i3, + const Unions& i4, + Unions* o1, + Unions* o2, + Unions* o3, + Unions* o4) override; + + virtual bool RecvTest10( + InfallibleTArray<Unions>&& i1, + InfallibleTArray<Unions>* o1) override; + + virtual bool RecvTest11( + const SIntDouble& i, + SIntDouble* o) override; + + virtual bool RecvTest12( + const SIntDoubleArrays& i, + SIntDoubleArrays* o) override; + + virtual bool RecvTest13( + const SActors& i, + SActors* o) override; + + virtual bool RecvTest14( + const Structs& i, + Structs* o) override; + + virtual bool RecvTest15( + const WithStructs& i1, + const WithStructs& i2, + const WithStructs& i3, + const WithStructs& i4, + const WithStructs& i5, + WithStructs* o1, + WithStructs* o2, + WithStructs* o3, + WithStructs* o4, + WithStructs* o5) override; + + virtual bool RecvTest16( + const WithUnions& i, + WithUnions* o) override; + + virtual bool RecvTest17(InfallibleTArray<Op>&& sa) override; + + virtual bool RecvTest18(InfallibleTArray<nsIntRegion>&& ra) override; + + virtual bool RecvDummy(const ShmemUnion& su, ShmemUnion* rsu) override + { + *rsu = su; + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + +private: + InfallibleTArray<PTestDataStructuresSubParent*> mKids; +}; + + +class TestDataStructuresChild : + public PTestDataStructuresChild +{ +public: + TestDataStructuresChild(); + virtual ~TestDataStructuresChild(); + +protected: + virtual PTestDataStructuresSubChild* AllocPTestDataStructuresSubChild(const int& i) override + { + PTestDataStructuresSubChild* actor = new TestDataStructuresSub(i); + mKids.AppendElement(actor); + return actor; + } + + virtual bool DeallocPTestDataStructuresSubChild(PTestDataStructuresSubChild* actor) override + { + delete actor; + return true; + } + + virtual bool RecvStart() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } + +private: + void Test1(); + void Test2(); + void Test3(); + void Test4(); + void Test5(); + void Test6(); + void Test7_0(); + void Test7(); + void Test8(); + void Test9(); + void Test10(); + void Test11(); + void Test12(); + void Test13(); + void Test14(); + void Test15(); + void Test16(); + void Test17(); + void Test18(); + + InfallibleTArray<PTestDataStructuresSubChild*> mKids; +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestDataStructures_h diff --git a/ipc/ipdl/test/cxx/TestDemon.cpp b/ipc/ipdl/test/cxx/TestDemon.cpp new file mode 100644 index 000000000..c349aafbe --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDemon.cpp @@ -0,0 +1,417 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=4 ts=4 et : + */ +#include "TestDemon.h" + +#include <stdlib.h> + +#include "IPDLUnitTests.h" // fail etc. +#if defined(OS_POSIX) +#include <sys/time.h> +#include <unistd.h> +#else +#include <time.h> +#include <windows.h> +#endif + +namespace mozilla { +namespace _ipdltest { + +const int kMaxStackHeight = 4; + +static LazyLogModule sLogModule("demon"); + +#define DEMON_LOG(...) MOZ_LOG(sLogModule, LogLevel::Debug, (__VA_ARGS__)) + +static int gStackHeight = 0; +static bool gFlushStack = false; + +static int +Choose(int count) +{ +#if defined(OS_POSIX) + return random() % count; +#else + return rand() % count; +#endif +} + +//----------------------------------------------------------------------------- +// parent + +TestDemonParent::TestDemonParent() + : mDone(false), + mIncoming(), + mOutgoing() +{ + MOZ_COUNT_CTOR(TestDemonParent); +} + +TestDemonParent::~TestDemonParent() +{ + MOZ_COUNT_DTOR(TestDemonParent); +} + +void +TestDemonParent::Main() +{ + if (!getenv("MOZ_TEST_IPC_DEMON")) { + QuitParent(); + return; + } +#if defined(OS_POSIX) + srandom(time(nullptr)); +#else + srand(time(nullptr)); +#endif + + DEMON_LOG("Start demon"); + + if (!SendStart()) + fail("sending Start"); + + RunUnlimitedSequence(); +} + +#ifdef DEBUG +bool +TestDemonParent::ShouldContinueFromReplyTimeout() +{ + return Choose(2) == 0; +} + +bool +TestDemonParent::ArtificialTimeout() +{ + return Choose(5) == 0; +} + +void +TestDemonParent::ArtificialSleep() +{ + if (Choose(2) == 0) { + // Sleep for anywhere from 0 to 100 milliseconds. + unsigned micros = Choose(100) * 1000; +#ifdef OS_POSIX + usleep(micros); +#else + Sleep(micros / 1000); +#endif + } +} +#endif + +bool +TestDemonParent::RecvAsyncMessage(const int& n) +{ + DEMON_LOG("Start RecvAsync [%d]", n); + + MOZ_ASSERT(n == mIncoming[0]); + mIncoming[0]++; + + RunLimitedSequence(); + + DEMON_LOG("End RecvAsync [%d]", n); + return true; +} + +bool +TestDemonParent::RecvHiPrioSyncMessage() +{ + DEMON_LOG("Start RecvHiPrioSyncMessage"); + RunLimitedSequence(); + DEMON_LOG("End RecvHiPrioSyncMessage"); + return true; +} + +bool +TestDemonParent::RecvSyncMessage(const int& n) +{ + DEMON_LOG("Start RecvSync [%d]", n); + + MOZ_ASSERT(n == mIncoming[0]); + mIncoming[0]++; + + RunLimitedSequence(ASYNC_ONLY); + + DEMON_LOG("End RecvSync [%d]", n); + return true; +} + +bool +TestDemonParent::RecvUrgentAsyncMessage(const int& n) +{ + DEMON_LOG("Start RecvUrgentAsyncMessage [%d]", n); + + MOZ_ASSERT(n == mIncoming[2]); + mIncoming[2]++; + + RunLimitedSequence(ASYNC_ONLY); + + DEMON_LOG("End RecvUrgentAsyncMessage [%d]", n); + return true; +} + +bool +TestDemonParent::RecvUrgentSyncMessage(const int& n) +{ + DEMON_LOG("Start RecvUrgentSyncMessage [%d]", n); + + MOZ_ASSERT(n == mIncoming[2]); + mIncoming[2]++; + + RunLimitedSequence(ASYNC_ONLY); + + DEMON_LOG("End RecvUrgentSyncMessage [%d]", n); + return true; +} + +void +TestDemonParent::RunUnlimitedSequence() +{ + if (mDone) { + return; + } + + gFlushStack = false; + DoAction(); + + MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(this, &TestDemonParent::RunUnlimitedSequence)); +} + +void +TestDemonParent::RunLimitedSequence(int flags) +{ + if (gStackHeight >= kMaxStackHeight) { + return; + } + gStackHeight++; + + int count = Choose(20); + for (int i = 0; i < count; i++) { + if (!DoAction(flags)) { + gFlushStack = true; + } + if (gFlushStack) { + gStackHeight--; + return; + } + } + + gStackHeight--; +} + +static bool +AllowAsync(int outgoing, int incoming) +{ + return incoming >= outgoing - 5; +} + +bool +TestDemonParent::DoAction(int flags) +{ + if (flags & ASYNC_ONLY) { + if (AllowAsync(mOutgoing[0], mIncoming[0])) { + DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]); + return SendAsyncMessage(mOutgoing[0]++); + } else { + return true; + } + } else { + switch (Choose(3)) { + case 0: + if (AllowAsync(mOutgoing[0], mIncoming[0])) { + DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]); + return SendAsyncMessage(mOutgoing[0]++); + } else { + return true; + } + + case 1: { + DEMON_LOG("Start SendHiPrioSyncMessage"); + bool r = SendHiPrioSyncMessage(); + DEMON_LOG("End SendHiPrioSyncMessage result=%d", r); + return r; + } + + case 2: + DEMON_LOG("Cancel"); + GetIPCChannel()->CancelCurrentTransaction(); + return true; + } + } + MOZ_CRASH(); + return false; +} + +//----------------------------------------------------------------------------- +// child + + +TestDemonChild::TestDemonChild() + : mIncoming(), + mOutgoing() +{ + MOZ_COUNT_CTOR(TestDemonChild); +} + +TestDemonChild::~TestDemonChild() +{ + MOZ_COUNT_DTOR(TestDemonChild); +} + +bool +TestDemonChild::RecvStart() +{ +#ifdef OS_POSIX + srandom(time(nullptr)); +#else + srand(time(nullptr)); +#endif + + DEMON_LOG("RecvStart"); + + RunUnlimitedSequence(); + return true; +} + +#ifdef DEBUG +void +TestDemonChild::ArtificialSleep() +{ + if (Choose(2) == 0) { + // Sleep for anywhere from 0 to 100 milliseconds. + unsigned micros = Choose(100) * 1000; +#ifdef OS_POSIX + usleep(micros); +#else + Sleep(micros / 1000); +#endif + } +} +#endif + +bool +TestDemonChild::RecvAsyncMessage(const int& n) +{ + DEMON_LOG("Start RecvAsyncMessage [%d]", n); + + MOZ_ASSERT(n == mIncoming[0]); + mIncoming[0]++; + + RunLimitedSequence(); + + DEMON_LOG("End RecvAsyncMessage [%d]", n); + return true; +} + +bool +TestDemonChild::RecvHiPrioSyncMessage() +{ + DEMON_LOG("Start RecvHiPrioSyncMessage"); + RunLimitedSequence(); + DEMON_LOG("End RecvHiPrioSyncMessage"); + return true; +} + +void +TestDemonChild::RunUnlimitedSequence() +{ + gFlushStack = false; + DoAction(); + + MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(this, &TestDemonChild::RunUnlimitedSequence)); +} + +void +TestDemonChild::RunLimitedSequence() +{ + if (gStackHeight >= kMaxStackHeight) { + return; + } + gStackHeight++; + + int count = Choose(20); + for (int i = 0; i < count; i++) { + if (!DoAction()) { + gFlushStack = true; + } + if (gFlushStack) { + gStackHeight--; + return; + } + } + + gStackHeight--; +} + +bool +TestDemonChild::DoAction() +{ + switch (Choose(6)) { + case 0: + if (AllowAsync(mOutgoing[0], mIncoming[0])) { + DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]); + return SendAsyncMessage(mOutgoing[0]++); + } else { + return true; + } + + case 1: { + DEMON_LOG("Start SendHiPrioSyncMessage"); + bool r = SendHiPrioSyncMessage(); + DEMON_LOG("End SendHiPrioSyncMessage result=%d", r); + return r; + } + + case 2: { + DEMON_LOG("Start SendSyncMessage [%d]", mOutgoing[0]); + bool r = SendSyncMessage(mOutgoing[0]++); + switch (GetIPCChannel()->LastSendError()) { + case SyncSendError::PreviousTimeout: + case SyncSendError::SendingCPOWWhileDispatchingSync: + case SyncSendError::SendingCPOWWhileDispatchingUrgent: + case SyncSendError::NotConnectedBeforeSend: + case SyncSendError::CancelledBeforeSend: + mOutgoing[0]--; + break; + default: + break; + } + DEMON_LOG("End SendSyncMessage result=%d", r); + return r; + } + + case 3: + DEMON_LOG("SendUrgentAsyncMessage [%d]", mOutgoing[2]); + return SendUrgentAsyncMessage(mOutgoing[2]++); + + case 4: { + DEMON_LOG("Start SendUrgentSyncMessage [%d]", mOutgoing[2]); + bool r = SendUrgentSyncMessage(mOutgoing[2]++); + switch (GetIPCChannel()->LastSendError()) { + case SyncSendError::PreviousTimeout: + case SyncSendError::SendingCPOWWhileDispatchingSync: + case SyncSendError::SendingCPOWWhileDispatchingUrgent: + case SyncSendError::NotConnectedBeforeSend: + case SyncSendError::CancelledBeforeSend: + mOutgoing[2]--; + break; + default: + break; + } + DEMON_LOG("End SendUrgentSyncMessage result=%d", r); + return r; + } + + case 5: + DEMON_LOG("Cancel"); + GetIPCChannel()->CancelCurrentTransaction(); + return true; + } + MOZ_CRASH(); + return false; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestDemon.h b/ipc/ipdl/test/cxx/TestDemon.h new file mode 100644 index 000000000..d481039a6 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDemon.h @@ -0,0 +1,106 @@ +#ifndef mozilla__ipdltest_TestDemon_h +#define mozilla__ipdltest_TestDemon_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestDemonParent.h" +#include "mozilla/_ipdltest/PTestDemonChild.h" + +using namespace mozilla::ipc; + +namespace mozilla { +namespace _ipdltest { + + +class TestDemonParent : + public PTestDemonParent +{ +public: + TestDemonParent(); + virtual ~TestDemonParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +#ifdef DEBUG + bool ShouldContinueFromReplyTimeout() override; + bool ArtificialTimeout() override; + + bool NeedArtificialSleep() override { return true; } + void ArtificialSleep() override; +#endif + + bool RecvAsyncMessage(const int& n) override; + bool RecvHiPrioSyncMessage() override; + + bool RecvSyncMessage(const int& n) override; + bool RecvUrgentAsyncMessage(const int& n) override; + bool RecvUrgentSyncMessage(const int& n) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + mDone = true; + printf("Parent ActorDestroy\n"); + passed("ok"); + QuitParent(); + } + +private: + bool mDone; + int mIncoming[3]; + int mOutgoing[3]; + + enum { + ASYNC_ONLY = 1, + }; + + void RunUnlimitedSequence(); + void RunLimitedSequence(int flags = 0); + bool DoAction(int flags = 0); +}; + + +class TestDemonChild : + public PTestDemonChild +{ +public: + TestDemonChild(); + virtual ~TestDemonChild(); + + bool RecvStart() override; + +#ifdef DEBUG + bool NeedArtificialSleep() override { return true; } + void ArtificialSleep() override; +#endif + + bool RecvAsyncMessage(const int& n) override; + bool RecvHiPrioSyncMessage() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + _exit(0); + } + + virtual void IntentionalCrash() override + { + _exit(0); + } + +private: + int mIncoming[3]; + int mOutgoing[3]; + + void RunUnlimitedSequence(); + void RunLimitedSequence(); + bool DoAction(); +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestDemon_h diff --git a/ipc/ipdl/test/cxx/TestDesc.cpp b/ipc/ipdl/test/cxx/TestDesc.cpp new file mode 100644 index 000000000..696e5a4c5 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDesc.cpp @@ -0,0 +1,105 @@ +#include "TestDesc.h" + +#include "IPDLUnitTests.h" // fail etc. + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent +void +TestDescParent::Main() +{ + PTestDescSubParent* p = CallPTestDescSubConstructor(0); + if (!p) + fail("can't allocate Sub"); + + PTestDescSubsubParent* pp = p->CallPTestDescSubsubConstructor(); + if (!pp) + fail("can't allocate Subsub"); + + if (!SendTest(pp)) + fail("can't send Subsub"); +} + +bool +TestDescParent::RecvOk(PTestDescSubsubParent* a) +{ + if (!a) + fail("didn't receive Subsub"); + + if (!PTestDescSubsubParent::Call__delete__(a)) + fail("deleting Subsub"); + + Close(); + + return true; +} + + +PTestDescSubParent* +TestDescParent::AllocPTestDescSubParent(PTestDescSubsubParent* dummy) { + if (dummy) + fail("actor supposed to be null"); + return new TestDescSubParent(); +} +bool +TestDescParent::DeallocPTestDescSubParent(PTestDescSubParent* actor) +{ + delete actor; + return true; +} + +PTestDescSubsubParent* +TestDescSubParent::AllocPTestDescSubsubParent() +{ + return new TestDescSubsubParent(); +} +bool +TestDescSubParent::DeallocPTestDescSubsubParent(PTestDescSubsubParent* actor) +{ + delete actor; + return true; +} + + +//----------------------------------------------------------------------------- +// child + +bool +TestDescChild::RecvTest(PTestDescSubsubChild* a) +{ + if (!a) + fail("didn't receive Subsub"); + if (!SendOk(a)) + fail("couldn't send Ok()"); + return true; +} + +PTestDescSubChild* +TestDescChild::AllocPTestDescSubChild(PTestDescSubsubChild* dummy) { + if (dummy) + fail("actor supposed to be null"); + return new TestDescSubChild(); +} +bool +TestDescChild::DeallocPTestDescSubChild(PTestDescSubChild* actor) +{ + delete actor; + return true; +} + +PTestDescSubsubChild* +TestDescSubChild::AllocPTestDescSubsubChild() +{ + return new TestDescSubsubChild(); +} +bool +TestDescSubChild::DeallocPTestDescSubsubChild(PTestDescSubsubChild* actor) +{ + delete actor; + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestDesc.h b/ipc/ipdl/test/cxx/TestDesc.h new file mode 100644 index 000000000..876de7201 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDesc.h @@ -0,0 +1,128 @@ +#ifndef mozilla_ipdltest_TestDesc_h +#define mozilla_ipdltest_TestDesc_h + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestDescParent.h" +#include "mozilla/_ipdltest/PTestDescChild.h" + +#include "mozilla/_ipdltest/PTestDescSubParent.h" +#include "mozilla/_ipdltest/PTestDescSubChild.h" + +#include "mozilla/_ipdltest/PTestDescSubsubParent.h" +#include "mozilla/_ipdltest/PTestDescSubsubChild.h" + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// Top-level +// +class TestDescParent : + public PTestDescParent +{ +public: + TestDescParent() { } + virtual ~TestDescParent() { } + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + virtual bool RecvOk(PTestDescSubsubParent* a) override; + +protected: + virtual PTestDescSubParent* AllocPTestDescSubParent(PTestDescSubsubParent*) override; + virtual bool DeallocPTestDescSubParent(PTestDescSubParent* actor) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + + +class TestDescChild : + public PTestDescChild +{ +public: + TestDescChild() { } + virtual ~TestDescChild() { } + +protected: + virtual PTestDescSubChild* AllocPTestDescSubChild(PTestDescSubsubChild*) override; + + virtual bool DeallocPTestDescSubChild(PTestDescSubChild* actor) override; + + virtual bool RecvTest(PTestDescSubsubChild* a) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +//----------------------------------------------------------------------------- +// First descendent +// +class TestDescSubParent : + public PTestDescSubParent +{ +public: + TestDescSubParent() { } + virtual ~TestDescSubParent() { } + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} + virtual PTestDescSubsubParent* AllocPTestDescSubsubParent() override; + virtual bool DeallocPTestDescSubsubParent(PTestDescSubsubParent* actor) override; +}; + + +class TestDescSubChild : + public PTestDescSubChild +{ +public: + TestDescSubChild() { } + virtual ~TestDescSubChild() { } + +protected: + virtual PTestDescSubsubChild* AllocPTestDescSubsubChild() override; + virtual bool DeallocPTestDescSubsubChild(PTestDescSubsubChild* actor) override; +}; + + +//----------------------------------------------------------------------------- +// Grand-descendent +// +class TestDescSubsubParent : + public PTestDescSubsubParent +{ +public: + TestDescSubsubParent() { } + virtual ~TestDescSubsubParent() { } + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} +}; + +class TestDescSubsubChild : + public PTestDescSubsubChild +{ +public: + TestDescSubsubChild() { } + virtual ~TestDescSubsubChild() { } +}; + + +} +} + +#endif // ifndef mozilla_ipdltest_TestDesc_h diff --git a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp new file mode 100644 index 000000000..1b646f425 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ + +#include "TestEndpointBridgeMain.h" + +#include "base/task.h" +#include "IPDLUnitTests.h" // fail etc. +#include "IPDLUnitTestSubprocess.h" + +using namespace std; + +namespace mozilla { +namespace _ipdltest { + + +//----------------------------------------------------------------------------- +// main process +void +TestEndpointBridgeMainParent::Main() +{ + if (!SendStart()) { + fail("sending Start"); + } +} + +bool +TestEndpointBridgeMainParent::RecvBridged(Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint) +{ + TestEndpointBridgeMainSubParent* a = new TestEndpointBridgeMainSubParent(); + if (!endpoint.Bind(a)) { + fail("Bind failed"); + } + return true; +} + +void +TestEndpointBridgeMainParent::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) { + fail("unexpected destruction!"); + } + passed("ok"); + QuitParent(); +} + +bool +TestEndpointBridgeMainSubParent::RecvHello() +{ + return SendHi(); +} + +bool +TestEndpointBridgeMainSubParent::RecvHelloSync() +{ + return true; +} + +bool +TestEndpointBridgeMainSubParent::AnswerHelloRpc() +{ + return CallHiRpc(); +} + +void +TestEndpointBridgeMainSubParent::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) { + fail("unexpected destruction!"); + } + + // ActorDestroy() is just a callback from IPDL-generated code, + // which needs the top-level actor (this) to stay alive a little + // longer so other things can be cleaned up. + MessageLoop::current()->PostTask( + do_AddRef(new DeleteTask<TestEndpointBridgeMainSubParent>(this))); +} + +//----------------------------------------------------------------------------- +// sub process --- child of main +TestEndpointBridgeMainChild* gEndpointBridgeMainChild; + +TestEndpointBridgeMainChild::TestEndpointBridgeMainChild() + : mSubprocess(nullptr) +{ + gEndpointBridgeMainChild = this; +} + +bool +TestEndpointBridgeMainChild::RecvStart() +{ + vector<string> subsubArgs; + subsubArgs.push_back("TestEndpointBridgeSub"); + + mSubprocess = new IPDLUnitTestSubprocess(); + if (!mSubprocess->SyncLaunch(subsubArgs)) { + fail("problem launching subprocess"); + } + + IPC::Channel* transport = mSubprocess->GetChannel(); + if (!transport) { + fail("no transport"); + } + + TestEndpointBridgeSubParent* bsp = new TestEndpointBridgeSubParent(); + bsp->Open(transport, base::GetProcId(mSubprocess->GetChildProcessHandle())); + + bsp->Main(); + return true; +} + +void +TestEndpointBridgeMainChild::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) { + fail("unexpected destruction!"); + } + // NB: this is kosher because QuitChild() joins with the IO thread + XRE_GetIOMessageLoop()->PostTask( + do_AddRef(new DeleteTask<IPDLUnitTestSubprocess>(mSubprocess))); + QuitChild(); +} + +void +TestEndpointBridgeSubParent::Main() +{ + if (!SendPing()) { + fail("sending Ping"); + } +} + +bool +TestEndpointBridgeSubParent::RecvBridgeEm() +{ + Endpoint<PTestEndpointBridgeMainSubParent> parent; + Endpoint<PTestEndpointBridgeMainSubChild> child; + nsresult rv; + rv = PTestEndpointBridgeMainSub::CreateEndpoints( + gEndpointBridgeMainChild->OtherPid(), OtherPid(), + &parent, &child); + if (NS_FAILED(rv)) { + fail("opening PTestEndpointOpensOpened"); + } + + if (!gEndpointBridgeMainChild->SendBridged(mozilla::Move(parent))) { + fail("SendBridge failed for parent"); + } + if (!SendBridged(mozilla::Move(child))) { + fail("SendBridge failed for child"); + } + + return true; +} + +void +TestEndpointBridgeSubParent::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) { + fail("unexpected destruction!"); + } + gEndpointBridgeMainChild->Close(); + + // ActorDestroy() is just a callback from IPDL-generated code, + // which needs the top-level actor (this) to stay alive a little + // longer so other things can be cleaned up. + MessageLoop::current()->PostTask( + do_AddRef(new DeleteTask<TestEndpointBridgeSubParent>(this))); +} + +//----------------------------------------------------------------------------- +// subsub process --- child of sub + +static TestEndpointBridgeSubChild* gBridgeSubChild; + +TestEndpointBridgeSubChild::TestEndpointBridgeSubChild() +{ + gBridgeSubChild = this; +} + +bool +TestEndpointBridgeSubChild::RecvPing() +{ + if (!SendBridgeEm()) { + fail("sending BridgeEm"); + } + return true; +} + +bool +TestEndpointBridgeSubChild::RecvBridged(Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint) +{ + TestEndpointBridgeMainSubChild* a = new TestEndpointBridgeMainSubChild(); + + if (!endpoint.Bind(a)) { + fail("failed to Bind"); + } + + if (!a->SendHello()) { + fail("sending Hello"); + } + + return true; +} + +void +TestEndpointBridgeSubChild::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) { + fail("unexpected destruction!"); + } + QuitChild(); +} + +bool +TestEndpointBridgeMainSubChild::RecvHi() +{ + if (!SendHelloSync()) { + fail("sending HelloSync"); + } + if (!CallHelloRpc()) { + fail("calling HelloRpc"); + } + if (!mGotHi) { + fail("didn't answer HiRpc"); + } + + // Need to close the channel without message-processing frames on + // the C++ stack + MessageLoop::current()->PostTask( + NewNonOwningRunnableMethod(this, &TestEndpointBridgeMainSubChild::Close)); + return true; +} + +bool +TestEndpointBridgeMainSubChild::AnswerHiRpc() +{ + mGotHi = true; // d00d + return true; +} + +void +TestEndpointBridgeMainSubChild::ActorDestroy(ActorDestroyReason why) +{ + if (NormalShutdown != why) { + fail("unexpected destruction!"); + } + + gBridgeSubChild->Close(); + + // ActorDestroy() is just a callback from IPDL-generated code, + // which needs the top-level actor (this) to stay alive a little + // longer so other things can be cleaned up. + MessageLoop::current()->PostTask( + do_AddRef(new DeleteTask<TestEndpointBridgeMainSubChild>(this))); +} + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h new file mode 100644 index 000000000..a180ba8eb --- /dev/null +++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ + +#ifndef mozilla__ipdltest_TestEndpointBridgeMain_h +#define mozilla__ipdltest_TestEndpointBridgeMain_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestEndpointBridgeMainParent.h" +#include "mozilla/_ipdltest/PTestEndpointBridgeMainChild.h" + +#include "mozilla/_ipdltest/PTestEndpointBridgeSubParent.h" +#include "mozilla/_ipdltest/PTestEndpointBridgeSubChild.h" + +#include "mozilla/_ipdltest/PTestEndpointBridgeMainSubParent.h" +#include "mozilla/_ipdltest/PTestEndpointBridgeMainSubChild.h" + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// "Main" process +// +class TestEndpointBridgeMainParent : + public PTestEndpointBridgeMainParent +{ +public: + TestEndpointBridgeMainParent() {} + virtual ~TestEndpointBridgeMainParent() {} + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + bool RecvBridged(mozilla::ipc::Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint) override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +class TestEndpointBridgeMainSubParent : + public PTestEndpointBridgeMainSubParent +{ +public: + explicit TestEndpointBridgeMainSubParent() + {} + virtual ~TestEndpointBridgeMainSubParent() {} + +protected: + virtual bool RecvHello() override; + virtual bool RecvHelloSync() override; + virtual bool AnswerHelloRpc() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +//----------------------------------------------------------------------------- +// "Sub" process --- child of "main" +// +class TestEndpointBridgeSubParent; + +class TestEndpointBridgeMainChild : + public PTestEndpointBridgeMainChild +{ +public: + TestEndpointBridgeMainChild(); + virtual ~TestEndpointBridgeMainChild() {} + +protected: + virtual bool RecvStart() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + IPDLUnitTestSubprocess* mSubprocess; +}; + +class TestEndpointBridgeSubParent : + public PTestEndpointBridgeSubParent +{ +public: + TestEndpointBridgeSubParent() {} + virtual ~TestEndpointBridgeSubParent() {} + + void Main(); + +protected: + virtual bool RecvBridgeEm() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +//----------------------------------------------------------------------------- +// "Subsub" process --- child of "sub" +// +class TestEndpointBridgeSubChild : + public PTestEndpointBridgeSubChild +{ +public: + TestEndpointBridgeSubChild(); + virtual ~TestEndpointBridgeSubChild() {} + +protected: + virtual bool RecvPing() override; + + bool RecvBridged(Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint) override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +class TestEndpointBridgeMainSubChild : + public PTestEndpointBridgeMainSubChild +{ +public: + explicit TestEndpointBridgeMainSubChild() + : mGotHi(false) + {} + virtual ~TestEndpointBridgeMainSubChild() {} + +protected: + virtual bool RecvHi() override; + virtual bool AnswerHiRpc() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + bool mGotHi; +}; + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestEndpointBridgeMain_h diff --git a/ipc/ipdl/test/cxx/TestEndpointOpens.cpp b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp new file mode 100644 index 000000000..820273ad7 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ + +#include "base/task.h" +#include "base/thread.h" + +#include "TestEndpointOpens.h" + +#include "IPDLUnitTests.h" // fail etc. + +using namespace mozilla::ipc; + +using base::ProcessHandle; +using base::Thread; + +namespace mozilla { +// NB: this is generally bad style, but I am lazy. +using namespace _ipdltest; +using namespace _ipdltest2; + +static MessageLoop* gMainThread; + +static void +AssertNotMainThread() +{ + if (!gMainThread) { + fail("gMainThread is not initialized"); + } + if (MessageLoop::current() == gMainThread) { + fail("unexpectedly called on the main thread"); + } +} + +//----------------------------------------------------------------------------- +// parent + +// Thread on which TestEndpointOpensOpenedParent runs +static Thread* gParentThread; + +void +TestEndpointOpensParent::Main() +{ + if (!SendStart()) { + fail("sending Start"); + } +} + +static void +OpenParent(TestEndpointOpensOpenedParent* aParent, + Endpoint<PTestEndpointOpensOpenedParent>&& aEndpoint) +{ + AssertNotMainThread(); + + // Open the actor on the off-main thread to park it there. + // Messages will be delivered to this thread's message loop + // instead of the main thread's. + if (!aEndpoint.Bind(aParent)) { + fail("binding Parent"); + } +} + +bool +TestEndpointOpensParent::RecvStartSubprotocol( + mozilla::ipc::Endpoint<PTestEndpointOpensOpenedParent>&& endpoint) +{ + gMainThread = MessageLoop::current(); + + gParentThread = new Thread("ParentThread"); + if (!gParentThread->Start()) { + fail("starting parent thread"); + } + + TestEndpointOpensOpenedParent* a = new TestEndpointOpensOpenedParent(); + gParentThread->message_loop()->PostTask( + NewRunnableFunction(OpenParent, a, mozilla::Move(endpoint))); + + return true; +} + +void +TestEndpointOpensParent::ActorDestroy(ActorDestroyReason why) +{ + // Stops the thread and joins it + delete gParentThread; + + if (NormalShutdown != why) { + fail("unexpected destruction A!"); + } + passed("ok"); + QuitParent(); +} + +bool +TestEndpointOpensOpenedParent::RecvHello() +{ + AssertNotMainThread(); + return SendHi(); +} + +bool +TestEndpointOpensOpenedParent::RecvHelloSync() +{ + AssertNotMainThread(); + return true; +} + +bool +TestEndpointOpensOpenedParent::AnswerHelloRpc() +{ + AssertNotMainThread(); + return CallHiRpc(); +} + +static void +ShutdownTestEndpointOpensOpenedParent(TestEndpointOpensOpenedParent* parent, + Transport* transport) +{ + delete parent; +} + +void +TestEndpointOpensOpenedParent::ActorDestroy(ActorDestroyReason why) +{ + AssertNotMainThread(); + + if (NormalShutdown != why) { + fail("unexpected destruction B!"); + } + + // ActorDestroy() is just a callback from IPDL-generated code, + // which needs the top-level actor (this) to stay alive a little + // longer so other things can be cleaned up. + gParentThread->message_loop()->PostTask( + NewRunnableFunction(ShutdownTestEndpointOpensOpenedParent, + this, GetTransport())); +} + +//----------------------------------------------------------------------------- +// child + +static TestEndpointOpensChild* gOpensChild; +// Thread on which TestEndpointOpensOpenedChild runs +static Thread* gChildThread; + +TestEndpointOpensChild::TestEndpointOpensChild() +{ + gOpensChild = this; +} + +static void +OpenChild(TestEndpointOpensOpenedChild* aChild, + Endpoint<PTestEndpointOpensOpenedChild>&& endpoint) +{ + AssertNotMainThread(); + + // Open the actor on the off-main thread to park it there. + // Messages will be delivered to this thread's message loop + // instead of the main thread's. + if (!endpoint.Bind(aChild)) { + fail("binding child endpoint"); + } + + // Kick off the unit tests + if (!aChild->SendHello()) { + fail("sending Hello"); + } +} + +bool +TestEndpointOpensChild::RecvStart() +{ + Endpoint<PTestEndpointOpensOpenedParent> parent; + Endpoint<PTestEndpointOpensOpenedChild> child; + nsresult rv; + rv = PTestEndpointOpensOpened::CreateEndpoints(OtherPid(), base::GetCurrentProcId(), + &parent, &child); + if (NS_FAILED(rv)) { + fail("opening PTestEndpointOpensOpened"); + } + + gMainThread = MessageLoop::current(); + + gChildThread = new Thread("ChildThread"); + if (!gChildThread->Start()) { + fail("starting child thread"); + } + + TestEndpointOpensOpenedChild* a = new TestEndpointOpensOpenedChild(); + gChildThread->message_loop()->PostTask( + NewRunnableFunction(OpenChild, a, mozilla::Move(child))); + + if (!SendStartSubprotocol(parent)) { + fail("send StartSubprotocol"); + } + + return true; +} + +void +TestEndpointOpensChild::ActorDestroy(ActorDestroyReason why) +{ + // Stops the thread and joins it + delete gChildThread; + + if (NormalShutdown != why) { + fail("unexpected destruction C!"); + } + QuitChild(); +} + +bool +TestEndpointOpensOpenedChild::RecvHi() +{ + AssertNotMainThread(); + + if (!SendHelloSync()) { + fail("sending HelloSync"); + } + if (!CallHelloRpc()) { + fail("calling HelloRpc"); + } + if (!mGotHi) { + fail("didn't answer HiRpc"); + } + + // Need to close the channel without message-processing frames on + // the C++ stack + MessageLoop::current()->PostTask( + NewNonOwningRunnableMethod(this, &TestEndpointOpensOpenedChild::Close)); + return true; +} + +bool +TestEndpointOpensOpenedChild::AnswerHiRpc() +{ + AssertNotMainThread(); + + mGotHi = true; // d00d + return true; +} + +static void +ShutdownTestEndpointOpensOpenedChild(TestEndpointOpensOpenedChild* child, + Transport* transport) +{ + delete child; + + // Kick off main-thread shutdown. + gMainThread->PostTask( + NewNonOwningRunnableMethod(gOpensChild, &TestEndpointOpensChild::Close)); +} + +void +TestEndpointOpensOpenedChild::ActorDestroy(ActorDestroyReason why) +{ + AssertNotMainThread(); + + if (NormalShutdown != why) { + fail("unexpected destruction D!"); + } + + // ActorDestroy() is just a callback from IPDL-generated code, + // which needs the top-level actor (this) to stay alive a little + // longer so other things can be cleaned up. Defer shutdown to + // let cleanup finish. + gChildThread->message_loop()->PostTask( + NewRunnableFunction(ShutdownTestEndpointOpensOpenedChild, + this, GetTransport())); +} + +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestEndpointOpens.h b/ipc/ipdl/test/cxx/TestEndpointOpens.h new file mode 100644 index 000000000..6bfaace2a --- /dev/null +++ b/ipc/ipdl/test/cxx/TestEndpointOpens.h @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ + +#ifndef mozilla__ipdltest_TestEndpointOpens_h +#define mozilla__ipdltest_TestEndpointOpens_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestEndpointOpensParent.h" +#include "mozilla/_ipdltest/PTestEndpointOpensChild.h" + +#include "mozilla/_ipdltest2/PTestEndpointOpensOpenedParent.h" +#include "mozilla/_ipdltest2/PTestEndpointOpensOpenedChild.h" + +namespace mozilla { + +// parent process + +namespace _ipdltest { + +class TestEndpointOpensParent : public PTestEndpointOpensParent +{ +public: + TestEndpointOpensParent() {} + virtual ~TestEndpointOpensParent() {} + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + virtual bool RecvStartSubprotocol(mozilla::ipc::Endpoint<PTestEndpointOpensOpenedParent>&& endpoint); + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +} // namespace _ipdltest + +namespace _ipdltest2 { + +class TestEndpointOpensOpenedParent : public PTestEndpointOpensOpenedParent +{ +public: + explicit TestEndpointOpensOpenedParent() + {} + virtual ~TestEndpointOpensOpenedParent() {} + +protected: + virtual bool RecvHello() override; + virtual bool RecvHelloSync() override; + virtual bool AnswerHelloRpc() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +} // namespace _ipdltest2 + +// child process + +namespace _ipdltest { + +class TestEndpointOpensChild : public PTestEndpointOpensChild +{ +public: + TestEndpointOpensChild(); + virtual ~TestEndpointOpensChild() {} + +protected: + virtual bool RecvStart() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +} // namespace _ipdltest + +namespace _ipdltest2 { + +class TestEndpointOpensOpenedChild : public PTestEndpointOpensOpenedChild +{ +public: + explicit TestEndpointOpensOpenedChild() + : mGotHi(false) + {} + virtual ~TestEndpointOpensOpenedChild() {} + +protected: + virtual bool RecvHi() override; + virtual bool AnswerHiRpc() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + bool mGotHi; +}; + +} // namespace _ipdltest2 + +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestEndpointOpens_h diff --git a/ipc/ipdl/test/cxx/TestFailedCtor.cpp b/ipc/ipdl/test/cxx/TestFailedCtor.cpp new file mode 100644 index 000000000..12ea8fdc1 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestFailedCtor.cpp @@ -0,0 +1,137 @@ +#include "TestFailedCtor.h" + +#include "IPDLUnitTests.h" // fail etc. + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent +void +TestFailedCtorParent::Main() +{ + PTestFailedCtorSubParent* p = CallPTestFailedCtorSubConstructor(); + if (p) + fail("expected ctor to fail"); + + Close(); +} + +PTestFailedCtorSubParent* +TestFailedCtorParent::AllocPTestFailedCtorSubParent() +{ + return new TestFailedCtorSubParent(); +} +bool +TestFailedCtorParent::DeallocPTestFailedCtorSubParent(PTestFailedCtorSubParent* actor) +{ + delete actor; + return true; +} + +PTestFailedCtorSubsubParent* +TestFailedCtorSubParent::AllocPTestFailedCtorSubsubParent() +{ + TestFailedCtorSubsub* a = new TestFailedCtorSubsub(); + if (!mOne) { + return mOne = a; + } else if (!mTwo) { + return mTwo = a; + } else if (!mThree) { + return mThree = a; + } else { + fail("unexpected Alloc()"); + return nullptr; + } +} +bool +TestFailedCtorSubParent::DeallocPTestFailedCtorSubsubParent(PTestFailedCtorSubsubParent* actor) +{ + static_cast<TestFailedCtorSubsub*>(actor)->mDealloced = true; + return true; +} + +void +TestFailedCtorSubParent::ActorDestroy(ActorDestroyReason why) +{ + + if (mOne->mWhy != Deletion) + fail("Subsub one got wrong ActorDestroyReason"); + if (mTwo->mWhy != AncestorDeletion) + fail("Subsub two got wrong ActorDestroyReason"); + if (mThree->mWhy != AncestorDeletion) + fail("Subsub three got wrong ActorDestroyReason"); + + if (FailedConstructor != why) + fail("unexpected destruction!"); +} + +TestFailedCtorSubParent::~TestFailedCtorSubParent() +{ + if (!(mOne->mDealloced && mTwo->mDealloced && mThree->mDealloced)) + fail("Not all subsubs were Dealloc'd"); + delete mOne; + delete mTwo; + delete mThree; +} + + +//----------------------------------------------------------------------------- +// child + +PTestFailedCtorSubChild* +TestFailedCtorChild::AllocPTestFailedCtorSubChild() +{ + return new TestFailedCtorSubChild(); +} + +bool +TestFailedCtorChild::AnswerPTestFailedCtorSubConstructor(PTestFailedCtorSubChild* actor) +{ + PTestFailedCtorSubsubChild* c1 = actor->SendPTestFailedCtorSubsubConstructor(); + PTestFailedCtorSubsubChild::Send__delete__(c1); + + if (!actor->SendPTestFailedCtorSubsubConstructor() || + !actor->SendPTestFailedCtorSubsubConstructor() || + !actor->SendSync()) + fail("setting up test"); + + // This causes our process to die + return false; +} + +bool +TestFailedCtorChild::DeallocPTestFailedCtorSubChild(PTestFailedCtorSubChild* actor) +{ + delete actor; + return true; +} + +void +TestFailedCtorChild::ProcessingError(Result aCode, const char* aReason) +{ + if (OtherPid() != base::GetCurrentProcId()) // thread-mode + _exit(0); +} + +PTestFailedCtorSubsubChild* +TestFailedCtorSubChild::AllocPTestFailedCtorSubsubChild() +{ + return new TestFailedCtorSubsub(); +} + +bool +TestFailedCtorSubChild::DeallocPTestFailedCtorSubsubChild(PTestFailedCtorSubsubChild* actor) +{ + delete actor; + return true; +} + +void +TestFailedCtorSubChild::ActorDestroy(ActorDestroyReason why) +{ +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestFailedCtor.h b/ipc/ipdl/test/cxx/TestFailedCtor.h new file mode 100644 index 000000000..97f9013cc --- /dev/null +++ b/ipc/ipdl/test/cxx/TestFailedCtor.h @@ -0,0 +1,136 @@ +#ifndef mozilla_ipdltest_TestFailedCtor_h +#define mozilla_ipdltest_TestFailedCtor_h + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestFailedCtorParent.h" +#include "mozilla/_ipdltest/PTestFailedCtorChild.h" + +#include "mozilla/_ipdltest/PTestFailedCtorSubParent.h" +#include "mozilla/_ipdltest/PTestFailedCtorSubChild.h" + +#include "mozilla/_ipdltest/PTestFailedCtorSubsubParent.h" +#include "mozilla/_ipdltest/PTestFailedCtorSubsubChild.h" + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// Top-level +// +class TestFailedCtorParent : + public PTestFailedCtorParent +{ +public: + TestFailedCtorParent() { } + virtual ~TestFailedCtorParent() { } + + static bool RunTestInProcesses() { return true; } + + // FIXME/bug 703322 Disabled because child calls exit() to end + // test, not clear how to handle failed ctor in + // threaded mode. + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + virtual PTestFailedCtorSubParent* AllocPTestFailedCtorSubParent() override; + virtual bool DeallocPTestFailedCtorSubParent(PTestFailedCtorSubParent* actor) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (AbnormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + + +class TestFailedCtorChild : + public PTestFailedCtorChild +{ +public: + TestFailedCtorChild() { } + virtual ~TestFailedCtorChild() { } + +protected: + virtual PTestFailedCtorSubChild* AllocPTestFailedCtorSubChild() override; + + virtual bool AnswerPTestFailedCtorSubConstructor(PTestFailedCtorSubChild* actor) override; + + virtual bool DeallocPTestFailedCtorSubChild(PTestFailedCtorSubChild* actor) override; + + virtual void ProcessingError(Result aCode, const char* aReason) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + fail("should have _exit()ed"); + } +}; + + +//----------------------------------------------------------------------------- +// First descendent +// +class TestFailedCtorSubsub; + +class TestFailedCtorSubParent : + public PTestFailedCtorSubParent +{ +public: + TestFailedCtorSubParent() : mOne(nullptr), mTwo(nullptr), mThree(nullptr) { } + virtual ~TestFailedCtorSubParent(); + +protected: + virtual PTestFailedCtorSubsubParent* AllocPTestFailedCtorSubsubParent() override; + + virtual bool DeallocPTestFailedCtorSubsubParent(PTestFailedCtorSubsubParent* actor) override; + virtual bool RecvSync() override { return true; } + + virtual void ActorDestroy(ActorDestroyReason why) override; + + TestFailedCtorSubsub* mOne; + TestFailedCtorSubsub* mTwo; + TestFailedCtorSubsub* mThree; +}; + + +class TestFailedCtorSubChild : + public PTestFailedCtorSubChild +{ +public: + TestFailedCtorSubChild() { } + virtual ~TestFailedCtorSubChild() { } + +protected: + virtual PTestFailedCtorSubsubChild* AllocPTestFailedCtorSubsubChild() override; + virtual bool DeallocPTestFailedCtorSubsubChild(PTestFailedCtorSubsubChild* actor) override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + + +//----------------------------------------------------------------------------- +// Grand-descendent +// +class TestFailedCtorSubsub : + public PTestFailedCtorSubsubParent, + public PTestFailedCtorSubsubChild +{ +public: + TestFailedCtorSubsub() : mWhy(ActorDestroyReason(-1)), mDealloced(false) {} + virtual ~TestFailedCtorSubsub() {} + + virtual void ActorDestroy(ActorDestroyReason why) override { mWhy = why; } + + ActorDestroyReason mWhy; + bool mDealloced; +}; + + +} +} + +#endif // ifndef mozilla_ipdltest_TestFailedCtor_h diff --git a/ipc/ipdl/test/cxx/TestHangs.cpp b/ipc/ipdl/test/cxx/TestHangs.cpp new file mode 100644 index 000000000..b96823aee --- /dev/null +++ b/ipc/ipdl/test/cxx/TestHangs.cpp @@ -0,0 +1,146 @@ +#include "base/process_util.h" + +#include "TestHangs.h" + +#include "IPDLUnitTests.h" // fail etc. + +using base::KillProcess; + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestHangsParent::TestHangsParent() : mDetectedHang(false) +{ + MOZ_COUNT_CTOR(TestHangsParent); +} + +TestHangsParent::~TestHangsParent() +{ + MOZ_COUNT_DTOR(TestHangsParent); +} + +void +TestHangsParent::Main() +{ + // Here we try to set things up to test the following sequence of events: + // + // - subprocess causes an OnMaybeDequeueOne() task to be posted to + // this thread + // + // - subprocess hangs just long enough for the hang timer to expire + // + // - hang-kill code in the parent starts running + // + // - subprocess replies to message while hang code runs + // + // - reply is processed in OnMaybeDequeueOne() before Close() has + // been called or the channel error notification has been posted + + // this tells the subprocess to send us Nonce() + if (!SendStart()) + fail("sending Start"); + + // now we sleep here for a while awaiting the Nonce() message from + // the child. since we're not blocked on anything, the IO thread + // will enqueue an OnMaybeDequeueOne() task to process that + // message + // + // NB: PR_Sleep is exactly what we want, only the current thread + // sleeping + PR_Sleep(5000); + + // when we call into this, we'll pull the Nonce() message out of + // the mPending queue, but that doesn't matter ... the + // OnMaybeDequeueOne() event will remain + if (CallStackFrame() && mDetectedHang) + fail("should have timed out!"); + + // the Close() task in the queue will shut us down +} + +bool +TestHangsParent::ShouldContinueFromReplyTimeout() +{ + mDetectedHang = true; + + // so we've detected a timeout after 2 ms ... now we cheat and + // sleep for a long time, to allow the subprocess's reply to come + // in + + PR_Sleep(5000); + + // reply should be here; we'll post a task to shut things down. + // This must be after OnMaybeDequeueOne() in the event queue. + MessageLoop::current()->PostTask( + NewNonOwningRunnableMethod(this, &TestHangsParent::CleanUp)); + + GetIPCChannel()->CloseWithTimeout(); + + return false; +} + +bool +TestHangsParent::AnswerStackFrame() +{ + if (PTestHangs::HANG != state()) { + if (CallStackFrame()) + fail("should have timed out!"); + } + else { + // minimum possible, 2 ms. We want to detecting a hang to race + // with the reply coming in, as reliably as possible + SetReplyTimeoutMs(2); + + if (CallHang()) + fail("should have timed out!"); + } + + return true; +} + +void +TestHangsParent::CleanUp() +{ + ipc::ScopedProcessHandle otherProcessHandle; + if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) { + fail("couldn't open child process"); + } else { + if (!KillProcess(otherProcessHandle, 0, false)) { + fail("terminating child process"); + } + } + Close(); +} + + +//----------------------------------------------------------------------------- +// child + +TestHangsChild::TestHangsChild() +{ + MOZ_COUNT_CTOR(TestHangsChild); +} + +TestHangsChild::~TestHangsChild() +{ + MOZ_COUNT_DTOR(TestHangsChild); +} + +bool +TestHangsChild::AnswerHang() +{ + puts(" (child process is 'hanging' now)"); + + // just sleep until we're reasonably confident the 1ms hang + // detector fired in the parent process and it's sleeping in + // ShouldContinueFromReplyTimeout() + PR_Sleep(1000); + + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestHangs.h b/ipc/ipdl/test/cxx/TestHangs.h new file mode 100644 index 000000000..676215172 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestHangs.h @@ -0,0 +1,87 @@ +#ifndef mozilla__ipdltest_TestHangs_h +#define mozilla__ipdltest_TestHangs_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestHangsParent.h" +#include "mozilla/_ipdltest/PTestHangsChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestHangsParent : + public PTestHangsParent +{ +public: + TestHangsParent(); + virtual ~TestHangsParent(); + + static bool RunTestInProcesses() { return true; } + + // FIXME/bug 703320 Disabled because parent kills child proc, not + // clear how that should work in threads. + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + virtual bool ShouldContinueFromReplyTimeout() override; + + virtual bool RecvNonce() { + return true; + } + + virtual bool AnswerStackFrame() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (AbnormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + + void CleanUp(); + + bool mDetectedHang; +}; + + +class TestHangsChild : + public PTestHangsChild +{ +public: + TestHangsChild(); + virtual ~TestHangsChild(); + +protected: + virtual bool RecvStart() override { + if (!SendNonce()) + fail("sending Nonce"); + return true; + } + + virtual bool AnswerStackFrame() override + { + if (CallStackFrame()) + fail("should have failed"); + return true; + } + + virtual bool AnswerHang() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (AbnormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestHangs_h diff --git a/ipc/ipdl/test/cxx/TestHighestPrio.cpp b/ipc/ipdl/test/cxx/TestHighestPrio.cpp new file mode 100644 index 000000000..524943de4 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestHighestPrio.cpp @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +/* 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/. */ + +#include "TestHighestPrio.h" + +#include "IPDLUnitTests.h" // fail etc. +#if defined(OS_POSIX) +#include <unistd.h> +#else +#include <windows.h> +#endif + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestHighestPrioParent::TestHighestPrioParent() + : msg_num_(0) +{ + MOZ_COUNT_CTOR(TestHighestPrioParent); +} + +TestHighestPrioParent::~TestHighestPrioParent() +{ + MOZ_COUNT_DTOR(TestHighestPrioParent); +} + +void +TestHighestPrioParent::Main() +{ + if (!SendStart()) + fail("sending Start"); +} + +bool +TestHighestPrioParent::RecvMsg1() +{ + MOZ_ASSERT(msg_num_ == 0); + msg_num_ = 1; + return true; +} + +bool +TestHighestPrioParent::RecvMsg2() +{ + + MOZ_ASSERT(msg_num_ == 1); + msg_num_ = 2; + + if (!SendStartInner()) + fail("sending StartInner"); + + return true; +} + +bool +TestHighestPrioParent::RecvMsg3() +{ + MOZ_ASSERT(msg_num_ == 2); + msg_num_ = 3; + return true; +} + +bool +TestHighestPrioParent::RecvMsg4() +{ + MOZ_ASSERT(msg_num_ == 3); + msg_num_ = 4; + return true; +} + +//----------------------------------------------------------------------------- +// child + + +TestHighestPrioChild::TestHighestPrioChild() +{ + MOZ_COUNT_CTOR(TestHighestPrioChild); +} + +TestHighestPrioChild::~TestHighestPrioChild() +{ + MOZ_COUNT_DTOR(TestHighestPrioChild); +} + +bool +TestHighestPrioChild::RecvStart() +{ + if (!SendMsg1()) + fail("sending Msg1"); + + if (!SendMsg2()) + fail("sending Msg2"); + + Close(); + return true; +} + +bool +TestHighestPrioChild::RecvStartInner() +{ + if (!SendMsg3()) + fail("sending Msg3"); + + if (!SendMsg4()) + fail("sending Msg4"); + + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestHighestPrio.h b/ipc/ipdl/test/cxx/TestHighestPrio.h new file mode 100644 index 000000000..e6dc8c427 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestHighestPrio.h @@ -0,0 +1,68 @@ +#ifndef mozilla__ipdltest_TestHighestPrio_h +#define mozilla__ipdltest_TestHighestPrio_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestHighestPrioParent.h" +#include "mozilla/_ipdltest/PTestHighestPrioChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestHighestPrioParent : + public PTestHighestPrioParent +{ +public: + TestHighestPrioParent(); + virtual ~TestHighestPrioParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + + bool RecvMsg1() override; + bool RecvMsg2() override; + bool RecvMsg3() override; + bool RecvMsg4() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + if (msg_num_ != 4) + fail("missed IPC call"); + passed("ok"); + QuitParent(); + } + +private: + int msg_num_; +}; + + +class TestHighestPrioChild : + public PTestHighestPrioChild +{ +public: + TestHighestPrioChild(); + virtual ~TestHighestPrioChild(); + + bool RecvStart() override; + bool RecvStartInner() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestHighestPrio_h diff --git a/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp new file mode 100644 index 000000000..21a5bc592 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp @@ -0,0 +1,155 @@ +#include "TestInterruptErrorCleanup.h" + +#include "base/task.h" +#include "mozilla/CondVar.h" +#include "mozilla/Mutex.h" + +#include "IPDLUnitTests.h" // fail etc. +#include "IPDLUnitTestSubprocess.h" + +using mozilla::CondVar; +using mozilla::Mutex; +using mozilla::MutexAutoLock; + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +namespace { + +// NB: this test does its own shutdown, rather than going through +// QuitParent(), because it's testing degenerate edge cases + +void DeleteSubprocess(Mutex* mutex, CondVar* cvar) +{ + MutexAutoLock lock(*mutex); + + delete gSubprocess; + gSubprocess = nullptr; + + cvar->Notify(); +} + +void DeleteTheWorld() +{ + delete static_cast<TestInterruptErrorCleanupParent*>(gParentActor); + gParentActor = nullptr; + + // needs to be synchronous to avoid affecting event ordering on + // the main thread + Mutex mutex("TestInterruptErrorCleanup.DeleteTheWorld.mutex"); + CondVar cvar(mutex, "TestInterruptErrorCleanup.DeleteTheWorld.cvar"); + + MutexAutoLock lock(mutex); + + XRE_GetIOMessageLoop()->PostTask( + NewRunnableFunction(DeleteSubprocess, &mutex, &cvar)); + + cvar.Wait(); +} + +void Done() +{ + static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID)); + appShell->Exit(); + + passed(__FILE__); +} + +} // namespace <anon> + +TestInterruptErrorCleanupParent::TestInterruptErrorCleanupParent() + : mGotProcessingError(false) +{ + MOZ_COUNT_CTOR(TestInterruptErrorCleanupParent); +} + +TestInterruptErrorCleanupParent::~TestInterruptErrorCleanupParent() +{ + MOZ_COUNT_DTOR(TestInterruptErrorCleanupParent); +} + +void +TestInterruptErrorCleanupParent::Main() +{ + // This test models the following sequence of events + // + // (1) Parent: Interrupt out-call + // (2) Child: crash + // --[Parent-only hereafter]-- + // (3) Interrupt out-call return false + // (4) Close() + // --[event loop]-- + // (5) delete parentActor + // (6) delete childProcess + // --[event loop]-- + // (7) Channel::OnError notification + // --[event loop]-- + // (8) Done, quit + // + // See bug 535298 and friends; this seqeunce of events captures + // three differnent potential errors + // - Close()-after-error (semantic error previously) + // - use-after-free of parentActor + // - use-after-free of channel + // + // Because of legacy constraints related to nsNPAPI* code, we need + // to ensure that this sequence of events can occur without + // errors/crashes. + + MessageLoop::current()->PostTask( + NewRunnableFunction(DeleteTheWorld)); + + // it's a failure if this *succeeds* + if (CallError()) + fail("expected an error!"); + + if (!mGotProcessingError) + fail("expected a ProcessingError() notification"); + + // it's OK to Close() a channel after an error, because nsNPAPI* + // wants to do this + Close(); + + // we know that this event *must* be after the MaybeError + // notification enqueued by AsyncChannel, because that event is + // enqueued within the same mutex that ends up signaling the + // wakeup-on-error of |CallError()| above + MessageLoop::current()->PostTask(NewRunnableFunction(Done)); +} + +void +TestInterruptErrorCleanupParent::ProcessingError(Result aCode, const char* aReason) +{ + if (aCode != MsgDropped) + fail("unexpected processing error"); + mGotProcessingError = true; +} + +//----------------------------------------------------------------------------- +// child + +TestInterruptErrorCleanupChild::TestInterruptErrorCleanupChild() +{ + MOZ_COUNT_CTOR(TestInterruptErrorCleanupChild); +} + +TestInterruptErrorCleanupChild::~TestInterruptErrorCleanupChild() +{ + MOZ_COUNT_DTOR(TestInterruptErrorCleanupChild); +} + +bool +TestInterruptErrorCleanupChild::AnswerError() +{ + _exit(0); + NS_RUNTIMEABORT("unreached"); + return false; +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h new file mode 100644 index 000000000..bd45b2c98 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h @@ -0,0 +1,60 @@ +#ifndef mozilla__ipdltest_TestInterruptErrorCleanup_h +#define mozilla__ipdltest_TestInterruptErrorCleanup_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestInterruptErrorCleanupParent.h" +#include "mozilla/_ipdltest/PTestInterruptErrorCleanupChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestInterruptErrorCleanupParent : + public PTestInterruptErrorCleanupParent +{ +public: + TestInterruptErrorCleanupParent(); + virtual ~TestInterruptErrorCleanupParent(); + + static bool RunTestInProcesses() { return true; } + // FIXME/bug 703323 Could work if modified + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (AbnormalShutdown != why) + fail("unexpected destruction!"); + } + + virtual void ProcessingError(Result aCode, const char* aReason) override; + + bool mGotProcessingError; +}; + + +class TestInterruptErrorCleanupChild : + public PTestInterruptErrorCleanupChild +{ +public: + TestInterruptErrorCleanupChild(); + virtual ~TestInterruptErrorCleanupChild(); + +protected: + virtual bool AnswerError() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + fail("should have 'crashed'!"); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestInterruptErrorCleanup_h diff --git a/ipc/ipdl/test/cxx/TestInterruptRaces.cpp b/ipc/ipdl/test/cxx/TestInterruptRaces.cpp new file mode 100644 index 000000000..6986506bb --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptRaces.cpp @@ -0,0 +1,220 @@ +#include "TestInterruptRaces.h" + +#include "IPDLUnitTests.h" // fail etc. + +using mozilla::ipc::MessageChannel; + +namespace mozilla { +namespace _ipdltest { + +ipc::RacyInterruptPolicy +MediateRace(const MessageChannel::MessageInfo& parent, + const MessageChannel::MessageInfo& child) +{ + return (PTestInterruptRaces::Msg_Child__ID == parent.type()) ? + ipc::RIPParentWins : ipc::RIPChildWins; +} + +//----------------------------------------------------------------------------- +// parent +void +TestInterruptRacesParent::Main() +{ + if (!SendStart()) + fail("sending Start()"); +} + +bool +TestInterruptRacesParent::RecvStartRace() +{ + MessageLoop::current()->PostTask( + NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::OnRaceTime)); + return true; +} + +void +TestInterruptRacesParent::OnRaceTime() +{ + if (!CallRace(&mChildHasReply)) + fail("problem calling Race()"); + + if (!mChildHasReply) + fail("child should have got a reply already"); + + mHasReply = true; + + MessageLoop::current()->PostTask( + NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::Test2)); +} + +bool +TestInterruptRacesParent::AnswerRace(bool* hasReply) +{ + if (mHasReply) + fail("apparently the parent won the Interrupt race!"); + *hasReply = hasReply; + return true; +} + +void +TestInterruptRacesParent::Test2() +{ + puts(" passed"); + puts("Test 2"); + + mHasReply = false; + mChildHasReply = false; + + if (!CallStackFrame()) + fail("can't set up a stack frame"); + + puts(" passed"); + + MessageLoop::current()->PostTask( + NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::Test3)); +} + +bool +TestInterruptRacesParent::AnswerStackFrame() +{ + if (!SendWakeup()) + fail("can't wake up the child"); + + if (!CallRace(&mChildHasReply)) + fail("can't set up race condition"); + mHasReply = true; + + if (!mChildHasReply) + fail("child should have got a reply already"); + + return true; +} + +void +TestInterruptRacesParent::Test3() +{ + puts("Test 3"); + + if (!CallStackFrame3()) + fail("can't set up a stack frame"); + + puts(" passed"); + + Close(); +} + +bool +TestInterruptRacesParent::AnswerStackFrame3() +{ + if (!SendWakeup3()) + fail("can't wake up the child"); + + if (!CallChild()) + fail("can't set up race condition"); + + return true; +} + +bool +TestInterruptRacesParent::AnswerParent() +{ + mAnsweredParent = true; + return true; +} + +bool +TestInterruptRacesParent::RecvGetAnsweredParent(bool* answeredParent) +{ + *answeredParent = mAnsweredParent; + return true; +} + +//----------------------------------------------------------------------------- +// child +bool +TestInterruptRacesChild::RecvStart() +{ + puts("Test 1"); + + if (!SendStartRace()) + fail("problem sending StartRace()"); + + bool dontcare; + if (!CallRace(&dontcare)) + fail("problem calling Race()"); + + mHasReply = true; + return true; +} + +bool +TestInterruptRacesChild::AnswerRace(bool* hasReply) +{ + if (!mHasReply) + fail("apparently the child lost the Interrupt race!"); + + *hasReply = mHasReply; + + return true; +} + +bool +TestInterruptRacesChild::AnswerStackFrame() +{ + // reset for the second test + mHasReply = false; + + if (!CallStackFrame()) + fail("can't set up stack frame"); + + if (!mHasReply) + fail("should have had reply by now"); + + return true; +} + +bool +TestInterruptRacesChild::RecvWakeup() +{ + bool dontcare; + if (!CallRace(&dontcare)) + fail("can't set up race condition"); + + mHasReply = true; + return true; +} + +bool +TestInterruptRacesChild::AnswerStackFrame3() +{ + if (!CallStackFrame3()) + fail("can't set up stack frame"); + return true; +} + +bool +TestInterruptRacesChild::RecvWakeup3() +{ + if (!CallParent()) + fail("can't set up race condition"); + return true; +} + +bool +TestInterruptRacesChild::AnswerChild() +{ + bool parentAnsweredParent; + // the parent is supposed to win the race, which means its + // message, Child(), is supposed to be processed before the + // child's message, Parent() + if (!SendGetAnsweredParent(&parentAnsweredParent)) + fail("sending GetAnsweredParent"); + + if (parentAnsweredParent) + fail("parent was supposed to win the race!"); + + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestInterruptRaces.h b/ipc/ipdl/test/cxx/TestInterruptRaces.h new file mode 100644 index 000000000..f83c49fd1 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptRaces.h @@ -0,0 +1,131 @@ +#ifndef mozilla__ipdltest_TestInterruptRaces_h +#define mozilla__ipdltest_TestInterruptRaces_h + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestInterruptRacesParent.h" +#include "mozilla/_ipdltest/PTestInterruptRacesChild.h" + +namespace mozilla { +namespace _ipdltest { + +mozilla::ipc::RacyInterruptPolicy +MediateRace(const mozilla::ipc::MessageChannel::MessageInfo& parent, + const mozilla::ipc::MessageChannel::MessageInfo& child); + +class TestInterruptRacesParent : + public PTestInterruptRacesParent +{ +public: + TestInterruptRacesParent() : mHasReply(false), + mChildHasReply(false), + mAnsweredParent(false) + { } + virtual ~TestInterruptRacesParent() { } + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool + RecvStartRace() override; + + virtual bool + AnswerRace(bool* hasRace) override; + + virtual bool + AnswerStackFrame() override; + + virtual bool + AnswerStackFrame3() override; + + virtual bool + AnswerParent() override; + + virtual bool + RecvGetAnsweredParent(bool* answeredParent) override; + + virtual mozilla::ipc::RacyInterruptPolicy + MediateInterruptRace(const MessageInfo& parent, + const MessageInfo& child) override + { + return MediateRace(parent, child); + } + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + if (!(mHasReply && mChildHasReply)) + fail("both sides should have replies!"); + passed("ok"); + QuitParent(); + } + +private: + void OnRaceTime(); + + void Test2(); + void Test3(); + + bool mHasReply; + bool mChildHasReply; + bool mAnsweredParent; +}; + + +class TestInterruptRacesChild : + public PTestInterruptRacesChild +{ +public: + TestInterruptRacesChild() : mHasReply(false) { } + virtual ~TestInterruptRacesChild() { } + +protected: + virtual bool + RecvStart() override; + + virtual bool + AnswerRace(bool* hasRace) override; + + virtual bool + AnswerStackFrame() override; + + virtual bool + AnswerStackFrame3() override; + + virtual bool + RecvWakeup() override; + + virtual bool + RecvWakeup3() override; + + virtual bool + AnswerChild() override; + + virtual mozilla::ipc::RacyInterruptPolicy + MediateInterruptRace(const MessageInfo& parent, + const MessageInfo& child) override + { + return MediateRace(parent, child); + } + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } + +private: + bool mHasReply; +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestInterruptRaces_h diff --git a/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp new file mode 100644 index 000000000..38f7993da --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp @@ -0,0 +1,134 @@ +#include "TestInterruptShutdownRace.h" + +#include "base/task.h" +#include "IPDLUnitTests.h" // fail etc. +#include "IPDLUnitTestSubprocess.h" + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +namespace { + +// NB: this test does its own shutdown, rather than going through +// QuitParent(), because it's testing degenerate edge cases + +void DeleteSubprocess() +{ + delete gSubprocess; + gSubprocess = nullptr; +} + +void Done() +{ + passed(__FILE__); + QuitParent(); +} + +} // namespace <anon> + +TestInterruptShutdownRaceParent::TestInterruptShutdownRaceParent() +{ + MOZ_COUNT_CTOR(TestInterruptShutdownRaceParent); +} + +TestInterruptShutdownRaceParent::~TestInterruptShutdownRaceParent() +{ + MOZ_COUNT_DTOR(TestInterruptShutdownRaceParent); +} + +void +TestInterruptShutdownRaceParent::Main() +{ + if (!SendStart()) + fail("sending Start"); +} + +bool +TestInterruptShutdownRaceParent::RecvStartDeath() +{ + // this will be ordered before the OnMaybeDequeueOne event of + // Orphan in the queue + MessageLoop::current()->PostTask( + NewNonOwningRunnableMethod(this, + &TestInterruptShutdownRaceParent::StartShuttingDown)); + return true; +} + +void +TestInterruptShutdownRaceParent::StartShuttingDown() +{ + // NB: we sleep here to try and avoid receiving the Orphan message + // while waiting for the CallExit() reply. if we fail at that, it + // will cause the test to pass spuriously, because there won't be + // an OnMaybeDequeueOne task for Orphan + PR_Sleep(2000); + + if (CallExit()) + fail("connection was supposed to be interrupted"); + + Close(); + + delete static_cast<TestInterruptShutdownRaceParent*>(gParentActor); + gParentActor = nullptr; + + XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction(DeleteSubprocess)); + + // this is ordered after the OnMaybeDequeueOne event in the queue + MessageLoop::current()->PostTask(NewRunnableFunction(Done)); + + // |this| has been deleted, be mindful +} + +bool +TestInterruptShutdownRaceParent::RecvOrphan() +{ + // it would be nice to fail() here, but we'll process this message + // while waiting for the reply CallExit(). The OnMaybeDequeueOne + // task will still be in the queue, it just wouldn't have had any + // work to do, if we hadn't deleted ourself + return true; +} + +//----------------------------------------------------------------------------- +// child + +TestInterruptShutdownRaceChild::TestInterruptShutdownRaceChild() +{ + MOZ_COUNT_CTOR(TestInterruptShutdownRaceChild); +} + +TestInterruptShutdownRaceChild::~TestInterruptShutdownRaceChild() +{ + MOZ_COUNT_DTOR(TestInterruptShutdownRaceChild); +} + +bool +TestInterruptShutdownRaceChild::RecvStart() +{ + if (!SendStartDeath()) + fail("sending StartDeath"); + + // See comment in StartShuttingDown(): we want to send Orphan() + // while the parent is in its PR_Sleep() + PR_Sleep(1000); + + if (!SendOrphan()) + fail("sending Orphan"); + + return true; +} + +bool +TestInterruptShutdownRaceChild::AnswerExit() +{ + _exit(0); + NS_RUNTIMEABORT("unreached"); + return false; +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestInterruptShutdownRace.h b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.h new file mode 100644 index 000000000..c291b14a6 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.h @@ -0,0 +1,64 @@ +#ifndef mozilla__ipdltest_TestInterruptShutdownRace_h +#define mozilla__ipdltest_TestInterruptShutdownRace_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestInterruptShutdownRaceParent.h" +#include "mozilla/_ipdltest/PTestInterruptShutdownRaceChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestInterruptShutdownRaceParent : + public PTestInterruptShutdownRaceParent +{ +public: + TestInterruptShutdownRaceParent(); + virtual ~TestInterruptShutdownRaceParent(); + + static bool RunTestInProcesses() { return true; } + // FIXME/bug 703323 Could work if modified + static bool RunTestInThreads() { return false; } + + void Main(); + + virtual bool RecvStartDeath() override; + + virtual bool RecvOrphan() override; + +protected: + void StartShuttingDown(); + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (AbnormalShutdown != why) + fail("unexpected destruction!"); + } +}; + + +class TestInterruptShutdownRaceChild : + public PTestInterruptShutdownRaceChild +{ +public: + TestInterruptShutdownRaceChild(); + virtual ~TestInterruptShutdownRaceChild(); + +protected: + virtual bool RecvStart() override; + + virtual bool AnswerExit() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + fail("should have 'crashed'!"); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestInterruptShutdownRace_h diff --git a/ipc/ipdl/test/cxx/TestJSON.cpp b/ipc/ipdl/test/cxx/TestJSON.cpp new file mode 100644 index 000000000..ea742b705 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestJSON.cpp @@ -0,0 +1,129 @@ +#include "TestJSON.h" + +#include "IPDLUnitTests.h" // fail etc. + +#define test_assert(_cond, _msg) \ + if (!(_cond)) fail(_msg) + +namespace mozilla { +namespace _ipdltest { + +static nsString +String(const char* const str) +{ + return NS_ConvertUTF8toUTF16(str); +} + +static void +Array123(InfallibleTArray<JSONVariant>& a123) +{ + a123.AppendElement(1); a123.AppendElement(2); a123.AppendElement(3); + + test_assert(a123 == a123, "operator== is broken"); +} + +template<class HandleT> +JSONVariant +MakeTestVariant(HandleT* handle) +{ + // In JS syntax: + // + // return [ + // undefined, null, true, 1.25, "test string", + // handle, + // [ 1, 2, 3 ], + // { "undefined" : undefined, + // "null" : null, + // "true" : true, + // "1.25" : 1.25, + // "string" : "string" + // "handle" : handle, + // "array" : [ 1, 2, 3 ] + // } + // ] + // + InfallibleTArray<JSONVariant> outer; + + outer.AppendElement(void_t()); + outer.AppendElement(null_t()); + outer.AppendElement(true); + outer.AppendElement(1.25); + outer.AppendElement(String("test string")); + + outer.AppendElement(handle); + + InfallibleTArray<JSONVariant> tmp; + Array123(tmp); + outer.AppendElement(tmp); + + InfallibleTArray<KeyValue> obj; + obj.AppendElement(KeyValue(String("undefined"), void_t())); + obj.AppendElement(KeyValue(String("null"), null_t())); + obj.AppendElement(KeyValue(String("true"), true)); + obj.AppendElement(KeyValue(String("1.25"), 1.25)); + obj.AppendElement(KeyValue(String("string"), String("value"))); + obj.AppendElement(KeyValue(String("handle"), handle)); + InfallibleTArray<JSONVariant> tmp2; + Array123(tmp2); + obj.AppendElement(KeyValue(String("array"), tmp2)); + + outer.AppendElement(obj); + + test_assert(outer == outer, "operator== is broken"); + + return JSONVariant(outer); +} + +//----------------------------------------------------------------------------- +// parent + +void +TestJSONParent::Main() +{ + if (!SendStart()) + fail("sending Start"); +} + + +bool +TestJSONParent::RecvTest(const JSONVariant& i, + JSONVariant* o) +{ + test_assert(i == i, "operator== is broken"); + test_assert(i == MakeTestVariant(mKid), "inparam mangled en route"); + + *o = i; + + test_assert(i == *o, "operator= is broken"); + + return true; +} + + +//----------------------------------------------------------------------------- +// child + +bool +TestJSONChild::RecvStart() +{ + if (!SendPTestHandleConstructor()) + fail("sending Handle ctor"); + + JSONVariant i(MakeTestVariant(mKid)); + test_assert(i == i, "operator== is broken"); + test_assert(i == MakeTestVariant(mKid), "copy ctor is broken"); + + JSONVariant o; + if (!SendTest(i, &o)) + fail("sending Test"); + + test_assert(i == o, "round-trip mangled input data"); + test_assert(o == MakeTestVariant(mKid), "outparam mangled en route"); + + Close(); + return true; +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestJSON.h b/ipc/ipdl/test/cxx/TestJSON.h new file mode 100644 index 000000000..55d6e041b --- /dev/null +++ b/ipc/ipdl/test/cxx/TestJSON.h @@ -0,0 +1,111 @@ +#ifndef mozilla__ipdltest_TestJSON_h +#define mozilla__ipdltest_TestJSON_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestJSONParent.h" +#include "mozilla/_ipdltest/PTestJSONChild.h" + +#include "mozilla/_ipdltest/PTestHandleParent.h" +#include "mozilla/_ipdltest/PTestHandleChild.h" + +namespace mozilla { +namespace _ipdltest { + +class TestHandleParent : + public PTestHandleParent +{ +public: + TestHandleParent() { } + virtual ~TestHandleParent() { } + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} +}; + +class TestJSONParent : + public PTestJSONParent +{ +public: + TestJSONParent() { } + virtual ~TestJSONParent() { } + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool + RecvTest(const JSONVariant& i, + JSONVariant* o) override; + + virtual PTestHandleParent* AllocPTestHandleParent() override + { + return mKid = new TestHandleParent(); + } + + virtual bool DeallocPTestHandleParent(PTestHandleParent* actor) override + { + delete actor; + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + + PTestHandleParent* mKid; +}; + + +class TestHandleChild : + public PTestHandleChild +{ +public: + TestHandleChild() { } + virtual ~TestHandleChild() { } +}; + +class TestJSONChild : + public PTestJSONChild +{ +public: + TestJSONChild() { } + virtual ~TestJSONChild() { } + +protected: + virtual bool + RecvStart() override; + + virtual PTestHandleChild* AllocPTestHandleChild() override + { + return mKid = new TestHandleChild(); + } + + virtual bool DeallocPTestHandleChild(PTestHandleChild* actor) override + { + delete actor; + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } + + PTestHandleChild* mKid; +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestJSON_h diff --git a/ipc/ipdl/test/cxx/TestLatency.cpp b/ipc/ipdl/test/cxx/TestLatency.cpp new file mode 100644 index 000000000..094622b82 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestLatency.cpp @@ -0,0 +1,258 @@ +#include "TestLatency.h" + +#include "IPDLUnitTests.h" // fail etc. + +// A ping/pong trial takes O(100us) or more, so if we don't have 10us +// resolution or better, the results will not be terribly useful +static const double kTimingResolutionCutoff = 0.00001; // 10us + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestLatencyParent::TestLatencyParent() : + mStart(), + mPPTimeTotal(), + mPP5TimeTotal(), + mRpcTimeTotal(), + mPPTrialsToGo(NR_TRIALS), + mPP5TrialsToGo(NR_TRIALS), + mNumChildProcessedCompressedSpams(0) +{ + MOZ_COUNT_CTOR(TestLatencyParent); +} + +TestLatencyParent::~TestLatencyParent() +{ + MOZ_COUNT_DTOR(TestLatencyParent); +} + +void +TestLatencyParent::Main() +{ + TimeDuration resolution = TimeDuration::Resolution(); + if (resolution.ToSeconds() > kTimingResolutionCutoff) { + puts(" (skipping TestLatency, timing resolution is too poor)"); + Close(); + return; + } + + printf(" timing resolution: %g seconds\n", + resolution.ToSecondsSigDigits()); + + if (mozilla::ipc::LoggingEnabled()) + NS_RUNTIMEABORT("you really don't want to log all IPC messages during this test, trust me"); + + PingPongTrial(); +} + +void +TestLatencyParent::PingPongTrial() +{ + mStart = TimeStamp::Now(); + if (!SendPing()) + fail("sending Ping()"); +} + +void +TestLatencyParent::Ping5Pong5Trial() +{ + mStart = TimeStamp::Now(); + + if (!SendPing5() || + !SendPing5() || + !SendPing5() || + !SendPing5() || + !SendPing5()) + fail("sending Ping5()"); +} + +bool +TestLatencyParent::RecvPong() +{ + TimeDuration thisTrial = (TimeStamp::Now() - mStart); + mPPTimeTotal += thisTrial; + + if (0 == (mPPTrialsToGo % 1000)) + printf(" PP trial %d: %g\n", + mPPTrialsToGo, thisTrial.ToSecondsSigDigits()); + + if (--mPPTrialsToGo > 0) + PingPongTrial(); + else + Ping5Pong5Trial(); + return true; +} + +bool +TestLatencyParent::RecvPong5() +{ + if (PTestLatency::PING5 != state()) + return true; + + TimeDuration thisTrial = (TimeStamp::Now() - mStart); + mPP5TimeTotal += thisTrial; + + if (0 == (mPP5TrialsToGo % 1000)) + printf(" PP5 trial %d: %g\n", + mPP5TrialsToGo, thisTrial.ToSecondsSigDigits()); + + if (0 < --mPP5TrialsToGo) + Ping5Pong5Trial(); + else + RpcTrials(); + + return true; +} + +void +TestLatencyParent::RpcTrials() +{ + TimeStamp start = TimeStamp::Now(); + for (int i = 0; i < NR_TRIALS; ++i) { + if (!CallRpc()) + fail("can't call Rpc()"); + if (0 == (i % 1000)) + printf(" Rpc trial %d\n", i); + } + mRpcTimeTotal = (TimeStamp::Now() - start); + + SpamTrial(); +} + +void +TestLatencyParent::SpamTrial() +{ + TimeStamp start = TimeStamp::Now(); + for (int i = 0; i < NR_SPAMS - 1; ++i) { + if (!SendSpam()) + fail("sending Spam()"); + if (0 == (i % 10000)) + printf(" Spam trial %d\n", i); + } + + // Synchronize with the child process to ensure all messages have + // been processed. This adds the overhead of a reply message from + // child-->here, but should be insignificant compared to >> + // NR_SPAMS. + if (!CallSynchro()) + fail("calling Synchro()"); + + mSpamTimeTotal = (TimeStamp::Now() - start); + + CompressedSpamTrial(); +} + +void +TestLatencyParent::CompressedSpamTrial() +{ + for (int i = 0; i < NR_SPAMS; ++i) { + if (!SendCompressedSpam(i + 1)) + fail("sending CompressedSpam()"); + if (0 == (i % 10000)) + printf(" CompressedSpam trial %d\n", i); + } + + uint32_t lastSeqno; + if (!CallSynchro2(&lastSeqno, &mNumChildProcessedCompressedSpams)) + fail("calling Synchro2()"); + + if (lastSeqno != NR_SPAMS) + fail("last seqno was %u, expected %u", lastSeqno, NR_SPAMS); + + // NB: since this is testing an optimization, it's somewhat bogus. + // Need to make a warning if it actually intermittently fails in + // practice, which is doubtful. + if (!(mNumChildProcessedCompressedSpams < NR_SPAMS)) + fail("Didn't compress any messages?"); + + Exit(); +} + +void +TestLatencyParent::Exit() +{ + Close(); +} + +//----------------------------------------------------------------------------- +// child + +TestLatencyChild::TestLatencyChild() + : mLastSeqno(0) + , mNumProcessedCompressedSpams(0) +{ + MOZ_COUNT_CTOR(TestLatencyChild); +} + +TestLatencyChild::~TestLatencyChild() +{ + MOZ_COUNT_DTOR(TestLatencyChild); +} + +bool +TestLatencyChild::RecvPing() +{ + SendPong(); + return true; +} + +bool +TestLatencyChild::RecvPing5() +{ + if (PTestLatency::PONG1 != state()) + return true; + + if (!SendPong5() || + !SendPong5() || + !SendPong5() || + !SendPong5() || + !SendPong5()) + fail("sending Pong5()"); + + return true; +} + +bool +TestLatencyChild::AnswerRpc() +{ + return true; +} + +bool +TestLatencyChild::RecvSpam() +{ + // no-op + return true; +} + +bool +TestLatencyChild::AnswerSynchro() +{ + return true; +} + +bool +TestLatencyChild::RecvCompressedSpam(const uint32_t& seqno) +{ + if (seqno <= mLastSeqno) + fail("compressed seqnos must monotonically increase"); + + mLastSeqno = seqno; + ++mNumProcessedCompressedSpams; + return true; +} + +bool +TestLatencyChild::AnswerSynchro2(uint32_t* lastSeqno, + uint32_t* numMessagesDispatched) +{ + *lastSeqno = mLastSeqno; + *numMessagesDispatched = mNumProcessedCompressedSpams; + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestLatency.h b/ipc/ipdl/test/cxx/TestLatency.h new file mode 100644 index 000000000..d179213db --- /dev/null +++ b/ipc/ipdl/test/cxx/TestLatency.h @@ -0,0 +1,111 @@ +#ifndef mozilla__ipdltest_TestLatency_h +#define mozilla__ipdltest_TestLatency_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestLatencyParent.h" +#include "mozilla/_ipdltest/PTestLatencyChild.h" + +#include "mozilla/TimeStamp.h" + +#define NR_TRIALS 10000 +#define NR_SPAMS 25000 + +namespace mozilla { +namespace _ipdltest { + +class TestLatencyParent : + public PTestLatencyParent +{ +private: + typedef mozilla::TimeStamp TimeStamp; + typedef mozilla::TimeDuration TimeDuration; + +public: + TestLatencyParent(); + virtual ~TestLatencyParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool RecvPong() override; + virtual bool RecvPong5() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + + passed("\n" + " average #ping-pong/sec: %g\n" + " average #ping5-pong5/sec: %g\n" + " average #RPC call-answer/sec: %g\n" + " average #spams/sec: %g\n" + " pct. spams compressed away: %g\n", + double(NR_TRIALS) / mPPTimeTotal.ToSecondsSigDigits(), + double(NR_TRIALS) / mPP5TimeTotal.ToSecondsSigDigits(), + double(NR_TRIALS) / mRpcTimeTotal.ToSecondsSigDigits(), + double(NR_SPAMS) / mSpamTimeTotal.ToSecondsSigDigits(), + 100.0 * (double(NR_SPAMS - mNumChildProcessedCompressedSpams) / + double(NR_SPAMS))); + + QuitParent(); + } + +private: + void PingPongTrial(); + void Ping5Pong5Trial(); + void RpcTrials(); + void SpamTrial(); + void CompressedSpamTrial(); + void Exit(); + + TimeStamp mStart; + TimeDuration mPPTimeTotal; + TimeDuration mPP5TimeTotal; + TimeDuration mRpcTimeTotal; + TimeDuration mSpamTimeTotal; + + int mPPTrialsToGo; + int mPP5TrialsToGo; + uint32_t mNumChildProcessedCompressedSpams; +}; + + +class TestLatencyChild : + public PTestLatencyChild +{ +public: + TestLatencyChild(); + virtual ~TestLatencyChild(); + +protected: + virtual bool RecvPing() override; + virtual bool RecvPing5() override; + virtual bool AnswerRpc() override; + virtual bool RecvSpam() override; + virtual bool AnswerSynchro() override; + virtual bool RecvCompressedSpam(const uint32_t& seqno) override; + virtual bool AnswerSynchro2(uint32_t* lastSeqno, + uint32_t* numMessagesDispatched) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } + + uint32_t mLastSeqno; + uint32_t mNumProcessedCompressedSpams; +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestLatency_h diff --git a/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp b/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp new file mode 100644 index 000000000..7dee30e63 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp @@ -0,0 +1,104 @@ +#include "TestManyChildAllocs.h" + +#include "IPDLUnitTests.h" // fail etc. + + +#define NALLOCS 10 + +namespace mozilla { +namespace _ipdltest { + +// parent code + +TestManyChildAllocsParent::TestManyChildAllocsParent() +{ + MOZ_COUNT_CTOR(TestManyChildAllocsParent); +} + +TestManyChildAllocsParent::~TestManyChildAllocsParent() +{ + MOZ_COUNT_DTOR(TestManyChildAllocsParent); +} + +void +TestManyChildAllocsParent::Main() +{ + if (!SendGo()) + fail("can't send Go()"); +} + +bool +TestManyChildAllocsParent::RecvDone() +{ + // explicitly *not* cleaning up, so we can sanity-check IPDL's + // auto-shutdown/cleanup handling + Close(); + + return true; +} + +bool +TestManyChildAllocsParent::DeallocPTestManyChildAllocsSubParent( + PTestManyChildAllocsSubParent* __a) +{ + delete __a; return true; +} + +PTestManyChildAllocsSubParent* +TestManyChildAllocsParent::AllocPTestManyChildAllocsSubParent() +{ + return new TestManyChildAllocsSubParent(); +} + + +// child code + +TestManyChildAllocsChild::TestManyChildAllocsChild() +{ + MOZ_COUNT_CTOR(TestManyChildAllocsChild); +} + +TestManyChildAllocsChild::~TestManyChildAllocsChild() +{ + MOZ_COUNT_DTOR(TestManyChildAllocsChild); +} + +bool TestManyChildAllocsChild::RecvGo() +{ + for (int i = 0; i < NALLOCS; ++i) { + PTestManyChildAllocsSubChild* child = + SendPTestManyChildAllocsSubConstructor(); + + if (!child) + fail("can't send ctor()"); + + if (!child->SendHello()) + fail("can't send Hello()"); + } + + size_t len = ManagedPTestManyChildAllocsSubChild().Count(); + if (NALLOCS != len) + fail("expected %lu kids, got %lu", NALLOCS, len); + + if (!SendDone()) + fail("can't send Done()"); + + return true; +} + +bool +TestManyChildAllocsChild::DeallocPTestManyChildAllocsSubChild( + PTestManyChildAllocsSubChild* __a) +{ + delete __a; return true; +} + +PTestManyChildAllocsSubChild* +TestManyChildAllocsChild::AllocPTestManyChildAllocsSubChild() +{ + return new TestManyChildAllocsSubChild(); +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestManyChildAllocs.h b/ipc/ipdl/test/cxx/TestManyChildAllocs.h new file mode 100644 index 000000000..d29a35cb8 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.h @@ -0,0 +1,94 @@ +#ifndef mozilla__ipdltest_TestManyChildAllocs_h +#define mozilla__ipdltest_TestManyChildAllocs_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestManyChildAllocsParent.h" +#include "mozilla/_ipdltest/PTestManyChildAllocsChild.h" + +#include "mozilla/_ipdltest/PTestManyChildAllocsSubParent.h" +#include "mozilla/_ipdltest/PTestManyChildAllocsSubChild.h" + +namespace mozilla { +namespace _ipdltest { + +// top-level protocol + +class TestManyChildAllocsParent : + public PTestManyChildAllocsParent +{ +public: + TestManyChildAllocsParent(); + virtual ~TestManyChildAllocsParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool RecvDone() override; + virtual bool DeallocPTestManyChildAllocsSubParent(PTestManyChildAllocsSubParent* __a) override; + virtual PTestManyChildAllocsSubParent* AllocPTestManyChildAllocsSubParent() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + + +class TestManyChildAllocsChild : + public PTestManyChildAllocsChild +{ +public: + TestManyChildAllocsChild(); + virtual ~TestManyChildAllocsChild(); + +protected: + virtual bool RecvGo() override; + virtual bool DeallocPTestManyChildAllocsSubChild(PTestManyChildAllocsSubChild* __a) override; + virtual PTestManyChildAllocsSubChild* AllocPTestManyChildAllocsSubChild() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +// do-nothing sub-protocol actors + +class TestManyChildAllocsSubParent : + public PTestManyChildAllocsSubParent +{ +public: + TestManyChildAllocsSubParent() { } + virtual ~TestManyChildAllocsSubParent() { } + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} + virtual bool RecvHello() override { return true; } +}; + + +class TestManyChildAllocsSubChild : + public PTestManyChildAllocsSubChild +{ +public: + TestManyChildAllocsSubChild() { } + virtual ~TestManyChildAllocsSubChild() { } +}; + + + +} // namepsace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestManyChildAllocs_h diff --git a/ipc/ipdl/test/cxx/TestMultiMgrs.cpp b/ipc/ipdl/test/cxx/TestMultiMgrs.cpp new file mode 100644 index 000000000..422b3fea3 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestMultiMgrs.cpp @@ -0,0 +1,104 @@ +#include "TestMultiMgrs.h" + +#include "IPDLUnitTests.h" // fail etc. +#include "mozilla/ipc/ProtocolUtils.h" + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +void +TestMultiMgrsParent::Main() +{ + TestMultiMgrsLeftParent* leftie = new TestMultiMgrsLeftParent(); + if (!SendPTestMultiMgrsLeftConstructor(leftie)) + fail("error sending ctor"); + + TestMultiMgrsRightParent* rightie = new TestMultiMgrsRightParent(); + if (!SendPTestMultiMgrsRightConstructor(rightie)) + fail("error sending ctor"); + + TestMultiMgrsBottomParent* bottomL = new TestMultiMgrsBottomParent(); + if (!leftie->SendPTestMultiMgrsBottomConstructor(bottomL)) + fail("error sending ctor"); + + TestMultiMgrsBottomParent* bottomR = new TestMultiMgrsBottomParent(); + if (!rightie->SendPTestMultiMgrsBottomConstructor(bottomR)) + fail("error sending ctor"); + + if (!leftie->HasChild(bottomL)) + fail("leftie didn't have a child it was supposed to!"); + if (leftie->HasChild(bottomR)) + fail("leftie had rightie's child!"); + + if (!rightie->HasChild(bottomR)) + fail("rightie didn't have a child it was supposed to!"); + if (rightie->HasChild(bottomL)) + fail("rightie had rightie's child!"); + + if (!SendCheck()) + fail("couldn't kick off the child-side check"); +} + +bool +TestMultiMgrsParent::RecvOK() +{ + Close(); + return true; +} + +//----------------------------------------------------------------------------- +// child + +bool +TestMultiMgrsLeftChild::RecvPTestMultiMgrsBottomConstructor( + PTestMultiMgrsBottomChild* actor) +{ + static_cast<TestMultiMgrsChild*>(Manager())->mBottomL = actor; + return true; +} + +bool +TestMultiMgrsRightChild::RecvPTestMultiMgrsBottomConstructor( + PTestMultiMgrsBottomChild* actor) +{ + static_cast<TestMultiMgrsChild*>(Manager())->mBottomR = actor; + return true; +} + +bool +TestMultiMgrsChild::RecvCheck() +{ + if (1 != ManagedPTestMultiMgrsLeftChild().Count()) + fail("where's leftie?"); + if (1 != ManagedPTestMultiMgrsRightChild().Count()) + fail("where's rightie?"); + + TestMultiMgrsLeftChild* leftie = + static_cast<TestMultiMgrsLeftChild*>( + LoneManagedOrNullAsserts(ManagedPTestMultiMgrsLeftChild())); + TestMultiMgrsRightChild* rightie = + static_cast<TestMultiMgrsRightChild*>( + LoneManagedOrNullAsserts(ManagedPTestMultiMgrsRightChild())); + + if (!leftie->HasChild(mBottomL)) + fail("leftie didn't have a child it was supposed to!"); + if (leftie->HasChild(mBottomR)) + fail("leftie had rightie's child!"); + + if (!rightie->HasChild(mBottomR)) + fail("rightie didn't have a child it was supposed to!"); + if (rightie->HasChild(mBottomL)) + fail("rightie had leftie's child!"); + + if (!SendOK()) + fail("couldn't send OK()"); + + return true; +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestMultiMgrs.h b/ipc/ipdl/test/cxx/TestMultiMgrs.h new file mode 100644 index 000000000..3dfab7430 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestMultiMgrs.h @@ -0,0 +1,250 @@ +#ifndef mozilla__ipdltest_TestMultiMgrs_h +#define mozilla__ipdltest_TestMultiMgrs_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestMultiMgrsParent.h" +#include "mozilla/_ipdltest/PTestMultiMgrsChild.h" +#include "mozilla/_ipdltest/PTestMultiMgrsBottomParent.h" +#include "mozilla/_ipdltest/PTestMultiMgrsBottomChild.h" +#include "mozilla/_ipdltest/PTestMultiMgrsLeftParent.h" +#include "mozilla/_ipdltest/PTestMultiMgrsLeftChild.h" +#include "mozilla/_ipdltest/PTestMultiMgrsRightParent.h" +#include "mozilla/_ipdltest/PTestMultiMgrsRightChild.h" + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// Parent side +// + +class TestMultiMgrsBottomParent : + public PTestMultiMgrsBottomParent +{ +public: + TestMultiMgrsBottomParent() { } + virtual ~TestMultiMgrsBottomParent() { } + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} +}; + +class TestMultiMgrsLeftParent : + public PTestMultiMgrsLeftParent +{ +public: + TestMultiMgrsLeftParent() { } + virtual ~TestMultiMgrsLeftParent() { } + + bool HasChild(TestMultiMgrsBottomParent* c) + { + return ManagedPTestMultiMgrsBottomParent().Contains(c); + } + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} + + virtual PTestMultiMgrsBottomParent* AllocPTestMultiMgrsBottomParent() override + { + return new TestMultiMgrsBottomParent(); + } + + virtual bool DeallocPTestMultiMgrsBottomParent(PTestMultiMgrsBottomParent* actor) override + { + delete actor; + return true; + } +}; + +class TestMultiMgrsRightParent : + public PTestMultiMgrsRightParent +{ +public: + TestMultiMgrsRightParent() { } + virtual ~TestMultiMgrsRightParent() { } + + bool HasChild(TestMultiMgrsBottomParent* c) + { + return ManagedPTestMultiMgrsBottomParent().Contains(c); + } + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} + + virtual PTestMultiMgrsBottomParent* AllocPTestMultiMgrsBottomParent() override + { + return new TestMultiMgrsBottomParent(); + } + + virtual bool DeallocPTestMultiMgrsBottomParent(PTestMultiMgrsBottomParent* actor) override + { + delete actor; + return true; + } +}; + +class TestMultiMgrsParent : + public PTestMultiMgrsParent +{ +public: + TestMultiMgrsParent() { } + virtual ~TestMultiMgrsParent() { } + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool RecvOK() override; + + virtual PTestMultiMgrsLeftParent* AllocPTestMultiMgrsLeftParent() override + { + return new TestMultiMgrsLeftParent(); + } + + virtual bool DeallocPTestMultiMgrsLeftParent(PTestMultiMgrsLeftParent* actor) override + { + delete actor; + return true; + } + + virtual PTestMultiMgrsRightParent* AllocPTestMultiMgrsRightParent() override + { + return new TestMultiMgrsRightParent(); + } + + virtual bool DeallocPTestMultiMgrsRightParent(PTestMultiMgrsRightParent* actor) override + { + delete actor; + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + +//----------------------------------------------------------------------------- +// Child side +// + +class TestMultiMgrsBottomChild : + public PTestMultiMgrsBottomChild +{ +public: + TestMultiMgrsBottomChild() { } + virtual ~TestMultiMgrsBottomChild() { } +}; + +class TestMultiMgrsLeftChild : + public PTestMultiMgrsLeftChild +{ +public: + TestMultiMgrsLeftChild() { } + virtual ~TestMultiMgrsLeftChild() { } + + bool HasChild(PTestMultiMgrsBottomChild* c) + { + return ManagedPTestMultiMgrsBottomChild().Contains(c); + } + +protected: + virtual bool RecvPTestMultiMgrsBottomConstructor(PTestMultiMgrsBottomChild* actor) override; + + virtual PTestMultiMgrsBottomChild* AllocPTestMultiMgrsBottomChild() override + { + return new TestMultiMgrsBottomChild(); + } + + virtual bool DeallocPTestMultiMgrsBottomChild(PTestMultiMgrsBottomChild* actor) override + { + delete actor; + return true; + } +}; + +class TestMultiMgrsRightChild : + public PTestMultiMgrsRightChild +{ +public: + TestMultiMgrsRightChild() { } + virtual ~TestMultiMgrsRightChild() { } + + bool HasChild(PTestMultiMgrsBottomChild* c) + { + return ManagedPTestMultiMgrsBottomChild().Contains(c); + } + +protected: + virtual bool RecvPTestMultiMgrsBottomConstructor(PTestMultiMgrsBottomChild* actor) override; + + virtual PTestMultiMgrsBottomChild* AllocPTestMultiMgrsBottomChild() override + { + return new TestMultiMgrsBottomChild(); + } + + virtual bool DeallocPTestMultiMgrsBottomChild(PTestMultiMgrsBottomChild* actor) override + { + delete actor; + return true; + } +}; + +class TestMultiMgrsChild : + public PTestMultiMgrsChild +{ +public: + TestMultiMgrsChild() { } + virtual ~TestMultiMgrsChild() { } + + void Main(); + + PTestMultiMgrsBottomChild* mBottomL; + PTestMultiMgrsBottomChild* mBottomR; + +protected: + virtual bool RecvCheck() override; + + virtual PTestMultiMgrsLeftChild* AllocPTestMultiMgrsLeftChild() override + { + return new TestMultiMgrsLeftChild(); + } + + virtual bool DeallocPTestMultiMgrsLeftChild(PTestMultiMgrsLeftChild* actor) override + { + delete actor; + return true; + } + + virtual PTestMultiMgrsRightChild* AllocPTestMultiMgrsRightChild() override + { + return new TestMultiMgrsRightChild(); + } + + virtual bool DeallocPTestMultiMgrsRightChild(PTestMultiMgrsRightChild* actor) override + { + delete actor; + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestMultiMgrs_h diff --git a/ipc/ipdl/test/cxx/TestNestedLoops.cpp b/ipc/ipdl/test/cxx/TestNestedLoops.cpp new file mode 100644 index 000000000..0e0ba496d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestNestedLoops.cpp @@ -0,0 +1,98 @@ +#include "base/basictypes.h" + +#include "nsThreadUtils.h" + +#include "TestNestedLoops.h" + +#include "IPDLUnitTests.h" // fail etc. + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestNestedLoopsParent::TestNestedLoopsParent() : mBreakNestedLoop(false) +{ + MOZ_COUNT_CTOR(TestNestedLoopsParent); +} + +TestNestedLoopsParent::~TestNestedLoopsParent() +{ + MOZ_COUNT_DTOR(TestNestedLoopsParent); +} + +void +TestNestedLoopsParent::Main() +{ + if (!SendStart()) + fail("sending Start"); + + // sigh ... spin for a while to let Nonce arrive + puts(" (sleeping to wait for nonce ... sorry)"); + PR_Sleep(5000); + + // while waiting for the reply to R, we'll receive Nonce + if (!CallR()) + fail("calling R"); + + Close(); +} + +bool +TestNestedLoopsParent::RecvNonce() +{ + // if we have an OnMaybeDequeueOne waiting for us (we may not, due + // to the inherent race condition in this test, then this event + // must be ordered after it in the queue + MessageLoop::current()->PostTask( + NewNonOwningRunnableMethod(this, &TestNestedLoopsParent::BreakNestedLoop)); + + // sigh ... spin for a while to let the reply to R arrive + puts(" (sleeping to wait for reply to R ... sorry)"); + PR_Sleep(5000); + + // sigh ... we have no idea when code might do this + do { + if (!NS_ProcessNextEvent(nullptr, false)) + fail("expected at least one pending event"); + } while (!mBreakNestedLoop); + + return true; +} + +void +TestNestedLoopsParent::BreakNestedLoop() +{ + mBreakNestedLoop = true; +} + +//----------------------------------------------------------------------------- +// child + +TestNestedLoopsChild::TestNestedLoopsChild() +{ + MOZ_COUNT_CTOR(TestNestedLoopsChild); +} + +TestNestedLoopsChild::~TestNestedLoopsChild() +{ + MOZ_COUNT_DTOR(TestNestedLoopsChild); +} + +bool +TestNestedLoopsChild::RecvStart() +{ + if (!SendNonce()) + fail("sending Nonce"); + return true; +} + +bool +TestNestedLoopsChild::AnswerR() +{ + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestNestedLoops.h b/ipc/ipdl/test/cxx/TestNestedLoops.h new file mode 100644 index 000000000..a91258a43 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestNestedLoops.h @@ -0,0 +1,67 @@ +#ifndef mozilla__ipdltest_TestNestedLoops_h +#define mozilla__ipdltest_TestNestedLoops_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestNestedLoopsParent.h" +#include "mozilla/_ipdltest/PTestNestedLoopsChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestNestedLoopsParent : + public PTestNestedLoopsParent +{ +public: + TestNestedLoopsParent(); + virtual ~TestNestedLoopsParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool RecvNonce() override; + + void BreakNestedLoop(); + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + + bool mBreakNestedLoop; +}; + + +class TestNestedLoopsChild : + public PTestNestedLoopsChild +{ +public: + TestNestedLoopsChild(); + virtual ~TestNestedLoopsChild(); + +protected: + virtual bool RecvStart() override; + + virtual bool AnswerR() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestNestedLoops_h diff --git a/ipc/ipdl/test/cxx/TestOpens.cpp b/ipc/ipdl/test/cxx/TestOpens.cpp new file mode 100644 index 000000000..cb1ed00b5 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestOpens.cpp @@ -0,0 +1,251 @@ +#include "base/task.h" +#include "base/thread.h" + +#include "TestOpens.h" + +#include "IPDLUnitTests.h" // fail etc. + +using namespace mozilla::ipc; + +using base::ProcessHandle; +using base::Thread; + +namespace mozilla { +// NB: this is generally bad style, but I am lazy. +using namespace _ipdltest; +using namespace _ipdltest2; + +static MessageLoop* gMainThread; + +static void +AssertNotMainThread() +{ + if (!gMainThread) + fail("gMainThread is not initialized"); + if (MessageLoop::current() == gMainThread) + fail("unexpectedly called on the main thread"); +} + +//----------------------------------------------------------------------------- +// parent + +// Thread on which TestOpensOpenedParent runs +static Thread* gParentThread; + +void +TestOpensParent::Main() +{ + if (!SendStart()) + fail("sending Start"); +} + +static void +OpenParent(TestOpensOpenedParent* aParent, + Transport* aTransport, base::ProcessId aOtherPid) +{ + AssertNotMainThread(); + + // Open the actor on the off-main thread to park it there. + // Messages will be delivered to this thread's message loop + // instead of the main thread's. + if (!aParent->Open(aTransport, aOtherPid, + XRE_GetIOMessageLoop(), ipc::ParentSide)) + fail("opening Parent"); +} + +PTestOpensOpenedParent* +TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport, + ProcessId otherPid) +{ + gMainThread = MessageLoop::current(); + + gParentThread = new Thread("ParentThread"); + if (!gParentThread->Start()) + fail("starting parent thread"); + + TestOpensOpenedParent* a = new TestOpensOpenedParent(transport); + gParentThread->message_loop()->PostTask( + NewRunnableFunction(OpenParent, a, transport, otherPid)); + + return a; +} + +void +TestOpensParent::ActorDestroy(ActorDestroyReason why) +{ + // Stops the thread and joins it + delete gParentThread; + + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); +} + +bool +TestOpensOpenedParent::RecvHello() +{ + AssertNotMainThread(); + return SendHi(); +} + +bool +TestOpensOpenedParent::RecvHelloSync() +{ + AssertNotMainThread(); + return true; +} + +bool +TestOpensOpenedParent::AnswerHelloRpc() +{ + AssertNotMainThread(); + return CallHiRpc(); +} + +static void +ShutdownTestOpensOpenedParent(TestOpensOpenedParent* parent, + Transport* transport) +{ + delete parent; +} + +void +TestOpensOpenedParent::ActorDestroy(ActorDestroyReason why) +{ + AssertNotMainThread(); + + if (NormalShutdown != why) + fail("unexpected destruction!"); + + // ActorDestroy() is just a callback from IPDL-generated code, + // which needs the top-level actor (this) to stay alive a little + // longer so other things can be cleaned up. + gParentThread->message_loop()->PostTask( + NewRunnableFunction(ShutdownTestOpensOpenedParent, + this, mTransport)); +} + +//----------------------------------------------------------------------------- +// child + +static TestOpensChild* gOpensChild; +// Thread on which TestOpensOpenedChild runs +static Thread* gChildThread; + +TestOpensChild::TestOpensChild() +{ + gOpensChild = this; +} + +bool +TestOpensChild::RecvStart() +{ + if (!PTestOpensOpened::Open(this)) + fail("opening PTestOpensOpened"); + return true; +} + +static void +OpenChild(TestOpensOpenedChild* aChild, + Transport* aTransport, base::ProcessId aOtherPid) +{ + AssertNotMainThread(); + + // Open the actor on the off-main thread to park it there. + // Messages will be delivered to this thread's message loop + // instead of the main thread's. + if (!aChild->Open(aTransport, aOtherPid, + XRE_GetIOMessageLoop(), ipc::ChildSide)) + fail("opening Child"); + + // Kick off the unit tests + if (!aChild->SendHello()) + fail("sending Hello"); +} + +PTestOpensOpenedChild* +TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport, + ProcessId otherPid) +{ + gMainThread = MessageLoop::current(); + + gChildThread = new Thread("ChildThread"); + if (!gChildThread->Start()) + fail("starting child thread"); + + TestOpensOpenedChild* a = new TestOpensOpenedChild(transport); + gChildThread->message_loop()->PostTask( + NewRunnableFunction(OpenChild, a, transport, otherPid)); + + return a; +} + +void +TestOpensChild::ActorDestroy(ActorDestroyReason why) +{ + // Stops the thread and joins it + delete gChildThread; + + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); +} + +bool +TestOpensOpenedChild::RecvHi() +{ + AssertNotMainThread(); + + if (!SendHelloSync()) + fail("sending HelloSync"); + if (!CallHelloRpc()) + fail("calling HelloRpc"); + if (!mGotHi) + fail("didn't answer HiRpc"); + + // Need to close the channel without message-processing frames on + // the C++ stack + MessageLoop::current()->PostTask( + NewNonOwningRunnableMethod(this, &TestOpensOpenedChild::Close)); + return true; +} + +bool +TestOpensOpenedChild::AnswerHiRpc() +{ + AssertNotMainThread(); + + mGotHi = true; // d00d + return true; +} + +static void +ShutdownTestOpensOpenedChild(TestOpensOpenedChild* child, + Transport* transport) +{ + delete child; + + // Kick off main-thread shutdown. + gMainThread->PostTask( + NewNonOwningRunnableMethod(gOpensChild, &TestOpensChild::Close)); +} + +void +TestOpensOpenedChild::ActorDestroy(ActorDestroyReason why) +{ + AssertNotMainThread(); + + if (NormalShutdown != why) + fail("unexpected destruction!"); + + // ActorDestroy() is just a callback from IPDL-generated code, + // which needs the top-level actor (this) to stay alive a little + // longer so other things can be cleaned up. Defer shutdown to + // let cleanup finish. + gChildThread->message_loop()->PostTask( + NewRunnableFunction(ShutdownTestOpensOpenedChild, + this, mTransport)); +} + +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestOpens.h b/ipc/ipdl/test/cxx/TestOpens.h new file mode 100644 index 000000000..b83645a82 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestOpens.h @@ -0,0 +1,107 @@ +#ifndef mozilla__ipdltest_TestOpens_h +#define mozilla__ipdltest_TestOpens_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestOpensParent.h" +#include "mozilla/_ipdltest/PTestOpensChild.h" + +#include "mozilla/_ipdltest2/PTestOpensOpenedParent.h" +#include "mozilla/_ipdltest2/PTestOpensOpenedChild.h" + +namespace mozilla { + +// parent process + +namespace _ipdltest { + +class TestOpensParent : public PTestOpensParent +{ +public: + TestOpensParent() {} + virtual ~TestOpensParent() {} + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + virtual PTestOpensOpenedParent* + AllocPTestOpensOpenedParent(Transport* transport, ProcessId otherProcess) override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +} // namespace _ipdltest + +namespace _ipdltest2 { + +class TestOpensOpenedParent : public PTestOpensOpenedParent +{ +public: + explicit TestOpensOpenedParent(Transport* aTransport) + : mTransport(aTransport) + {} + virtual ~TestOpensOpenedParent() {} + +protected: + virtual bool RecvHello() override; + virtual bool RecvHelloSync() override; + virtual bool AnswerHelloRpc() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + Transport* mTransport; +}; + +} // namespace _ipdltest2 + +// child process + +namespace _ipdltest { + +class TestOpensChild : public PTestOpensChild +{ +public: + TestOpensChild(); + virtual ~TestOpensChild() {} + +protected: + virtual bool RecvStart() override; + + virtual PTestOpensOpenedChild* + AllocPTestOpensOpenedChild(Transport* transport, ProcessId otherProcess) override; + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +} // namespace _ipdltest + +namespace _ipdltest2 { + +class TestOpensOpenedChild : public PTestOpensOpenedChild +{ +public: + explicit TestOpensOpenedChild(Transport* aTransport) + : mGotHi(false) + , mTransport(aTransport) + {} + virtual ~TestOpensOpenedChild() {} + +protected: + virtual bool RecvHi() override; + virtual bool AnswerHiRpc() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + bool mGotHi; + Transport* mTransport; +}; + +} // namespace _ipdltest2 + +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestOpens_h diff --git a/ipc/ipdl/test/cxx/TestRPC.cpp b/ipc/ipdl/test/cxx/TestRPC.cpp new file mode 100644 index 000000000..2dfc51d33 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRPC.cpp @@ -0,0 +1,152 @@ +#include "TestRPC.h" + +#include "IPDLUnitTests.h" // fail etc. +#if defined(OS_POSIX) +#include <unistd.h> +#else +#include <windows.h> +#endif + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestRPCParent::TestRPCParent() + : reentered_(false), + resolved_first_cpow_(false) +{ + MOZ_COUNT_CTOR(TestRPCParent); +} + +TestRPCParent::~TestRPCParent() +{ + MOZ_COUNT_DTOR(TestRPCParent); +} + +void +TestRPCParent::Main() +{ + if (!SendStart()) + fail("sending Start"); +} + +bool +TestRPCParent::RecvTest1_Start(uint32_t* aResult) +{ + uint32_t result; + if (!SendTest1_InnerQuery(&result)) + fail("SendTest1_InnerQuery"); + if (result != 300) + fail("Wrong result (expected 300)"); + + *aResult = 100; + return true; +} + +bool +TestRPCParent::RecvTest1_InnerEvent(uint32_t* aResult) +{ + uint32_t result; + if (!SendTest1_NoReenter(&result)) + fail("SendTest1_NoReenter"); + if (result != 400) + fail("Wrong result (expected 400)"); + + *aResult = 200; + return true; +} + +bool +TestRPCParent::RecvTest2_Start() +{ + // Send a CPOW. During this time, we must NOT process the RPC message, as + // we could start receiving CPOW replies out-of-order. + if (!SendTest2_FirstUrgent()) + fail("SendTest2_FirstUrgent"); + + MOZ_ASSERT(!reentered_); + resolved_first_cpow_ = true; + return true; +} + +bool +TestRPCParent::RecvTest2_OutOfOrder() +{ + // Send a CPOW. If this RPC call was initiated while waiting for the first + // CPOW to resolve, replies will be processed out of order, and we'll crash. + if (!SendTest2_SecondUrgent()) + fail("SendTest2_SecondUrgent"); + + reentered_ = true; + return true; +} + +//----------------------------------------------------------------------------- +// child + + +TestRPCChild::TestRPCChild() +{ + MOZ_COUNT_CTOR(TestRPCChild); +} + +TestRPCChild::~TestRPCChild() +{ + MOZ_COUNT_DTOR(TestRPCChild); +} + +bool +TestRPCChild::RecvStart() +{ + uint32_t result; + if (!SendTest1_Start(&result)) + fail("SendTest1_Start"); + if (result != 100) + fail("Wrong result (expected 100)"); + + if (!SendTest2_Start()) + fail("SendTest2_Start"); + + if (!SendTest2_OutOfOrder()) + fail("SendTest2_OutOfOrder"); + + Close(); + return true; +} + +bool +TestRPCChild::RecvTest1_InnerQuery(uint32_t* aResult) +{ + uint32_t result; + if (!SendTest1_InnerEvent(&result)) + fail("SendTest1_InnerEvent"); + if (result != 200) + fail("Wrong result (expected 200)"); + + *aResult = 300; + return true; +} + +bool +TestRPCChild::RecvTest1_NoReenter(uint32_t* aResult) +{ + *aResult = 400; + return true; +} + +bool +TestRPCChild::RecvTest2_FirstUrgent() +{ + return true; +} + +bool +TestRPCChild::RecvTest2_SecondUrgent() +{ + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestRPC.h b/ipc/ipdl/test/cxx/TestRPC.h new file mode 100644 index 000000000..0a11b3e76 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRPC.h @@ -0,0 +1,74 @@ +#ifndef mozilla__ipdltest_TestRPC_h +#define mozilla__ipdltest_TestRPC_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestRPCParent.h" +#include "mozilla/_ipdltest/PTestRPCChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestRPCParent : + public PTestRPCParent +{ +public: + TestRPCParent(); + virtual ~TestRPCParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + + bool RecvTest1_Start(uint32_t* aResult) override; + bool RecvTest1_InnerEvent(uint32_t* aResult) override; + bool RecvTest2_Start() override; + bool RecvTest2_OutOfOrder() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + if (!reentered_) + fail("never processed raced RPC call!"); + if (!resolved_first_cpow_) + fail("never resolved first CPOW!"); + passed("ok"); + QuitParent(); + } + +private: + bool reentered_; + bool resolved_first_cpow_; +}; + + +class TestRPCChild : + public PTestRPCChild +{ +public: + TestRPCChild(); + virtual ~TestRPCChild(); + + bool RecvStart() override; + bool RecvTest1_InnerQuery(uint32_t* aResult) override; + bool RecvTest1_NoReenter(uint32_t* aResult) override; + bool RecvTest2_FirstUrgent() override; + bool RecvTest2_SecondUrgent() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestRPC_h diff --git a/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp b/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp new file mode 100644 index 000000000..7402f30d3 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp @@ -0,0 +1,131 @@ +#include "TestRaceDeadlock.h" + +#include "IPDLUnitTests.h" // fail etc. + +// #define TEST_TIMEOUT 5000 + +using namespace mozilla::ipc; +typedef mozilla::ipc::MessageChannel::Message Message; +typedef mozilla::ipc::MessageChannel::MessageInfo MessageInfo; + +namespace mozilla { +namespace _ipdltest { + +static RacyInterruptPolicy +MediateRace(const MessageInfo& parent, const MessageInfo& child) +{ + return (PTestRaceDeadlock::Msg_Win__ID == parent.type()) ? + RIPParentWins : RIPChildWins; +} + +//----------------------------------------------------------------------------- +// parent + +TestRaceDeadlockParent::TestRaceDeadlockParent() +{ + MOZ_COUNT_CTOR(TestRaceDeadlockParent); +} + +TestRaceDeadlockParent::~TestRaceDeadlockParent() +{ + MOZ_COUNT_DTOR(TestRaceDeadlockParent); +} + +void +TestRaceDeadlockParent::Main() +{ + Test1(); + + Close(); +} + +bool +TestRaceDeadlockParent::ShouldContinueFromReplyTimeout() +{ + fail("This test should not hang"); + GetIPCChannel()->CloseWithTimeout(); + return false; +} + +void +TestRaceDeadlockParent::Test1() +{ +#if defined(TEST_TIMEOUT) + SetReplyTimeoutMs(TEST_TIMEOUT); +#endif + if (!SendStartRace()) { + fail("sending StartRace"); + } + if (!CallRpc()) { + fail("calling Rpc"); + } +} + +bool +TestRaceDeadlockParent::AnswerLose() +{ + return true; +} + +RacyInterruptPolicy +TestRaceDeadlockParent::MediateInterruptRace(const MessageInfo& parent, + const MessageInfo& child) +{ + return MediateRace(parent, child); +} + +//----------------------------------------------------------------------------- +// child + +TestRaceDeadlockChild::TestRaceDeadlockChild() +{ + MOZ_COUNT_CTOR(TestRaceDeadlockChild); +} + +TestRaceDeadlockChild::~TestRaceDeadlockChild() +{ + MOZ_COUNT_DTOR(TestRaceDeadlockChild); +} + +bool +TestRaceDeadlockParent::RecvStartRace() +{ + if (!CallWin()) { + fail("calling Win"); + } + return true; +} + +bool +TestRaceDeadlockChild::RecvStartRace() +{ + if (!SendStartRace()) { + fail("calling SendStartRace"); + } + if (!CallLose()) { + fail("calling Lose"); + } + return true; +} + +bool +TestRaceDeadlockChild::AnswerWin() +{ + return true; +} + +bool +TestRaceDeadlockChild::AnswerRpc() +{ + return true; +} + +RacyInterruptPolicy +TestRaceDeadlockChild::MediateInterruptRace(const MessageInfo& parent, + const MessageInfo& child) +{ + return MediateRace(parent, child); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestRaceDeadlock.h b/ipc/ipdl/test/cxx/TestRaceDeadlock.h new file mode 100644 index 000000000..7723c7629 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRaceDeadlock.h @@ -0,0 +1,77 @@ +#ifndef mozilla__ipdltest_TestRaceDeadlock_h +#define mozilla__ipdltest_TestRaceDeadlock_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestRaceDeadlockParent.h" +#include "mozilla/_ipdltest/PTestRaceDeadlockChild.h" + +namespace mozilla { +namespace _ipdltest { + +class TestRaceDeadlockParent : + public PTestRaceDeadlockParent +{ +public: + TestRaceDeadlockParent(); + virtual ~TestRaceDeadlockParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool ShouldContinueFromReplyTimeout() override; + + void Test1(); + + virtual bool RecvStartRace() override; + virtual bool AnswerLose() override; + + virtual mozilla::ipc::RacyInterruptPolicy + MediateInterruptRace(const MessageInfo& parent, + const MessageInfo& child) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + + +class TestRaceDeadlockChild : + public PTestRaceDeadlockChild +{ +public: + TestRaceDeadlockChild(); + virtual ~TestRaceDeadlockChild(); + +protected: + virtual bool RecvStartRace() override; + + virtual bool AnswerWin() override; + + virtual bool AnswerRpc() override; + + virtual mozilla::ipc::RacyInterruptPolicy + MediateInterruptRace(const MessageInfo& parent, + const MessageInfo& child) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestRaceDeadlock_h diff --git a/ipc/ipdl/test/cxx/TestRaceDeferral.cpp b/ipc/ipdl/test/cxx/TestRaceDeferral.cpp new file mode 100644 index 000000000..b62d0e2f3 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRaceDeferral.cpp @@ -0,0 +1,118 @@ +#include "TestRaceDeferral.h" + +#include "IPDLUnitTests.h" // fail etc. + +using namespace mozilla::ipc; +typedef mozilla::ipc::MessageChannel::Message Message; +typedef mozilla::ipc::MessageChannel::MessageInfo MessageInfo; + +namespace mozilla { +namespace _ipdltest { + +static RacyInterruptPolicy +MediateRace(const MessageInfo& parent, const MessageInfo& child) +{ + return (PTestRaceDeferral::Msg_Win__ID == parent.type()) ? + RIPParentWins : RIPChildWins; +} + +//----------------------------------------------------------------------------- +// parent + +TestRaceDeferralParent::TestRaceDeferralParent() + : mProcessedLose(false) +{ + MOZ_COUNT_CTOR(TestRaceDeferralParent); +} + +TestRaceDeferralParent::~TestRaceDeferralParent() +{ + MOZ_COUNT_DTOR(TestRaceDeferralParent); + + if (!mProcessedLose) + fail("never processed Lose"); +} + +void +TestRaceDeferralParent::Main() +{ + Test1(); + + Close(); +} + +void +TestRaceDeferralParent::Test1() +{ + if (!SendStartRace()) + fail("sending StartRace"); + + if (!CallWin()) + fail("calling Win"); + if (mProcessedLose) + fail("Lose didn't lose"); + + if (!CallRpc()) + fail("calling Rpc"); + if (!mProcessedLose) + fail("didn't resolve Rpc vs. Lose 'race' correctly"); +} + +bool +TestRaceDeferralParent::AnswerLose() +{ + if (mProcessedLose) + fail("processed Lose twice"); + mProcessedLose = true; + return true; +} + +RacyInterruptPolicy +TestRaceDeferralParent::MediateInterruptRace(const MessageInfo& parent, + const MessageInfo& child) +{ + return MediateRace(parent, child); +} + +//----------------------------------------------------------------------------- +// child + +TestRaceDeferralChild::TestRaceDeferralChild() +{ + MOZ_COUNT_CTOR(TestRaceDeferralChild); +} + +TestRaceDeferralChild::~TestRaceDeferralChild() +{ + MOZ_COUNT_DTOR(TestRaceDeferralChild); +} + +bool +TestRaceDeferralChild::RecvStartRace() +{ + if (!CallLose()) + fail("calling Lose"); + return true; +} + +bool +TestRaceDeferralChild::AnswerWin() +{ + return true; +} + +bool +TestRaceDeferralChild::AnswerRpc() +{ + return true; +} + +RacyInterruptPolicy +TestRaceDeferralChild::MediateInterruptRace(const MessageInfo& parent, + const MessageInfo& child) +{ + return MediateRace(parent, child); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestRaceDeferral.h b/ipc/ipdl/test/cxx/TestRaceDeferral.h new file mode 100644 index 000000000..e465cf77c --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRaceDeferral.h @@ -0,0 +1,76 @@ +#ifndef mozilla__ipdltest_TestRaceDeferral_h +#define mozilla__ipdltest_TestRaceDeferral_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestRaceDeferralParent.h" +#include "mozilla/_ipdltest/PTestRaceDeferralChild.h" + +namespace mozilla { +namespace _ipdltest { + +class TestRaceDeferralParent : + public PTestRaceDeferralParent +{ +public: + TestRaceDeferralParent(); + virtual ~TestRaceDeferralParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + void Test1(); + + virtual bool AnswerLose() override; + + virtual mozilla::ipc::RacyInterruptPolicy + MediateInterruptRace(const MessageInfo& parent, + const MessageInfo& child) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + + bool mProcessedLose; +}; + + +class TestRaceDeferralChild : + public PTestRaceDeferralChild +{ +public: + TestRaceDeferralChild(); + virtual ~TestRaceDeferralChild(); + +protected: + virtual bool RecvStartRace() override; + + virtual bool AnswerWin() override; + + virtual bool AnswerRpc() override; + + virtual mozilla::ipc::RacyInterruptPolicy + MediateInterruptRace(const MessageInfo& parent, + const MessageInfo& child) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestRaceDeferral_h diff --git a/ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp new file mode 100644 index 000000000..513b84f73 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp @@ -0,0 +1,119 @@ +#include "TestRacyInterruptReplies.h" + +#include "IPDLUnitTests.h" // fail etc. + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestRacyInterruptRepliesParent::TestRacyInterruptRepliesParent() : mReplyNum(0) +{ + MOZ_COUNT_CTOR(TestRacyInterruptRepliesParent); +} + +TestRacyInterruptRepliesParent::~TestRacyInterruptRepliesParent() +{ + MOZ_COUNT_DTOR(TestRacyInterruptRepliesParent); +} + +void +TestRacyInterruptRepliesParent::Main() +{ + int replyNum = -1; + if (!CallR_(&replyNum)) + fail("calling R()"); + + if (1 != replyNum) + fail("this should have been the first reply to R()"); + + if (!SendChildTest()) + fail("sending ChildStart"); +} + +bool +TestRacyInterruptRepliesParent::RecvA_() +{ + int replyNum = -1; + // this R() call races with the reply being generated by the other + // side to the R() call from Main(). This is a pretty nasty edge + // case for which one could argue we're breaking in-order message + // delivery, since this side will process the second reply to R() + // before the first. + if (!CallR_(&replyNum)) + fail("calling R()"); + + if (2 != replyNum) + fail("this should have been the second reply to R()"); + + return true; +} + +bool +TestRacyInterruptRepliesParent::Answer_R(int* replyNum) +{ + *replyNum = ++mReplyNum; + + if (1 == *replyNum) + if (!Send_A()) + fail("sending _A()"); + + return true; +} + +//----------------------------------------------------------------------------- +// child + +TestRacyInterruptRepliesChild::TestRacyInterruptRepliesChild() : mReplyNum(0) +{ + MOZ_COUNT_CTOR(TestRacyInterruptRepliesChild); +} + +TestRacyInterruptRepliesChild::~TestRacyInterruptRepliesChild() +{ + MOZ_COUNT_DTOR(TestRacyInterruptRepliesChild); +} + +bool +TestRacyInterruptRepliesChild::AnswerR_(int* replyNum) +{ + *replyNum = ++mReplyNum; + + if (1 == *replyNum) + SendA_(); + + return true; +} + +bool +TestRacyInterruptRepliesChild::RecvChildTest() +{ + int replyNum = -1; + if (!Call_R(&replyNum)) + fail("calling R()"); + + if (1 != replyNum) + fail("this should have been the first reply to R()"); + + Close(); + + return true; +} + +bool +TestRacyInterruptRepliesChild::Recv_A() +{ + int replyNum = -1; + + if (!Call_R(&replyNum)) + fail("calling _R()"); + + if (2 != replyNum) + fail("this should have been the second reply to R()"); + + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestRacyInterruptReplies.h b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.h new file mode 100644 index 000000000..94f514188 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.h @@ -0,0 +1,73 @@ +#ifndef mozilla__ipdltest_TestRacyInterruptReplies_h +#define mozilla__ipdltest_TestRacyInterruptReplies_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestRacyInterruptRepliesParent.h" +#include "mozilla/_ipdltest/PTestRacyInterruptRepliesChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestRacyInterruptRepliesParent : + public PTestRacyInterruptRepliesParent +{ +public: + TestRacyInterruptRepliesParent(); + virtual ~TestRacyInterruptRepliesParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool RecvA_() override; + + virtual bool Answer_R(int* replyNum) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + +private: + int mReplyNum; +}; + + +class TestRacyInterruptRepliesChild : + public PTestRacyInterruptRepliesChild +{ +public: + TestRacyInterruptRepliesChild(); + virtual ~TestRacyInterruptRepliesChild(); + +protected: + virtual bool AnswerR_(int* replyNum) override; + + virtual bool RecvChildTest() override; + + virtual bool Recv_A() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } + +private: + int mReplyNum; +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestRacyInterruptReplies_h diff --git a/ipc/ipdl/test/cxx/TestRacyReentry.cpp b/ipc/ipdl/test/cxx/TestRacyReentry.cpp new file mode 100644 index 000000000..00996cc39 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyReentry.cpp @@ -0,0 +1,84 @@ +#include "TestRacyReentry.h" + +#include "IPDLUnitTests.h" // fail etc. + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestRacyReentryParent::TestRacyReentryParent() : mRecvdE(false) +{ + MOZ_COUNT_CTOR(TestRacyReentryParent); +} + +TestRacyReentryParent::~TestRacyReentryParent() +{ + MOZ_COUNT_DTOR(TestRacyReentryParent); +} + +void +TestRacyReentryParent::Main() +{ + if (!SendStart()) + fail("sending Start"); + + if (!SendN()) + fail("sending N"); +} + +bool +TestRacyReentryParent::AnswerE() +{ + if (!mRecvdE) { + mRecvdE = true; + return true; + } + + if (!CallH()) + fail("calling H"); + + return true; +} + +//----------------------------------------------------------------------------- +// child + +TestRacyReentryChild::TestRacyReentryChild() +{ + MOZ_COUNT_CTOR(TestRacyReentryChild); +} + +TestRacyReentryChild::~TestRacyReentryChild() +{ + MOZ_COUNT_DTOR(TestRacyReentryChild); +} + +bool +TestRacyReentryChild::RecvStart() +{ + if (!CallE()) + fail("calling E"); + + Close(); + + return true; +} + +bool +TestRacyReentryChild::RecvN() +{ + if (!CallE()) + fail("calling E"); + return true; +} + +bool +TestRacyReentryChild::AnswerH() +{ + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestRacyReentry.h b/ipc/ipdl/test/cxx/TestRacyReentry.h new file mode 100644 index 000000000..f496ef7bf --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyReentry.h @@ -0,0 +1,67 @@ +#ifndef mozilla__ipdltest_TestRacyReentry_h +#define mozilla__ipdltest_TestRacyReentry_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestRacyReentryParent.h" +#include "mozilla/_ipdltest/PTestRacyReentryChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestRacyReentryParent : + public PTestRacyReentryParent +{ +public: + TestRacyReentryParent(); + virtual ~TestRacyReentryParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool AnswerE() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + + bool mRecvdE; +}; + + +class TestRacyReentryChild : + public PTestRacyReentryChild +{ +public: + TestRacyReentryChild(); + virtual ~TestRacyReentryChild(); + +protected: + virtual bool RecvStart() override; + + virtual bool RecvN() override; + + virtual bool AnswerH() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestRacyReentry_h diff --git a/ipc/ipdl/test/cxx/TestRacyUndefer.cpp b/ipc/ipdl/test/cxx/TestRacyUndefer.cpp new file mode 100644 index 000000000..8bdd804cc --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyUndefer.cpp @@ -0,0 +1,115 @@ +#include "base/basictypes.h" + +#include "TestRacyUndefer.h" + +#include "IPDLUnitTests.h" // fail etc. + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestRacyUndeferParent::TestRacyUndeferParent() +{ + MOZ_COUNT_CTOR(TestRacyUndeferParent); +} + +TestRacyUndeferParent::~TestRacyUndeferParent() +{ + MOZ_COUNT_DTOR(TestRacyUndeferParent); +} + +void +TestRacyUndeferParent::Main() +{ + if (!SendStart()) + fail("sending Start"); +} + +bool +TestRacyUndeferParent::AnswerSpam() +{ + static bool spammed = false; + static bool raced = false; + if (!spammed) { + spammed = true; + + if (!SendAwakenSpam()) + fail("sending AwakenSpam"); + } + else if (!raced) { + raced = true; + + if (!SendAwakenRaceWinTwice()) + fail("sending WinRaceTwice"); + + if (!CallRace()) + fail("calling Race1"); + } + return true; +} + +bool +TestRacyUndeferParent::AnswerRaceWinTwice() +{ + return true; +} + +bool +TestRacyUndeferParent::RecvDone() +{ + Close(); + return true; +} + + +//----------------------------------------------------------------------------- +// child + +TestRacyUndeferChild::TestRacyUndeferChild() +{ + MOZ_COUNT_CTOR(TestRacyUndeferChild); +} + +TestRacyUndeferChild::~TestRacyUndeferChild() +{ + MOZ_COUNT_DTOR(TestRacyUndeferChild); +} + +bool +TestRacyUndeferChild::RecvStart() +{ + if (!CallSpam()) + fail("calling Spam"); + + if (!SendDone()) + fail("sending Done"); + + return true; +} + +bool +TestRacyUndeferChild::RecvAwakenSpam() +{ + if (!CallSpam()) + fail("calling Spam"); + return true; +} + +bool +TestRacyUndeferChild::RecvAwakenRaceWinTwice() +{ + if (!CallRaceWinTwice()) + fail("calling RaceWinTwice"); + return true; +} + +bool +TestRacyUndeferChild::AnswerRace() +{ + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestRacyUndefer.h b/ipc/ipdl/test/cxx/TestRacyUndefer.h new file mode 100644 index 000000000..eee06a86c --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyUndefer.h @@ -0,0 +1,70 @@ +#ifndef mozilla__ipdltest_TestRacyUndefer_h +#define mozilla__ipdltest_TestRacyUndefer_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestRacyUndeferParent.h" +#include "mozilla/_ipdltest/PTestRacyUndeferChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestRacyUndeferParent : + public PTestRacyUndeferParent +{ +public: + TestRacyUndeferParent(); + virtual ~TestRacyUndeferParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool AnswerSpam() override; + + virtual bool AnswerRaceWinTwice() override; + + virtual bool RecvDone() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + + +class TestRacyUndeferChild : + public PTestRacyUndeferChild +{ +public: + TestRacyUndeferChild(); + virtual ~TestRacyUndeferChild(); + +protected: + virtual bool RecvStart() override; + + virtual bool RecvAwakenSpam() override; + virtual bool RecvAwakenRaceWinTwice() override; + + virtual bool AnswerRace() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestRacyUndefer_h diff --git a/ipc/ipdl/test/cxx/TestSanity.cpp b/ipc/ipdl/test/cxx/TestSanity.cpp new file mode 100644 index 000000000..64ffceed6 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSanity.cpp @@ -0,0 +1,75 @@ +#include "TestSanity.h" + +#include "IPDLUnitTests.h" // fail etc. + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestSanityParent::TestSanityParent() +{ + MOZ_COUNT_CTOR(TestSanityParent); +} + +TestSanityParent::~TestSanityParent() +{ + MOZ_COUNT_DTOR(TestSanityParent); +} + +void +TestSanityParent::Main() +{ + if (!SendPing(0, 0.5f, 0)) + fail("sending Ping"); +} + + +bool +TestSanityParent::RecvPong(const int& one, const float& zeroPtTwoFive, + const uint8_t&/*unused*/) +{ + if (1 != one) + fail("invalid argument `%d', should have been `1'", one); + + if (0.25f != zeroPtTwoFive) + fail("invalid argument `%g', should have been `0.25'", zeroPtTwoFive); + + Close(); + + return true; +} + + +//----------------------------------------------------------------------------- +// child + +TestSanityChild::TestSanityChild() +{ + MOZ_COUNT_CTOR(TestSanityChild); +} + +TestSanityChild::~TestSanityChild() +{ + MOZ_COUNT_DTOR(TestSanityChild); +} + +bool +TestSanityChild::RecvPing(const int& zero, const float& zeroPtFive, + const int8_t&/*unused*/) +{ + if (0 != zero) + fail("invalid argument `%d', should have been `0'", zero); + + if (0.5f != zeroPtFive) + fail("invalid argument `%g', should have been `0.5'", zeroPtFive); + + if (!SendPong(1, 0.25f, 0)) + fail("sending Pong"); + return true; +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestSanity.h b/ipc/ipdl/test/cxx/TestSanity.h new file mode 100644 index 000000000..6e5b8cb59 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSanity.h @@ -0,0 +1,63 @@ +#ifndef mozilla__ipdltest_TestSanity_h +#define mozilla__ipdltest_TestSanity_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestSanityParent.h" +#include "mozilla/_ipdltest/PTestSanityChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestSanityParent : + public PTestSanityParent +{ +public: + TestSanityParent(); + virtual ~TestSanityParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool RecvPong(const int& one, const float& zeroPtTwoFive, + const uint8_t& dummy) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + + +class TestSanityChild : + public PTestSanityChild +{ +public: + TestSanityChild(); + virtual ~TestSanityChild(); + +protected: + virtual bool RecvPing(const int& zero, const float& zeroPtFive, + const int8_t& dummy) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestSanity_h diff --git a/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp b/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp new file mode 100644 index 000000000..e8b2330ba --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp @@ -0,0 +1,63 @@ +#include "TestSelfManageRoot.h" + +#include "IPDLUnitTests.h" // fail etc. + +#define ASSERT(c) \ + do { \ + if (!(c)) \ + fail(#c); \ + } while (0) + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +void +TestSelfManageRootParent::Main() +{ + TestSelfManageParent* a = + static_cast<TestSelfManageParent*>(SendPTestSelfManageConstructor()); + if (!a) + fail("constructing PTestSelfManage"); + + ASSERT(1 == ManagedPTestSelfManageParent().Count()); + + TestSelfManageParent* aa = + static_cast<TestSelfManageParent*>(a->SendPTestSelfManageConstructor()); + if (!aa) + fail("constructing PTestSelfManage"); + + ASSERT(1 == ManagedPTestSelfManageParent().Count() && + 1 == a->ManagedPTestSelfManageParent().Count()); + + if (!PTestSelfManageParent::Send__delete__(aa)) + fail("destroying PTestSelfManage"); + ASSERT(Deletion == aa->mWhy && + 1 == ManagedPTestSelfManageParent().Count() && + 0 == a->ManagedPTestSelfManageParent().Count()); + delete aa; + + aa = + static_cast<TestSelfManageParent*>(a->SendPTestSelfManageConstructor()); + if (!aa) + fail("constructing PTestSelfManage"); + + ASSERT(1 == ManagedPTestSelfManageParent().Count() && + 1 == a->ManagedPTestSelfManageParent().Count()); + + if (!PTestSelfManageParent::Send__delete__(a)) + fail("destroying PTestSelfManage"); + ASSERT(Deletion == a->mWhy && + AncestorDeletion == aa->mWhy && + 0 == ManagedPTestSelfManageParent().Count() && + 0 == a->ManagedPTestSelfManageParent().Count()); + delete a; + delete aa; + + Close(); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestSelfManageRoot.h b/ipc/ipdl/test/cxx/TestSelfManageRoot.h new file mode 100644 index 000000000..2a94fa392 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSelfManageRoot.h @@ -0,0 +1,141 @@ +#ifndef mozilla__ipdltest_TestSelfManageRoot_h +#define mozilla__ipdltest_TestSelfManageRoot_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestSelfManageRootParent.h" +#include "mozilla/_ipdltest/PTestSelfManageRootChild.h" +#include "mozilla/_ipdltest/PTestSelfManageParent.h" +#include "mozilla/_ipdltest/PTestSelfManageChild.h" + + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// Parent side + +class TestSelfManageParent : + public PTestSelfManageParent +{ +public: + TestSelfManageParent() { + MOZ_COUNT_CTOR(TestSelfManageParent); + } + virtual ~TestSelfManageParent() { + MOZ_COUNT_DTOR(TestSelfManageParent); + } + + ActorDestroyReason mWhy; + +protected: + virtual PTestSelfManageParent* AllocPTestSelfManageParent() override { + return new TestSelfManageParent(); + } + + virtual bool DeallocPTestSelfManageParent(PTestSelfManageParent* a) override { + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override { + mWhy = why; + } +}; + +class TestSelfManageRootParent : + public PTestSelfManageRootParent +{ +public: + TestSelfManageRootParent() { + MOZ_COUNT_CTOR(TestSelfManageRootParent); + } + virtual ~TestSelfManageRootParent() { + MOZ_COUNT_DTOR(TestSelfManageRootParent); + } + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual PTestSelfManageParent* AllocPTestSelfManageParent() override { + return new TestSelfManageParent(); + } + + virtual bool DeallocPTestSelfManageParent(PTestSelfManageParent* a) override { + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + +//----------------------------------------------------------------------------- +// Child side + +class TestSelfManageChild : + public PTestSelfManageChild +{ +public: + TestSelfManageChild() { + MOZ_COUNT_CTOR(TestSelfManageChild); + } + virtual ~TestSelfManageChild() { + MOZ_COUNT_DTOR(TestSelfManageChild); + } + +protected: + virtual PTestSelfManageChild* AllocPTestSelfManageChild() override { + return new TestSelfManageChild(); + } + + virtual bool DeallocPTestSelfManageChild(PTestSelfManageChild* a) override { + delete a; + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override { } +}; + +class TestSelfManageRootChild : + public PTestSelfManageRootChild +{ +public: + TestSelfManageRootChild() { + MOZ_COUNT_CTOR(TestSelfManageRootChild); + } + virtual ~TestSelfManageRootChild() { + MOZ_COUNT_DTOR(TestSelfManageRootChild); + } + + void Main(); + +protected: + virtual PTestSelfManageChild* AllocPTestSelfManageChild() override { + return new TestSelfManageChild(); + } + + virtual bool DeallocPTestSelfManageChild(PTestSelfManageChild* a) override { + delete a; + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestSelfManageRoot_h diff --git a/ipc/ipdl/test/cxx/TestShmem.cpp b/ipc/ipdl/test/cxx/TestShmem.cpp new file mode 100644 index 000000000..1ff62bc3a --- /dev/null +++ b/ipc/ipdl/test/cxx/TestShmem.cpp @@ -0,0 +1,115 @@ +#include "TestShmem.h" + +#include "IPDLUnitTests.h" // fail etc. + + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// Parent + +void +TestShmemParent::Main() +{ + Shmem mem; + Shmem unsafe; + + size_t size = 12345; + if (!AllocShmem(size, SharedMemory::TYPE_BASIC, &mem)) + fail("can't alloc shmem"); + if (!AllocUnsafeShmem(size, SharedMemory::TYPE_BASIC, &unsafe)) + fail("can't alloc shmem"); + + if (mem.Size<char>() != size) + fail("shmem is wrong size: expected %lu, got %lu", + size, mem.Size<char>()); + if (unsafe.Size<char>() != size) + fail("shmem is wrong size: expected %lu, got %lu", + size, unsafe.Size<char>()); + + char* ptr = mem.get<char>(); + memcpy(ptr, "Hello!", sizeof("Hello!")); + + char* unsafeptr = unsafe.get<char>(); + memcpy(unsafeptr, "Hello!", sizeof("Hello!")); + + Shmem unsafecopy = unsafe; + if (!SendGive(mem, unsafe, size)) + fail("can't send Give()"); + + // uncomment the following line for a (nondeterministic) surprise! + //char c1 = *ptr; (void)c1; + + // uncomment the following line for a deterministic surprise! + //char c2 = *mem.get<char>(); (void)c2; + + // unsafe shmem gets rid of those checks + char uc1 = *unsafeptr; (void)uc1; + char uc2 = *unsafecopy.get<char>(); (void)uc2; +} + + +bool +TestShmemParent::RecvTake(Shmem&& mem, Shmem&& unsafe, + const size_t& expectedSize) +{ + if (mem.Size<char>() != expectedSize) + fail("expected shmem size %lu, but it has size %lu", + expectedSize, mem.Size<char>()); + if (unsafe.Size<char>() != expectedSize) + fail("expected shmem size %lu, but it has size %lu", + expectedSize, unsafe.Size<char>()); + + if (strcmp(mem.get<char>(), "And yourself!")) + fail("expected message was not written"); + if (strcmp(unsafe.get<char>(), "And yourself!")) + fail("expected message was not written"); + + if (!DeallocShmem(mem)) + fail("DeallocShmem"); + if (!DeallocShmem(unsafe)) + fail("DeallocShmem"); + + Close(); + + return true; +} + +//----------------------------------------------------------------------------- +// Child + +bool +TestShmemChild::RecvGive(Shmem&& mem, Shmem&& unsafe, const size_t& expectedSize) +{ + if (mem.Size<char>() != expectedSize) + fail("expected shmem size %lu, but it has size %lu", + expectedSize, mem.Size<char>()); + if (unsafe.Size<char>() != expectedSize) + fail("expected shmem size %lu, but it has size %lu", + expectedSize, unsafe.Size<char>()); + + if (strcmp(mem.get<char>(), "Hello!")) + fail("expected message was not written"); + if (strcmp(unsafe.get<char>(), "Hello!")) + fail("expected message was not written"); + + char* unsafeptr = unsafe.get<char>(); + + memcpy(mem.get<char>(), "And yourself!", sizeof("And yourself!")); + memcpy(unsafeptr, "And yourself!", sizeof("And yourself!")); + + Shmem unsafecopy = unsafe; + if (!SendTake(mem, unsafe, expectedSize)) + fail("can't send Take()"); + + // these checks also shouldn't fail in the child + char uc1 = *unsafeptr; (void)uc1; + char uc2 = *unsafecopy.get<char>(); (void)uc2; + + return true; +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestShmem.h b/ipc/ipdl/test/cxx/TestShmem.h new file mode 100644 index 000000000..d583fe978 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestShmem.h @@ -0,0 +1,66 @@ +#ifndef mozilla__ipdltest_TestShmem_h +#define mozilla__ipdltest_TestShmem_h + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestShmemParent.h" +#include "mozilla/_ipdltest/PTestShmemChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestShmemParent : + public PTestShmemParent +{ +public: + TestShmemParent() { } + virtual ~TestShmemParent() { } + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool RecvTake( + Shmem&& mem, + Shmem&& unsafe, + const size_t& expectedSize) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + + +class TestShmemChild : + public PTestShmemChild +{ +public: + TestShmemChild() { } + virtual ~TestShmemChild() { } + +protected: + virtual bool RecvGive( + Shmem&& mem, + Shmem&& unsafe, + const size_t& expectedSize) override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + +#endif // ifndef mozilla__ipdltest_TestShmem_h diff --git a/ipc/ipdl/test/cxx/TestShutdown.cpp b/ipc/ipdl/test/cxx/TestShutdown.cpp new file mode 100644 index 000000000..95a242bff --- /dev/null +++ b/ipc/ipdl/test/cxx/TestShutdown.cpp @@ -0,0 +1,235 @@ +#include "TestShutdown.h" + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// Parent side +void +TestShutdownParent::Main() +{ + if (!SendStart()) + fail("sending Start()"); +} + +void +TestShutdownParent::ActorDestroy(ActorDestroyReason why) +{ + if (AbnormalShutdown != why) + fail("should have ended test with crash!"); + + passed("ok"); + + QuitParent(); +} + +void +TestShutdownSubParent::ActorDestroy(ActorDestroyReason why) +{ + if (Manager()->ManagedPTestShutdownSubParent().Count() == 0) + fail("manager should still have managees!"); + + if (mExpectCrash && AbnormalShutdown != why) + fail("expected crash!"); + else if (!mExpectCrash && AbnormalShutdown == why) + fail("wasn't expecting crash!"); + + if (mExpectCrash && 0 == ManagedPTestShutdownSubsubParent().Count()) + fail("expected to *still* have kids"); +} + +void +TestShutdownSubsubParent::ActorDestroy(ActorDestroyReason why) +{ + if (Manager()->ManagedPTestShutdownSubsubParent().Count() == 0) + fail("manager should still have managees!"); + + if (mExpectParentDeleted && AncestorDeletion != why) + fail("expected ParentDeleted == why"); + else if (!mExpectParentDeleted && AncestorDeletion == why) + fail("wasn't expecting parent delete"); +} + +//----------------------------------------------------------------------------- +// Child side + +bool +TestShutdownChild::RecvStart() +{ + // test 1: alloc some actors and subactors, delete in + // managee-before-manager order + { + bool expectCrash = false, expectParentDeleted = false; + + PTestShutdownSubChild* c1 = + SendPTestShutdownSubConstructor(expectCrash); + if (!c1) + fail("problem sending ctor"); + + PTestShutdownSubChild* c2 = + SendPTestShutdownSubConstructor(expectCrash); + if (!c2) + fail("problem sending ctor"); + + PTestShutdownSubsubChild* c1s1 = + c1->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c1s1) + fail("problem sending ctor"); + PTestShutdownSubsubChild* c1s2 = + c1->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c1s2) + fail("problem sending ctor"); + + PTestShutdownSubsubChild* c2s1 = + c2->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c2s1) + fail("problem sending ctor"); + PTestShutdownSubsubChild* c2s2 = + c2->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c2s2) + fail("problem sending ctor"); + + if (!PTestShutdownSubsubChild::Send__delete__(c1s1)) + fail("problem sending dtor"); + if (!PTestShutdownSubsubChild::Send__delete__(c1s2)) + fail("problem sending dtor"); + if (!PTestShutdownSubsubChild::Send__delete__(c2s1)) + fail("problem sending dtor"); + if (!PTestShutdownSubsubChild::Send__delete__(c2s2)) + fail("problem sending dtor"); + + if (!c1->CallStackFrame()) + fail("problem creating dummy stack frame"); + if (!c2->CallStackFrame()) + fail("problem creating dummy stack frame"); + } + + // test 2: alloc some actors and subactors, delete managers first + { + bool expectCrash = false, expectParentDeleted = true; + + PTestShutdownSubChild* c1 = + SendPTestShutdownSubConstructor(expectCrash); + if (!c1) + fail("problem sending ctor"); + + PTestShutdownSubChild* c2 = + SendPTestShutdownSubConstructor(expectCrash); + if (!c2) + fail("problem sending ctor"); + + PTestShutdownSubsubChild* c1s1 = + c1->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c1s1) + fail("problem sending ctor"); + PTestShutdownSubsubChild* c1s2 = + c1->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c1s2) + fail("problem sending ctor"); + + PTestShutdownSubsubChild* c2s1 = + c2->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c2s1) + fail("problem sending ctor"); + PTestShutdownSubsubChild* c2s2 = + c2->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c2s2) + fail("problem sending ctor"); + + // delete parents without deleting kids + if (!c1->CallStackFrame()) + fail("problem creating dummy stack frame"); + if (!c2->CallStackFrame()) + fail("problem creating dummy stack frame"); + } + + // test 3: alloc some actors and subactors, then crash + { + bool expectCrash = true, expectParentDeleted = false; + + PTestShutdownSubChild* c1 = + SendPTestShutdownSubConstructor(expectCrash); + if (!c1) + fail("problem sending ctor"); + + PTestShutdownSubChild* c2 = + SendPTestShutdownSubConstructor(expectCrash); + if (!c2) + fail("problem sending ctor"); + + PTestShutdownSubsubChild* c1s1 = + c1->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c1s1) + fail("problem sending ctor"); + PTestShutdownSubsubChild* c1s2 = + c1->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c1s2) + fail("problem sending ctor"); + + PTestShutdownSubsubChild* c2s1 = + c2->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c2s1) + fail("problem sending ctor"); + PTestShutdownSubsubChild* c2s2 = + c2->SendPTestShutdownSubsubConstructor(expectParentDeleted); + if (!c2s2) + fail("problem sending ctor"); + + // make sure the ctors have been processed by the other side; + // the write end of the socket may temporarily be unwriteable + if (!SendSync()) + fail("can't synchronize with parent"); + + // "crash", but without tripping tinderbox assert/abort + // detectors + _exit(0); + } +} + +void +TestShutdownChild::ActorDestroy(ActorDestroyReason why) +{ + fail("hey wait ... we should have crashed!"); +} + +bool +TestShutdownSubChild::AnswerStackFrame() +{ + if (!PTestShutdownSubChild::Send__delete__(this)) + fail("problem sending dtor"); + + // WATCH OUT! |this| has just deleted + + return true; +} + +void +TestShutdownSubChild::ActorDestroy(ActorDestroyReason why) +{ + if (Manager()->ManagedPTestShutdownSubChild().Count() == 0) + fail("manager should still have managees!"); + + if (mExpectCrash && AbnormalShutdown != why) + fail("expected crash!"); + else if (!mExpectCrash && AbnormalShutdown == why) + fail("wasn't expecting crash!"); + + if (mExpectCrash && 0 == ManagedPTestShutdownSubsubChild().Count()) + fail("expected to *still* have kids"); +} + +void +TestShutdownSubsubChild::ActorDestroy(ActorDestroyReason why) +{ + if (Manager()->ManagedPTestShutdownSubsubChild().Count() == 0) + fail("manager should still have managees!"); + + if (mExpectParentDeleted && AncestorDeletion != why) + fail("expected ParentDeleted == why"); + else if (!mExpectParentDeleted && AncestorDeletion == why) + fail("wasn't expecting parent delete"); +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestShutdown.h b/ipc/ipdl/test/cxx/TestShutdown.h new file mode 100644 index 000000000..c79cbb8fc --- /dev/null +++ b/ipc/ipdl/test/cxx/TestShutdown.h @@ -0,0 +1,225 @@ +#ifndef mozilla__ipdltest_TestShutdown_h +#define mozilla__ipdltest_TestShutdown_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestShutdownParent.h" +#include "mozilla/_ipdltest/PTestShutdownChild.h" + +#include "mozilla/_ipdltest/PTestShutdownSubParent.h" +#include "mozilla/_ipdltest/PTestShutdownSubChild.h" + +#include "mozilla/_ipdltest/PTestShutdownSubsubParent.h" +#include "mozilla/_ipdltest/PTestShutdownSubsubChild.h" + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// Parent side + +class TestShutdownSubsubParent : + public PTestShutdownSubsubParent +{ +public: + explicit TestShutdownSubsubParent(bool expectParentDeleted) : + mExpectParentDeleted(expectParentDeleted) + { + } + + virtual ~TestShutdownSubsubParent() + { + } + +protected: + virtual void + ActorDestroy(ActorDestroyReason why) override; + +private: + bool mExpectParentDeleted; +}; + + +class TestShutdownSubParent : + public PTestShutdownSubParent +{ +public: + explicit TestShutdownSubParent(bool expectCrash) : + mExpectCrash(expectCrash), + mDeletedCount(0) + { + } + + virtual ~TestShutdownSubParent() + { + if (2 != mDeletedCount) + fail("managees outliving manager!"); + } + +protected: + virtual bool + AnswerStackFrame() override + { + return CallStackFrame(); + } + + virtual PTestShutdownSubsubParent* + AllocPTestShutdownSubsubParent(const bool& expectParentDelete) override + { + return new TestShutdownSubsubParent(expectParentDelete); + } + + virtual bool + DeallocPTestShutdownSubsubParent(PTestShutdownSubsubParent* actor) override + { + delete actor; + ++mDeletedCount; + return true; + } + + virtual void + ActorDestroy(ActorDestroyReason why) override; + +private: + bool mExpectCrash; + int mDeletedCount; +}; + + +class TestShutdownParent : + public PTestShutdownParent +{ +public: + TestShutdownParent() + { + } + virtual ~TestShutdownParent() + { + } + + static bool RunTestInProcesses() { return true; } + // FIXME/bug 703323 Could work if modified + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + virtual bool RecvSync() override { return true; } + + virtual PTestShutdownSubParent* + AllocPTestShutdownSubParent(const bool& expectCrash) override + { + return new TestShutdownSubParent(expectCrash); + } + + virtual bool + DeallocPTestShutdownSubParent(PTestShutdownSubParent* actor) override + { + delete actor; + return true; + } + + virtual void + ActorDestroy(ActorDestroyReason why) override; +}; + + +//----------------------------------------------------------------------------- +// Child side + +class TestShutdownSubsubChild : + public PTestShutdownSubsubChild +{ +public: + explicit TestShutdownSubsubChild(bool expectParentDeleted) : + mExpectParentDeleted(expectParentDeleted) + { + } + virtual ~TestShutdownSubsubChild() + { + } + +protected: + virtual void + ActorDestroy(ActorDestroyReason why) override; + +private: + bool mExpectParentDeleted; +}; + + +class TestShutdownSubChild : + public PTestShutdownSubChild +{ +public: + explicit TestShutdownSubChild(bool expectCrash) : mExpectCrash(expectCrash) + { + } + + virtual ~TestShutdownSubChild() + { + } + +protected: + virtual bool AnswerStackFrame() override; + + virtual PTestShutdownSubsubChild* + AllocPTestShutdownSubsubChild(const bool& expectParentDelete) override + { + return new TestShutdownSubsubChild(expectParentDelete); + } + + virtual bool + DeallocPTestShutdownSubsubChild(PTestShutdownSubsubChild* actor) override + { + delete actor; + return true; + } + + virtual void + ActorDestroy(ActorDestroyReason why) override; + +private: + bool mExpectCrash; +}; + + +class TestShutdownChild : + public PTestShutdownChild +{ +public: + TestShutdownChild() + { + } + virtual ~TestShutdownChild() + { + } + +protected: + virtual bool + RecvStart(); + + virtual PTestShutdownSubChild* + AllocPTestShutdownSubChild( + const bool& expectCrash) override + { + return new TestShutdownSubChild(expectCrash); + } + + virtual bool + DeallocPTestShutdownSubChild(PTestShutdownSubChild* actor) override + { + delete actor; + return true; + } + + virtual void + ActorDestroy(ActorDestroyReason why) override; +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestShutdown_h diff --git a/ipc/ipdl/test/cxx/TestStackHooks.cpp b/ipc/ipdl/test/cxx/TestStackHooks.cpp new file mode 100644 index 000000000..b4181985c --- /dev/null +++ b/ipc/ipdl/test/cxx/TestStackHooks.cpp @@ -0,0 +1,168 @@ +#include "TestStackHooks.h" + +#include "base/task.h" +#include "IPDLUnitTests.h" // fail etc. + + + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestStackHooksParent::TestStackHooksParent() : + mOnStack(false), mIncallDepth(0) +{ + MOZ_COUNT_CTOR(TestStackHooksParent); +} + +TestStackHooksParent::~TestStackHooksParent() +{ + MOZ_COUNT_DTOR(TestStackHooksParent); +} + +void +TestStackHooksParent::Main() +{ + if (!SendStart()) + fail("sending Start()"); +} + + +bool +TestStackHooksParent::AnswerStackFrame() +{ + if (!mOnStack) + fail("not on C++ stack?!"); + + if (!CallStackFrame()) + fail("calling StackFrame()"); + + if (!mOnStack) + fail("not on C++ stack?!"); + + if (1 != mIncallDepth) + fail("missed EnteredCall or ExitedCall hook"); + + return true; +} + +//----------------------------------------------------------------------------- +// child + +TestStackHooksChild::TestStackHooksChild() : + mOnStack(false), + mEntered(0), + mExited(0), + mIncallDepth(0) +{ + MOZ_COUNT_CTOR(TestStackHooksChild); +} + +TestStackHooksChild::~TestStackHooksChild() +{ + MOZ_COUNT_DTOR(TestStackHooksChild); +} + +namespace { +void RunTestsFn() { + static_cast<TestStackHooksChild*>(gChildActor)->RunTests(); +} +} + +bool +TestStackHooksChild::RecvStart() +{ + if (!mOnStack) + fail("missed stack notification"); + + if (0 != mIncallDepth) + fail("EnteredCall/ExitedCall malfunction"); + + // kick off tests from a runnable so that we can start with + // MessageChannel code on the C++ stack + MessageLoop::current()->PostTask(NewRunnableFunction(RunTestsFn)); + + return true; +} + +bool +TestStackHooksChild::AnswerStackFrame() +{ + if (!mOnStack) + fail("missed stack notification"); + + if (1 != mIncallDepth) + fail("missed EnteredCall or ExitedCall hook"); + + if (PTestStackHooks::TEST4_3 == state()) { + if (!SendAsync()) + fail("sending Async()"); + } + else if (PTestStackHooks::TEST5_3 == state()) { + if (!SendSync()) + fail("sending Sync()"); + } + else { + fail("unexpected state"); + } + + if (!mOnStack) + fail("bad stack exit notification"); + + return true; +} + +void +TestStackHooksChild::RunTests() +{ + // 1 because of RecvStart() + if (1 != mEntered) + fail("missed stack notification"); + if (mOnStack) + fail("spurious stack notification"); + if (0 != mIncallDepth) + fail("EnteredCall/ExitedCall malfunction"); + + if (!SendAsync()) + fail("sending Async()"); + if (mOnStack) + fail("spurious stack notification"); + if (0 != mIncallDepth) + fail("EnteredCall/ExitedCall malfunction"); + if (2 != mEntered) + fail("missed stack notification"); + + if (!SendSync()) + fail("sending Sync()"); + if (mOnStack) + fail("spurious stack notification"); + if (0 != mIncallDepth) + fail("EnteredCall/ExitedCall malfunction"); + if (3 != mEntered) + fail("missed stack notification"); + + if (!CallRpc()) + fail("calling RPC()"); + if (mOnStack) + fail("spurious stack notification"); + if (0 != mIncallDepth) + fail("EnteredCall/ExitedCall malfunction"); + if (4 != mEntered) + fail("missed stack notification"); + + if (!CallStackFrame()) + fail("calling StackFrame()"); + if (mOnStack) + fail("spurious stack notification"); + if (0 != mIncallDepth) + fail("EnteredCall/ExitedCall malfunction"); + if (5 != mEntered) + fail("missed stack notification"); + + Close(); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestStackHooks.h b/ipc/ipdl/test/cxx/TestStackHooks.h new file mode 100644 index 000000000..c26d5f937 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestStackHooks.h @@ -0,0 +1,130 @@ +#ifndef mozilla__ipdltest_TestStackHooks_h +#define mozilla__ipdltest_TestStackHooks_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestStackHooksParent.h" +#include "mozilla/_ipdltest/PTestStackHooksChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestStackHooksParent : + public PTestStackHooksParent +{ +public: + TestStackHooksParent(); + virtual ~TestStackHooksParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool RecvAsync() override { + if (!mOnStack) + fail("not on C++ stack?!"); + return true; + } + + virtual bool RecvSync() override { + if (!mOnStack) + fail("not on C++ stack?!"); + return true; + } + + virtual bool AnswerRpc() override { + if (!mOnStack) + fail("not on C++ stack?!"); + return true; + } + + virtual bool AnswerStackFrame() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + + virtual void EnteredCxxStack() override { + mOnStack = true; + } + virtual void ExitedCxxStack() override { + mOnStack = false; + } + + virtual void EnteredCall() override { + ++mIncallDepth; + } + virtual void ExitedCall() override { + --mIncallDepth; + } + +private: + bool mOnStack; + int mIncallDepth; +}; + + +class TestStackHooksChild : + public PTestStackHooksChild +{ +public: + TestStackHooksChild(); + virtual ~TestStackHooksChild(); + + void RunTests(); + +protected: + virtual bool RecvStart() override; + + virtual bool AnswerStackFrame() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + + if (mEntered != mExited) + fail("unbalanced enter/exit notifications"); + + if (mOnStack) + fail("computing mOnStack went awry; should have failed above assertion"); + + QuitChild(); + } + + virtual void EnteredCxxStack() override { + ++mEntered; + mOnStack = true; + } + virtual void ExitedCxxStack() override { + ++mExited; + mOnStack = false; + } + + virtual void EnteredCall() override { + ++mIncallDepth; + } + virtual void ExitedCall() override { + --mIncallDepth; + } + +private: + bool mOnStack; + int mEntered; + int mExited; + int mIncallDepth; +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestStackHooks_h diff --git a/ipc/ipdl/test/cxx/TestSyncError.cpp b/ipc/ipdl/test/cxx/TestSyncError.cpp new file mode 100644 index 000000000..b9d8ec938 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncError.cpp @@ -0,0 +1,61 @@ +#include "TestSyncError.h" + +#include "IPDLUnitTests.h" // fail etc. + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestSyncErrorParent::TestSyncErrorParent() +{ + MOZ_COUNT_CTOR(TestSyncErrorParent); +} + +TestSyncErrorParent::~TestSyncErrorParent() +{ + MOZ_COUNT_DTOR(TestSyncErrorParent); +} + +void +TestSyncErrorParent::Main() +{ + if (!SendStart()) + fail("sending Start"); +} + +bool +TestSyncErrorParent::RecvError() +{ + return false; +} + + +//----------------------------------------------------------------------------- +// child + +TestSyncErrorChild::TestSyncErrorChild() +{ + MOZ_COUNT_CTOR(TestSyncErrorChild); +} + +TestSyncErrorChild::~TestSyncErrorChild() +{ + MOZ_COUNT_DTOR(TestSyncErrorChild); +} + +bool +TestSyncErrorChild::RecvStart() +{ + if (SendError()) + fail("Error() should have return false"); + + Close(); + + return true; +} + + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestSyncError.h b/ipc/ipdl/test/cxx/TestSyncError.h new file mode 100644 index 000000000..c39402a87 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncError.h @@ -0,0 +1,71 @@ +#ifndef mozilla__ipdltest_TestSyncError_h +#define mozilla__ipdltest_TestSyncError_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestSyncErrorParent.h" +#include "mozilla/_ipdltest/PTestSyncErrorChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestSyncErrorParent : + public PTestSyncErrorParent +{ +public: + TestSyncErrorParent(); + virtual ~TestSyncErrorParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool RecvError() override; + + virtual void ProcessingError(Result aCode, const char* aReason) override + { + // Ignore errors + } + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + + +class TestSyncErrorChild : + public PTestSyncErrorChild +{ +public: + TestSyncErrorChild(); + virtual ~TestSyncErrorChild(); + +protected: + virtual bool RecvStart() override; + + virtual void ProcessingError(Result aCode, const char* aReason) override + { + // Ignore errors + } + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestSyncError_h diff --git a/ipc/ipdl/test/cxx/TestSyncHang.cpp b/ipc/ipdl/test/cxx/TestSyncHang.cpp new file mode 100644 index 000000000..62b9ae723 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncHang.cpp @@ -0,0 +1,70 @@ +#include "TestSyncHang.h" +#include "base/task.h" +#include "mozilla/ipc/GeckoChildProcessHost.h" + +#include "IPDLUnitTests.h" // fail etc. + +using std::vector; +using std::string; + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +mozilla::ipc::GeckoChildProcessHost* gSyncHangSubprocess; + +TestSyncHangParent::TestSyncHangParent() +{ + MOZ_COUNT_CTOR(TestSyncHangParent); +} + +TestSyncHangParent::~TestSyncHangParent() +{ + MOZ_COUNT_DTOR(TestSyncHangParent); +} + +void +DeleteSyncHangSubprocess(MessageLoop* uiLoop) +{ + delete gSyncHangSubprocess; +} + +void +DeferredSyncHangParentShutdown() +{ + // ping to DeleteSubprocess + XRE_GetIOMessageLoop()->PostTask( + NewRunnableFunction(DeleteSyncHangSubprocess, MessageLoop::current())); +} + +void +TestSyncHangParent::Main() +{ + vector<string> args; + args.push_back("fake/path"); + gSyncHangSubprocess = new mozilla::ipc::GeckoChildProcessHost(GeckoProcessType_Plugin); + bool launched = gSyncHangSubprocess->SyncLaunch(args, 2); + if (launched) + fail("Calling SyncLaunch with an invalid path should return false"); + + MessageLoop::current()->PostTask(NewRunnableFunction(DeferredSyncHangParentShutdown)); + Close(); +} + +//----------------------------------------------------------------------------- +// child + +TestSyncHangChild::TestSyncHangChild() +{ + MOZ_COUNT_CTOR(TestSyncHangChild); +} + +TestSyncHangChild::~TestSyncHangChild() +{ + MOZ_COUNT_DTOR(TestSyncHangChild); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestSyncHang.h b/ipc/ipdl/test/cxx/TestSyncHang.h new file mode 100644 index 000000000..87533d661 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncHang.h @@ -0,0 +1,58 @@ +#ifndef mozilla__ipdltest_TestSyncHang_h +#define mozilla__ipdltest_TestSyncHang_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestSyncHangParent.h" +#include "mozilla/_ipdltest/PTestSyncHangChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestSyncHangParent : + public PTestSyncHangParent +{ +public: + TestSyncHangParent(); + virtual ~TestSyncHangParent(); + + static bool RunTestInProcesses() { return true; } + // FIXME/bug 703323 Could work if modified + static bool RunTestInThreads() { return false; } + + void Main(); + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + + +class TestSyncHangChild : + public PTestSyncHangChild +{ +public: + TestSyncHangChild(); + virtual ~TestSyncHangChild(); + +protected: + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestSyncHang_h diff --git a/ipc/ipdl/test/cxx/TestSyncWakeup.cpp b/ipc/ipdl/test/cxx/TestSyncWakeup.cpp new file mode 100644 index 000000000..cce0ca34f --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncWakeup.cpp @@ -0,0 +1,134 @@ +#if defined(OS_POSIX) +#include <unistd.h> // sleep() +#endif + +#include "TestSyncWakeup.h" + +#include "IPDLUnitTests.h" // fail etc. + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestSyncWakeupParent::TestSyncWakeupParent() +{ + MOZ_COUNT_CTOR(TestSyncWakeupParent); +} + +TestSyncWakeupParent::~TestSyncWakeupParent() +{ + MOZ_COUNT_DTOR(TestSyncWakeupParent); +} + +void +TestSyncWakeupParent::Main() +{ + if (!SendStart()) + fail("sending Start()"); +} + +bool +TestSyncWakeupParent::AnswerStackFrame() +{ + if (!CallStackFrame()) + fail("calling StackFrame()"); + return true; +} + +bool +TestSyncWakeupParent::RecvSync1() +{ + if (!SendNote1()) + fail("sending Note1()"); + + // XXX ugh ... need to ensure that the async message and sync + // reply come in "far enough" apart that this test doesn't pass on + // accident +#if defined(OS_POSIX) + // NB: can't use PR_Sleep (i.e. Sleep() on windows) because it's + // only spec'd to block the current thread, not the current + // process. We need the IO thread to sleep as well. + puts(" (sleeping for 5 seconds. sorry!)"); + sleep(5); +#endif + + return true; +} + +bool +TestSyncWakeupParent::RecvSync2() +{ + if (!SendNote2()) + fail("sending Note2()"); + +#if defined(OS_POSIX) + // see above + sleep(5); + puts(" (sleeping for 5 seconds. sorry!)"); +#endif + + return true; +} + +//----------------------------------------------------------------------------- +// child + +TestSyncWakeupChild::TestSyncWakeupChild() : mDone(false) +{ + MOZ_COUNT_CTOR(TestSyncWakeupChild); +} + +TestSyncWakeupChild::~TestSyncWakeupChild() +{ + MOZ_COUNT_DTOR(TestSyncWakeupChild); +} + +bool +TestSyncWakeupChild::RecvStart() +{ + // First test: the parent fires back an async message while + // replying to a sync one + if (!SendSync1()) + fail("sending Sync()"); + + // drop back into the event loop to get Note1(), then kick off the + // second test + return true; +} + +bool +TestSyncWakeupChild::RecvNote1() +{ + // Second test: the parent fires back an async message while + // replying to a sync one, with a frame on the RPC stack + if (!CallStackFrame()) + fail("calling StackFrame()"); + + if (!mDone) + fail("should have received Note2()!"); + + Close(); + + return true; +} + +bool +TestSyncWakeupChild::AnswerStackFrame() +{ + if (!SendSync2()) + fail("sending Sync()"); + + return true; +} + +bool +TestSyncWakeupChild::RecvNote2() +{ + mDone = true; + return true; +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestSyncWakeup.h b/ipc/ipdl/test/cxx/TestSyncWakeup.h new file mode 100644 index 000000000..dac3f4312 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncWakeup.h @@ -0,0 +1,74 @@ +#ifndef mozilla__ipdltest_TestSyncWakeup_h +#define mozilla__ipdltest_TestSyncWakeup_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestSyncWakeupParent.h" +#include "mozilla/_ipdltest/PTestSyncWakeupChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestSyncWakeupParent : + public PTestSyncWakeupParent +{ +public: + TestSyncWakeupParent(); + virtual ~TestSyncWakeupParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + +protected: + virtual bool AnswerStackFrame() override; + + virtual bool RecvSync1() override; + + virtual bool RecvSync2() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + + +class TestSyncWakeupChild : + public PTestSyncWakeupChild +{ +public: + TestSyncWakeupChild(); + virtual ~TestSyncWakeupChild(); + +protected: + virtual bool RecvStart() override; + + virtual bool RecvNote1() override; + + virtual bool AnswerStackFrame() override; + + virtual bool RecvNote2() override; + + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } + +private: + bool mDone; +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestSyncWakeup_h diff --git a/ipc/ipdl/test/cxx/TestUrgency.cpp b/ipc/ipdl/test/cxx/TestUrgency.cpp new file mode 100644 index 000000000..b9b05bcea --- /dev/null +++ b/ipc/ipdl/test/cxx/TestUrgency.cpp @@ -0,0 +1,162 @@ +#include "TestUrgency.h" + +#include "IPDLUnitTests.h" // fail etc. +#if defined(OS_POSIX) +#include <unistd.h> +#else +#include <windows.h> +#endif + +namespace mozilla { +namespace _ipdltest { + +#if defined(OS_POSIX) +static void Sleep(int ms) +{ + sleep(ms / 1000); +} +#endif + +//----------------------------------------------------------------------------- +// parent + +TestUrgencyParent::TestUrgencyParent() + : inreply_(false) +{ + MOZ_COUNT_CTOR(TestUrgencyParent); +} + +TestUrgencyParent::~TestUrgencyParent() +{ + MOZ_COUNT_DTOR(TestUrgencyParent); +} + +void +TestUrgencyParent::Main() +{ + if (!SendStart()) + fail("sending Start"); +} + +bool +TestUrgencyParent::RecvTest1(uint32_t *value) +{ + if (!SendReply1(value)) + fail("sending Reply1"); + if (*value != 99) + fail("bad value"); + return true; +} + +bool +TestUrgencyParent::RecvTest2() +{ + uint32_t value; + inreply_ = true; + if (!SendReply2(&value)) + fail("sending Reply2"); + inreply_ = false; + if (value != 500) + fail("bad value"); + return true; +} + +bool +TestUrgencyParent::RecvTest3(uint32_t *value) +{ + if (inreply_) + fail("nested non-urgent on top of urgent rpc"); + *value = 1000; + return true; +} + +bool +TestUrgencyParent::RecvFinalTest_Begin() +{ + return true; +} + +//----------------------------------------------------------------------------- +// child + +enum { + kFirstTestBegin = 1, + kFirstTestGotReply, + kSecondTestBegin, + kSecondTestGotReply, +}; + +bool +TestUrgencyChild::RecvStart() +{ + uint32_t result; + + // Send a synchronous message, expect to get an urgent message while + // blocked. + test_ = kFirstTestBegin; + if (!SendTest1(&result)) + fail("calling SendTest1"); + if (result != 99) + fail("bad result in RecvStart"); + if (test_ != kFirstTestGotReply) + fail("never received urgent message"); + + // Initiate the next test by sending an asynchronous message, then becoming + // blocked. This tests that the urgent message is still delivered properly, + // and that the parent does not try to service the sync + test_ = kSecondTestBegin; + if (!SendTest2()) + fail("calling SendTest2"); + if (!SendTest3(&result)) + fail("calling SendTest3"); + if (test_ != kSecondTestGotReply) + fail("never received urgent message #2"); + if (result != 1000) + fail("wrong value from test3"); + + if (!SendFinalTest_Begin()) + fail("Final test should have succeeded"); + + Close(); + + return true; +} + +bool +TestUrgencyChild::RecvReply1(uint32_t *reply) +{ + if (test_ != kFirstTestBegin) + fail("wrong test # in RecvReply1"); + + *reply = 99; + test_ = kFirstTestGotReply; + return true; +} + +bool +TestUrgencyChild::RecvReply2(uint32_t *reply) +{ + if (test_ != kSecondTestBegin) + fail("wrong test # in RecvReply2"); + + // sleep for 5 seconds so the parent process tries to deliver more messages. + Sleep(5000); + + *reply = 500; + test_ = kSecondTestGotReply; + return true; +} + +TestUrgencyChild::TestUrgencyChild() + : test_(0) +{ + MOZ_COUNT_CTOR(TestUrgencyChild); +} + +TestUrgencyChild::~TestUrgencyChild() +{ + MOZ_COUNT_DTOR(TestUrgencyChild); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestUrgency.h b/ipc/ipdl/test/cxx/TestUrgency.h new file mode 100644 index 000000000..07a31b1c1 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestUrgency.h @@ -0,0 +1,72 @@ +#ifndef mozilla__ipdltest_TestUrgency_h +#define mozilla__ipdltest_TestUrgency_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestUrgencyParent.h" +#include "mozilla/_ipdltest/PTestUrgencyChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestUrgencyParent : + public PTestUrgencyParent +{ +public: + TestUrgencyParent(); + virtual ~TestUrgencyParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + + bool RecvTest1(uint32_t *value); + bool RecvTest2(); + bool RecvTest3(uint32_t *value); + bool RecvTest4_Begin(); + bool RecvTest4_NestedSync(); + bool RecvFinalTest_Begin(); + + bool ShouldContinueFromReplyTimeout() override + { + return false; + } + virtual void ActorDestroy(ActorDestroyReason why) override + { + passed("ok"); + QuitParent(); + } + +private: + bool inreply_; +}; + + +class TestUrgencyChild : + public PTestUrgencyChild +{ +public: + TestUrgencyChild(); + virtual ~TestUrgencyChild(); + + bool RecvStart(); + bool RecvReply1(uint32_t *reply); + bool RecvReply2(uint32_t *reply); + + virtual void ActorDestroy(ActorDestroyReason why) override + { + QuitChild(); + } + +private: + uint32_t test_; +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestUrgency_h diff --git a/ipc/ipdl/test/cxx/TestUrgentHangs.cpp b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp new file mode 100644 index 000000000..b798ae18d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + */ +#include "TestUrgentHangs.h" + +#include "IPDLUnitTests.h" // fail etc. +#include "prthread.h" +#if defined(OS_POSIX) +#include <unistd.h> +#else +#include <windows.h> +#endif + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +TestUrgentHangsParent::TestUrgentHangsParent() + : mInnerCount(0), + mInnerUrgentCount(0) +{ + MOZ_COUNT_CTOR(TestUrgentHangsParent); +} + +TestUrgentHangsParent::~TestUrgentHangsParent() +{ + MOZ_COUNT_DTOR(TestUrgentHangsParent); +} + +void +TestUrgentHangsParent::Main() +{ + SetReplyTimeoutMs(1000); + + // Should succeed despite the nested sleep call because the content process + // responded to the transaction. + if (!SendTest1_1()) + fail("sending Test1_1"); + + // Fails with a timeout. + if (SendTest2()) + fail("sending Test2"); + + // Also fails since we haven't gotten a response for Test2 yet. + if (SendTest3()) + fail("sending Test3"); + + // Do a second round of testing once the reply to Test2 comes back. + MessageLoop::current()->PostDelayedTask( + NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::SecondStage), + 3000); +} + +void +TestUrgentHangsParent::SecondStage() +{ + // Send an async message that waits 2 seconds and then sends a sync message + // (which should be processed). + if (!SendTest4()) + fail("sending Test4"); + + // Send a sync message that will time out because the child is waiting + // inside RecvTest4. + if (SendTest4_1()) + fail("sending Test4_1"); + + MessageLoop::current()->PostDelayedTask( + NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::ThirdStage), + 3000); +} + +void +TestUrgentHangsParent::ThirdStage() +{ + // The third stage does the same thing as the second stage except that the + // child sends an urgent message to us. In this case, we actually answer + // that message unconditionally. + + // Send an async message that waits 2 seconds and then sends a sync message + // (which should be processed). + if (!SendTest5()) + fail("sending Test5"); + + // Send a sync message that will time out because the child is waiting + // inside RecvTest5. + if (SendTest5_1()) + fail("sending Test5_1"); + + // Close the channel after the child finishes its work in RecvTest5. + MessageLoop::current()->PostDelayedTask( + NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::Close), + 3000); +} + +bool +TestUrgentHangsParent::RecvTest1_2() +{ + if (!SendTest1_3()) + fail("sending Test1_3"); + return true; +} + +bool +TestUrgentHangsParent::RecvTestInner() +{ + mInnerCount++; + return true; +} + +bool +TestUrgentHangsParent::RecvTestInnerUrgent() +{ + mInnerUrgentCount++; + return true; +} + +//----------------------------------------------------------------------------- +// child + +bool +TestUrgentHangsChild::RecvTest1_1() +{ + if (!SendTest1_2()) + fail("sending Test1_2"); + + return true; +} + +bool +TestUrgentHangsChild::RecvTest1_3() +{ + PR_Sleep(PR_SecondsToInterval(2)); + + return true; +} + +bool +TestUrgentHangsChild::RecvTest2() +{ + PR_Sleep(PR_SecondsToInterval(2)); + + // Should fail because of the timeout. + if (SendTestInner()) + fail("sending TestInner"); + + return true; +} + +bool +TestUrgentHangsChild::RecvTest3() +{ + fail("RecvTest3 should never be called"); + return true; +} + +bool +TestUrgentHangsChild::RecvTest4() +{ + PR_Sleep(PR_SecondsToInterval(2)); + + // This won't fail because we should handle Test4_1 here before actually + // sending TestInner to the parent. + if (!SendTestInner()) + fail("sending TestInner"); + + return true; +} + +bool +TestUrgentHangsChild::RecvTest4_1() +{ + // This should fail because Test4_1 timed out and hasn't gotten a response + // yet. + if (SendTestInner()) + fail("sending TestInner"); + + return true; +} + +bool +TestUrgentHangsChild::RecvTest5() +{ + PR_Sleep(PR_SecondsToInterval(2)); + + // This message will actually be handled by the parent even though it's in + // the timeout state. + if (!SendTestInnerUrgent()) + fail("sending TestInner"); + + return true; +} + +bool +TestUrgentHangsChild::RecvTest5_1() +{ + // This message will actually be handled by the parent even though it's in + // the timeout state. + if (!SendTestInnerUrgent()) + fail("sending TestInner"); + + return true; +} + +TestUrgentHangsChild::TestUrgentHangsChild() +{ + MOZ_COUNT_CTOR(TestUrgentHangsChild); +} + +TestUrgentHangsChild::~TestUrgentHangsChild() +{ + MOZ_COUNT_DTOR(TestUrgentHangsChild); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestUrgentHangs.h b/ipc/ipdl/test/cxx/TestUrgentHangs.h new file mode 100644 index 000000000..bbe63d11e --- /dev/null +++ b/ipc/ipdl/test/cxx/TestUrgentHangs.h @@ -0,0 +1,79 @@ +#ifndef mozilla__ipdltest_TestUrgentHangs_h +#define mozilla__ipdltest_TestUrgentHangs_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestUrgentHangsParent.h" +#include "mozilla/_ipdltest/PTestUrgentHangsChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestUrgentHangsParent : + public PTestUrgentHangsParent +{ +public: + TestUrgentHangsParent(); + virtual ~TestUrgentHangsParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + void SecondStage(); + void ThirdStage(); + + bool RecvTest1_2(); + bool RecvTestInner(); + bool RecvTestInnerUrgent(); + + bool ShouldContinueFromReplyTimeout() override + { + return false; + } + virtual void ActorDestroy(ActorDestroyReason why) override + { + if (mInnerCount != 1) { + fail("wrong mInnerCount"); + } + if (mInnerUrgentCount != 2) { + fail("wrong mInnerUrgentCount"); + } + passed("ok"); + QuitParent(); + } + +private: + size_t mInnerCount, mInnerUrgentCount; +}; + + +class TestUrgentHangsChild : + public PTestUrgentHangsChild +{ +public: + TestUrgentHangsChild(); + virtual ~TestUrgentHangsChild(); + + bool RecvTest1_1(); + bool RecvTest1_3(); + bool RecvTest2(); + bool RecvTest3(); + bool RecvTest4(); + bool RecvTest4_1(); + bool RecvTest5(); + bool RecvTest5_1(); + + virtual void ActorDestroy(ActorDestroyReason why) override + { + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestUrgentHangs_h diff --git a/ipc/ipdl/test/cxx/app/Makefile.in b/ipc/ipdl/test/cxx/app/Makefile.in new file mode 100644 index 000000000..3738d44d3 --- /dev/null +++ b/ipc/ipdl/test/cxx/app/Makefile.in @@ -0,0 +1,5 @@ +# 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/. + +NSDISTMODE = copy diff --git a/ipc/ipdl/test/cxx/app/TestIPDL.cpp b/ipc/ipdl/test/cxx/app/TestIPDL.cpp new file mode 100644 index 000000000..a34cca080 --- /dev/null +++ b/ipc/ipdl/test/cxx/app/TestIPDL.cpp @@ -0,0 +1,20 @@ +/* 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/. */ + +#include "nsXULAppAPI.h" + +#if defined(XP_WIN) +#include <windows.h> +#include "nsWindowsWMain.cpp" +#endif + +int +main(int argc, char** argv) +{ + // the first argument specifies which IPDL test case/suite to load + if (argc < 2) + return 1; + + return XRE_RunIPDLTest(argc, argv); +} diff --git a/ipc/ipdl/test/cxx/app/moz.build b/ipc/ipdl/test/cxx/app/moz.build new file mode 100644 index 000000000..d464e78a6 --- /dev/null +++ b/ipc/ipdl/test/cxx/app/moz.build @@ -0,0 +1,20 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=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/. + +GeckoProgram('ipdlunittest', linkage='dependent') + +SOURCES += [ + 'TestIPDL.cpp', +] +include('/ipc/chromium/chromium-config.mozbuild') + +LOCAL_INCLUDES += [ + '/toolkit/xre', + '/xpcom/base', +] + +if CONFIG['_MSC_VER']: + WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup'] diff --git a/ipc/ipdl/test/cxx/genIPDLUnitTests.py b/ipc/ipdl/test/cxx/genIPDLUnitTests.py new file mode 100644 index 000000000..24026451b --- /dev/null +++ b/ipc/ipdl/test/cxx/genIPDLUnitTests.py @@ -0,0 +1,141 @@ +# 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 string, sys + +def usage(): + print >>sys.stderr, """ +%s template_file -t unit_tests... -e extra_protocols... + + TEMPLATE_FILE is used to generate to generate the unit-tester .cpp + UNIT_TESTS are the top-level protocols defining unit tests + EXTRA_PROTOCOLS are top-level protocols for subprocesses that can be + spawned in tests but are not unit tests in and of + themselves +"""% (sys.argv[0]) + sys.exit(1) + +def main(argv): + template = argv[1] + + if argv[2] != '-t': usage() + i = 3 + unittests = [] + while argv[i] != '-e': + unittests.append(argv[i]) + i += 1 + + extras = argv[(i+1):] + + includes = '\n'.join([ + '#include "%s.h"'% (t) for t in unittests ]) + + + enum_values = '\n'.join([ + ' %s,'% (t) for t in unittests+extras ]) + last_enum = unittests[-1] + + + string_to_enums = '\n'.join([ + ''' else if (!strcmp(aString, "%s")) + return %s;'''% (t, t) for t in unittests+extras ]) + + enum_to_strings = '\n'.join([ + ''' case %s: + return "%s";'''%(t, t) for t in unittests+extras ]) + + parent_delete_cases = '\n'.join([ +''' case %s: { + delete reinterpret_cast<%sParent*>(gParentActor); + return; + } +'''% (t, t) for t in unittests ]) + + parent_enabled_cases_proc = '\n'.join([ +''' case %s: { + if (!%sParent::RunTestInProcesses()) { + passed("N/A to proc"); + DeferredParentShutdown(); + return; + } + break; + } +''' % (t, t) for t in unittests ]) + + parent_main_cases_proc = '\n'.join([ +''' case %s: { + %sParent** parent = + reinterpret_cast<%sParent**>(&gParentActor); + *parent = new %sParent(); + (*parent)->Open(transport, child); + return (*parent)->Main(); + } +'''% (t, t, t, t) for t in unittests ]) + + parent_enabled_cases_thread = '\n'.join([ +''' case %s: { + if (!%sParent::RunTestInThreads()) { + passed("N/A to threads"); + DeferredParentShutdown(); + return; + } + break; + } +''' % (t, t) for t in unittests ]) + + parent_main_cases_thread = '\n'.join([ +''' case %s: { + %sParent** parent = + reinterpret_cast<%sParent**>(&gParentActor); + *parent = new %sParent(); + + %sChild** child = + reinterpret_cast<%sChild**>(&gChildActor); + *child = new %sChild(); + + ::mozilla::ipc::MessageChannel *childChannel = (*child)->GetIPCChannel(); + ::mozilla::ipc::Side parentSide = + ::mozilla::ipc::ParentSide; + + (*parent)->Open(childChannel, childMessageLoop, parentSide); + return (*parent)->Main(); + } +'''% (t, t, t, t, t, t, t) for t in unittests ]) + + child_delete_cases = '\n'.join([ +''' case %s: { + delete reinterpret_cast<%sChild*>(gChildActor); + return; + } +'''% (t, t) for t in unittests+extras ]) + + + child_init_cases = '\n'.join([ +''' case %s: { + %sChild** child = + reinterpret_cast<%sChild**>(&gChildActor); + *child = new %sChild(); + (*child)->Open(transport, parentPid, worker); + return; + } +'''% (t, t, t, t) for t in unittests+extras ]) + + templatefile = open(template, 'r') + sys.stdout.write( + string.Template(templatefile.read()).substitute( + INCLUDES=includes, + ENUM_VALUES=enum_values, LAST_ENUM=last_enum, + STRING_TO_ENUMS=string_to_enums, + ENUM_TO_STRINGS=enum_to_strings, + PARENT_DELETE_CASES=parent_delete_cases, + PARENT_ENABLED_CASES_PROC=parent_enabled_cases_proc, + PARENT_MAIN_CASES_PROC=parent_main_cases_proc, + PARENT_ENABLED_CASES_THREAD=parent_enabled_cases_thread, + PARENT_MAIN_CASES_THREAD=parent_main_cases_thread, + CHILD_DELETE_CASES=child_delete_cases, + CHILD_INIT_CASES=child_init_cases)) + templatefile.close() + +if __name__ == '__main__': + main(sys.argv) diff --git a/ipc/ipdl/test/cxx/moz.build b/ipc/ipdl/test/cxx/moz.build new file mode 100644 index 000000000..c87455e02 --- /dev/null +++ b/ipc/ipdl/test/cxx/moz.build @@ -0,0 +1,134 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=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/. + +DIRS += ['app'] + +EXPORTS.mozilla._ipdltest += [ + 'IPDLUnitTestProcessChild.h', + 'IPDLUnitTests.h', + 'IPDLUnitTestTypes.h', + 'IPDLUnitTestUtils.h', +] + +SOURCES += [ + 'TestActorPunning.cpp', + 'TestBadActor.cpp', + 'TestBridgeMain.cpp', + 'TestCancel.cpp', + 'TestCrashCleanup.cpp', + 'TestDataStructures.cpp', + 'TestDemon.cpp', + 'TestDesc.cpp', + 'TestEndpointBridgeMain.cpp', + 'TestEndpointOpens.cpp', + 'TestFailedCtor.cpp', + 'TestHangs.cpp', + 'TestHighestPrio.cpp', + 'TestInterruptErrorCleanup.cpp', + 'TestInterruptRaces.cpp', + 'TestInterruptShutdownRace.cpp', + 'TestJSON.cpp', + 'TestLatency.cpp', + 'TestManyChildAllocs.cpp', + 'TestMultiMgrs.cpp', + 'TestNestedLoops.cpp', + 'TestOpens.cpp', + 'TestRaceDeadlock.cpp', + 'TestRaceDeferral.cpp', + 'TestRacyInterruptReplies.cpp', + 'TestRacyReentry.cpp', + 'TestRacyUndefer.cpp', + 'TestRPC.cpp', + 'TestSanity.cpp', + 'TestSelfManageRoot.cpp', + 'TestShmem.cpp', + 'TestShutdown.cpp', + 'TestStackHooks.cpp', + 'TestSyncError.cpp', + 'TestSyncHang.cpp', + 'TestSyncWakeup.cpp', + 'TestUrgency.cpp', + 'TestUrgentHangs.cpp', +] + +SOURCES += [ + '!IPDLUnitTests.cpp', + 'IPDLUnitTestProcessChild.cpp', + 'IPDLUnitTestSubprocess.cpp', +] + +IPDL_SOURCES += [ + 'PTestActorPunning.ipdl', + 'PTestActorPunningPunned.ipdl', + 'PTestActorPunningSub.ipdl', + 'PTestBadActor.ipdl', + 'PTestBadActorSub.ipdl', + 'PTestBridgeMain.ipdl', + 'PTestBridgeMainSub.ipdl', + 'PTestBridgeSub.ipdl', + 'PTestCancel.ipdl', + 'PTestCrashCleanup.ipdl', + 'PTestDataStructures.ipdl', + 'PTestDataStructuresCommon.ipdlh', + 'PTestDataStructuresSub.ipdl', + 'PTestDemon.ipdl', + 'PTestDesc.ipdl', + 'PTestDescSub.ipdl', + 'PTestDescSubsub.ipdl', + 'PTestEndpointBridgeMain.ipdl', + 'PTestEndpointBridgeMainSub.ipdl', + 'PTestEndpointBridgeSub.ipdl', + 'PTestEndpointOpens.ipdl', + 'PTestEndpointOpensOpened.ipdl', + 'PTestFailedCtor.ipdl', + 'PTestFailedCtorSub.ipdl', + 'PTestFailedCtorSubsub.ipdl', + 'PTestHandle.ipdl', + 'PTestHangs.ipdl', + 'PTestHighestPrio.ipdl', + 'PTestIndirectProtocolParam.ipdlh', + 'PTestIndirectProtocolParamFirst.ipdl', + 'PTestIndirectProtocolParamManage.ipdl', + 'PTestIndirectProtocolParamSecond.ipdl', + 'PTestInterruptErrorCleanup.ipdl', + 'PTestInterruptRaces.ipdl', + 'PTestInterruptShutdownRace.ipdl', + 'PTestJSON.ipdl', + 'PTestLatency.ipdl', + 'PTestManyChildAllocs.ipdl', + 'PTestManyChildAllocsSub.ipdl', + 'PTestMultiMgrs.ipdl', + 'PTestMultiMgrsBottom.ipdl', + 'PTestMultiMgrsLeft.ipdl', + 'PTestMultiMgrsRight.ipdl', + 'PTestNestedLoops.ipdl', + 'PTestOpens.ipdl', + 'PTestOpensOpened.ipdl', + 'PTestPriority.ipdl', + 'PTestRaceDeadlock.ipdl', + 'PTestRaceDeferral.ipdl', + 'PTestRacyInterruptReplies.ipdl', + 'PTestRacyReentry.ipdl', + 'PTestRacyUndefer.ipdl', + 'PTestRPC.ipdl', + 'PTestSanity.ipdl', + 'PTestSelfManage.ipdl', + 'PTestSelfManageRoot.ipdl', + 'PTestShmem.ipdl', + 'PTestShutdown.ipdl', + 'PTestShutdownSub.ipdl', + 'PTestShutdownSubsub.ipdl', + 'PTestStackHooks.ipdl', + 'PTestSyncError.ipdl', + 'PTestSyncHang.ipdl', + 'PTestSyncWakeup.ipdl', + 'PTestUrgency.ipdl', + 'PTestUrgentHangs.ipdl', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' diff --git a/ipc/ipdl/test/ipdl/IPDLCompile.py b/ipc/ipdl/test/ipdl/IPDLCompile.py new file mode 100644 index 000000000..e9f9d42b0 --- /dev/null +++ b/ipc/ipdl/test/ipdl/IPDLCompile.py @@ -0,0 +1,75 @@ +import copy, re, os, subprocess, sys, tempfile + +# We test the compiler indirectly, rather than reaching into the ipdl/ +# module, to make the testing framework as general as possible. + +class IPDLCompile: + def __init__(self, specfilename, ipdlargv=[ 'python', 'ipdl.py' ]): + self.argv = copy.deepcopy(ipdlargv) + self.specfilename = specfilename + self.stdout = None + self.stderr = None + self.returncode = None + + + def run(self): + '''Run |self.specstring| through the IPDL compiler.''' + assert self.returncode is None + + tmpoutdir = tempfile.mkdtemp(prefix='ipdl_unit_test') + + try: + self.argv.extend([ + '-d', tmpoutdir, + self.specfilename + ]) + + proc = subprocess.Popen(args=self.argv, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + self.stdout, self.stderr = proc.communicate() + + self.returncode = proc.returncode + assert self.returncode is not None + + finally: + for root, dirs, files in os.walk(tmpoutdir, topdown=0): + for name in files: + os.remove(os.path.join(root, name)) + for name in dirs: + os.rmdir(os.path.join(root, name)) + os.rmdir(tmpoutdir) + + if proc.returncode is None: + proc.kill() + + + def completed(self): + return (self.returncode is not None + and isinstance(self.stdout, str) + and isinstance(self.stderr, str)) + + + def error(self): + '''Return True iff compiling self.specstring resulted in an +IPDL compiler error.''' + assert self.completed() + + return None is not re.search(r'error:', self.stderr) + + + def exception(self): + '''Return True iff compiling self.specstring resulted in a Python +exception being raised.''' + assert self.completed() + + return None is not re.search(r'Traceback (most recent call last):', + self.stderr) + + def ok(self): + '''Return True iff compiling self.specstring was successful.''' + assert self.completed() + + return (not self.exception() + and not self.error() + and (0 == self.returncode)) diff --git a/ipc/ipdl/test/ipdl/Makefile.in b/ipc/ipdl/test/ipdl/Makefile.in new file mode 100644 index 000000000..71981c616 --- /dev/null +++ b/ipc/ipdl/test/ipdl/Makefile.in @@ -0,0 +1,17 @@ +# 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/. + +include $(topsrcdir)/config/rules.mk + +OKTESTS := $(wildcard $(srcdir)/ok/*.ipdl) $(wildcard $(srcdir)/ok/*.ipdlh) +ERRORTESTS := $(wildcard $(srcdir)/error/*.ipdl) $(wildcard $(srcdir)/error/*.ipdlh) + +check:: + @$(PYTHON) $(srcdir)/runtests.py \ + $(srcdir)/ok $(srcdir)/error \ + $(PYTHON) $(topsrcdir)/config/pythonpath.py \ + $(PLY_INCLUDE) \ + $(topsrcdir)/ipc/ipdl/ipdl.py \ + OKTESTS $(OKTESTS) \ + ERRORTESTS $(ERRORTESTS) diff --git a/ipc/ipdl/test/ipdl/error/DeleteRace.ipdl b/ipc/ipdl/test/ipdl/error/DeleteRace.ipdl new file mode 100644 index 000000000..992bbabdf --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/DeleteRace.ipdl @@ -0,0 +1,14 @@ +// XXX kind of a gray area whether |__delete__| should be a part of the +// top-level protocol. but if it's ever not, this test will break and +// we'll notice, right? +protocol DeleteRace { +parent: + async M1(); + +child: + async __delete__(); + +state START: + send __delete__; + recv M1 goto START; +}; diff --git a/ipc/ipdl/test/ipdl/error/Nullable.ipdl b/ipc/ipdl/test/ipdl/error/Nullable.ipdl new file mode 100644 index 000000000..cb7097f0e --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/Nullable.ipdl @@ -0,0 +1,4 @@ +protocol Nullable { +child: + async Msg(nullable int i); +}; diff --git a/ipc/ipdl/test/ipdl/error/Nullable2.ipdl b/ipc/ipdl/test/ipdl/error/Nullable2.ipdl new file mode 100644 index 000000000..d572951ae --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/Nullable2.ipdl @@ -0,0 +1,8 @@ +union Union { + nullable int; +}; + +protocol Nullable2 { +child: + async Msg(Union i); +}; diff --git a/ipc/ipdl/test/ipdl/error/actorparam_badState.ipdl b/ipc/ipdl/test/ipdl/error/actorparam_badState.ipdl new file mode 100644 index 000000000..93b8ba218 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/actorparam_badState.ipdl @@ -0,0 +1,10 @@ +protocol actorparam_badState { + +child: + async Msg(actorparam_badState:FARGEL p); + async __delete__(); + +state S1: + send Msg goto S1; + send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/array_Recursive.ipdl b/ipc/ipdl/test/ipdl/error/array_Recursive.ipdl new file mode 100644 index 000000000..898307193 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/array_Recursive.ipdl @@ -0,0 +1,3 @@ +protocol array_Recursive { +child: Msg(int[][] aa); +}; diff --git a/ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl b/ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl new file mode 100644 index 000000000..cf310f34f --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl @@ -0,0 +1,7 @@ +include protocol IDONTEXIST; + +// error: nonexistent protocol ^^^ + +protocol badProtocolInclude { +child: Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/bridgesNonexistent.ipdl b/ipc/ipdl/test/ipdl/error/bridgesNonexistent.ipdl new file mode 100644 index 000000000..313de0fd1 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/bridgesNonexistent.ipdl @@ -0,0 +1,6 @@ +protocol bridgesNonexistent { + bridges Leftie, Rightie; + +child: __delete__(); +state DEAD: send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/bridgesSubprotocol.ipdl b/ipc/ipdl/test/ipdl/error/bridgesSubprotocol.ipdl new file mode 100644 index 000000000..a07c295d9 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/bridgesSubprotocol.ipdl @@ -0,0 +1,13 @@ +include protocol subprotocolBridges; + +protocol bridgesSubprotocol { + bridges subprotocolBridges, subprotocolBridges; + + manages subprotocolBridges; + +child: + subprotocolBridges(); + async __delete__(); + +state DEAD: send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/compressCtor.ipdl b/ipc/ipdl/test/ipdl/error/compressCtor.ipdl new file mode 100644 index 000000000..f1ea880b1 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/compressCtor.ipdl @@ -0,0 +1,8 @@ +include protocol compressCtorManagee; + +intr protocol compressCtor { + manages compressCtorManagee; + +parent: + async compressCtorManagee() compress; +}; diff --git a/ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl b/ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl new file mode 100644 index 000000000..3d0c66c21 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl @@ -0,0 +1,8 @@ +include protocol compressCtor; + +intr protocol compressCtorManagee { + manager compressCtor; + +child: + async __delete__() compress; +}; diff --git a/ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl b/ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl new file mode 100644 index 000000000..9090c9395 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl @@ -0,0 +1,7 @@ +protocol conflictProtocolMsg { + + // it's an error to re-use the protocol name as a message ID; these + // are reserved +child: conflictProtocolMsg(); + +}; diff --git a/ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl b/ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl new file mode 100644 index 000000000..2e1b93299 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl @@ -0,0 +1,12 @@ +include protocol cyclecheck_Parent; +include protocol cyclecheck_Grandchild; + +protocol cyclecheck_Child { + manager cyclecheck_Parent; + manages cyclecheck_Grandchild; + +child: + cyclecheck_Grandchild(); + async __delete__(); +}; + diff --git a/ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl b/ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl new file mode 100644 index 000000000..d91949de4 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl @@ -0,0 +1,12 @@ +include protocol cyclecheck_Child; +include protocol cyclecheck_Parent; + +protocol cyclecheck_Grandchild { + manager cyclecheck_Child; + manages cyclecheck_Parent; + +child: + cyclecheck_Parent(); + async __delete__(); +}; + diff --git a/ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl b/ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl new file mode 100644 index 000000000..666d44c54 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl @@ -0,0 +1,10 @@ +include protocol cyclecheck_Child; + +protocol cyclecheck_Parent { + manages cyclecheck_Child; + +child: + cyclecheck_Child(); + async __delete__(); +}; + diff --git a/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl b/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl new file mode 100644 index 000000000..c59987b9c --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl @@ -0,0 +1,8 @@ +protocol dtorReserved { + + // it's an error to use old-style dtor syntax + +child: + ~SomeMsg(); + +}; diff --git a/ipc/ipdl/test/ipdl/error/empty.ipdl b/ipc/ipdl/test/ipdl/error/empty.ipdl new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/empty.ipdl @@ -0,0 +1 @@ + diff --git a/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl b/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl new file mode 100644 index 000000000..dc0cdd800 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl @@ -0,0 +1,6 @@ +intr protocol intrMessageCompress { +parent: + intr foo() compress; +child: + intr bar() compress; +}; diff --git a/ipc/ipdl/test/ipdl/error/lex1.ipdl b/ipc/ipdl/test/ipdl/error/lex1.ipdl new file mode 100644 index 000000000..0ab9e1e4f --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/lex1.ipdl @@ -0,0 +1 @@ +slkdjfl*&^* diff --git a/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl b/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl new file mode 100644 index 000000000..d70889126 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl @@ -0,0 +1,8 @@ +protocol manageSelfToplevel { + manager manageSelfToplevel; + manages manageSelfToplevel; + +child: + manageSelfToplevel(); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl b/ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl new file mode 100644 index 000000000..ddc04a005 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl @@ -0,0 +1,7 @@ +include protocol managerNoCtor; + +protocol managedNoCtor { + manager managerNoCtor; + // empty +child: __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl b/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl new file mode 100644 index 000000000..8d98018ee --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl @@ -0,0 +1,6 @@ +include protocol managerNoDtor; + +protocol managedNoDtor { + manager managerNoDtor; + // empty +}; diff --git a/ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl b/ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl new file mode 100644 index 000000000..e39b89009 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl @@ -0,0 +1,8 @@ +include protocol managedNoCtor; + +protocol managerNoCtor { + manages managedNoCtor; + +parent: + // error: no ctor defined +}; diff --git a/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl b/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl new file mode 100644 index 000000000..72e95149f --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl @@ -0,0 +1,9 @@ +include protocol managedNoDtor; + +protocol managerNoDtor { + manages managedNoDtor; + +parent: + managedNoDtor(); + // error: no ctor defined +}; diff --git a/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl b/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl new file mode 100644 index 000000000..a3d9585d4 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl @@ -0,0 +1,3 @@ +protocol messageNoDirection { + async NoDirection(); +}; diff --git a/ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl b/ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl new file mode 100644 index 000000000..eb89f77d8 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl @@ -0,0 +1,8 @@ +include protocol multimanDupMgrsMgr; + +protocol multimanDupMgrs { + manager multimanDupMgrsMgr or multimanDupMgrsMgr; + +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl b/ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl new file mode 100644 index 000000000..be8dfbc83 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl @@ -0,0 +1,9 @@ +include protocol multimanDupMgrs; + +protocol multimanDupMgrsMgr { + manages multimanDupMgrs; + +child: + multimanDupMgrs(); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl b/ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl new file mode 100644 index 000000000..dd4ba254c --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl @@ -0,0 +1,7 @@ +protocol multimanNonexistentManagers { + manager Starsky or Hutch; + +child: + async Dummy(); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl b/ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl new file mode 100644 index 000000000..fd4ee4d18 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl @@ -0,0 +1,20 @@ +struct X { + int i; + Y[] y; +}; + +struct Y { + X x; + Z z; +}; + +struct Z { + double d; + X x; +}; + +protocol mutualRecStruct { +child: + async Test(X x, Y y, Z z); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl b/ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl new file mode 100644 index 000000000..f475952d1 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl @@ -0,0 +1,20 @@ +struct X { + int i; + Y[] y; +}; + +union Y { + X; + Z; +}; + +struct Z { + double d; + X x; +}; + +protocol mutualRecStructUnion { +child: + async Test(X x, Y y, Z z); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl b/ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl new file mode 100644 index 000000000..08f13694e --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl @@ -0,0 +1,5 @@ +protocol noEmptyToplevel { + + // it's an error for top-level protocols to be empty + +}; diff --git a/ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh b/ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh new file mode 100644 index 000000000..33571c415 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh @@ -0,0 +1,4 @@ +protocol noProtocolInHeader { +child: + __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl b/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl new file mode 100644 index 000000000..571c017c3 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl @@ -0,0 +1,5 @@ +include protocol "Foo.ipdl"; + +protocol oldIncludeSyntax { + +}; diff --git a/ipc/ipdl/test/ipdl/error/opensNonexistent.ipdl b/ipc/ipdl/test/ipdl/error/opensNonexistent.ipdl new file mode 100644 index 000000000..521f69ace --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/opensNonexistent.ipdl @@ -0,0 +1,6 @@ +protocol opensNonexistent { + parent opens Unicorn; + +child: __delete__(); +state DEAD: send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/opensSubprotocol.ipdl b/ipc/ipdl/test/ipdl/error/opensSubprotocol.ipdl new file mode 100644 index 000000000..af884a636 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/opensSubprotocol.ipdl @@ -0,0 +1,13 @@ +include protocol subprotocolOpens; + +protocol opensSubprotocol { + child opens subprotocolOpens; + + manages subprotocolOpens; + +child: + subprotocolOpens(); + async __delete__(); + +state DEAD: send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/parser.ipdl b/ipc/ipdl/test/ipdl/error/parser.ipdl new file mode 100644 index 000000000..f3f273a02 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/parser.ipdl @@ -0,0 +1 @@ +protocol parser { diff --git a/ipc/ipdl/test/ipdl/error/race_MultiOut.ipdl b/ipc/ipdl/test/ipdl/error/race_MultiOut.ipdl new file mode 100644 index 000000000..263d87f59 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/race_MultiOut.ipdl @@ -0,0 +1,20 @@ +protocol race_MultiOut { +child: M1(); +parent: M2(); + +state S1: + send M1 goto S2; + recv M2 goto S3; + +state S2: + recv M2 goto S4 or S5; + +state S3: + send M1 goto S4 or S5; + +state S4: + send M1 goto S4; + +state S5: + recv M2 goto S5; +}; diff --git a/ipc/ipdl/test/ipdl/error/race_OverlappingMultiOut.ipdl b/ipc/ipdl/test/ipdl/error/race_OverlappingMultiOut.ipdl new file mode 100644 index 000000000..8aa831842 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/race_OverlappingMultiOut.ipdl @@ -0,0 +1,35 @@ +protocol race_OverlappingMultiOut { +child: + async Msg1(); + async Msg1_(); + async __delete__(); suppressUndeleteableError(); + +parent: + async Msg2(); + async Msg2_(); + + +start state _: + send __delete__; + send suppressUndeleteableError goto S10; + + // *** ERROR: send/recv of Msg1/Msg2 in state S10 goes to overlapping + // sets { S11, S12 }, { S12, S13 } and thus can't be unidirectional +state S10: + send Msg1 goto S11 or S12; + recv Msg2 goto S12 or S13; + +state S11: + recv Msg2 goto S14; + +state S12: + send Msg1 goto S14; + recv Msg2 goto S14; + +state S13: + send Msg1 goto S14; + +state S14: + send Msg1 goto S14; + recv Msg2 goto S14; +}; diff --git a/ipc/ipdl/test/ipdl/error/race_ToDiffStates.ipdl b/ipc/ipdl/test/ipdl/error/race_ToDiffStates.ipdl new file mode 100644 index 000000000..db431b146 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/race_ToDiffStates.ipdl @@ -0,0 +1,20 @@ +protocol race_ToDiffStates { +child: M1(); +parent: M2(); + +state S1: + send M1 goto S2; + recv M2 goto S3; + +state S2: + recv M2 goto S4; + +state S3: + send M1 goto S5; + +state S4: + send M1 goto S4; + +state S5: + recv M2 goto S5; +}; diff --git a/ipc/ipdl/test/ipdl/error/race_ToError.ipdl b/ipc/ipdl/test/ipdl/error/race_ToError.ipdl new file mode 100644 index 000000000..66951fa39 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/race_ToError.ipdl @@ -0,0 +1,14 @@ +protocol race_ToError { +child: M1(); +parent: M2(); + +state S1: + send M1 goto S2; + recv M2 goto S3; + +state S2: + send M1 goto S2; + +state S3: + recv M2 goto S3; +}; diff --git a/ipc/ipdl/test/ipdl/error/race_ViolateSameDirection.ipdl b/ipc/ipdl/test/ipdl/error/race_ViolateSameDirection.ipdl new file mode 100644 index 000000000..a1f964ec7 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/race_ViolateSameDirection.ipdl @@ -0,0 +1,30 @@ +protocol race_ViolateSameDirection { +child: + async Msg1(); + async Msg1_(); + async __delete__(); + async suppressUndeleteableError(); +parent: + async Msg2(); + async Msg2_(); + + // *** ERROR: state S7 doesn't have all-same-direction +start state _: + send __delete__; + send suppressUndeleteableError goto S6; + +state S6: + send Msg1 goto S7; + recv Msg2 goto S8; + +state S7: + recv Msg2 goto S9; + send Msg1 goto S9; + +state S8: + send Msg1 goto S9; + +state S9: + send Msg1 goto S9; + recv Msg2 goto S9; +}; diff --git a/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl b/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl new file mode 100644 index 000000000..5b2cdcf70 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl @@ -0,0 +1,9 @@ +protocol redeclMessage { + + // can't declare two messages with the same name + +child: + async Msg(); + async Msg(); + +}; diff --git a/ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl b/ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl new file mode 100644 index 000000000..0bc2f135c --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl @@ -0,0 +1,7 @@ +sync protocol redeclParamReturn { + + // it's an error to name a parameter with the same id as a return + +parent: Msg(int f) returns (bool f); + +}; diff --git a/ipc/ipdl/test/ipdl/error/redefState.ipdl b/ipc/ipdl/test/ipdl/error/redefState.ipdl new file mode 100644 index 000000000..b9da7212b --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/redefState.ipdl @@ -0,0 +1,14 @@ +protocol redefState { + + // error: redefining state in state machine +child: + async Msg(); + async __delete__(); + +state S1: send Msg goto S1; + +state S1: send Msg goto S1; + +start state _: + send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/repeatedOutState.ipdl b/ipc/ipdl/test/ipdl/error/repeatedOutState.ipdl new file mode 100644 index 000000000..461f7dbff --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/repeatedOutState.ipdl @@ -0,0 +1,12 @@ +protocol repeatedOutState { +child: Msg(); __delete__(); + + // error: S2 repeated in multi-out set + +state S1: + send Msg goto S2 or S2 or S4; + +state S2: send Msg goto S2; +state S3: send Msg goto S3; +state S4: send Mesg goto S4; send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/rpcParentToChild.ipdl b/ipc/ipdl/test/ipdl/error/rpcParentToChild.ipdl new file mode 100644 index 000000000..fb6cd122b --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/rpcParentToChild.ipdl @@ -0,0 +1,6 @@ +intr protocol rpcParentToChild { + + // can't declare rpc parent-to-child messages +child: rpc Msg(); + +}; diff --git a/ipc/ipdl/test/ipdl/error/shmem.ipdl b/ipc/ipdl/test/ipdl/error/shmem.ipdl new file mode 100644 index 000000000..f9046fb4c --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/shmem.ipdl @@ -0,0 +1,5 @@ +using class mozilla::ipc::Shmem from "mozilla/ipc/Shmem.h"; // redeclaration + +protocol shmem { +child: Msg(Shmem s); +}; diff --git a/ipc/ipdl/test/ipdl/error/shmem_access_union.ipdl b/ipc/ipdl/test/ipdl/error/shmem_access_union.ipdl new file mode 100644 index 000000000..2ed162368 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/shmem_access_union.ipdl @@ -0,0 +1,8 @@ +union Union { + [-r-w|+r+w] Shmem; +}; + +protocol shmem_access_union { +child: + async Msg(Union u); +}; diff --git a/ipc/ipdl/test/ipdl/error/spawnsNonexistent.ipdl b/ipc/ipdl/test/ipdl/error/spawnsNonexistent.ipdl new file mode 100644 index 000000000..1bc4919bf --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/spawnsNonexistent.ipdl @@ -0,0 +1,6 @@ +protocol spawnsNonexistent { + parent spawns Nonexistent; + +child: __delete__(); +state DEAD: send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/spawnsSubprotocol.ipdl b/ipc/ipdl/test/ipdl/error/spawnsSubprotocol.ipdl new file mode 100644 index 000000000..fc056a254 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/spawnsSubprotocol.ipdl @@ -0,0 +1,13 @@ +include protocol subprotocolSpawns; + +protocol spawnsSubprotocol { + parent spawns subprotocolSpawns; + + manages subprotocolSpawns; + +child: + subprotocolSpawns(); + async __delete__(); + +state DEAD: send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/structRedecl.ipdl b/ipc/ipdl/test/ipdl/error/structRedecl.ipdl new file mode 100644 index 000000000..a2e6f5c76 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/structRedecl.ipdl @@ -0,0 +1,8 @@ +struct Redecl { + int a; + double a; +}; + +protocol structRedecl { +child: __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl b/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl new file mode 100644 index 000000000..1d5fe8896 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl @@ -0,0 +1,7 @@ +struct S { + Foobers i; +}; + +protocol structUnknownField { +child: __delete__(S s); +}; diff --git a/ipc/ipdl/test/ipdl/error/subprotocolBridges.ipdl b/ipc/ipdl/test/ipdl/error/subprotocolBridges.ipdl new file mode 100644 index 000000000..68cf94547 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/subprotocolBridges.ipdl @@ -0,0 +1,10 @@ +include protocol bridgesSubprotocol; + +protocol subprotocolBridges { + bridges bridgesSubprotocol, bridgesSubprotocol; + + manager bridgesSubprotocol; + +child: __delete__(); +state DEAD: send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/subprotocolOpens.ipdl b/ipc/ipdl/test/ipdl/error/subprotocolOpens.ipdl new file mode 100644 index 000000000..1d6a5851d --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/subprotocolOpens.ipdl @@ -0,0 +1,10 @@ +include protocol opensSubprotocol; + +protocol subprotocolOpens { + parent opens opensSubprotocol; + + manager opensSubprotocol; + +child: __delete__(); +state DEAD: send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/subprotocolSpawns.ipdl b/ipc/ipdl/test/ipdl/error/subprotocolSpawns.ipdl new file mode 100644 index 000000000..e3caab624 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/subprotocolSpawns.ipdl @@ -0,0 +1,10 @@ +include protocol spawnsSubprotocol; + +protocol subprotocolSpawns { + parent spawns spawnsSubprotocol; + + manager spawnsSubprotocol; + +child: __delete__(); +state DEAD: send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl b/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl new file mode 100644 index 000000000..73c92664e --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl @@ -0,0 +1,4 @@ +sync protocol syncMessageCompress { +parent: + sync foo() compress; +}; diff --git a/ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl b/ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl new file mode 100644 index 000000000..f0d298fe5 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl @@ -0,0 +1,6 @@ +intr protocol syncParentToChild { + + // can't declare sync parent-to-child messages +child: sync Msg(); + +}; diff --git a/ipc/ipdl/test/ipdl/error/tooWeakInterruptAsync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakInterruptAsync.ipdl new file mode 100644 index 000000000..a0ee9a47c --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/tooWeakInterruptAsync.ipdl @@ -0,0 +1,7 @@ +protocol tooWeakRPCAsync { + + // it's an error to declare an async protocol with an intr message + +parent: intr Msg(); + +}; diff --git a/ipc/ipdl/test/ipdl/error/tooWeakRpcSync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakRpcSync.ipdl new file mode 100644 index 000000000..2d368de63 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/tooWeakRpcSync.ipdl @@ -0,0 +1,6 @@ +sync protocol tooWeakRpcSync { + + // it's an error to declare a sync protocol with an rpc message +parent: + rpc Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl new file mode 100644 index 000000000..2448a0cd9 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl @@ -0,0 +1,7 @@ +protocol tooWeakSyncAsync { + + // it's an error to declare an async protocol with a sync message + +parent: sync Msg(); + +}; diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongDirection.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongDirection.ipdl new file mode 100644 index 000000000..53c2fd952 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection.ipdl @@ -0,0 +1,12 @@ +protocol trans_WrongDirection { + +child: + async Msg(); + async __delete__(); + +state S1: + recv Msg goto S1; + +start state _: + send __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongDirection2.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongDirection2.ipdl new file mode 100644 index 000000000..c15dfa5af --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection2.ipdl @@ -0,0 +1,12 @@ +protocol trans_WrongDirection2 { + +parent: + async Msg(); + async __delete__(); + +state S1: + send Msg goto S1; + +start state _: + recv __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongDirection3.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongDirection3.ipdl new file mode 100644 index 000000000..80621ecd1 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection3.ipdl @@ -0,0 +1,12 @@ +sync protocol trans_WrongDirection3 { + +parent: + sync Msg(); + async __delete__(); + +state S1: + send Msg goto S1; + +start state _: + recv __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongDirection4.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongDirection4.ipdl new file mode 100644 index 000000000..c27ad331d --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection4.ipdl @@ -0,0 +1,12 @@ +intr protocol trans_WrongDirection4 { + +child: + intr Msg(); + async __delete__(); + +state S1: + answer Msg goto S1; + +start state _: + send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongDirection5.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongDirection5.ipdl new file mode 100644 index 000000000..7997649c8 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/trans_WrongDirection5.ipdl @@ -0,0 +1,12 @@ +intr protocol trans_WrongDirection5 { + +parent: + intr Msg(); + async __delete__() + +state S1: + call Msg goto S1; + +start state_: + recv __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongName.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongName.ipdl new file mode 100644 index 000000000..a944d5879 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/trans_WrongName.ipdl @@ -0,0 +1,10 @@ +protocol trans_WrongName { + +child: + async Msg(); + async __delete__(); + +state S1: + call Msg goto S1; + send __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongName2.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongName2.ipdl new file mode 100644 index 000000000..ece935b7b --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/trans_WrongName2.ipdl @@ -0,0 +1,10 @@ +protocol trans_WrongName2 { + +parent: + async Msg(); + async __delete__(); + +state S1: + answer Msg goto S1; + recv __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongName3.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongName3.ipdl new file mode 100644 index 000000000..80c54383f --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/trans_WrongName3.ipdl @@ -0,0 +1,10 @@ +sync protocol trans_WrongName3 { + +parent: + sync Msg(); + async __delete__(); + +state S1: + answer Msg goto S1; + recv __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongName4.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongName4.ipdl new file mode 100644 index 000000000..f8a9746e4 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/trans_WrongName4.ipdl @@ -0,0 +1,10 @@ +intr protocol trans_WrongName4 { + +child: + intr Msg(); + async __delete__(); + +state S1: + send Msg goto S1; + send __delete__ +}; diff --git a/ipc/ipdl/test/ipdl/error/trans_WrongName5.ipdl b/ipc/ipdl/test/ipdl/error/trans_WrongName5.ipdl new file mode 100644 index 000000000..a793905d2 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/trans_WrongName5.ipdl @@ -0,0 +1,10 @@ +intr protocol trans_WrongName5 { + +parent: + intr Msg(); + async __delete__(); + +state S1: + recv Msg goto S1; + recv __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl b/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl new file mode 100644 index 000000000..8d5cb79fc --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl @@ -0,0 +1,9 @@ +// it's an error to define two protocols in the same file + +protocol p1 { +child: Msg(); +}; + +protocol p2 { +child: Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl b/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl new file mode 100644 index 000000000..25aa1af4f --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl @@ -0,0 +1,5 @@ +protocol undeclParamType { + +child: Msg(FARGLEGARGLE p); + +}; diff --git a/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl b/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl new file mode 100644 index 000000000..9462e0463 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl @@ -0,0 +1,6 @@ +protocol undeclProtocol { + manages undeclared; + +child: + undeclared(); +}; diff --git a/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl b/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl new file mode 100644 index 000000000..b4bee83d1 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl @@ -0,0 +1,5 @@ +sync protocol undeclReturnType { + +child: sync Msg() returns (FARGLEGARGLE r); + +}; diff --git a/ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl b/ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl new file mode 100644 index 000000000..4dd89647f --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl @@ -0,0 +1,7 @@ +struct X { Y y; }; +struct Y { Z z; }; +struct Z { X x; }; + +protocol undefMutualRecStruct { +child: __delete__(X x); +}; diff --git a/ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl b/ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl new file mode 100644 index 000000000..f9177b371 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl @@ -0,0 +1,7 @@ +struct X { Y y; }; +union Y { Z; }; +struct Z { X x; }; + +protocol undefMutualRecStructUnion { +child: __delete__(X x); +}; diff --git a/ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl b/ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl new file mode 100644 index 000000000..be64fc040 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl @@ -0,0 +1,7 @@ +union X { Y; }; +union Y { Z; }; +union Z { X; }; + +protocol undefMutualRecUnion { +child: __delete__(X x); +}; diff --git a/ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl b/ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl new file mode 100644 index 000000000..92f47164b --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl @@ -0,0 +1,5 @@ +struct X { X x; }; + +protocol undefSelfRecStruct { +child: __delete__(X x); +}; diff --git a/ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl b/ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl new file mode 100644 index 000000000..24faf3130 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl @@ -0,0 +1,5 @@ +union X { X; }; + +protocol undefSelfRecUnion { +child: __delete__(X x); +}; diff --git a/ipc/ipdl/test/ipdl/error/unreachedDelete.ipdl b/ipc/ipdl/test/ipdl/error/unreachedDelete.ipdl new file mode 100644 index 000000000..8af41b55c --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/unreachedDelete.ipdl @@ -0,0 +1,5 @@ +protocol unreachedDelete { +child: M1(); __delete__(); + +state S1: send M1 goto S1; +}; diff --git a/ipc/ipdl/test/ipdl/error/unreachedDeleteMultiStart.ipdl b/ipc/ipdl/test/ipdl/error/unreachedDeleteMultiStart.ipdl new file mode 100644 index 000000000..80e28a26d --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/unreachedDeleteMultiStart.ipdl @@ -0,0 +1,10 @@ +protocol unreachedDeleteMultiStart { +child: + async M1(); async M2(); async __delete__(); + +start state S1: send M1 goto S2; +state S2: send __delete__; + +start state S3: send M2 goto S4; +state S4: send M1 goto S3; +}; diff --git a/ipc/ipdl/test/ipdl/moz.build b/ipc/ipdl/test/ipdl/moz.build new file mode 100644 index 000000000..28919c271 --- /dev/null +++ b/ipc/ipdl/test/ipdl/moz.build @@ -0,0 +1,6 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=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/. + diff --git a/ipc/ipdl/test/ipdl/ok/Delete.ipdl b/ipc/ipdl/test/ipdl/ok/Delete.ipdl new file mode 100644 index 000000000..311d82537 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Delete.ipdl @@ -0,0 +1,8 @@ +include protocol DeleteSub; + +sync protocol Delete { + manages DeleteSub; + +child: + async DeleteSub(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/DeleteSub.ipdl b/ipc/ipdl/test/ipdl/ok/DeleteSub.ipdl new file mode 100644 index 000000000..45dd2a827 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/DeleteSub.ipdl @@ -0,0 +1,12 @@ +include protocol Delete; + +sync protocol DeleteSub { + manager Delete; + +parent: + sync __delete__(int x) returns (double d); + +state START: + recv __delete__; +}; + diff --git a/ipc/ipdl/test/ipdl/ok/Nullable.ipdl b/ipc/ipdl/test/ipdl/ok/Nullable.ipdl new file mode 100644 index 000000000..6a81e125d --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Nullable.ipdl @@ -0,0 +1,11 @@ +union Union { + nullable Nullable; + nullable Nullable[]; +}; + +protocol Nullable { +child: + async Msg(nullable Nullable n); + async Msg2(nullable Nullable[] N); + async Msg3(Union u); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Struct.ipdl b/ipc/ipdl/test/ipdl/ok/Struct.ipdl new file mode 100644 index 000000000..da3ca055a --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Struct.ipdl @@ -0,0 +1,10 @@ +struct S { + int i; + double d; +}; + +sync protocol Struct { +parent: + sync test(S s) returns (S ss); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/actorparam.ipdl b/ipc/ipdl/test/ipdl/ok/actorparam.ipdl new file mode 100644 index 000000000..1a3e7b972 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/actorparam.ipdl @@ -0,0 +1,5 @@ +protocol actorparam { + +child: Msg(actorparam p); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/actorparam_state.ipdl b/ipc/ipdl/test/ipdl/ok/actorparam_state.ipdl new file mode 100644 index 000000000..5a3109f5f --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/actorparam_state.ipdl @@ -0,0 +1,10 @@ +protocol actorparam_state { + +child: + async Msg(actorparam_state:S1 p); + async __delete__(); + +state S1: + send Msg goto S1; + send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/ok/actorreturn.ipdl b/ipc/ipdl/test/ipdl/ok/actorreturn.ipdl new file mode 100644 index 000000000..9642ca854 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/actorreturn.ipdl @@ -0,0 +1,6 @@ +sync protocol actorreturn { + +parent: + sync Msg(actorreturn p) returns (actorreturn r); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/array_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/array_Basic.ipdl new file mode 100644 index 000000000..391917708 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/array_Basic.ipdl @@ -0,0 +1,4 @@ +protocol array_Basic { +child: + async Msg(int[] array); +}; diff --git a/ipc/ipdl/test/ipdl/ok/array_OfActors.ipdl b/ipc/ipdl/test/ipdl/ok/array_OfActors.ipdl new file mode 100644 index 000000000..8d0c7fb6a --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/array_OfActors.ipdl @@ -0,0 +1,10 @@ +include protocol array_OfActorsSub; + +protocol array_OfActors { + manages array_OfActorsSub; + +child: + async Msg(array_OfActorsSub[] p); + + array_OfActorsSub(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/array_OfActorsSub.ipdl b/ipc/ipdl/test/ipdl/ok/array_OfActorsSub.ipdl new file mode 100644 index 000000000..3b765511f --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/array_OfActorsSub.ipdl @@ -0,0 +1,7 @@ +include protocol array_OfActors; + +protocol array_OfActorsSub { + manager array_OfActors; + +child: __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/array_Union.ipdl b/ipc/ipdl/test/ipdl/ok/array_Union.ipdl new file mode 100644 index 000000000..64d8adbd7 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/array_Union.ipdl @@ -0,0 +1,10 @@ +union Union { + int[]; + int; + double; +}; + +sync protocol array_Union { +parent: + sync Msg(Union u, Union[] au) returns (Union r); +}; diff --git a/ipc/ipdl/test/ipdl/ok/builtins.ipdl b/ipc/ipdl/test/ipdl/ok/builtins.ipdl new file mode 100644 index 000000000..e407c5ed2 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/builtins.ipdl @@ -0,0 +1,21 @@ +protocol builtins { + + // sanity-check that "essential" builtins are being declared + +child: Msg(bool b, + char c, + int i, + long l, + + float f, + double d, + + int8_t i8t, + uint8_t u8t, + int16_t i16t, + uint16_t u16t, + int32_t i32t, + uint32_t u32t, + int64_t i64t, + uint64_t u64t); +}; diff --git a/ipc/ipdl/test/ipdl/ok/compositor.ipdl b/ipc/ipdl/test/ipdl/ok/compositor.ipdl new file mode 100644 index 000000000..3f1b16335 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/compositor.ipdl @@ -0,0 +1,11 @@ +include protocol content; + +sync protocol compositor { + bridges compositor, content; + +child: + async __delete__(); + +state DEAD: + send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/ok/content.ipdl b/ipc/ipdl/test/ipdl/ok/content.ipdl new file mode 100644 index 000000000..655998bac --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/content.ipdl @@ -0,0 +1,17 @@ +include protocol compositor; +include protocol jetpack; +include protocol media; +include protocol plugin; + +sync protocol content { + parent spawns compositor as parent; + parent spawns jetpack; + child spawns plugin; + child opens media; + +child: + async __delete__(); + +state DEAD: + send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/ok/empty.ipdl b/ipc/ipdl/test/ipdl/ok/empty.ipdl new file mode 100644 index 000000000..73d512586 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/empty.ipdl @@ -0,0 +1,3 @@ +protocol empty { +child: Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/emptyStruct.ipdl b/ipc/ipdl/test/ipdl/ok/emptyStruct.ipdl new file mode 100644 index 000000000..f6be5eb86 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/emptyStruct.ipdl @@ -0,0 +1,3 @@ +struct empty { }; + +protocol emptyStruct { child: __delete__(); }; diff --git a/ipc/ipdl/test/ipdl/ok/header.ipdlh b/ipc/ipdl/test/ipdl/ok/header.ipdlh new file mode 100644 index 000000000..fc3f8c827 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/header.ipdlh @@ -0,0 +1,15 @@ +using foo from "foo.h"; +using bar::baz from "foo.h"; + +struct Outer { }; + +namespace a { struct Inner1 { }; } + +namespace b { struct Inner2 { }; } + +namespace c { +union X { + int32_t; + float; +}; +} diff --git a/ipc/ipdl/test/ipdl/ok/headerProto.ipdl b/ipc/ipdl/test/ipdl/ok/headerProto.ipdl new file mode 100644 index 000000000..0bccc3834 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/headerProto.ipdl @@ -0,0 +1,10 @@ +include header; + +namespace c { + +protocol headerProto { +child: + async __delete__(foo a, baz b, Inner1 c, Inner2 d, X x); +}; + +} diff --git a/ipc/ipdl/test/ipdl/ok/intrProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/intrProtocol.ipdl new file mode 100644 index 000000000..1c9327f01 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/intrProtocol.ipdl @@ -0,0 +1,13 @@ +intr protocol intrProtocol { + + // sanity check of Interrupt protocols +child: + async AsyncMsg(); + +parent: + sync SyncMsg(int i) returns (int r); + +both: + intr InterruptMsg(int x) returns (int y); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/jetpack.ipdl b/ipc/ipdl/test/ipdl/ok/jetpack.ipdl new file mode 100644 index 000000000..7cdb5c329 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/jetpack.ipdl @@ -0,0 +1,7 @@ +sync protocol jetpack { +child: + async __delete__(); + +state DEAD: + send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/ok/jetpackContent.ipdl b/ipc/ipdl/test/ipdl/ok/jetpackContent.ipdl new file mode 100644 index 000000000..1e19c541f --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/jetpackContent.ipdl @@ -0,0 +1,12 @@ +include protocol content; +include protocol jetpack; + +intr protocol jetpackContent { + bridges jetpack, content; + +child: + async __delete__(); + +state DEAD: + send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/ok/manageSelf.ipdl b/ipc/ipdl/test/ipdl/ok/manageSelf.ipdl new file mode 100644 index 000000000..b90ad7237 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/manageSelf.ipdl @@ -0,0 +1,10 @@ +include protocol manageSelf_Toplevel; + +protocol manageSelf { + manager manageSelf_Toplevel or manageSelf; + manages manageSelf; + +child: + manageSelf(); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/manageSelf_Toplevel.ipdl b/ipc/ipdl/test/ipdl/ok/manageSelf_Toplevel.ipdl new file mode 100644 index 000000000..8788338f3 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/manageSelf_Toplevel.ipdl @@ -0,0 +1,9 @@ +include protocol manageSelf; + +protocol manageSelf_Toplevel { + manages manageSelf; + +child: + manageSelf(); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/managedProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/managedProtocol.ipdl new file mode 100644 index 000000000..f59ab6651 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/managedProtocol.ipdl @@ -0,0 +1,8 @@ +include protocol managerProtocol; + +protocol managedProtocol { + manager managerProtocol; + +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl new file mode 100644 index 000000000..fc1e57ada --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/managerProtocol.ipdl @@ -0,0 +1,13 @@ +include protocol managedProtocol; + +// sanity check of managed/manager protocols + +protocol managerProtocol { + manages managedProtocol; + +parent: + managedProtocol(int i); + +state CREATING: + recv managedProtocol goto CREATING; +}; diff --git a/ipc/ipdl/test/ipdl/ok/media.ipdl b/ipc/ipdl/test/ipdl/ok/media.ipdl new file mode 100644 index 000000000..032f7df6f --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/media.ipdl @@ -0,0 +1,7 @@ +sync protocol media { +child: + async __delete__(); + +state DEAD: + send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/ok/messageCompress.ipdl b/ipc/ipdl/test/ipdl/ok/messageCompress.ipdl new file mode 100644 index 000000000..dd563fa3b --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/messageCompress.ipdl @@ -0,0 +1,4 @@ +intr protocol messageCompress { +child: + async foo() compress; +}; diff --git a/ipc/ipdl/test/ipdl/ok/messageVerify.ipdl b/ipc/ipdl/test/ipdl/ok/messageVerify.ipdl new file mode 100644 index 000000000..0de9ef4ad --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/messageVerify.ipdl @@ -0,0 +1,14 @@ +include protocol messageVerifyTopLevel; + +intr protocol messageVerify { + manager messageVerifyTopLevel; + +parent: + sync __delete__(uint32_t x) returns (double rv1) verify; + async msg1() verify; + async msg2(uint32_t aParam1) verify; + sync msg3() + returns (uint32_t rv1) verify; + sync msg4(uint32_t aParam1) + returns (uint32_t rv1) verify; +}; diff --git a/ipc/ipdl/test/ipdl/ok/messageVerifyTopLevel.ipdl b/ipc/ipdl/test/ipdl/ok/messageVerifyTopLevel.ipdl new file mode 100644 index 000000000..14ff22111 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/messageVerifyTopLevel.ipdl @@ -0,0 +1,7 @@ +include protocol messageVerify; + +intr protocol messageVerifyTopLevel{ + manages messageVerify; + parent: + sync messageVerify(uint32_t aParam1) returns (double rv1) verify; +}; diff --git a/ipc/ipdl/test/ipdl/ok/multiManaged.ipdl b/ipc/ipdl/test/ipdl/ok/multiManaged.ipdl new file mode 100644 index 000000000..c845d5086 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/multiManaged.ipdl @@ -0,0 +1,9 @@ +include protocol multiManager1; +include protocol multiManager2; + +protocol multiManaged { + manager multiManager1 or multiManager2; + +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/multiManager1.ipdl b/ipc/ipdl/test/ipdl/ok/multiManager1.ipdl new file mode 100644 index 000000000..0507a28cd --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/multiManager1.ipdl @@ -0,0 +1,8 @@ +include protocol multiManaged; + +protocol multiManager1 { + manages multiManaged; + +child: + multiManaged(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/multiManager2.ipdl b/ipc/ipdl/test/ipdl/ok/multiManager2.ipdl new file mode 100644 index 000000000..0cad3f206 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/multiManager2.ipdl @@ -0,0 +1,8 @@ +include protocol multiManaged; + +protocol multiManager2 { + manages multiManaged; + +child: + multiManaged(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/multiOutStates.ipdl b/ipc/ipdl/test/ipdl/ok/multiOutStates.ipdl new file mode 100644 index 000000000..d3554f9e7 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/multiOutStates.ipdl @@ -0,0 +1,12 @@ +protocol multiOutStates { +child: Msg(); + + // sanity check that multi-out-states are being processed correctly + +state S1: + send Msg goto S2 or S3 or S4; + +state S2: send Msg goto S2; +state S3: send Msg goto S3; +state S4: send Msg goto S4; +}; diff --git a/ipc/ipdl/test/ipdl/ok/multiStartState.ipdl b/ipc/ipdl/test/ipdl/ok/multiStartState.ipdl new file mode 100644 index 000000000..bbeb016ac --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/multiStartState.ipdl @@ -0,0 +1,11 @@ +protocol multiStartState { + +child: Msg(); __delete__(); + +start state S1: + send Msg goto S1; send __delete__; + +start state S2: + send Msg goto S2; send __delete__; + +}; diff --git a/ipc/ipdl/test/ipdl/ok/multipleUsingCxxTypes.ipdl b/ipc/ipdl/test/ipdl/ok/multipleUsingCxxTypes.ipdl new file mode 100644 index 000000000..045d3fa6e --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/multipleUsingCxxTypes.ipdl @@ -0,0 +1,7 @@ +using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; +using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; + +protocol multipleUsingCxxTypes { +child: + async Msg(void_t foo); +}; diff --git a/ipc/ipdl/test/ipdl/ok/mutualRecStructUnion.ipdl b/ipc/ipdl/test/ipdl/ok/mutualRecStructUnion.ipdl new file mode 100644 index 000000000..0891d3e78 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/mutualRecStructUnion.ipdl @@ -0,0 +1,21 @@ +struct X { + int i; + Y[] y; +}; + +union Y { + double; + X; + Z; +}; + +struct Z { + X x; + Y y; +}; + +protocol mutualRecStructUnion { +child: + async Test(X x, Y y, Z z); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/mutualRecUnion.ipdl b/ipc/ipdl/test/ipdl/ok/mutualRecUnion.ipdl new file mode 100644 index 000000000..fcd364920 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/mutualRecUnion.ipdl @@ -0,0 +1,20 @@ +union X { + int; + Y[]; +}; + +union Y { + X; + Z; +}; + +union Z { + double; + X; +}; + +protocol mutualRecUnion { +child: + async Test(X x, Y y, Z z); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/namespace_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/namespace_Basic.ipdl new file mode 100644 index 000000000..4eee5ffd1 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/namespace_Basic.ipdl @@ -0,0 +1,12 @@ +namespace basic { + +// sanity check of namespaced protocols + +protocol namespace_Basic { + +child: + async Msg(); + +}; + +} // namespace basic diff --git a/ipc/ipdl/test/ipdl/ok/noRedeclCrossMessage.ipdl b/ipc/ipdl/test/ipdl/ok/noRedeclCrossMessage.ipdl new file mode 100644 index 000000000..578ac82d8 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/noRedeclCrossMessage.ipdl @@ -0,0 +1,9 @@ +protocol noRedeclCrossMessage { + + // each message has its own scope for param/return names + +child: + async Msg1(int f); + async Msg2(int f); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/plugin.ipdl b/ipc/ipdl/test/ipdl/ok/plugin.ipdl new file mode 100644 index 000000000..b1e2d24a9 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/plugin.ipdl @@ -0,0 +1,7 @@ +intr protocol plugin { +child: + async __delete__(); + +state DEAD: + send __delete__; +}; diff --git a/ipc/ipdl/test/ipdl/ok/race_DiamondRule1.ipdl b/ipc/ipdl/test/ipdl/ok/race_DiamondRule1.ipdl new file mode 100644 index 000000000..cc1a8b16a --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/race_DiamondRule1.ipdl @@ -0,0 +1,27 @@ +protocol race_DiamondRule1 { + +child: + async Msg1(); + async Msg1_(); +parent: + async Msg2(); + async Msg2_(); + + // OK: this state machine is one of the simplest that follows the + // Diamond Rule + +start state S1: + send Msg1 goto S2; + recv Msg2 goto S3; + +state S2: + recv Msg2 goto S4; + recv Msg2_ goto S2; + +state S3: + send Msg1 goto S4; + send Msg1_ goto S3; + +state S4: + send Msg1 goto S4; +}; diff --git a/ipc/ipdl/test/ipdl/ok/race_KitchenSink.ipdl b/ipc/ipdl/test/ipdl/ok/race_KitchenSink.ipdl new file mode 100644 index 000000000..55d674ada --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/race_KitchenSink.ipdl @@ -0,0 +1,61 @@ +protocol race_KitchenSink { +child: + async Msg1(); + async Msg1_(); +parent: + async Msg2(); + async Msg2_(); + + + // concatenation of a few other state machines, should be OK + +start state S1: + send Msg1 goto S2; + recv Msg2 goto S3; + +state S2: + recv Msg2 goto S4; + recv Msg2_ goto S2; + +state S3: + send Msg1 goto S4; + send Msg1_ goto S3; + +state S4: + send Msg1 goto S4; + + + +start state S5: + send Msg1 goto S5; + recv Msg2 goto S5; + + + +start state S15: + send Msg1 goto S16 or S17; + recv Msg2 goto S18 or S19; + +state S16: + recv Msg2 goto S20; + recv Msg2_ goto S18; + +state S17: + recv Msg2 goto S20; + recv Msg2_ goto S15; + +state S18: + send Msg1 goto S20; + send Msg1_ goto S15; + +state S19: + send Msg1 goto S20; + send Msg1_ goto S16; + +state S20: + send Msg1 goto S20; + send Msg1_ goto S20; + recv Msg2 goto S20; + recv Msg2_ goto S20; + +}; diff --git a/ipc/ipdl/test/ipdl/ok/race_MultiOut.ipdl b/ipc/ipdl/test/ipdl/ok/race_MultiOut.ipdl new file mode 100644 index 000000000..80cffd91c --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/race_MultiOut.ipdl @@ -0,0 +1,35 @@ +protocol race_MultiOut { +child: + async Msg1(); + async Msg1_(); +parent: + async Msg2(); + async Msg2_(); + +start state S15: + send Msg1 goto S16 or S17; + recv Msg2 goto S18 or S19; + +state S16: + recv Msg2 goto S20; + recv Msg2_ goto S18; + +state S17: + recv Msg2 goto S20; + recv Msg2_ goto S15; + +state S18: + send Msg1 goto S20; + send Msg1_ goto S15; + +state S19: + send Msg1 goto S20; + send Msg1_ goto S16; + +state S20: + send Msg1 goto S20; + send Msg1_ goto S20; + recv Msg2 goto S20; + recv Msg2_ goto S20; + +}; diff --git a/ipc/ipdl/test/ipdl/ok/race_Stateless.ipdl b/ipc/ipdl/test/ipdl/ok/race_Stateless.ipdl new file mode 100644 index 000000000..6cffe1e7c --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/race_Stateless.ipdl @@ -0,0 +1,16 @@ +protocol race_Stateless { +// manages Child; + +child: + async Msg1(); + async Msg1_(); +parent: + async Msg2(); + async Msg2_(); + + + // OK: this is trivial stateless protocol, so race-free "by definition" +start state S5: + send Msg1 goto S5; + recv Msg2 goto S5; +}; diff --git a/ipc/ipdl/test/ipdl/ok/selfRecUnion.ipdl b/ipc/ipdl/test/ipdl/ok/selfRecUnion.ipdl new file mode 100644 index 000000000..70f04602c --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/selfRecUnion.ipdl @@ -0,0 +1,11 @@ +union R { + int; + double; + R; +}; + +protocol selfRecUnion { +child: + async Test(R r); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/shmem.ipdl b/ipc/ipdl/test/ipdl/ok/shmem.ipdl new file mode 100644 index 000000000..8bc44010b --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/shmem.ipdl @@ -0,0 +1,13 @@ +union Foo { + int; + Shmem; +}; + +intr protocol shmem { +parent: + async Msg(Shmem s, Foo f); + sync SyncMsg(Shmem s, Foo f) + returns (Shmem t, Foo g); + intr InterruptMsg(Shmem s, Foo f) + returns (Shmem t, Foo g); +}; diff --git a/ipc/ipdl/test/ipdl/ok/syncProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/syncProtocol.ipdl new file mode 100644 index 000000000..cfe7760e6 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/syncProtocol.ipdl @@ -0,0 +1,11 @@ +sync protocol syncProtocol { + + // sanity check of sync protocols + +child: + async AsyncMsg(); + +parent: + sync SyncMsg() returns (int i); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/threeDirections.ipdl b/ipc/ipdl/test/ipdl/ok/threeDirections.ipdl new file mode 100644 index 000000000..c06f9d56f --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/threeDirections.ipdl @@ -0,0 +1,13 @@ +protocol threeDirections { + + // sanity check that the three direction specifiers are being accepted +child: + async ChildMsg(); + +parent: + async ParentMsg(); + +both: + async BothMsg(); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/union_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/union_Basic.ipdl new file mode 100644 index 000000000..ef5b1cbfe --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/union_Basic.ipdl @@ -0,0 +1,11 @@ +union Basic { + int; + double; +}; + +sync protocol union_Basic { + +parent: + sync Msg(Basic p) returns (Basic r); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/union_Namespaced.ipdl b/ipc/ipdl/test/ipdl/ok/union_Namespaced.ipdl new file mode 100644 index 000000000..02f86f9de --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/union_Namespaced.ipdl @@ -0,0 +1,18 @@ +namespace kitties { + +union Socks { + int; + double; +}; + +} // namespace kitties + + +namespace puppies { + +protocol union_Namespaced { +child: + async Msg(Socks s); +}; + +} // namespace puppies diff --git a/ipc/ipdl/test/ipdl/runtests.py b/ipc/ipdl/test/ipdl/runtests.py new file mode 100644 index 000000000..93a73729e --- /dev/null +++ b/ipc/ipdl/test/ipdl/runtests.py @@ -0,0 +1,75 @@ +import os, unittest + +from IPDLCompile import IPDLCompile + + +class IPDLTestCase(unittest.TestCase): + def __init__(self, ipdlargv, filename): + unittest.TestCase.__init__(self, 'test') + self.filename = filename + self.compile = IPDLCompile(filename, ipdlargv) + + def test(self): + self.compile.run() + self.assertFalse(self.compile.exception(), self.mkFailMsg()) + self.checkPassed() + + def mkFailMsg(self): + return ''' +### Command: %s +### stderr: +%s'''% (' '.join(self.compile.argv), self.compile.stderr) + + def shortDescription(self): + return '%s test of "%s"'% (self.__class__.__name__, self.filename) + + +class OkTestCase(IPDLTestCase): + '''An invocation of the IPDL compiler on a valid specification. +The IPDL compiler should not produce errors or exceptions.''' + + def __init__(self, ipdlargv, filename): + IPDLTestCase.__init__(self, ipdlargv, filename) + + def checkPassed(self): + self.assertTrue(self.compile.ok(), self.mkFailMsg()) + + +class ErrorTestCase(IPDLTestCase): + '''An invocation of the IPDL compiler on an *invalid* specification. +The IPDL compiler *should* produce errors but not exceptions.''' + + def __init__(self, ipdlargv, filename): + IPDLTestCase.__init__(self, ipdlargv, filename) + + def checkPassed(self): + self.assertTrue(self.compile.error(), self.mkFailMsg()) + + +if __name__ == '__main__': + import sys + + okdir = sys.argv[1] + assert os.path.isdir(okdir) + errordir = sys.argv[2] + assert os.path.isdir(errordir) + + ipdlargv = [ ] + oksuite = unittest.TestSuite() + errorsuite = unittest.TestSuite() + + oktests, errortests = 0, 0 + for arg in sys.argv[3:]: + if errortests: + errorsuite.addTest(ErrorTestCase(ipdlargv+ [ '-I', errordir ], + arg)) + elif oktests: + if 'ERRORTESTS' == arg: errortests = 1; continue + oksuite.addTest(OkTestCase(ipdlargv+ [ '-I', okdir ], + arg)) + else: + if 'OKTESTS' == arg: oktests = 1; continue + ipdlargv.append(arg) + + (unittest.TextTestRunner()).run( + unittest.TestSuite([ oksuite, errorsuite ])) diff --git a/ipc/ipdl/test/moz.build b/ipc/ipdl/test/moz.build new file mode 100644 index 000000000..5d5caacf8 --- /dev/null +++ b/ipc/ipdl/test/moz.build @@ -0,0 +1,12 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=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/. + +# we ignore MOZ_IPDL_TESTS for the IPDL-compiler-only tests, since they're +# quick and painless +DIRS += ['ipdl'] + +if CONFIG['MOZ_IPDL_TESTS']: + DIRS += ['cxx'] |