diff options
Diffstat (limited to 'ipc/ipdl')
329 files changed, 26030 insertions, 0 deletions
diff --git a/ipc/ipdl/Makefile.in b/ipc/ipdl/Makefile.in new file mode 100644 index 0000000000..7a4d2acfba --- /dev/null +++ b/ipc/ipdl/Makefile.in @@ -0,0 +1,33 @@ +# 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/. + +ifdef COMPILE_ENVIRONMENT + +# This file is generated by the moz.build backend. +include ipdlsrcs.mk + +include $(topsrcdir)/config/rules.mk + +ipdl_py_deps := \ + $(wildcard $(srcdir)/*.py) \ + $(wildcard $(srcdir)/ipdl/*.py) \ + $(wildcard $(srcdir)/ipdl/cxx/*.py) \ + $(wildcard $(topsrcdir)/other-licenses/ply/ply/*.py) \ + $(NULL) + +# NB: the IPDL compiler manages .ipdl-->.h/.cpp dependencies itself, +# which is why we don't have explicit .h/.cpp targets here +ipdl.track: $(ALL_IPDLSRCS) $(srcdir)/sync-messages.ini $(srcdir)/message-metadata.ini $(ipdl_py_deps) + $(PYTHON3) $(srcdir)/ipdl.py \ + --sync-msg-list=$(srcdir)/sync-messages.ini \ + --msg-metadata=$(srcdir)/message-metadata.ini \ + --outheaders-dir=_ipdlheaders \ + --outcpp-dir=. \ + $(IPDLDIRS:%=-I%) \ + $(ALL_IPDLSRCS) + touch $@ + +export:: ipdl.track +endif + diff --git a/ipc/ipdl/ipdl.py b/ipc/ipdl/ipdl.py new file mode 100644 index 0000000000..e41691a742 --- /dev/null +++ b/ipc/ipdl/ipdl.py @@ -0,0 +1,318 @@ +# 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/. +from __future__ import print_function + +from io import StringIO +import optparse +import os +import sys +from configparser import RawConfigParser + +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( + "-s", + "--sync-msg-list", + dest="syncMsgList", + default="sync-messages.ini", + help="Config file listing allowed sync messages", +) +op.add_option( + "-m", + "--msg-metadata", + dest="msgMetadata", + default="message-metadata.ini", + help="Predicted message sizes for reducing serialization malloc overhead.", +) +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 +syncMsgList = options.syncMsgList +msgMetadata = options.msgMetadata +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") + +log(2, 'Generated C++ headers will be generated relative to "%s"', headersdir) +log(2, 'Generated C++ sources will be generated in "%s"', cppdir) + +allmessages = {} +allmessageprognames = [] +allprotocols = [] + + +def normalizedFilename(f): + if f == "-": + return "<stdin>" + return f + + +log(2, "Reading sync message list") +parser = RawConfigParser() +parser.read_file(open(options.syncMsgList)) +syncMsgList = parser.sections() + +for section in syncMsgList: + if not parser.get(section, "description"): + print("Error: Sync message %s lacks a description" % section, file=sys.stderr) + sys.exit(1) + +# Read message metadata. Right now we only have 'segment_capacity' +# for the standard segment size used for serialization. +log(2, "Reading message metadata...") +msgMetadataConfig = RawConfigParser() +msgMetadataConfig.read_file(open(options.msgMetadata)) + +segmentCapacityDict = {} +for msgName in msgMetadataConfig.sections(): + if msgMetadataConfig.has_option(msgName, "segment_capacity"): + capacity = msgMetadataConfig.get(msgName, "segment_capacity") + segmentCapacityDict[msgName] = capacity + +# 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("Specification could not be parsed.", file=sys.stderr) + sys.exit(1) + + log(2, "checking types") + if not ipdl.typecheck(ast): + print("Specification is not well typed.", file=sys.stderr) + sys.exit(1) + + if not ipdl.checkSyncMessage(ast, syncMsgList): + print( + "Error: New sync IPC messages must be reviewed by an IPC peer and recorded in %s" + % options.syncMsgList, + file=sys.stderr, + ) # NOQA: E501 + sys.exit(1) + +if not ipdl.checkFixedSyncMessages(parser): + # Errors have alraedy been printed to stderr, just exit + sys.exit(1) + +# 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, segmentCapacityDict) + + if ast.protocol: + allmessages[ast.protocol.name] = ipdl.genmsgenum(ast) + allprotocols.append(ast.protocol.name) + # e.g. PContent::RequestMemoryReport (not prefixed or suffixed.) + for md in ast.protocol.messageDecls: + allmessageprognames.append("%s::%s" % (md.namespace, md.decl.progname)) + +allprotocols.sort() + +# Check if we have undefined message names in segmentCapacityDict. +# This is a fool-proof of the 'message-metadata.ini' file. +undefinedMessages = set(segmentCapacityDict.keys()) - set(allmessageprognames) +if len(undefinedMessages) > 0: + print("Error: Undefined message names in message-metadata.ini:", file=sys.stderr) + print(undefinedMessages, file=sys.stderr) + sys.exit(1) + +ipcmsgstart = StringIO() + +print( + """ +// CODE GENERATED by ipdl.py. Do not edit. + +#ifndef IPCMessageStart_h +#define IPCMessageStart_h + +enum IPCMessageStart { +""", + file=ipcmsgstart, +) + +for name in allprotocols: + print(" %sMsgStart," % name, file=ipcmsgstart) + +print( + """ + LastMsgIndex +}; + +static_assert(LastMsgIndex <= 65536, "need to update IPC_MESSAGE_MACRO"); + +#endif // ifndef IPCMessageStart_h +""", + file=ipcmsgstart, +) + +ipc_msgtype_name = StringIO() +print( + """ +// CODE GENERATED by ipdl.py. Do not edit. +#include <cstdint> + +#include "mozilla/ipc/ProtocolUtils.h" +#include "IPCMessageStart.h" + +using std::uint32_t; + +namespace { + +enum IPCMessages { +""", + file=ipc_msgtype_name, +) + +for protocol in sorted(allmessages.keys()): + for (msg, num) in allmessages[protocol].idnums: + if num: + print(" %s = %s," % (msg, num), file=ipc_msgtype_name) + elif not msg.endswith("End"): + print(" %s__%s," % (protocol, msg), file=ipc_msgtype_name) + +print( + """ +}; + +} // anonymous namespace + +namespace IPC { + +const char* StringFromIPCMessageType(uint32_t aMessageType) +{ + switch (aMessageType) { +""", + file=ipc_msgtype_name, +) + +for protocol in sorted(allmessages.keys()): + for (msg, num) in allmessages[protocol].idnums: + if num or msg.endswith("End"): + continue + print( + """ + case %s__%s: + return "%s::%s";""" + % (protocol, msg, protocol, msg), + file=ipc_msgtype_name, + ) + +print( + """ + case CHANNEL_OPENED_MESSAGE_TYPE: + return "CHANNEL_OPENED_MESSAGE"; + case SHMEM_DESTROYED_MESSAGE_TYPE: + return "SHMEM_DESTROYED_MESSAGE"; + case SHMEM_CREATED_MESSAGE_TYPE: + return "SHMEM_CREATED_MESSAGE"; + case GOODBYE_MESSAGE_TYPE: + return "GOODBYE_MESSAGE"; + case CANCEL_MESSAGE_TYPE: + return "CANCEL_MESSAGE"; + default: + return "<unknown IPC msg name>"; + } +} + +} // namespace IPC + +namespace mozilla { +namespace ipc { + +const char* ProtocolIdToName(IPCMessageStart aId) { + switch (aId) { +""", + file=ipc_msgtype_name, +) + +for name in allprotocols: + print(" case %sMsgStart:" % name, file=ipc_msgtype_name) + print(' return "%s";' % name, file=ipc_msgtype_name) + +print( + """ + default: + return "<unknown protocol id>"; + } +} + +} // namespace ipc +} // namespace mozilla +""", + file=ipc_msgtype_name, +) + +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 0000000000..a85a12e14a --- /dev/null +++ b/ipc/ipdl/ipdl/__init__.py @@ -0,0 +1,100 @@ +# 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/. + +from __future__ import print_function + +__all__ = [ + "gencxx", + "genipdl", + "parse", + "typecheck", + "writeifmodified", + "checkSyncMessage", + "checkFixedSyncMessages", +] + +import os +import sys +from io import StringIO + +from ipdl.cgen import IPDLCodeGen +from ipdl.lower import LowerToCxx, msgenums +from ipdl.parser import Parser, ParseError +from ipdl.type import TypeCheck +from ipdl.checker import checkSyncMessage, checkFixedSyncMessages + +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" + + try: + return Parser(type, name).parse( + specstring, os.path.abspath(filename), includedirs + ) + except ParseError as p: + print(p, file=errout) + return None + + +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, segmentcapacitydict): + headers, cpps = LowerToCxx().lower(ast, segmentcapacitydict) + + 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): + contents = contents.encode("utf-8") + 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 0000000000..cf37b39a76 --- /dev/null +++ b/ipc/ipdl/ipdl/ast.py @@ -0,0 +1,406 @@ +# 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/. + +from .util import hash_str + + +NOT_NESTED = 1 +INSIDE_SYNC_NESTED = 2 +INSIDE_CPOW_NESTED = 3 + +NORMAL_PRIORITY = 1 +INPUT_PRIORITY = 2 +HIGH_PRIORITY = 3 +MEDIUMHIGH_PRIORITY = 4 + + +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 mgr in p.managers: + mgr.accept(self) + for managed in p.managesStmts: + managed.accept(self) + for msgDecl in p.messageDecls: + msgDecl.accept(self) + + def visitNamespace(self, ns): + 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 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, + refcounted=False, + moveonly=False, + ): + 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 + self.refcounted = refcounted + self.moveonly = moveonly + + def canBeForwardDeclared(self): + return self.isClass() or self.isStruct() + + def isClass(self): + return self.kind == "class" + + def isStruct(self): + return self.kind == "struct" + + def isRefcounted(self): + return self.refcounted + + def isMoveonly(self): + return self.moveonly + + +# "singletons" + + +class PrettyPrinted: + @classmethod + def __hash__(cls): + return hash_str(cls.pretty) + + @classmethod + def __str__(cls): + return cls.pretty + + +class ASYNC(PrettyPrinted): + pretty = "async" + + +class TAINTED(PrettyPrinted): + pretty = "tainted" + + +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.managers = [] + self.managesStmts = [] + self.messageDecls = [] + + +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, comparable): + NamespacedNode.__init__(self, loc, name) + self.fields = fields + self.comparable = comparable + # A list of indices into `fields` for determining the order in + # which fields are laid out in memory. We don't just reorder + # `fields` itself so as to keep the ordering reasonably stable + # for e.g. C++ constructors when new fields are added. + self.packed_field_ordering = [] + + +class UnionDecl(NamespacedNode): + def __init__(self, loc, name, components, comparable): + NamespacedNode.__init__(self, loc, name) + self.components = components + self.comparable = comparable + + +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.tainted = "" + 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.startswith("tainted"): + self.tainted = modifier + elif modifier != "": + raise Exception("Unexpected message modifier `%s'" % modifier) + + +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): + Node.__init__(self, loc) + self.spec = spec # QualifiedId + self.array = False # bool + self.maybe = False # bool + self.nullable = False # bool + self.uniqueptr = False # bool + + def basename(self): + return self.spec.baseid + + 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 0000000000..13ad77e61f --- /dev/null +++ b/ipc/ipdl/ipdl/builtin.py @@ -0,0 +1,70 @@ +# 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", + # You may be tempted to add size_t. Do not! See bug 1525199. + # Mozilla types: "less" standard things we know how serialize/deserialize + "nsresult", + "nsString", + "nsCString", + "nsDependentSubstring", + "nsDependentCSubstring", + "mozilla::ipc::Shmem", + "mozilla::ipc::ByteBuf", + "mozilla::UniquePtr", + "mozilla::ipc::FileDescriptor", +) + + +# XXX(Bug 1677487) Can we restrict including ByteBuf.h, FileDescriptor.h, +# MozPromise.h and Shmem.h to those protocols that really use them? +HeaderIncludes = ( + "mozilla/Attributes.h", + "IPCMessageStart.h", + "mozilla/RefPtr.h", + "nsString.h", + "nsTArray.h", + "nsTHashtable.h", + "mozilla/MozPromise.h", + "mozilla/OperatorNewExtensions.h", + "mozilla/UniquePtr.h", + "mozilla/ipc/ByteBuf.h", + "mozilla/ipc/FileDescriptor.h", + "mozilla/ipc/ProtocolUtilsFwd.h", + "mozilla/ipc/Shmem.h", +) + +CppIncludes = ( + "ipc/IPCMessageUtils.h", + "nsIFile.h", + "GeckoProfiler.h", + "mozilla/ipc/Endpoint.h", + "mozilla/ipc/ProtocolMessageUtils.h", + "mozilla/ipc/ProtocolUtils.h", + "mozilla/ipc/ShmemMessageUtils.h", + "mozilla/ipc/TaintingIPCUtils.h", +) diff --git a/ipc/ipdl/ipdl/cgen.py b/ipc/ipdl/ipdl/cgen.py new file mode 100644 index 0000000000..8ed8da4d81 --- /dev/null +++ b/ipc/ipdl/ipdl/cgen.py @@ -0,0 +1,108 @@ +# 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.ast import Visitor + + +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) # NOQA: F821 + + 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() + + 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/checker.py b/ipc/ipdl/ipdl/checker.py new file mode 100644 index 0000000000..eb969b4d60 --- /dev/null +++ b/ipc/ipdl/ipdl/checker.py @@ -0,0 +1,81 @@ +# 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/. + +from __future__ import print_function + +import sys +from ipdl.ast import Visitor, ASYNC + + +class SyncMessageChecker(Visitor): + syncMsgList = [] + seenProtocols = [] + seenSyncMessages = [] + + def __init__(self, syncMsgList): + SyncMessageChecker.syncMsgList = syncMsgList + self.errors = [] + + def prettyMsgName(self, msg): + return "%s::%s" % (self.currentProtocol, msg) + + def errorUnknownSyncMessage(self, loc, msg): + self.errors.append("%s: error: Unknown sync IPC message %s" % (str(loc), msg)) + + def errorAsyncMessageCanRemove(self, loc, msg): + self.errors.append( + "%s: error: IPC message %s is async, can be delisted" % (str(loc), msg) + ) + + def visitProtocol(self, p): + self.errors = [] + self.currentProtocol = p.name + SyncMessageChecker.seenProtocols.append(p.name) + Visitor.visitProtocol(self, p) + + def visitMessageDecl(self, md): + pn = self.prettyMsgName(md.name) + if md.sendSemantics is not ASYNC: + if pn not in SyncMessageChecker.syncMsgList: + self.errorUnknownSyncMessage(md.loc, pn) + SyncMessageChecker.seenSyncMessages.append(pn) + elif pn in SyncMessageChecker.syncMsgList: + self.errorAsyncMessageCanRemove(md.loc, pn) + + @staticmethod + def getFixedSyncMessages(): + return set(SyncMessageChecker.syncMsgList) - set( + SyncMessageChecker.seenSyncMessages + ) + + +def checkSyncMessage(tu, syncMsgList, errout=sys.stderr): + checker = SyncMessageChecker(syncMsgList) + tu.accept(checker) + if len(checker.errors): + for error in checker.errors: + print(error, file=errout) + return False + return True + + +def checkFixedSyncMessages(config, errout=sys.stderr): + fixed = SyncMessageChecker.getFixedSyncMessages() + error_free = True + for item in fixed: + protocol = item.split("::")[0] + # Ignore things like sync messages in test protocols we didn't compile. + # Also, ignore platform-specific IPC messages. + if ( + protocol in SyncMessageChecker.seenProtocols + and "platform" not in config.options(item) + ): + print( + "Error: Sync IPC message %s not found, it appears to be fixed.\n" + "Please remove it from sync-messages.ini." % item, + file=errout, + ) + error_free = False + return error_free diff --git a/ipc/ipdl/ipdl/cxx/__init__.py b/ipc/ipdl/ipdl/cxx/__init__.py new file mode 100644 index 0000000000..6fbe8159b2 --- /dev/null +++ b/ipc/ipdl/ipdl/cxx/__init__.py @@ -0,0 +1,3 @@ +# 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/ipdl/cxx/ast.py b/ipc/ipdl/ipdl/cxx/ast.py new file mode 100644 index 0000000000..02f204f892 --- /dev/null +++ b/ipc/ipdl/ipdl/cxx/ast.py @@ -0,0 +1,1018 @@ +# 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 +import functools + +from ipdl.util import hash_str + + +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 visitVerbatimNode(self, verb): + pass + + def visitGroupNode(self, group): + for node in group.nodes: + node.accept(self) + + 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 visitTypeFunction(self, fn): + 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 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 visitExprLambda(self, l): + self.visitBlock(l) + + 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=False): + Node.__init__(self) + self.ws = ws + self.indent = indent + + +Whitespace.NL = Whitespace("\n") + + +class VerbatimNode(Node): + # A block of text to be written verbatim to the output file. + # + # NOTE: This node is usually created by `code`. See `code.py` for details. + # FIXME: Merge Whitespace and VerbatimNode? They're identical. + def __init__(self, text, indent=0): + Node.__init__(self) + self.text = text + self.indent = indent + + +class GroupNode(Node): + # A group of nodes to be treated as a single node. These nodes have an + # optional indentation level which should be applied when generating them. + # + # NOTE: This node is usually created by `code`. See `code.py` for details. + def __init__(self, nodes, offset=0): + Node.__init__(self) + self.nodes = nodes + self.offset = offset + + +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) + + def addcode(self, tmpl, **context): + from ipdl.cxx.code import StmtCode + + self.addstmt(StmtCode(tmpl, **context)) + + +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) + + def addcode(self, tmpl, **context): + from ipdl.cxx.code import StmtCode + + self.addstmt(StmtCode(tmpl, **context)) + + +# ------------------------------ +# 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=False, + ptr=False, + ptrptr=False, + ptrconstptr=False, + ref=False, + rvalref=False, + hasimplicitcopyctor=True, + T=None, + inner=None, + ): + """ + Represents the type |name<T>::inner| with the ptr and const + modifiers as specified. + + To avoid getting fancy with recursive types, we limit the kinds + of pointer types that can be be constructed. + + ptr => T* + ptrptr => T** + ptrconstptr => T* const* + ref => T& + rvalref => T&& + + Any type, naked or pointer, can be const (const T) or ref (T&).""" + assert isinstance(name, str) + assert isinstance(const, bool) + assert isinstance(ptr, bool) + assert isinstance(ptrptr, bool) + assert isinstance(ptrconstptr, bool) + assert isinstance(ref, bool) + assert isinstance(rvalref, bool) + assert not isinstance(T, str) + + Node.__init__(self) + self.name = name + self.const = const + self.ptr = ptr + self.ptrptr = ptrptr + self.ptrconstptr = ptrconstptr + self.ref = ref + self.rvalref = rvalref + self.hasimplicitcopyctor = hasimplicitcopyctor + self.T = T + self.inner = inner + # 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, + ptrptr=self.ptrptr, + ptrconstptr=self.ptrconstptr, + ref=self.ref, + rvalref=self.rvalref, + T=copy.deepcopy(self.T, memo), + inner=copy.deepcopy(self.inner, 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=True) +Type.SIZE = Type("size_t") +Type.VOID = Type("void") +Type.VOIDPTR = Type("void", ptr=True) +Type.AUTO = Type("auto") +Type.AUTORVAL = Type("auto", rvalref=True) + + +class TypeArray(Node): + def __init__(self, basetype, nmemb): + """the type |basetype DECLNAME[nmemb]|. |nmemb| is an Expr""" + self.basetype = basetype + self.nmemb = 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 TypeFunction(Node): + def __init__(self, params=[], ret=Type("void")): + """Anonymous function type std::function<>""" + self.params = params + self.ret = ret + + +@functools.total_ordering +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 __lt__(self, other): + return self.totypename < other.totypename + + def __eq__(self, other): + return self.__class__ == other.__class__ and self.totypename == other.totypename + + def __hash__(self): + return hash_str(self.totypename) + + +class Using(Node): + def __init__(self, type): + Node.__init__(self) + self.type = type + + +class ForwardDecl(Node): + def __init__(self, pqname, cls=False, struct=False): + # Exactly one of cls and struct must be set + assert cls ^ 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=False, + abstract=False, + final=False, + specializes=None, + struct=False, + ): + 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 + + +# Python2 polyfill for Python3's Enum() functional API. + + +def make_enum(name, members_str): + members_list = members_str.split() + members_dict = {} + for member_value, member in enumerate(members_list, start=1): + members_dict[member] = member_value + return type(name, (), members_dict) + + +MethodSpec = make_enum("MethodSpec", "NONE VIRTUAL PURE OVERRIDE STATIC") + + +class MethodDecl(Node): + def __init__( + self, + name, + params=[], + ret=Type("void"), + methodspec=MethodSpec.NONE, + const=False, + warn_unused=False, + force_inline=False, + typeop=None, + T=None, + cls=None, + ): + 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 isinstance(const, bool) + assert isinstance(warn_unused, bool) + assert isinstance(force_inline, bool) + + if typeop is not None: + assert methodspec == MethodSpec.NONE + ret = None + + Node.__init__(self) + self.name = name + self.params = params # [ Param ] + self.ret = ret # Type or None + self.methodspec = methodspec # enum + self.const = const # bool + self.warn_unused = warn_unused # bool + self.force_inline = force_inline or bool(T) # bool + self.typeop = typeop # Type or None + self.T = T # Type or None + self.cls = cls # Class 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), + methodspec=self.methodspec, + const=self.const, + warn_unused=self.warn_unused, + force_inline=self.force_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"), + methodspec=MethodSpec.NONE, + warn_unused=False, + force_inline=False, + T=None, + ): + assert methodspec == MethodSpec.NONE or methodspec == MethodSpec.STATIC + MethodDecl.__init__( + self, + name, + params=params, + ret=ret, + methodspec=methodspec, + warn_unused=warn_unused, + 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=False, force_inline=False): + 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, methodspec=MethodSpec.NONE, force_inline=False): + # C++ allows pure or override destructors, but ipdl cgen does not. + assert methodspec == MethodSpec.NONE or methodspec == MethodSpec.VIRTUAL + MethodDecl.__init__( + self, + name, + params=[], + ret=None, + methodspec=methodspec, + force_inline=force_inline, + ) + + def __deepcopy__(self, memo): + return DestructorDecl( + self.name, methodspec=self.methodspec, force_inline=self.force_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") + + 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, static=False, const=False): + # Exactly one of these should be set + assert static ^ const + + Node.__init__(self) + self.expr = expr + self.type = type + self.static = static + self.const = const + + +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 ExprSelect(Node): + def __init__(self, obj, op, field): + assert obj and op and field + assert not isinstance(obj, str) + assert isinstance(op, str) + + Node.__init__(self) + self.obj = obj + self.op = op + if isinstance(field, str): + self.field = ExprVar(field) + else: + 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("std::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 or ctype.rvalref) + + 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 ExprLambda(Block): + def __init__(self, captures=[], params=[], ret=None): + Block.__init__(self) + assert isinstance(captures, list) + assert isinstance(params, list) + self.captures = captures + self.params = params + self.ret = ret + + +# ------------------------------ +# 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 isinstance(block, GroupNode) + or isinstance(block, VerbatimNode) + or ( + hasattr(block, "stmts") + and ( + isinstance(block.stmts[-1], StmtBreak) + or isinstance(block.stmts[-1], StmtReturn) + or isinstance(block.stmts[-1], GroupNode) + or isinstance(block.stmts[-1], VerbatimNode) + ) + ) + ) + self.addstmt(case) + self.addstmt(block) + 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 0000000000..2fa74e8d35 --- /dev/null +++ b/ipc/ipdl/ipdl/cxx/cgen.py @@ -0,0 +1,558 @@ +# 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 MethodSpec, TypeArray, Visitor, DestructorDecl + + +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 visitVerbatimNode(self, verb): + if verb.indent: + self.printdent("") + self.write(verb.text) + + def visitGroupNode(self, group): + offsetCols = self.indentCols * group.offset + self.col += offsetCols + for node in group.nodes: + node.accept(self) + self.col -= offsetCols + + 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("<") + if type(t.T) is list: + t.T[0].accept(self) + for tt in t.T[1:]: + self.write(", ") + tt.accept(self) + else: + t.T.accept(self) + self.write(">") + + if t.inner is not None: + self.write("::") + t.inner.accept(self) + + ts = "" + if t.ptr: + ts += "*" + elif t.ptrptr: + ts += "**" + elif t.ptrconstptr: + ts += "* const*" + + if t.ref: + ts += "&" + elif t.rvalref: + ts += "&&" + + 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 visitTypeFunction(self, fn): + self.write("std::function<") + fn.ret.accept(self) + self.write("(") + self.writeDeclList(fn.params) + self.write(")>") + + 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): + if md.T: + self.write("template<") + self.write("typename ") + md.T.accept(self) + self.println(">") + self.printdent() + + if md.warn_unused: + self.write("[[nodiscard]] ") + + if md.methodspec == MethodSpec.STATIC: + self.write("static ") + elif md.methodspec == MethodSpec.VIRTUAL or md.methodspec == MethodSpec.PURE: + 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.cls is not None: + assert md.only_for_definition + + self.write(md.cls.name) + if md.cls.specializes is not None: + self.write("<") + md.cls.specializes.accept(self) + self.write(">") + self.write("::") + + if md.typeop is not None: + self.write("operator ") + md.typeop.accept(self) + else: + if isinstance(md, DestructorDecl): + self.write("~") + 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.methodspec == MethodSpec.OVERRIDE: + self.write(" override") + elif md.methodspec == MethodSpec.PURE: + self.write(" = 0") + + def visitMethodDefn(self, md): + # Method specifiers are for decls, not defns. + assert md.decl.methodspec == MethodSpec.NONE + + 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): + self.visitMethodDecl(dd) + + 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): + if c.static: + pfx, sfx = "static_cast<", ">" + else: + assert c.const + pfx, sfx = "const_cast<", ">" + 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 visitExprSelect(self, es): + self.write("(") + es.obj.accept(self) + self.write(")") + self.write(es.op) + es.field.accept(self) + + 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 visitExprLambda(self, l): + self.write("[") + ncaptures = len(l.captures) + for i, c in enumerate(l.captures): + c.accept(self) + if i != (ncaptures - 1): + self.write(", ") + self.write("](") + self.writeDeclList(l.params) + self.write(")") + if l.ret: + self.write(" -> ") + l.ret.accept(self) + self.println(" {") + self.indent() + self.visitBlock(l) + self.dedent() + self.printdent("}") + + 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/cxx/code.py b/ipc/ipdl/ipdl/cxx/code.py new file mode 100644 index 0000000000..0b5019b623 --- /dev/null +++ b/ipc/ipdl/ipdl/cxx/code.py @@ -0,0 +1,187 @@ +# 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/. + +# This module contains functionality for adding formatted, opaque "code" blocks +# into the IPDL ast. These code objects follow IPDL C++ ast patterns, and +# perform lowering in much the same way. + +# In general it is recommended to use these for blocks of code which would +# otherwise be specified by building a hardcoded IPDL-C++ AST, as users of this +# API are often easier to read than users of the AST APIs in these cases. + +import re +import math +import textwrap + +from ipdl.cxx.ast import Node, Whitespace, GroupNode, VerbatimNode + + +# ----------------------------------------------------------------------------- +# Public API. + + +def StmtCode(tmpl, **kwargs): + """Perform template substitution to build opaque C++ AST nodes. See the + module documentation for more information on the templating syntax. + + StmtCode nodes should be used where Stmt* nodes are used. They are placed + on their own line and indented.""" + return _code(tmpl, False, kwargs) + + +def ExprCode(tmpl, **kwargs): + """Perform template substitution to build opaque C++ AST nodes. See the + module documentation for more information on the templating syntax. + + ExprCode nodes should be used where Expr* nodes are used. They are placed + inline, and no trailing newline is added.""" + return _code(tmpl, True, kwargs) + + +def StmtVerbatim(text): + """Build an opaque C++ AST node which emits input text verbatim. + + StmtVerbatim nodes should be used where Stmt* nodes are used. They are placed + on their own line and indented.""" + return _verbatim(text, False) + + +def ExprVerbatim(text): + """Build an opaque C++ AST node which emits input text verbatim. + + ExprVerbatim nodes should be used where Expr* nodes are used. They are + placed inline, and no trailing newline is added.""" + return _verbatim(text, True) + + +# ----------------------------------------------------------------------------- +# Implementation + + +def _code(tmpl, inline, context): + # Remove common indentation, and strip the preceding newline from + # '''-quoting, because we usually don't want it. + if tmpl.startswith("\n"): + tmpl = tmpl[1:] + tmpl = textwrap.dedent(tmpl) + + # Process each line in turn, building up a list of nodes. + nodes = [] + for idx, line in enumerate(tmpl.splitlines()): + # Place newline tokens between lines in the input. + if idx > 0: + nodes.append(Whitespace.NL) + + # Don't indent the first line if `inline` is set. + skip_indent = inline and idx == 0 + nodes.append(_line(line.rstrip(), skip_indent, idx + 1, context)) + + # If we're inline, don't add the final trailing newline. + if not inline: + nodes.append(Whitespace.NL) + return GroupNode(nodes) + + +def _verbatim(text, inline): + # For simplicitly, _verbatim is implemented using the same logic as _code, + # but with '$' characters escaped. This ensures we only need to worry about + # a single, albeit complex, codepath. + return _code(text.replace("$", "$$"), inline, {}) + + +# Pattern used to identify substitutions. +_substPat = re.compile( + r""" + \$(?: + (?P<escaped>\$) | # '$$' is an escaped '$' + (?P<list>[*,])?{(?P<expr>[^}]+)} | # ${expr}, $*{expr}, or $,{expr} + (?P<invalid>) # For error reporting + ) + """, + re.IGNORECASE | re.VERBOSE, +) + + +def _line(raw, skip_indent, lineno, context): + assert "\n" not in raw + + # Determine the level of indentation used for this line + line = raw.lstrip() + offset = int(math.ceil((len(raw) - len(line)) / 4)) + + # If line starts with a directive, don't indent it. + if line.startswith("#"): + skip_indent = True + + column = 0 + children = [] + for match in _substPat.finditer(line): + if match.group("invalid") is not None: + raise ValueError("Invalid substitution on line %d" % lineno) + + # Any text from before the current entry should be written, and column + # advanced. + if match.start() > column: + before = line[column : match.start()] + children.append(VerbatimNode(before)) + column = match.end() + + # If we have an escaped group, emit a '$' node. + if match.group("escaped") is not None: + children.append(VerbatimNode("$")) + continue + + # At this point we should have an expression. + list_chr = match.group("list") + expr = match.group("expr") + assert expr is not None + + # Evaluate our expression in the context to get the values. + try: + values = eval(expr, context, {}) + except Exception as e: + msg = "%s in substitution on line %d" % (repr(e), lineno) + raise ValueError(msg) from e + + # If we aren't dealing with lists, wrap the result into a + # single-element list. + if list_chr is None: + values = [values] + + # Check if this substitution is inline, or the entire line. + inline = match.span() != (0, len(line)) + + for idx, value in enumerate(values): + # If we're using ',' as list mode, put a comma between each node. + if idx > 0 and list_chr == ",": + children.append(VerbatimNode(", ")) + + # If our value isn't a node, turn it into one. Verbatim should be + # inline unless indent isn't being skipped, and the match isn't + # inline. + if not isinstance(value, Node): + value = _verbatim(str(value), skip_indent or inline) + children.append(value) + + # If we were the entire line, indentation is handled by the added child + # nodes. Do this after the above loop such that created verbatims have + # the correct inline-ness. + if not inline: + skip_indent = True + + # Add any remaining text in the line. + if len(line) > column: + children.append(VerbatimNode(line[column:])) + + # If we have no children, just emit the empty string. This will become a + # blank line. + if len(children) == 0: + return VerbatimNode("") + + # Add the initial indent if we aren't skipping it. + if not skip_indent: + children.insert(0, VerbatimNode("", indent=True)) + + # Wrap ourselves into a group node with the correct indent offset + return GroupNode(children, offset=offset) diff --git a/ipc/ipdl/ipdl/direct_call.py b/ipc/ipdl/ipdl/direct_call.py new file mode 100644 index 0000000000..42d89df1b0 --- /dev/null +++ b/ipc/ipdl/ipdl/direct_call.py @@ -0,0 +1,763 @@ +# 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/. + + +# Our long term goal is to burn this list down, so new entries should be added +# extremely sparingly and only with a very good reason! You must have an IPC +# peer's r+ to add something new! + +# {(Protocol, side): (Class, HeaderFile)} +DIRECT_CALL_OVERRIDES = { + ("PAPZ", "parent"): ( + "RemoteContentController", + "mozilla/layers/RemoteContentController.h", + ), + ("PBackgroundMutableFile", "parent"): ( + "BackgroundMutableFileParentBase", + "mozilla/dom/filehandle/ActorsParent.h", + ), + ("PCanvas", "parent"): ("CanvasTranslator", "mozilla/layers/CanvasTranslator.h"), + ("PChromiumCDM", "parent"): ("ChromiumCDMParent", "ChromiumCDMParent.h"), + ("PCompositorBridge", "parent"): ( + "CompositorBridgeParentBase", + "mozilla/layers/CompositorBridgeParent.h", + ), + ("PContentPermissionRequest", "child"): ( + "RemotePermissionRequest", + "nsContentPermissionHelper.h", + ), + ("PFileSystemRequest", "child"): ( + "FileSystemTaskChildBase", + "mozilla/dom/FileSystemTaskBase.h", + ), + ("PGMP", "child"): ("GMPChild", "GMPChild.h"), + ("PGMP", "parent"): ("GMPParent", "GMPParent.h"), + ("PGMPContent", "child"): ("GMPContentChild", "GMPContentChild.h"), + ("PGMPStorage", "child"): ("GMPStorageChild", "GMPStorageChild.h"), + ("PGMPTimer", "child"): ("GMPTimerChild", "GMPTimerChild.h"), + ("PGMPTimer", "parent"): ("GMPTimerParent", "GMPTimerParent.h"), + ("PGMPVideoEncoder", "child"): ("GMPVideoEncoderChild", "GMPVideoEncoderChild.h"), + ("PGMPVideoDecoder", "child"): ("GMPVideoDecoderChild", "GMPVideoDecoderChild.h"), + ("PLoginReputation", "parent"): ( + "LoginReputationParent", + "mozilla/LoginReputationIPC.h", + ), + ("PMedia", "child"): ("Child", "mozilla/media/MediaChild.h"), + ("PPresentationRequest", "child"): ( + "PresentationRequestChild", + "mozilla/dom/PresentationChild.h", + ), + ("PPresentationRequest", "parent"): ( + "PresentationRequestParent", + "mozilla/dom/PresentationParent.h", + ), + ("PRemoteSpellcheckEngine", "child"): ( + "RemoteSpellcheckEngineChild", + "mozilla/RemoteSpellCheckEngineChild.h", + ), + ("PRemoteSpellcheckEngine", "parent"): ( + "RemoteSpellcheckEngineParent", + "mozilla/RemoteSpellCheckEngineParent.h", + ), + ("PScriptCache", "child"): ( + "ScriptCacheChild", + "mozilla/loader/ScriptCacheActors.h", + ), + ("PScriptCache", "parent"): ( + "ScriptCacheParent", + "mozilla/loader/ScriptCacheActors.h", + ), + ("PTCPServerSocket", "child"): ( + "mozilla::dom::TCPServerSocketChild", + "mozilla/dom/network/TCPServerSocketChild.h", + ), + ("PTCPServerSocket", "parent"): ( + "mozilla::dom::TCPServerSocketParent", + "mozilla/dom/network/TCPServerSocketParent.h", + ), + ("PTCPSocket", "child"): ( + "mozilla::dom::TCPSocketChild", + "mozilla/dom/network/TCPSocketChild.h", + ), + ("PTCPSocket", "parent"): ( + "mozilla::dom::TCPSocketParent", + "mozilla/dom/network/TCPSocketParent.h", + ), + ("PTestShellCommand", "parent"): ( + "TestShellCommandParent", + "mozilla/ipc/TestShellParent.h", + ), + ("PTransportProvider", "child"): ( + "TransportProviderChild", + "mozilla/net/IPCTransportProvider.h", + ), + ("PTransportProvider", "parent"): ( + "TransportProviderParent", + "mozilla/net/IPCTransportProvider.h", + ), + ("PUDPSocket", "child"): ( + "mozilla::dom::UDPSocketChild", + "mozilla/dom/network/UDPSocketChild.h", + ), + ("PUDPSocket", "parent"): ( + "mozilla::dom::UDPSocketParent", + "mozilla/dom/network/UDPSocketParent.h", + ), + ("PURLClassifierLocal", "child"): ( + "URLClassifierLocalChild", + "mozilla/dom/URLClassifierChild.h", + ), + ("PURLClassifierLocal", "parent"): ( + "URLClassifierLocalParent", + "mozilla/dom/URLClassifierParent.h", + ), + ("PVR", "child"): ("VRChild", "VRChild.h"), + ("PVR", "parent"): ("VRParent", "VRParent.h"), + ("PVRGPU", "child"): ("VRGPUChild", "VRGPUChild.h"), + ("PVRGPU", "parent"): ("VRGPUParent", "VRGPUParent.h"), + ("PVRLayer", "child"): ("VRLayerChild", "VRLayerChild.h"), + ("PVRManager", "child"): ("VRManagerChild", "VRManagerChild.h"), + ("PVRManager", "parent"): ("VRManagerParent", "VRManagerParent.h"), + ("PWebSocket", "child"): ( + "WebSocketChannelChild", + "mozilla/net/WebSocketChannelChild.h", + ), + ("PWebSocket", "parent"): ( + "WebSocketChannelParent", + "mozilla/net/WebSocketChannelParent.h", + ), + # _ipdltest + ("PTestActorPunning", "child"): ( + "TestActorPunningChild", + "mozilla/_ipdltest/TestActorPunning.h", + ), + ("PTestActorPunning", "parent"): ( + "TestActorPunningParent", + "mozilla/_ipdltest/TestActorPunning.h", + ), + ("PTestActorPunningPunned", "child"): ( + "TestActorPunningPunnedChild", + "mozilla/_ipdltest/TestActorPunning.h", + ), + ("PTestActorPunningPunned", "parent"): ( + "TestActorPunningPunnedParent", + "mozilla/_ipdltest/TestActorPunning.h", + ), + ("PTestActorPunningSub", "child"): ( + "TestActorPunningSubChild", + "mozilla/_ipdltest/TestActorPunning.h", + ), + ("PTestActorPunningSub", "parent"): ( + "TestActorPunningSubParent", + "mozilla/_ipdltest/TestActorPunning.h", + ), + ("PTestAsyncReturns", "child"): ( + "TestAsyncReturnsChild", + "mozilla/_ipdltest/TestAsyncReturns.h", + ), + ("PTestAsyncReturns", "parent"): ( + "TestAsyncReturnsParent", + "mozilla/_ipdltest/TestAsyncReturns.h", + ), + ("PTestBadActor", "parent"): ( + "TestBadActorParent", + "mozilla/_ipdltest/TestBadActor.h", + ), + ("PTestBadActor", "child"): ( + "TestBadActorChild", + "mozilla/_ipdltest/TestBadActor.h", + ), + ("PTestBadActorSub", "child"): ( + "TestBadActorSubChild", + "mozilla/_ipdltest/TestBadActor.h", + ), + ("PTestBadActorSub", "parent"): ( + "TestBadActorSubParent", + "mozilla/_ipdltest/TestBadActor.h", + ), + ("PTestCancel", "child"): ("TestCancelChild", "mozilla/_ipdltest/TestCancel.h"), + ("PTestCancel", "parent"): ("TestCancelParent", "mozilla/_ipdltest/TestCancel.h"), + ("PTestCrashCleanup", "child"): ( + "TestCrashCleanupChild", + "mozilla/_ipdltest/TestCrashCleanup.h", + ), + ("PTestCrashCleanup", "parent"): ( + "TestCrashCleanupParent", + "mozilla/_ipdltest/TestCrashCleanup.h", + ), + ("PTestDataStructures", "child"): ( + "TestDataStructuresChild", + "mozilla/_ipdltest/TestDataStructures.h", + ), + ("PTestDataStructures", "parent"): ( + "TestDataStructuresParent", + "mozilla/_ipdltest/TestDataStructures.h", + ), + ("PTestDataStructuresSub", "child"): ( + "TestDataStructuresSub", + "mozilla/_ipdltest/TestDataStructures.h", + ), + ("PTestDataStructuresSub", "parent"): ( + "TestDataStructuresSub", + "mozilla/_ipdltest/TestDataStructures.h", + ), + ("PTestDemon", "child"): ("TestDemonChild", "mozilla/_ipdltest/TestDemon.h"), + ("PTestDemon", "parent"): ("TestDemonParent", "mozilla/_ipdltest/TestDemon.h"), + ("PTestDesc", "child"): ("TestDescChild", "mozilla/_ipdltest/TestDesc.h"), + ("PTestDesc", "parent"): ("TestDescParent", "mozilla/_ipdltest/TestDesc.h"), + ("PTestDescSub", "child"): ("TestDescSubChild", "mozilla/_ipdltest/TestDesc.h"), + ("PTestDescSub", "parent"): ("TestDescSubParent", "mozilla/_ipdltest/TestDesc.h"), + ("PTestDescSubsub", "child"): ( + "TestDescSubsubChild", + "mozilla/_ipdltest/TestDesc.h", + ), + ("PTestDescSubsub", "parent"): ( + "TestDescSubsubParent", + "mozilla/_ipdltest/TestDesc.h", + ), + ("PTestEndpointBridgeMain", "child"): ( + "TestEndpointBridgeMainChild", + "mozilla/_ipdltest/TestEndpointBridgeMain.h", + ), + ("PTestEndpointBridgeMain", "parent"): ( + "TestEndpointBridgeMainParent", + "mozilla/_ipdltest/TestEndpointBridgeMain.h", + ), + ("PTestEndpointBridgeMainSub", "child"): ( + "TestEndpointBridgeMainSubChild", + "mozilla/_ipdltest/TestEndpointBridgeMain.h", + ), + ("PTestEndpointBridgeMainSub", "parent"): ( + "TestEndpointBridgeMainSubParent", + "mozilla/_ipdltest/TestEndpointBridgeMain.h", + ), + ("PTestEndpointBridgeSub", "child"): ( + "TestEndpointBridgeSubChild", + "mozilla/_ipdltest/TestEndpointBridgeMain.h", + ), + ("PTestEndpointBridgeSub", "parent"): ( + "TestEndpointBridgeSubParent", + "mozilla/_ipdltest/TestEndpointBridgeMain.h", + ), + ("PTestEndpointOpens", "child"): ( + "TestEndpointOpensChild", + "mozilla/_ipdltest/TestEndpointOpens.h", + ), + ("PTestEndpointOpens", "parent"): ( + "TestEndpointOpensParent", + "mozilla/_ipdltest/TestEndpointOpens.h", + ), + ("PTestEndpointOpensOpened", "child"): ( + "TestEndpointOpensOpenedChild", + "mozilla/_ipdltest/TestEndpointOpens.h", + ), + ("PTestEndpointOpensOpened", "parent"): ( + "TestEndpointOpensOpenedParent", + "mozilla/_ipdltest/TestEndpointOpens.h", + ), + ("PTestFailedCtor", "child"): ( + "TestFailedCtorChild", + "mozilla/_ipdltest/TestFailedCtor.h", + ), + ("PTestFailedCtor", "parent"): ( + "TestFailedCtorParent", + "mozilla/_ipdltest/TestFailedCtor.h", + ), + ("PTestFailedCtorSub", "child"): ( + "TestFailedCtorSubChild", + "mozilla/_ipdltest/TestFailedCtor.h", + ), + ("PTestFailedCtorSub", "parent"): ( + "TestFailedCtorSubParent", + "mozilla/_ipdltest/TestFailedCtor.h", + ), + ("PTestFailedCtorSubsub", "child"): ( + "TestFailedCtorSubsub", + "mozilla/_ipdltest/TestFailedCtor.h", + ), + ("PTestFailedCtorSubsub", "parent"): ( + "TestFailedCtorSubsub", + "mozilla/_ipdltest/TestFailedCtor.h", + ), + ("PTestHandle", "child"): ("TestHandleChild", "mozilla/_ipdltest/TestJSON.h"), + ("PTestHandle", "parent"): ("TestHandleParent", "mozilla/_ipdltest/TestJSON.h"), + ("PTestJSON", "child"): ("TestJSONChild", "mozilla/_ipdltest/TestJSON.h"), + ("PTestJSON", "parent"): ("TestJSONParent", "mozilla/_ipdltest/TestJSON.h"), + ("PTestHangs", "child"): ("TestHangsChild", "mozilla/_ipdltest/TestHangs.h"), + ("PTestHangs", "parent"): ("TestHangsParent", "mozilla/_ipdltest/TestHangs.h"), + ("PTestHighestPrio", "child"): ( + "TestHighestPrioChild", + "mozilla/_ipdltest/TestHighestPrio.h", + ), + ("PTestHighestPrio", "parent"): ( + "TestHighestPrioParent", + "mozilla/_ipdltest/TestHighestPrio.h", + ), + ("PTestInterruptErrorCleanup", "child"): ( + "TestInterruptErrorCleanupChild", + "mozilla/_ipdltest/TestInterruptErrorCleanup.h", + ), + ("PTestInterruptErrorCleanup", "parent"): ( + "TestInterruptErrorCleanupParent", + "mozilla/_ipdltest/TestInterruptErrorCleanup.h", + ), + ("PTestInterruptRaces", "child"): ( + "TestInterruptRacesChild", + "mozilla/_ipdltest/TestInterruptRaces.h", + ), + ("PTestInterruptRaces", "parent"): ( + "TestInterruptRacesParent", + "mozilla/_ipdltest/TestInterruptRaces.h", + ), + ("PTestInterruptShutdownRace", "child"): ( + "TestInterruptShutdownRaceChild", + "mozilla/_ipdltest/TestInterruptShutdownRace.h", + ), + ("PTestInterruptShutdownRace", "parent"): ( + "TestInterruptShutdownRaceParent", + "mozilla/_ipdltest/TestInterruptShutdownRace.h", + ), + ("PTestLatency", "child"): ("TestLatencyChild", "mozilla/_ipdltest/TestLatency.h"), + ("PTestLatency", "parent"): ( + "TestLatencyParent", + "mozilla/_ipdltest/TestLatency.h", + ), + ("PTestLayoutThread", "child"): ( + "TestOffMainThreadPaintingChild", + "mozilla/_ipdltest/TestOffMainThreadPainting.h", + ), + ("PTestLayoutThread", "parent"): ( + "TestOffMainThreadPaintingParent", + "mozilla/_ipdltest/TestOffMainThreadPainting.h", + ), + ("PTestPaintThread", "child"): ( + "TestPaintThreadChild", + "mozilla/_ipdltest/TestOffMainThreadPainting.h", + ), + ("PTestPaintThread", "parent"): ( + "TestPaintThreadParent", + "mozilla/_ipdltest/TestOffMainThreadPainting.h", + ), + ("PTestManyChildAllocs", "child"): ( + "TestManyChildAllocsChild", + "mozilla/_ipdltest/TestManyChildAllocs.h", + ), + ("PTestManyChildAllocs", "parent"): ( + "TestManyChildAllocsParent", + "mozilla/_ipdltest/TestManyChildAllocs.h", + ), + ("PTestManyChildAllocsSub", "child"): ( + "TestManyChildAllocsSubChild", + "mozilla/_ipdltest/TestManyChildAllocs.h", + ), + ("PTestManyChildAllocsSub", "parent"): ( + "TestManyChildAllocsSubParent", + "mozilla/_ipdltest/TestManyChildAllocs.h", + ), + ("PTestMultiMgrs", "child"): ( + "TestMultiMgrsChild", + "mozilla/_ipdltest/TestMultiMgrs.h", + ), + ("PTestMultiMgrs", "parent"): ( + "TestMultiMgrsParent", + "mozilla/_ipdltest/TestMultiMgrs.h", + ), + ("PTestMultiMgrsBottom", "child"): ( + "TestMultiMgrsBottomChild", + "mozilla/_ipdltest/TestMultiMgrs.h", + ), + ("PTestMultiMgrsBottom", "parent"): ( + "TestMultiMgrsBottomParent", + "mozilla/_ipdltest/TestMultiMgrs.h", + ), + ("PTestMultiMgrsLeft", "child"): ( + "TestMultiMgrsLeftChild", + "mozilla/_ipdltest/TestMultiMgrs.h", + ), + ("PTestMultiMgrsLeft", "parent"): ( + "TestMultiMgrsLeftParent", + "mozilla/_ipdltest/TestMultiMgrs.h", + ), + ("PTestMultiMgrsRight", "child"): ( + "TestMultiMgrsRightChild", + "mozilla/_ipdltest/TestMultiMgrs.h", + ), + ("PTestMultiMgrsRight", "parent"): ( + "TestMultiMgrsRightParent", + "mozilla/_ipdltest/TestMultiMgrs.h", + ), + ("PTestNestedLoops", "child"): ( + "TestNestedLoopsChild", + "mozilla/_ipdltest/TestNestedLoops.h", + ), + ("PTestNestedLoops", "parent"): ( + "TestNestedLoopsParent", + "mozilla/_ipdltest/TestNestedLoops.h", + ), + ("PTestRaceDeadlock", "child"): ( + "TestRaceDeadlockChild", + "mozilla/_ipdltest/TestRaceDeadlock.h", + ), + ("PTestRaceDeadlock", "parent"): ( + "TestRaceDeadlockParent", + "mozilla/_ipdltest/TestRaceDeadlock.h", + ), + ("PTestRaceDeferral", "child"): ( + "TestRaceDeferralChild", + "mozilla/_ipdltest/TestRaceDeferral.h", + ), + ("PTestRaceDeferral", "parent"): ( + "TestRaceDeferralParent", + "mozilla/_ipdltest/TestRaceDeferral.h", + ), + ("PTestRacyInterruptReplies", "child"): ( + "TestRacyInterruptRepliesChild", + "mozilla/_ipdltest/TestRacyInterruptReplies.h", + ), + ("PTestRacyInterruptReplies", "parent"): ( + "TestRacyInterruptRepliesParent", + "mozilla/_ipdltest/TestRacyInterruptReplies.h", + ), + ("PTestRacyReentry", "child"): ( + "TestRacyReentryChild", + "mozilla/_ipdltest/TestRacyReentry.h", + ), + ("PTestRacyReentry", "parent"): ( + "TestRacyReentryParent", + "mozilla/_ipdltest/TestRacyReentry.h", + ), + ("PTestRacyUndefer", "child"): ( + "TestRacyUndeferChild", + "mozilla/_ipdltest/TestRacyUndefer.h", + ), + ("PTestRacyUndefer", "parent"): ( + "TestRacyUndeferParent", + "mozilla/_ipdltest/TestRacyUndefer.h", + ), + ("PTestRPC", "child"): ("TestRPCChild", "mozilla/_ipdltest/TestRPC.h"), + ("PTestRPC", "parent"): ("TestRPCParent", "mozilla/_ipdltest/TestRPC.h"), + ("PTestSanity", "child"): ("TestSanityChild", "mozilla/_ipdltest/TestSanity.h"), + ("PTestSanity", "parent"): ("TestSanityParent", "mozilla/_ipdltest/TestSanity.h"), + ("PTestSelfManage", "child"): ( + "TestSelfManageChild", + "mozilla/_ipdltest/TestSelfManageRoot.h", + ), + ("PTestSelfManage", "parent"): ( + "TestSelfManageParent", + "mozilla/_ipdltest/TestSelfManageRoot.h", + ), + ("PTestSelfManageRoot", "child"): ( + "TestSelfManageRootChild", + "mozilla/_ipdltest/TestSelfManageRoot.h", + ), + ("PTestSelfManageRoot", "parent"): ( + "TestSelfManageRootParent", + "mozilla/_ipdltest/TestSelfManageRoot.h", + ), + ("PTestShmem", "child"): ("TestShmemChild", "mozilla/_ipdltest/TestShmem.h"), + ("PTestShmem", "parent"): ("TestShmemParent", "mozilla/_ipdltest/TestShmem.h"), + ("PTestShutdown", "child"): ( + "TestShutdownChild", + "mozilla/_ipdltest/TestShutdown.h", + ), + ("PTestShutdown", "parent"): ( + "TestShutdownParent", + "mozilla/_ipdltest/TestShutdown.h", + ), + ("PTestShutdownSub", "child"): ( + "TestShutdownSubChild", + "mozilla/_ipdltest/TestShutdown.h", + ), + ("PTestShutdownSub", "parent"): ( + "TestShutdownSubParent", + "mozilla/_ipdltest/TestShutdown.h", + ), + ("PTestShutdownSubsub", "child"): ( + "TestShutdownSubsubChild", + "mozilla/_ipdltest/TestShutdown.h", + ), + ("PTestShutdownSubsub", "parent"): ( + "TestShutdownSubsubParent", + "mozilla/_ipdltest/TestShutdown.h", + ), + ("PTestStackHooks", "child"): ( + "TestStackHooksChild", + "mozilla/_ipdltest/TestStackHooks.h", + ), + ("PTestStackHooks", "parent"): ( + "TestStackHooksParent", + "mozilla/_ipdltest/TestStackHooks.h", + ), + ("PTestSyncError", "child"): ( + "TestSyncErrorChild", + "mozilla/_ipdltest/TestSyncError.h", + ), + ("PTestSyncError", "parent"): ( + "TestSyncErrorParent", + "mozilla/_ipdltest/TestSyncError.h", + ), + ("PTestSyncHang", "child"): ( + "TestSyncHangChild", + "mozilla/_ipdltest/TestSyncHang.h", + ), + ("PTestSyncHang", "parent"): ( + "TestSyncHangParent", + "mozilla/_ipdltest/TestSyncHang.h", + ), + ("PTestSyncWakeup", "child"): ( + "TestSyncWakeupChild", + "mozilla/_ipdltest/TestSyncWakeup.h", + ), + ("PTestSyncWakeup", "parent"): ( + "TestSyncWakeupParent", + "mozilla/_ipdltest/TestSyncWakeup.h", + ), + ("PTestUniquePtrIPC", "child"): ( + "TestUniquePtrIPCChild", + "mozilla/_ipdltest/TestUniquePtrIPC.h", + ), + ("PTestUniquePtrIPC", "parent"): ( + "TestUniquePtrIPCParent", + "mozilla/_ipdltest/TestUniquePtrIPC.h", + ), + ("PTestUrgency", "child"): ("TestUrgencyChild", "mozilla/_ipdltest/TestUrgency.h"), + ("PTestUrgency", "parent"): ( + "TestUrgencyParent", + "mozilla/_ipdltest/TestUrgency.h", + ), + ("PTestUrgentHangs", "child"): ( + "TestUrgentHangsChild", + "mozilla/_ipdltest/TestUrgentHangs.h", + ), + ("PTestUrgentHangs", "parent"): ( + "TestUrgentHangsParent", + "mozilla/_ipdltest/TestUrgentHangs.h", + ), + ("PBackgroundFileHandle", "child"): ( + "indexedDB::BackgroundFileHandleChild", + "mozilla/dom/indexedDB/ActorsChild.h", + ), + ("PBackgroundFileRequest", "child"): ( + "indexedDB::BackgroundFileRequestChild", + "mozilla/dom/indexedDB/ActorsChild.h", + ), + ("PBackgroundIDBCursor", "child"): ( + "indexedDB::BackgroundCursorChildBase", + "mozilla/dom/indexedDB/ActorsChild.h", + ), + ("PBackgroundIDBDatabase", "child"): ( + "indexedDB::BackgroundDatabaseChild", + "mozilla/dom/indexedDB/ActorsChild.h", + ), + ("PBackgroundIDBDatabaseRequest", "child"): ( + "indexedDB::BackgroundDatabaseRequestChild", + "mozilla/dom/indexedDB/ActorsChild.h", + ), + ("PBackgroundIDBFactory", "child"): ( + "indexedDB::BackgroundFactoryChild", + "mozilla/dom/indexedDB/ActorsChild.h", + ), + ("PBackgroundIDBFactoryRequest", "child"): ( + "indexedDB::BackgroundFactoryRequestChild", + "mozilla/dom/indexedDB/ActorsChild.h", + ), + ("PBackgroundIDBRequest", "child"): ( + "indexedDB::BackgroundRequestChild", + "mozilla/dom/indexedDB/ActorsChild.h", + ), + ("PBackgroundIDBTransaction", "child"): ( + "indexedDB::BackgroundTransactionChild", + "mozilla/dom/indexedDB/ActorsChild.h", + ), + ("PBackgroundIDBVersionChangeTransaction", "child"): ( + "indexedDB::BackgroundVersionChangeTransactionChild", + "mozilla/dom/indexedDB/ActorsChild.h", + ), + ("PBackgroundIndexedDBUtils", "child"): ( + "indexedDB::BackgroundUtilsChild", + "mozilla/dom/indexedDB/ActorsChild.h", + ), + ("PBackgroundMutableFile", "child"): ( + "indexedDB::BackgroundMutableFileChild", + "mozilla/dom/indexedDB/ActorsChild.h", + ), +} + +# Our long term goal is to burn this list down, so new entries should be added +# extremely sparingly and only with a very good reason! You must have an IPC +# peer's r+ to add something new! + +# set() of (Protocol, side) +VIRTUAL_CALL_CLASSES = set( + [ + # Defined as a strange template + ("PMedia", "parent"), + ("PTexture", "parent"), + # Defined in a .cpp + ("PBackgroundFileHandle", "parent"), + ("PBackgroundFileRequest", "parent"), + ("PBackgroundIDBCursor", "parent"), + ("PBackgroundIDBDatabase", "parent"), + ("PBackgroundIDBDatabaseFile", "child"), + ("PBackgroundIDBDatabaseFile", "parent"), + ("PBackgroundIDBDatabaseRequest", "parent"), + ("PBackgroundIDBFactory", "parent"), + ("PBackgroundIDBFactoryRequest", "parent"), + ("PBackgroundIDBRequest", "parent"), + ("PBackgroundIDBTransaction", "parent"), + ("PBackgroundIDBVersionChangeTransaction", "parent"), + ("PBackgroundIndexedDBUtils", "parent"), + ("PBackgroundLSDatabase", "parent"), + ("PBackgroundLSObserver", "parent"), + ("PBackgroundLSRequest", "parent"), + ("PBackgroundLSSimpleRequest", "parent"), + ("PBackgroundLSSnapshot", "parent"), + ("PBackgroundSDBConnection", "parent"), + ("PBackgroundSDBRequest", "parent"), + ("PBackgroundTest", "child"), + ("PBackgroundTest", "parent"), + ("PChildToParentStream", "child"), + ("PChildToParentStream", "parent"), + ("PContentPermissionRequest", "parent"), + ("PCycleCollectWithLogs", "child"), + ("PCycleCollectWithLogs", "parent"), + ("PHal", "child"), + ("PHal", "parent"), + ("PIndexedDBPermissionRequest", "parent"), + ("PParentToChildStream", "child"), + ("PParentToChildStream", "parent"), + ("PProcessHangMonitor", "child"), + ("PProcessHangMonitor", "parent"), + ("PQuota", "parent"), + ("PQuotaRequest", "parent"), + ("PQuotaUsageRequest", "parent"), + ("PSimpleChannel", "child"), + ("PTexture", "child"), + # .h is not exported + ("PBackground", "child"), + ("PBackground", "parent"), + ("PBackgroundLSDatabase", "child"), + ("PBackgroundLSObserver", "child"), + ("PBackgroundLSRequest", "child"), + ("PBackgroundLSSimpleRequest", "child"), + ("PBackgroundLSSnapshot", "child"), + ("PBackgroundSDBConnection", "child"), + ("PBackgroundSDBRequest", "child"), + ("PBroadcastChannel", "child"), + ("PBroadcastChannel", "parent"), + ("PChromiumCDM", "child"), + ("PClientHandle", "child"), + ("PClientHandle", "parent"), + ("PClientHandleOp", "child"), + ("PClientHandleOp", "parent"), + ("PClientManager", "child"), + ("PClientManager", "parent"), + ("PClientManagerOp", "child"), + ("PClientManagerOp", "parent"), + ("PClientNavigateOp", "child"), + ("PClientNavigateOp", "parent"), + ("PClientOpenWindowOp", "child"), + ("PClientOpenWindowOp", "parent"), + ("PClientSource", "child"), + ("PClientSource", "parent"), + ("PClientSourceOp", "child"), + ("PClientSourceOp", "parent"), + ("PColorPicker", "child"), + ("PColorPicker", "parent"), + ("PDataChannel", "child"), + ("PFileChannel", "child"), + ("PFilePicker", "child"), + ("PFunctionBroker", "child"), + ("PFunctionBroker", "parent"), + ("PHandlerService", "child"), + ("PHandlerService", "parent"), + ("PPluginBackgroundDestroyer", "child"), + ("PPluginBackgroundDestroyer", "parent"), + ("PRemotePrintJob", "child"), + ("PRemotePrintJob", "parent"), + # PRemotePrintJob, PPrinting, PPrintProgressDialog and PPrintSettingsDialog + # need to be virtual for --disable-printing builds. + ("PPrinting", "child"), + ("PPrinting", "parent"), + ("PPrintProgressDialog", "child"), + ("PPrintProgressDialog", "parent"), + ("PPrintSettingsDialog", "child"), + ("PPrintSettingsDialog", "parent"), + ("PQuota", "child"), + ("PQuotaRequest", "child"), + ("PQuotaUsageRequest", "child"), + ("PServiceWorker", "child"), + ("PServiceWorker", "parent"), + ("PServiceWorkerContainer", "child"), + ("PServiceWorkerContainer", "parent"), + ("PServiceWorkerRegistration", "child"), + ("PServiceWorkerRegistration", "parent"), + ("PServiceWorkerUpdater", "child"), + ("PServiceWorkerUpdater", "parent"), + ("PVRLayer", "parent"), + ("PWebBrowserPersistResources", "child"), + ("PWebBrowserPersistResources", "parent"), + ("PWebBrowserPersistSerialize", "child"), + ("PWebBrowserPersistSerialize", "parent"), + ("PWebrtcGlobal", "child"), + ("PWebrtcGlobal", "parent"), + # .h is only exported on some platforms/configs + ("PCameras", "child"), + ("PCameras", "parent"), + ("PCompositorWidget", "child"), + ("PCompositorWidget", "parent"), + ("PDocAccessible", "child"), + ("PDocAccessible", "parent"), + ("PPluginSurface", "parent"), + ("PPluginWidget", "child"), + ("PPluginWidget", "parent"), + ("PProfiler", "child"), + ("PProfiler", "parent"), + ("PSpeechSynthesisRequest", "child"), + ("PSpeechSynthesisRequest", "parent"), + ("PStunAddrsRequest", "child"), + ("PStunAddrsRequest", "parent"), + ("PWebrtcTCPSocket", "child"), + ("PWebrtcTCPSocket", "parent"), + # .h includes something that's a LOCAL_INCLUDE + ("PBackgroundLocalStorageCache", "child"), + ("PBackgroundLocalStorageCache", "parent"), + ("PBackgroundSessionStorageCache", "child"), + ("PBackgroundSessionStorageCache", "parent"), + ("PBackgroundSessionStorageManager", "child"), + ("PBackgroundSessionStorageManager", "parent"), + ("PBackgroundStorage", "child"), + ("PBackgroundStorage", "parent"), + ("PBrowserStream", "parent"), + ("PExternalHelperApp", "parent"), + ("PFTPChannel", "child"), + ("PFTPChannel", "parent"), + ("PHttpChannel", "child"), + ("PHttpChannel", "parent"), + ("PSessionStorageObserver", "child"), + ("PSessionStorageObserver", "parent"), + # can't be included safely for compilation error reasons + ("PGMPContent", "parent"), + ("PGMPService", "child"), + ("PGMPService", "parent"), + ("PGMPStorage", "parent"), + ("PGMPVideoDecoder", "parent"), + ("PGMPVideoEncoder", "parent"), + ("PWebRenderBridge", "parent"), + # Not actually subclassed + ("PLoginReputation", "child"), + ("PPluginSurface", "child"), + ("PTestShellCommand", "child"), + # _ipdltest + # Not actually subclassed + ("PTestIndirectProtocolParamFirst", "child"), + ("PTestIndirectProtocolParamFirst", "parent"), + ("PTestIndirectProtocolParamManage", "child"), + ("PTestIndirectProtocolParamManage", "parent"), + ("PTestIndirectProtocolParamSecond", "child"), + ("PTestIndirectProtocolParamSecond", "parent"), + ("PTestPriority", "child"), + ("PTestPriority", "parent"), + ] +) diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py new file mode 100644 index 0000000000..36046091cf --- /dev/null +++ b/ipc/ipdl/ipdl/lower.py @@ -0,0 +1,5757 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import re +from copy import deepcopy +from collections import OrderedDict +import itertools + +import ipdl.ast +import ipdl.builtin +from ipdl.cxx.ast import * +from ipdl.cxx.code import * +from ipdl.direct_call import VIRTUAL_CALL_CLASSES, DIRECT_CALL_OVERRIDES +from ipdl.type import ActorType, UnionType, TypeVisitor, builtinHeaderIncludes +from ipdl.util import hash_str + + +# ----------------------------------------------------------------------------- +# "Public" interface to lowering +## + + +class LowerToCxx: + def lower(self, tu, segmentcapacitydict): + """returns |[ header: File ], [ cpp : File ]| representing the + lowered form of |tu|""" + # annotate the AST with IPDL/C++ IR-type stuff used later + tu.accept(_DecorateWithCxxStuff()) + + # Any modifications to the filename scheme here need corresponding + # modifications in the ipdl.py driver script. + name = tu.name + pheader, pcpp = File(name + ".h"), File(name + ".cpp") + + _GenerateProtocolCode().lower(tu, pheader, pcpp, segmentcapacitydict) + headers = [pheader] + cpps = [pcpp] + + if tu.protocol: + pname = tu.protocol.name + + parentheader, parentcpp = ( + File(pname + "Parent.h"), + File(pname + "Parent.cpp"), + ) + _GenerateProtocolParentCode().lower( + tu, pname + "Parent", parentheader, parentcpp + ) + + childheader, childcpp = File(pname + "Child.h"), File(pname + "Child.cpp") + _GenerateProtocolChildCode().lower( + tu, pname + "Child", childheader, childcpp + ) + + headers += [parentheader, childheader] + cpps += [parentcpp, childcpp] + + return headers, cpps + + +# ----------------------------------------------------------------------------- +# Helper code +## + + +def hashfunc(value): + h = hash_str(value) % 2 ** 32 + if h < 0: + h += 2 ** 32 + return h + + +_NULL_ACTOR_ID = ExprLiteral.ZERO +_FREED_ACTOR_ID = ExprLiteral.ONE + +_DISCLAIMER = Whitespace( + """// +// Automatically generated by ipdlc. +// Edit at your own risk +// + +""" +) + + +class _struct: + pass + + +def _namespacedHeaderName(name, namespaces): + pfx = "/".join([ns.name for ns in namespaces]) + if pfx: + return pfx + "/" + name + else: + return name + + +def _ipdlhHeaderName(tu): + assert tu.filetype == "header" + return _namespacedHeaderName(tu.name, tu.namespaces) + + +def _protocolHeaderName(p, side=""): + if side: + side = side.title() + base = p.name + side + return _namespacedHeaderName(base, p.namespaces) + + +def _includeGuardMacroName(headerfile): + return re.sub(r"[./]", "_", headerfile.name) + + +def _includeGuardStart(headerfile): + guard = _includeGuardMacroName(headerfile) + return [CppDirective("ifndef", guard), CppDirective("define", guard)] + + +def _includeGuardEnd(headerfile): + guard = _includeGuardMacroName(headerfile) + return [CppDirective("endif", "// ifndef " + guard)] + + +def _messageStartName(ptype): + return ptype.name() + "MsgStart" + + +def _protocolId(ptype): + return ExprVar(_messageStartName(ptype)) + + +def _protocolIdType(): + return Type.INT32 + + +def _actorName(pname, side): + """|pname| is the protocol name. |side| is 'Parent' or 'Child'.""" + tag = side + if not tag[0].isupper(): + tag = side.title() + return pname + tag + + +def _actorIdType(): + return Type.INT32 + + +def _actorTypeTagType(): + return Type.INT32 + + +def _actorId(actor=None): + if actor is not None: + return ExprCall(ExprSelect(actor, "->", "Id")) + return ExprCall(ExprVar("Id")) + + +def _actorHId(actorhandle): + return ExprSelect(actorhandle, ".", "mId") + + +def _backstagePass(): + return ExprCall(ExprVar("mozilla::ipc::PrivateIPDLInterface")) + + +def _iterType(ptr): + return Type("PickleIterator", ptr=ptr) + + +def _deleteId(): + return ExprVar("Msg___delete____ID") + + +def _deleteReplyId(): + return ExprVar("Reply___delete____ID") + + +def _lookupListener(idexpr): + return ExprCall(ExprVar("Lookup"), args=[idexpr]) + + +def _makeForwardDeclForQClass(clsname, quals, cls=True, struct=False): + fd = ForwardDecl(clsname, cls=cls, struct=struct) + if 0 == len(quals): + return fd + + outerns = Namespace(quals[0]) + innerns = outerns + for ns in quals[1:]: + tmpns = Namespace(ns) + innerns.addstmt(tmpns) + innerns = tmpns + + innerns.addstmt(fd) + return outerns + + +def _makeForwardDeclForActor(ptype, side): + return _makeForwardDeclForQClass( + _actorName(ptype.qname.baseid, side), ptype.qname.quals + ) + + +def _makeForwardDecl(type): + return _makeForwardDeclForQClass(type.name(), type.qname.quals) + + +def _putInNamespaces(cxxthing, namespaces): + """|namespaces| is in order [ outer, ..., inner ]""" + if 0 == len(namespaces): + return cxxthing + + outerns = Namespace(namespaces[0].name) + innerns = outerns + for ns in namespaces[1:]: + newns = Namespace(ns.name) + innerns.addstmt(newns) + innerns = newns + innerns.addstmt(cxxthing) + return outerns + + +def _sendPrefix(msgtype): + """Prefix of the name of the C++ method that sends |msgtype|.""" + 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() + if ipdltype.isIPDL() and ipdltype.isMaybe(): + return "Maybe" + 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.hasBaseType() and _hasVisibleActor(ipdltype.basetype)) + ) + + +def _abortIfFalse(cond, msg): + return StmtExpr( + ExprCall(ExprVar("MOZ_RELEASE_ASSERT"), [cond, ExprLiteral.String(msg)]) + ) + + +def _refptr(T): + return Type("RefPtr", T=T) + + +def _uniqueptr(T): + return Type("UniquePtr", T=T) + + +def _alreadyaddrefed(T): + return Type("already_AddRefed", T=T) + + +def _tuple(types, const=False, ref=False): + return Type("Tuple", T=types, const=const, ref=ref) + + +def _promise(resolvetype, rejecttype, tail, resolver=False): + inner = Type("Private") if resolver else None + return Type("MozPromise", T=[resolvetype, rejecttype, tail], inner=inner) + + +def _makePromise(returns, side, resolver=False): + if len(returns) > 1: + resolvetype = _tuple([d.bareType(side) for d in returns]) + else: + resolvetype = returns[0].bareType(side) + + # MozPromise is purposefully made to be exclusive only. Really, we mean it. + return _promise( + resolvetype, _ResponseRejectReason.Type(), ExprLiteral.TRUE, resolver=resolver + ) + + +def _resolveType(returns, side): + if len(returns) > 1: + return _tuple([d.moveType(side) for d in returns]) + return returns[0].moveType(side) + + +def _makeResolver(returns, side): + return TypeFunction([Decl(_resolveType(returns, side), "")]) + + +def _cxxArrayType(basetype, const=False, ref=False): + return Type("nsTArray", T=basetype, const=const, ref=ref, hasimplicitcopyctor=False) + + +def _cxxMaybeType(basetype, const=False, ref=False): + return Type( + "mozilla::Maybe", + T=basetype, + const=const, + ref=ref, + hasimplicitcopyctor=basetype.hasimplicitcopyctor, + ) + + +def _cxxManagedContainerType(basetype, const=False, ref=False): + return Type( + "ManagedContainer", T=basetype, const=const, ref=ref, hasimplicitcopyctor=False + ) + + +def _cxxLifecycleProxyType(ptr=False): + return Type("mozilla::ipc::ActorLifecycleProxy", ptr=ptr) + + +def _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 _callGetLifecycleProxy(actor=ExprVar.THIS): + return ExprCall(ExprSelect(actor, "->", "GetLifecycleProxy")) + + +def _releaseLifecycleProxy(actor=None): + return StmtCode( + """ + mozilla::ipc::ActorLifecycleProxy* proxy = + (${actor})->GetLifecycleProxy(); + NS_IF_RELEASE(proxy); + """, + actor=actor or ExprVar.THIS, + ) + + +def _otherSide(side): + if side == "child": + return "parent" + if side == "parent": + return "child" + assert 0 + + +def _ifLogging(topLevelProtocol, stmts): + return StmtCode( + """ + if (mozilla::ipc::LoggingEnabledFor(${proto})) { + $*{stmts} + } + """, + proto=topLevelProtocol, + stmts=stmts, + ) + + +# XXX we need to remove these and install proper error handling + + +def _printErrorMessage(msg): + if isinstance(msg, str): + msg = ExprLiteral.String(msg) + return StmtExpr(ExprCall(ExprVar("NS_ERROR"), args=[msg])) + + +def _protocolErrorBreakpoint(msg): + if isinstance(msg, str): + msg = ExprLiteral.String(msg) + return StmtExpr( + ExprCall(ExprVar("mozilla::ipc::ProtocolErrorBreakpoint"), args=[msg]) + ) + + +def _printWarningMessage(msg): + if isinstance(msg, str): + msg = ExprLiteral.String(msg) + return StmtExpr(ExprCall(ExprVar("NS_WARNING"), args=[msg])) + + +def _fatalError(msg): + return StmtExpr(ExprCall(ExprVar("FatalError"), args=[ExprLiteral.String(msg)])) + + +def _logicError(msg): + return StmtExpr( + ExprCall(ExprVar("mozilla::ipc::LogicError"), args=[ExprLiteral.String(msg)]) + ) + + +def _sentinelReadError(classname): + return StmtExpr( + ExprCall( + ExprVar("mozilla::ipc::SentinelReadError"), + args=[ExprLiteral.String(classname)], + ) + ) + + +# 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)] + + +def errfnSentinel(rvalue=ExprLiteral.FALSE): + def inner(msg): + return [_sentinelReadError(msg), StmtReturn(rvalue)] + + return inner + + +def _destroyMethod(): + return ExprVar("ActorDestroy") + + +def errfnUnreachable(msg): + return [_logicError(msg)] + + +class _DestroyReason: + @staticmethod + def Type(): + return Type("ActorDestroyReason") + + Deletion = ExprVar("Deletion") + AncestorDeletion = ExprVar("AncestorDeletion") + NormalShutdown = ExprVar("NormalShutdown") + AbnormalShutdown = ExprVar("AbnormalShutdown") + FailedConstructor = ExprVar("FailedConstructor") + + +class _ResponseRejectReason: + @staticmethod + def Type(): + return Type("ResponseRejectReason") + + SendError = ExprVar("ResponseRejectReason::SendError") + ChannelClosed = ExprVar("ResponseRejectReason::ChannelClosed") + HandlerRejected = ExprVar("ResponseRejectReason::HandlerRejected") + ActorDestroyed = ExprVar("ResponseRejectReason::ActorDestroyed") + + +# ----------------------------------------------------------------------------- +# Intermediate representation (IR) nodes used during lowering + + +class _ConvertToCxxType(TypeVisitor): + def __init__(self, side, fq): + self.side = side + self.fq = fq + + def typename(self, thing): + if self.fq: + return thing.fullname() + return thing.name() + + def visitImportedCxxType(self, t): + cxxtype = Type(self.typename(t)) + if t.isRefcounted(): + cxxtype = _refptr(cxxtype) + return cxxtype + + def visitActorType(self, a): + return Type(_actorName(self.typename(a.protocol), self.side), ptr=True) + + def visitStructType(self, s): + return Type(self.typename(s)) + + def visitUnionType(self, u): + return Type(self.typename(u)) + + def visitArrayType(self, a): + basecxxtype = a.basetype.accept(self) + return _cxxArrayType(basecxxtype) + + def visitMaybeType(self, m): + basecxxtype = m.basetype.accept(self) + return _cxxMaybeType(basecxxtype) + + def visitShmemType(self, s): + return Type(self.typename(s)) + + def visitByteBufType(self, s): + return Type(self.typename(s)) + + def visitFDType(self, s): + return Type(self.typename(s)) + + def visitEndpointType(self, s): + return Type(self.typename(s)) + + def visitManagedEndpointType(self, s): + return Type(self.typename(s)) + + def visitUniquePtrType(self, s): + return Type(self.typename(s)) + + def visitProtocolType(self, p): + assert 0 + + def visitMessageType(self, m): + assert 0 + + def visitVoidType(self, v): + assert 0 + + +def _cxxBareType(ipdltype, side, fq=False): + return ipdltype.accept(_ConvertToCxxType(side, fq)) + + +def _cxxRefType(ipdltype, side): + t = _cxxBareType(ipdltype, side) + t.ref = True + return t + + +def _cxxConstRefType(ipdltype, side): + t = _cxxBareType(ipdltype, side) + if ipdltype.isIPDL() and ipdltype.isActor(): + return t + if ipdltype.isIPDL() and ipdltype.isShmem(): + t.ref = True + return t + if ipdltype.isIPDL() and ipdltype.isByteBuf(): + t.ref = True + return t + if ipdltype.isIPDL() and ipdltype.hasBaseType(): + # Keep same constness as inner type. + inner = _cxxConstRefType(ipdltype.basetype, side) + t.const = inner.const or not inner.ref + t.ref = True + return t + if ipdltype.isCxx() and ipdltype.isMoveonly(): + t.const = True + t.ref = True + return t + if ipdltype.isCxx() and ipdltype.isRefcounted(): + # Use T* instead of const RefPtr<T>& + t = t.T + t.ptr = True + return t + if ipdltype.isUniquePtr(): + t.ref = True + return t + t.const = True + t.ref = True + return t + + +def _cxxTypeCanMoveSend(ipdltype): + return ipdltype.isUniquePtr() + + +def _cxxTypeNeedsMove(ipdltype): + if _cxxTypeNeedsMoveForSend(ipdltype): + return True + + if ipdltype.isIPDL(): + return ipdltype.isArray() + + return False + + +def _cxxTypeNeedsMoveForSend(ipdltype): + if ipdltype.isUniquePtr(): + return True + + if ipdltype.isCxx(): + return ipdltype.isMoveonly() + + if ipdltype.isIPDL(): + if ipdltype.hasBaseType(): + return _cxxTypeNeedsMove(ipdltype.basetype) + return ( + ipdltype.isShmem() + or ipdltype.isByteBuf() + or ipdltype.isEndpoint() + or ipdltype.isManagedEndpoint() + ) + + return False + + +# FIXME Bug 1547019 This should be the same as _cxxTypeNeedsMoveForSend, but +# a lot of existing code needs to be updated and fixed before +# we can do that. +def _cxxTypeCanOnlyMove(ipdltype, visited=None): + if visited is None: + visited = set() + + visited.add(ipdltype) + + if ipdltype.isCxx(): + return ipdltype.isMoveonly() + + if ipdltype.isIPDL(): + if ipdltype.isMaybe() or ipdltype.isArray(): + return _cxxTypeCanOnlyMove(ipdltype.basetype, visited) + if ipdltype.isStruct() or ipdltype.isUnion(): + return any( + _cxxTypeCanOnlyMove(t, visited) + for t in ipdltype.itercomponents() + if t not in visited + ) + return ipdltype.isManagedEndpoint() + + return False + + +def _cxxTypeCanMove(ipdltype): + return not (ipdltype.isIPDL() and ipdltype.isActor()) + + +def _cxxMoveRefType(ipdltype, side): + t = _cxxBareType(ipdltype, side) + if _cxxTypeNeedsMove(ipdltype): + t.rvalref = True + return t + return _cxxConstRefType(ipdltype, side) + + +def _cxxForceMoveRefType(ipdltype, side): + assert _cxxTypeCanMove(ipdltype) + t = _cxxBareType(ipdltype, side) + t.rvalref = True + return t + + +def _cxxPtrToType(ipdltype, side): + t = _cxxBareType(ipdltype, side) + if ipdltype.isIPDL() and ipdltype.isActor(): + t.ptr = False + t.ptrptr = True + return t + t.ptr = True + return t + + +def _cxxConstPtrToType(ipdltype, side): + t = _cxxBareType(ipdltype, side) + if ipdltype.isIPDL() and ipdltype.isActor(): + t.ptr = False + t.ptrconstptr = True + return t + t.const = True + t.ptr = True + return t + + +def _allocMethod(ptype, side): + return "Alloc" + ptype.name() + side.title() + + +def _deallocMethod(ptype, side): + return "Dealloc" + ptype.name() + side.title() + + +## +# A _HybridDecl straddles IPDL and C++ decls. It knows which C++ +# types correspond to which IPDL types, and it also knows how +# serialize and deserialize "special" IPDL C++ types. +## + + +class _HybridDecl: + """A hybrid decl stores both an IPDL type and all the C++ type + info needed by later passes, along with a basic name for the decl.""" + + def __init__(self, ipdltype, name): + self.ipdltype = ipdltype + self.name = name + + def var(self): + return ExprVar(self.name) + + def bareType(self, side, fq=False): + """Return this decl's unqualified C++ type.""" + return _cxxBareType(self.ipdltype, side, fq=fq) + + def refType(self, side): + """Return this decl's C++ type as a 'reference' type, which is not + necessarily a C++ reference.""" + return _cxxRefType(self.ipdltype, side) + + def constRefType(self, side): + """Return this decl's C++ type as a const, 'reference' type.""" + return _cxxConstRefType(self.ipdltype, side) + + def 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) + elif _cxxTypeNeedsMoveForSend(self.ipdltype): + return self.rvalueRefType(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 = False + t.ptrptr = True + return t + t.ptr = True + return t + + def forceMoveType(self, side): + """Return this decl's C++ Type with forced move semantics.""" + assert _cxxTypeCanMove(self.ipdltype) + return _cxxForceMoveRefType(self.ipdltype, side) + + +# -------------------------------------------------- + + +class HasFQName: + def fqClassName(self): + return self.decl.type.fullname() + + +class _CompoundTypeComponent(_HybridDecl): + def __init__(self, ipdltype, name, side, ct): + _HybridDecl.__init__(self, ipdltype, name) + self.side = side + self.special = _hasVisibleActor(ipdltype) + + # @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, fq=False): + return _HybridDecl.bareType(self, self.side, fq=fq) + + 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) + + def forceMoveType(self, side=None): + return _HybridDecl.forceMoveType(self, self.side) + + +class StructDecl(ipdl.ast.StructDecl, HasFQName): + def fields_ipdl_order(self): + for f in self.fields: + yield f + + def fields_member_order(self): + assert len(self.packed_field_order) == len(self.fields) + + for i in self.packed_field_order: + yield self.fields[i] + + @staticmethod + def upgrade(structDecl): + assert isinstance(structDecl, ipdl.ast.StructDecl) + structDecl.__class__ = StructDecl + + +class _StructField(_CompoundTypeComponent): + def __init__(self, ipdltype, name, sd, 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 refExpr(self, thisexpr=None): + ref = self.memberVar() + if thisexpr is not None: + ref = ExprSelect(thisexpr, ".", ref.name) + return ref + + def constRefExpr(self, thisexpr=None): + # sigh, gross hack + refexpr = self.refExpr(thisexpr) + if "Shmem" == self.ipdltype.name(): + refexpr = ExprCast(refexpr, Type("Shmem", ref=True), const=True) + if "ByteBuf" == self.ipdltype.name(): + refexpr = ExprCast(refexpr, Type("ByteBuf", ref=True), const=True) + if "FileDescriptor" == self.ipdltype.name(): + refexpr = ExprCast(refexpr, Type("FileDescriptor", ref=True), const=True) + return refexpr + + def argVar(self): + return ExprVar("_" + self.name) + + def memberVar(self): + return ExprVar(self.name + "_") + + +class UnionDecl(ipdl.ast.UnionDecl, HasFQName): + def callType(self, var=None): + func = ExprVar("type") + if var is not None: + func = ExprSelect(var, ".", func.name) + return ExprCall(func) + + @staticmethod + def upgrade(unionDecl): + assert isinstance(unionDecl, ipdl.ast.UnionDecl) + unionDecl.__class__ = UnionDecl + + +class _UnionMember(_CompoundTypeComponent): + """Not in the AFL sense, but rather a member (e.g. |int;|) of an + IPDL union type.""" + + def __init__(self, ipdltype, ud, 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) + + # To create a finite object with a mutually recursive type, a union must + # be present somewhere in the recursive loop. Because of that we only + # need to care about introducing indirections inside unions. + self.recursive = ud.decl.type.mutuallyRecursiveWith(ipdltype) + + def enum(self): + return "T" + self.flattypename + + def enumvar(self): + return ExprVar(self.enum()) + + def internalType(self): + if self.recursive: + return self.ptrToType() + else: + return self.bareType() + + def unionType(self): + """Type used for storage in generated C union decl.""" + if self.recursive: + return self.ptrToType() + else: + return 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=True) + elif ( + self.ipdltype.isIPDL() + and self.ipdltype.isArray() + and not isinstance(rhs, ExprMove) + ): + rhs = ExprCall(ExprSelect(rhs, ".", "Clone"), args=[]) + 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=True)] + elif ( + self.ipdltype.isIPDL() + and self.ipdltype.isArray() + and not isinstance(expr, ExprMove) + ): + args = [ExprCall(ExprSelect(expr, ".", "Clone"), args=[])] + else: + args = [expr] + + if self.recursive: + return ExprAssn( + self.callGetPtr(), ExprNew(self.bareType(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 = True + return t + + def defaultValue(self, fq=False): + # Use the default constructor for any class that does not have an + # implicit copy constructor. + if not self.bareType().hasimplicitcopyctor: + return None + + if self.ipdltype.isIPDL() and self.ipdltype.isActor(): + return ExprLiteral.NULL + # XXX sneaky here, maybe need ExprCtor()? + return ExprCall(self.bareType(fq=fq)) + + def getConstValue(self): + v = ExprDeref(self.callGetConstPtr()) + # sigh + if "ByteBuf" == self.ipdltype.name(): + v = ExprCast(v, Type("ByteBuf", ref=True), const=True) + if "Shmem" == self.ipdltype.name(): + v = ExprCast(v, Type("Shmem", ref=True), const=True) + if "FileDescriptor" == self.ipdltype.name(): + v = ExprCast(v, Type("FileDescriptor", ref=True), const=True) + return v + + +# -------------------------------------------------- + + +class MessageDecl(ipdl.ast.MessageDecl): + def baseName(self): + return self.name + + def recvMethod(self): + name = _recvPrefix(self.decl.type) + self.baseName() + if self.decl.type.isCtor(): + name += "Constructor" + return name + + def sendMethod(self): + name = _sendPrefix(self.decl.type) + self.baseName() + if self.decl.type.isCtor(): + name += "Constructor" + return name + + def hasReply(self): + return ( + self.decl.type.hasReply() + or self.decl.type.isCtor() + or self.decl.type.isDtor() + ) + + def hasAsyncReturns(self): + return self.decl.type.isAsync() and self.returns + + def msgCtorFunc(self): + return "Msg_%s" % (self.decl.progname) + + def prettyMsgName(self, pfx=""): + return pfx + self.msgCtorFunc() + + def pqMsgCtorFunc(self): + return "%s::%s" % (self.namespace, self.msgCtorFunc()) + + def msgId(self): + return self.msgCtorFunc() + "__ID" + + def pqMsgId(self): + return "%s::%s" % (self.namespace, self.msgId()) + + def replyCtorFunc(self): + return "Reply_%s" % (self.decl.progname) + + def pqReplyCtorFunc(self): + return "%s::%s" % (self.namespace, self.replyCtorFunc()) + + def replyId(self): + return self.replyCtorFunc() + "__ID" + + def pqReplyId(self): + return "%s::%s" % (self.namespace, self.replyId()) + + def prettyReplyName(self, pfx=""): + return pfx + self.replyCtorFunc() + + def promiseName(self): + name = self.baseName() + if self.decl.type.isCtor(): + name += "Constructor" + name += "Promise" + return name + + def resolverName(self): + return self.baseName() + "Resolver" + + def actorDecl(self): + return self.params[0] + + def makeCxxParams( + self, paramsems="in", returnsems="out", side=None, implicit=True, direction=None + ): + """Return a list of C++ decls per the spec'd configuration. + |params| and |returns| is the C++ semantics of those: 'in', 'out', or None.""" + + def makeDecl(d, sems): + if self.decl.type.tainted and direction == "recv": + # Tainted types are passed by-value, allowing the receiver to move them if desired. + assert sems != "out" + return Decl(Type("Tainted", T=d.bareType(side)), d.name) + + if sems == "in": + return Decl(d.inType(side), d.name) + elif sems == "move": + return Decl(d.moveType(side), d.name) + elif sems == "out": + return Decl(d.outType(side), d.name) + else: + assert 0 + + def makeResolverDecl(returns): + return Decl(Type(self.resolverName(), rvalref=True), "aResolve") + + def makeCallbackResolveDecl(returns): + if len(returns) > 1: + resolvetype = _tuple([d.bareType(side) for d in returns]) + else: + resolvetype = returns[0].bareType(side) + + return Decl( + Type("mozilla::ipc::ResolveCallback", T=resolvetype, rvalref=True), + "aResolve", + ) + + def makeCallbackRejectDecl(returns): + return Decl(Type("mozilla::ipc::RejectCallback", rvalref=True), "aReject") + + cxxparams = [] + if paramsems is not None: + cxxparams.extend([makeDecl(d, paramsems) for d in self.params]) + + if returnsems == "promise" and self.returns: + pass + elif returnsems == "callback" and self.returns: + cxxparams.extend( + [ + makeCallbackResolveDecl(self.returns), + makeCallbackRejectDecl(self.returns), + ] + ) + elif returnsems == "resolver" and self.returns: + cxxparams.extend([makeResolverDecl(self.returns)]) + elif returnsems is not None: + cxxparams.extend([makeDecl(r, returnsems) for r in self.returns]) + + if not implicit and self.decl.type.hasImplicitActorParam(): + cxxparams = cxxparams[1:] + + return cxxparams + + def makeCxxArgs( + self, paramsems="in", retsems="out", retcallsems="out", implicit=True + ): + assert not retcallsems or retsems # retcallsems => returnsems + cxxargs = [] + + if paramsems == "move": + # We don't std::move() RefPtr<T> types because current Recv*() + # implementors take these parameters as T*, and + # std::move(RefPtr<T>) doesn't coerce to T*. + cxxargs.extend( + [ + p.var() if p.ipdltype.isRefcounted() else ExprMove(p.var()) + for p in self.params + ] + ) + elif paramsems == "in": + cxxargs.extend([p.var() for p in self.params]) + else: + assert False + + for ret in self.returns: + if retsems == "in": + if retcallsems == "in": + cxxargs.append(ret.var()) + elif retcallsems == "out": + cxxargs.append(ExprAddrOf(ret.var())) + else: + assert 0 + elif retsems == "out": + if retcallsems == "in": + cxxargs.append(ExprDeref(ret.var())) + elif retcallsems == "out": + cxxargs.append(ret.var()) + else: + assert 0 + elif retsems == "resolver": + pass + if retsems == "resolver": + cxxargs.append(ExprMove(ExprVar("resolver"))) + + if not implicit: + assert self.decl.type.hasImplicitActorParam() + cxxargs = cxxargs[1:] + + return cxxargs + + @staticmethod + def upgrade(messageDecl): + assert isinstance(messageDecl, ipdl.ast.MessageDecl) + if messageDecl.decl.type.hasImplicitActorParam(): + messageDecl.params.insert( + 0, + _HybridDecl( + ipdl.type.ActorType(messageDecl.decl.type.constructedType()), + "actor", + ), + ) + messageDecl.__class__ = MessageDecl + + +# -------------------------------------------------- +def _usesShmem(p): + for md in p.messageDecls: + for param in md.inParams: + if ipdl.type.hasshmem(param.type): + return True + for ret in md.outParams: + if ipdl.type.hasshmem(ret.type): + return True + return False + + +def _subtreeUsesShmem(p): + if _usesShmem(p): + return True + + ptype = p.decl.type + for mgd in ptype.manages: + if ptype is not mgd: + if _subtreeUsesShmem(mgd._ast): + return True + return False + + +class Protocol(ipdl.ast.Protocol): + def cxxTypedefs(self): + return self.decl.cxxtypedefs + + def managerInterfaceType(self, ptr=False): + return Type("mozilla::ipc::IProtocol", ptr=ptr) + + def openedProtocolInterfaceType(self, ptr=False): + 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=False): + return Type(_actorName(self._ipdlmgrtype().name(), side), ptr=ptr) + + def unregisterMethod(self, actorThis=None): + if actorThis is not None: + return ExprSelect(actorThis, "->", "Unregister") + return ExprVar("Unregister") + + def removeManageeMethod(self): + return ExprVar("RemoveManagee") + + def deallocManageeMethod(self): + return ExprVar("DeallocManagee") + + def 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 routingId(self, actorThis=None): + if self.decl.type.isToplevel(): + return ExprVar("MSG_ROUTING_CONTROL") + if actorThis is not None: + return ExprCall(ExprSelect(actorThis, "->", "Id")) + return ExprCall(ExprVar("Id")) + + def managerVar(self, thisexpr=None): + assert thisexpr is not None or not self.decl.type.isToplevel() + mvar = ExprCall(ExprVar("Manager"), args=[]) + if thisexpr is not None: + mvar = ExprCall(ExprSelect(thisexpr, "->", "Manager"), args=[]) + return mvar + + def managedCxxType(self, actortype, side): + assert self.decl.type.isManagerOf(actortype) + return Type(_actorName(actortype.name(), side), ptr=True) + + def managedMethod(self, actortype, side): + assert self.decl.type.isManagerOf(actortype) + return ExprVar("Managed" + _actorName(actortype.name(), side)) + + def managedVar(self, actortype, side): + assert self.decl.type.isManagerOf(actortype) + return ExprVar("mManaged" + _actorName(actortype.name(), side)) + + def managedVarType(self, actortype, side, const=False, ref=False): + assert self.decl.type.isManagerOf(actortype) + return _cxxManagedContainerType( + Type(_actorName(actortype.name(), side)), const=const, ref=ref + ) + + def subtreeUsesShmem(self): + return _subtreeUsesShmem(self) + + @staticmethod + def upgrade(protocol): + assert isinstance(protocol, ipdl.ast.Protocol) + protocol.__class__ = Protocol + + +class TranslationUnit(ipdl.ast.TranslationUnit): + @staticmethod + def upgrade(tu): + assert isinstance(tu, ipdl.ast.TranslationUnit) + tu.__class__ = TranslationUnit + + +# ----------------------------------------------------------------------------- + +pod_types = { + "int8_t": 1, + "uint8_t": 1, + "int16_t": 2, + "uint16_t": 2, + "int32_t": 4, + "uint32_t": 4, + "int64_t": 8, + "uint64_t": 8, + "float": 4, + "double": 8, +} +max_pod_size = max(pod_types.values()) +# We claim that all types we don't recognize are automatically "bigger" +# than pod types for ease of sorting. +pod_size_sentinel = max_pod_size * 2 + + +def pod_size(ipdltype): + if not isinstance(ipdltype, ipdl.type.ImportedCxxType): + return pod_size_sentinel + + return pod_types.get(ipdltype.name(), pod_size_sentinel) + + +class _DecorateWithCxxStuff(ipdl.ast.Visitor): + """Phase 1 of lowering: decorate the IPDL AST with information + relevant to C++ code generation. + + This pass results in an AST that is a poor man's "IR"; in reality, a + "hybrid" AST mainly consisting of IPDL nodes with new C++ info along + with some new IPDL/C++ nodes that are tuned for C++ codegen.""" + + def __init__(self): + self.visitedTus = set() + # 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::ManagedEndpoint"), + "ManagedEndpoint", + ["FooSide"], + ), + Typedef( + Type("mozilla::ipc::TransportDescriptor"), "TransportDescriptor" + ), + Typedef(Type("mozilla::UniquePtr"), "UniquePtr", ["T"]), + Typedef( + Type("mozilla::ipc::ResponseRejectReason"), "ResponseRejectReason" + ), + ] + ) + 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 = False + newfields = [] + for f in sd.fields: + ftype = f.decl.type + if _hasVisibleActor(ftype): + sd.decl.special = True + # 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)) + + # Compute a permutation of the fields for in-memory storage such + # that the memory layout of the structure will be well-packed. + permutation = list(range(len(newfields))) + + # Note that the results of `pod_size` ensure that non-POD fields + # sort before POD ones. + def size(idx): + return pod_size(newfields[idx].ipdltype) + + permutation.sort(key=size, reverse=True) + + sd.fields = newfields + sd.packed_field_order = permutation + StructDecl.upgrade(sd) + + if sd.decl.fullname is not None: + self.typedefSet.add(Typedef(Type(sd.fqClassName()), sd.name)) + + def visitUnionDecl(self, ud): + ud.decl.special = False + newcomponents = [] + for ctype in ud.decl.type.components: + if _hasVisibleActor(ctype): + ud.decl.special = True + # 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 msgenums(protocol, pretty=False): + msgenum = TypeEnum("MessageType") + msgstart = _messageStartName(protocol.decl.type) + " << 16" + msgenum.addId(protocol.name + "Start", msgstart) + + for md in protocol.messageDecls: + msgenum.addId(md.prettyMsgName() if pretty else md.msgId()) + if md.hasReply(): + msgenum.addId(md.prettyReplyName() if pretty else md.replyId()) + + msgenum.addId(protocol.name + "End") + return msgenum + + +class _GenerateProtocolCode(ipdl.ast.Visitor): + """Creates code common to both the parent and child actors.""" + + def __init__(self): + self.protocol = None # protocol we're generating a class for + self.hdrfile = None # what will become Protocol.h + self.cppfile = None # what will become Protocol.cpp + self.cppIncludeHeaders = [] + self.structUnionDefns = [] + self.funcDefns = [] + + def lower(self, tu, cxxHeaderFile, cxxFile, segmentcapacitydict): + self.protocol = tu.protocol + self.hdrfile = cxxHeaderFile + self.cppfile = cxxFile + self.segmentcapacitydict = segmentcapacitydict + tu.accept(self) + + def visitTranslationUnit(self, tu): + hf = self.hdrfile + + hf.addthing(_DISCLAIMER) + hf.addthings(_includeGuardStart(hf)) + hf.addthing(Whitespace.NL) + + for inc in builtinHeaderIncludes: + self.visitBuiltinCxxInclude(inc) + + # Compute the set of includes we need for declared structure/union + # classes for this protocol. + typesToIncludes = {} + for using in tu.using: + typestr = str(using.type.spec) + if typestr not in typesToIncludes: + typesToIncludes[typestr] = using.header + else: + assert typesToIncludes[typestr] == using.header + + aggregateTypeIncludes = set() + for su in tu.structsAndUnions: + typedeps = _ComputeTypeDeps(su.decl.type, 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 + '"')) + + # Manually run Visitor.visitTranslationUnit. For dependency resolution + # we need to handle structs and unions separately. + for cxxInc in tu.cxxIncludes: + cxxInc.accept(self) + for inc in tu.includes: + inc.accept(self) + self.generateStructsAndUnions(tu) + for using in tu.builtinUsing: + using.accept(self) + for using in tu.using: + using.accept(self) + if tu.protocol: + tu.protocol.accept(self) + + if tu.filetype == "header": + self.cppIncludeHeaders.append(_ipdlhHeaderName(tu) + ".h") + + hf.addthing(Whitespace.NL) + hf.addthings(_includeGuardEnd(hf)) + + cf = self.cppfile + cf.addthings( + ( + [_DISCLAIMER, Whitespace.NL] + + [ + CppDirective("include", '"' + h + '"') + for h in self.cppIncludeHeaders + ] + + [Whitespace.NL] + + [ + CppDirective("include", '"%s"' % filename) + for filename in ipdl.builtin.CppIncludes + ] + + [Whitespace.NL] + ) + ) + + if self.protocol: + # construct the namespace into which we'll stick all our defns + ns = Namespace(self.protocol.name) + cf.addthing(_putInNamespaces(ns, self.protocol.namespaces)) + ns.addstmts(([Whitespace.NL] + self.funcDefns + [Whitespace.NL])) + + cf.addthings(self.structUnionDefns) + + def visitBuiltinCxxInclude(self, inc): + self.hdrfile.addthing(CppDirective("include", '"' + inc.file + '"')) + + def visitCxxInclude(self, inc): + self.cppIncludeHeaders.append(inc.file) + + def visitInclude(self, inc): + if inc.tu.filetype == "header": + self.hdrfile.addthing( + CppDirective("include", '"' + _ipdlhHeaderName(inc.tu) + '.h"') + ) + else: + self.cppIncludeHeaders += [ + _protocolHeaderName(inc.tu.protocol, "parent") + ".h", + _protocolHeaderName(inc.tu.protocol, "child") + ".h", + ] + + def generateStructsAndUnions(self, tu): + """Generate the definitions for all structs and unions. This will + re-order the declarations if needed in the C++ code such that + dependencies have already been defined.""" + decls = OrderedDict() + for su in tu.structsAndUnions: + if isinstance(su, StructDecl): + which = "struct" + forwarddecls, fulldecltypes, cls = _generateCxxStruct(su) + traitsdecl, traitsdefns = _ParamTraits.structPickling(su.decl.type) + else: + assert isinstance(su, UnionDecl) + which = "union" + forwarddecls, fulldecltypes, cls = _generateCxxUnion(su) + traitsdecl, traitsdefns = _ParamTraits.unionPickling(su.decl.type) + + clsdecl, methoddefns = _splitClassDeclDefn(cls) + + # Store the declarations in the decls map so we can emit in + # dependency order. + decls[su.decl.type] = ( + fulldecltypes, + [Whitespace.NL] + + forwarddecls + + [ + Whitespace( + """ +//----------------------------------------------------------------------------- +// Declaration of the IPDL type |%s %s| +// +""" + % (which, su.name) + ), + _putInNamespaces(clsdecl, su.namespaces), + ] + + [Whitespace.NL, traitsdecl], + ) + + self.structUnionDefns.extend( + [ + Whitespace( + """ +//----------------------------------------------------------------------------- +// Method definitions for the IPDL type |%s %s| +// +""" + % (which, su.name) + ), + _putInNamespaces(methoddefns, su.namespaces), + Whitespace.NL, + traitsdefns, + ] + ) + + # Generate the declarations structs in dependency order. + def gen_struct(deps, defn): + for dep in deps: + if dep in decls: + d, t = decls[dep] + del decls[dep] + gen_struct(d, t) + self.hdrfile.addthings(defn) + + while len(decls) > 0: + _, (d, t) = decls.popitem(False) + gen_struct(d, t) + + def visitProtocol(self, p): + self.cppIncludeHeaders.append(_protocolHeaderName(self.protocol, "") + ".h") + self.cppIncludeHeaders.append( + _protocolHeaderName(self.protocol, "Parent") + ".h" + ) + self.cppIncludeHeaders.append( + _protocolHeaderName(self.protocol, "Child") + ".h" + ) + + # Forward declare our own actors. + self.hdrfile.addthings( + [ + Whitespace.NL, + _makeForwardDeclForActor(p.decl.type, "Parent"), + _makeForwardDeclForActor(p.decl.type, "Child"), + ] + ) + + self.hdrfile.addthing( + Whitespace( + """ +//----------------------------------------------------------------------------- +// Code common to %sChild and %sParent +// +""" + % (p.name, p.name) + ) + ) + + # construct the namespace into which we'll stick all our decls + ns = Namespace(self.protocol.name) + self.hdrfile.addthing(_putInNamespaces(ns, p.namespaces)) + ns.addstmt(Whitespace.NL) + + edecl, edefn = _splitFuncDeclDefn(self.genEndpointFunc()) + ns.addstmts([edecl, Whitespace.NL]) + self.funcDefns.append(edefn) + + # spit out message type enum and classes + msgenum = msgenums(self.protocol) + ns.addstmts([StmtDecl(Decl(msgenum, "")), Whitespace.NL]) + + for md in p.messageDecls: + decls = [] + + # Look up the segment capacity used for serializing this + # message. If the capacity is not specified, use '0' for + # the default capacity (defined in ipc_message.cc) + name = "%s::%s" % (md.namespace, md.decl.progname) + segmentcapacity = self.segmentcapacitydict.get(name, 0) + + mfDecl, mfDefn = _splitFuncDeclDefn( + _generateMessageConstructor(md, segmentcapacity, p, forReply=False) + ) + decls.append(mfDecl) + self.funcDefns.append(mfDefn) + + if md.hasReply(): + rfDecl, rfDefn = _splitFuncDeclDefn( + _generateMessageConstructor(md, 0, p, forReply=True) + ) + decls.append(rfDecl) + self.funcDefns.append(rfDefn) + + decls.append(Whitespace.NL) + ns.addstmts(decls) + + ns.addstmts([Whitespace.NL, Whitespace.NL]) + + # Generate code for PFoo::CreateEndpoints. + def genEndpointFunc(self): + p = self.protocol.decl.type + tparent = _cxxBareType(ActorType(p), "Parent", fq=True) + tchild = _cxxBareType(ActorType(p), "Child", fq=True) + + openfunc = MethodDefn( + MethodDecl( + "CreateEndpoints", + params=[ + Decl(Type("base::ProcessId"), "aParentDestPid"), + Decl(Type("base::ProcessId"), "aChildDestPid"), + Decl( + Type("mozilla::ipc::Endpoint<" + tparent.name + ">", ptr=True), + "aParent", + ), + Decl( + Type("mozilla::ipc::Endpoint<" + tchild.name + ">", ptr=True), + "aChild", + ), + ], + ret=Type.NSRESULT, + ) + ) + openfunc.addcode( + """ + return mozilla::ipc::CreateEndpoints( + mozilla::ipc::PrivateIPDLInterface(), + aParentDestPid, aChildDestPid, + aParent, aChild); + """ + ) + return openfunc + + +# -------------------------------------------------- + + +def _generateMessageConstructor(md, segmentSize, protocol, forReply=False): + if forReply: + clsname = md.replyCtorFunc() + msgid = md.replyId() + replyEnum = "REPLY" + else: + clsname = md.msgCtorFunc() + msgid = md.msgId() + replyEnum = "NOT_REPLY" + + nested = md.decl.type.nested + prio = md.decl.type.prio + compress = md.decl.type.compress + + routingId = ExprVar("routingId") + + func = FunctionDefn( + FunctionDecl( + clsname, + params=[Decl(Type("int32_t"), routingId.name)], + ret=Type("IPC::Message", ptr=True), + ) + ) + + if compress == "compress": + compression = "COMPRESSION_ENABLED" + elif compress: + assert compress == "compressall" + compression = "COMPRESSION_ALL" + else: + compression = "COMPRESSION_NONE" + + if nested == ipdl.ast.NOT_NESTED: + nestedEnum = "NOT_NESTED" + elif nested == ipdl.ast.INSIDE_SYNC_NESTED: + nestedEnum = "NESTED_INSIDE_SYNC" + else: + assert nested == ipdl.ast.INSIDE_CPOW_NESTED + nestedEnum = "NESTED_INSIDE_CPOW" + + if prio == ipdl.ast.NORMAL_PRIORITY: + prioEnum = "NORMAL_PRIORITY" + elif prio == ipdl.ast.INPUT_PRIORITY: + prioEnum = "INPUT_PRIORITY" + elif prio == ipdl.ast.HIGH_PRIORITY: + prioEnum = "HIGH_PRIORITY" + else: + prioEnum = "MEDIUMHIGH_PRIORITY" + + if md.decl.type.isSync(): + syncEnum = "SYNC" + else: + syncEnum = "ASYNC" + + if md.decl.type.isInterrupt(): + interruptEnum = "INTERRUPT" + else: + interruptEnum = "NOT_INTERRUPT" + + if md.decl.type.isCtor(): + ctorEnum = "CONSTRUCTOR" + else: + ctorEnum = "NOT_CONSTRUCTOR" + + def messageEnum(valname): + return ExprVar("IPC::Message::" + valname) + + flags = ExprCall( + ExprVar("IPC::Message::HeaderFlags"), + args=[ + messageEnum(nestedEnum), + messageEnum(prioEnum), + messageEnum(compression), + messageEnum(ctorEnum), + messageEnum(syncEnum), + messageEnum(interruptEnum), + messageEnum(replyEnum), + ], + ) + + segmentSize = int(segmentSize) + if segmentSize: + func.addstmt( + StmtReturn( + ExprNew( + Type("IPC::Message"), + args=[ + routingId, + ExprVar(msgid), + ExprLiteral.Int(int(segmentSize)), + flags, + # Pass `true` to recordWriteLatency to collect telemetry + ExprLiteral.TRUE, + ], + ) + ) + ) + else: + func.addstmt( + StmtReturn( + ExprCall( + ExprVar("IPC::Message::IPDLMessage"), + args=[routingId, ExprVar(msgid), flags], + ) + ) + ) + + return func + + +# -------------------------------------------------- + + +class _ParamTraits: + var = ExprVar("aVar") + msgvar = ExprVar("aMsg") + itervar = ExprVar("aIter") + actor = ExprVar("aActor") + + @classmethod + def ifsideis(cls, side, then, els=None): + cxxside = ExprVar("mozilla::ipc::ChildSide") + if side == "parent": + cxxside = ExprVar("mozilla::ipc::ParentSide") + + ifstmt = StmtIf( + ExprBinary(cxxside, "==", ExprCall(ExprSelect(cls.actor, "->", "GetSide"))) + ) + ifstmt.addifstmt(then) + if els is not None: + ifstmt.addelsestmt(els) + return ifstmt + + @classmethod + def fatalError(cls, reason): + return StmtCode( + "aActor->FatalError(${reason});", reason=ExprLiteral.String(reason) + ) + + @classmethod + def writeSentinel(cls, msgvar, sentinelKey): + return [ + Whitespace("// Sentinel = " + repr(sentinelKey) + "\n", indent=True), + StmtExpr( + ExprCall( + ExprSelect(msgvar, "->", "WriteSentinel"), + args=[ExprLiteral.Int(hashfunc(sentinelKey))], + ) + ), + ] + + @classmethod + def readSentinel(cls, msgvar, itervar, sentinelKey, sentinelFail): + # Read the sentinel + read = ExprCall( + ExprSelect(msgvar, "->", "ReadSentinel"), + args=[itervar, ExprLiteral.Int(hashfunc(sentinelKey))], + ) + ifsentinel = StmtIf(ExprNot(read)) + ifsentinel.addifstmts(sentinelFail) + + return [ + Whitespace("// Sentinel = " + repr(sentinelKey) + "\n", indent=True), + ifsentinel, + ] + + @classmethod + def write(cls, var, msgvar, actor, ipdltype=None): + # WARNING: This doesn't set AutoForActor for you, make sure this is + # only called when the actor is already correctly set. + if ipdltype and _cxxTypeNeedsMoveForSend(ipdltype): + var = ExprMove(var) + return ExprCall(ExprVar("WriteIPDLParam"), args=[msgvar, actor, var]) + + @classmethod + def checkedWrite(cls, ipdltype, var, msgvar, sentinelKey, actor): + assert sentinelKey + block = Block() + + # Assert we aren't serializing a null non-nullable actor + if ( + ipdltype + and ipdltype.isIPDL() + and ipdltype.isActor() + and not ipdltype.nullable + ): + block.addstmt( + _abortIfFalse(var, "NULL actor value passed to non-nullable param") + ) + + block.addstmts( + [ + StmtExpr(cls.write(var, msgvar, actor, ipdltype)), + ] + ) + block.addstmts(cls.writeSentinel(msgvar, sentinelKey)) + return block + + @classmethod + def bulkSentinelKey(cls, fields): + return " | ".join(f.basename for f in fields) + + @classmethod + def checkedBulkWrite(cls, size, fields): + block = Block() + first = fields[0] + + block.addstmts( + [ + StmtExpr( + ExprCall( + ExprSelect(cls.msgvar, "->", "WriteBytes"), + args=[ + ExprAddrOf( + ExprCall(first.getMethod(thisexpr=cls.var, sel=".")) + ), + ExprLiteral.Int(size * len(fields)), + ], + ) + ) + ] + ) + block.addstmts(cls.writeSentinel(cls.msgvar, cls.bulkSentinelKey(fields))) + + return block + + @classmethod + def checkedBulkRead(cls, size, fields): + block = Block() + first = fields[0] + + readbytes = ExprCall( + ExprSelect(cls.msgvar, "->", "ReadBytesInto"), + args=[ + cls.itervar, + ExprAddrOf(ExprCall(first.getMethod(thisexpr=cls.var, sel="->"))), + ExprLiteral.Int(size * len(fields)), + ], + ) + ifbad = StmtIf(ExprNot(readbytes)) + errmsg = "Error bulk reading fields from %s" % first.ipdltype.name() + ifbad.addifstmts([cls.fatalError(errmsg), StmtReturn.FALSE]) + block.addstmt(ifbad) + block.addstmts( + cls.readSentinel( + cls.msgvar, + cls.itervar, + cls.bulkSentinelKey(fields), + errfnSentinel()(errmsg), + ) + ) + + return block + + @classmethod + def checkedRead( + cls, + ipdltype, + var, + msgvar, + itervar, + errfn, + paramtype, + sentinelKey, + errfnSentinel, + actor, + ): + block = Block() + + # Read the data + ifbad = StmtIf( + ExprNot( + ExprCall(ExprVar("ReadIPDLParam"), args=[msgvar, itervar, actor, var]) + ) + ) + if not isinstance(paramtype, list): + paramtype = ["Error deserializing " + paramtype] + ifbad.addifstmts(errfn(*paramtype)) + block.addstmt(ifbad) + + # Check if we got a null non-nullable actor + if ( + ipdltype + and ipdltype.isIPDL() + and ipdltype.isActor() + and not ipdltype.nullable + ): + ifnull = StmtIf(ExprNot(ExprDeref(var))) + ifnull.addifstmts(errfn(*paramtype)) + block.addstmt(ifnull) + + block.addstmts( + cls.readSentinel(msgvar, itervar, sentinelKey, errfnSentinel(*paramtype)) + ) + + return block + + # Helper wrapper for checkedRead for use within _ParamTraits + @classmethod + def _checkedRead(cls, ipdltype, var, sentinelKey, what): + def errfn(msg): + return [cls.fatalError(msg), StmtReturn.FALSE] + + return cls.checkedRead( + ipdltype, + var, + cls.msgvar, + cls.itervar, + errfn=errfn, + paramtype=what, + sentinelKey=sentinelKey, + errfnSentinel=errfnSentinel(), + actor=cls.actor, + ) + + @classmethod + def generateDecl(cls, fortype, write, read, constin=True): + # IPDLParamTraits impls are selected ignoring constness, and references. + pt = Class( + "IPDLParamTraits", + specializes=Type( + fortype.name, T=fortype.T, inner=fortype.inner, ptr=fortype.ptr + ), + struct=True, + ) + + # typedef T paramType; + pt.addstmt(Typedef(fortype, "paramType")) + + iprotocoltype = Type("mozilla::ipc::IProtocol", ptr=True) + + # static void Write(Message*, const T&); + intype = Type("paramType", ref=True, const=constin) + writemthd = MethodDefn( + MethodDecl( + "Write", + params=[ + Decl(Type("IPC::Message", ptr=True), cls.msgvar.name), + Decl(iprotocoltype, cls.actor.name), + Decl(intype, cls.var.name), + ], + methodspec=MethodSpec.STATIC, + ) + ) + writemthd.addstmts(write) + pt.addstmt(writemthd) + + # static bool Read(const Message*, PickleIterator*, T*); + outtype = Type("paramType", ptr=True) + readmthd = MethodDefn( + MethodDecl( + "Read", + params=[ + Decl(Type("IPC::Message", ptr=True, const=True), cls.msgvar.name), + Decl(_iterType(ptr=True), cls.itervar.name), + Decl(iprotocoltype, cls.actor.name), + Decl(outtype, cls.var.name), + ], + ret=Type.BOOL, + methodspec=MethodSpec.STATIC, + ) + ) + readmthd.addstmts(read) + pt.addstmt(readmthd) + + # Split the class into declaration and definition + clsdecl, methoddefns = _splitClassDeclDefn(pt) + + namespaces = [Namespace("mozilla"), Namespace("ipc")] + clsns = _putInNamespaces(clsdecl, namespaces) + defns = _putInNamespaces(methoddefns, namespaces) + return clsns, defns + + @classmethod + def actorPickling(cls, actortype, side): + """Generates pickling for IPDL actors. This is a |nullable| deserializer. + Write and read callers will perform nullability validation.""" + + cxxtype = _cxxBareType(actortype, side, fq=True) + + write = StmtCode( + """ + int32_t id; + if (!${var}) { + id = 0; // kNullActorId + } else { + id = ${var}->Id(); + if (id == 1) { // kFreedActorId + ${var}->FatalError("Actor has been |delete|d"); + } + MOZ_RELEASE_ASSERT( + ${actor}->GetIPCChannel() == ${var}->GetIPCChannel(), + "Actor must be from the same channel as the" + " actor it's being sent over"); + MOZ_RELEASE_ASSERT( + ${var}->CanSend(), + "Actor must still be open when sending"); + } + + ${write}; + """, + var=cls.var, + actor=cls.actor, + write=cls.write(ExprVar("id"), cls.msgvar, cls.actor), + ) + + # bool Read(..) impl + read = StmtCode( + """ + mozilla::Maybe<mozilla::ipc::IProtocol*> actor = + ${actor}->ReadActor(${msgvar}, ${itervar}, true, ${actortype}, ${protocolid}); + if (actor.isNothing()) { + return false; + } + + *${var} = static_cast<${cxxtype}>(actor.value()); + return true; + """, + actor=cls.actor, + msgvar=cls.msgvar, + itervar=cls.itervar, + actortype=ExprLiteral.String(actortype.name()), + protocolid=_protocolId(actortype), + var=cls.var, + cxxtype=cxxtype, + ) + + return cls.generateDecl(cxxtype, [write], [read]) + + @classmethod + def structPickling(cls, structtype): + sd = structtype._ast + # NOTE: Not using _cxxBareType here as we don't have a side + cxxtype = Type(structtype.fullname()) + + def get(sel, f): + return ExprCall(f.getMethod(thisexpr=cls.var, sel=sel)) + + write = [] + read = [] + + for (size, fields) in itertools.groupby( + sd.fields_member_order(), lambda f: pod_size(f.ipdltype) + ): + fields = list(fields) + + if size == pod_size_sentinel: + for f in fields: + writefield = cls.checkedWrite( + f.ipdltype, + get(".", f), + cls.msgvar, + sentinelKey=f.basename, + actor=cls.actor, + ) + readfield = cls._checkedRead( + f.ipdltype, + ExprAddrOf(get("->", f)), + f.basename, + "'" + + f.getMethod().name + + "' " + + "(" + + f.ipdltype.name() + + ") member of " + + "'" + + structtype.name() + + "'", + ) + + # Wrap the read/write in a side check if the field is special. + if f.special: + writefield = cls.ifsideis(f.side, writefield) + readfield = cls.ifsideis(f.side, readfield) + + write.append(writefield) + read.append(readfield) + else: + for f in fields: + assert not f.special + + writefield = cls.checkedBulkWrite(size, fields) + readfield = cls.checkedBulkRead(size, fields) + + write.append(writefield) + read.append(readfield) + + read.append(StmtReturn.TRUE) + + return cls.generateDecl(cxxtype, write, read) + + @classmethod + def unionPickling(cls, uniontype): + # NOTE: Not using _cxxBareType here as we don't have a side + cxxtype = Type(uniontype.fullname()) + ud = uniontype._ast + + # Use typedef to set up an alias so it's easier to reference the struct type. + alias = "union__" + typevar = ExprVar("type") + + prelude = [ + Typedef(cxxtype, alias), + ] + + writeswitch = StmtSwitch(typevar) + write = prelude + [ + StmtDecl(Decl(Type.INT, typevar.name), init=ud.callType(cls.var)), + cls.checkedWrite( + None, typevar, cls.msgvar, sentinelKey=uniontype.name(), actor=cls.actor + ), + Whitespace.NL, + writeswitch, + ] + + readswitch = StmtSwitch(typevar) + read = prelude + [ + StmtDecl(Decl(Type.INT, typevar.name), init=ExprLiteral.ZERO), + cls._checkedRead( + None, + ExprAddrOf(typevar), + uniontype.name(), + "type of union " + uniontype.name(), + ), + Whitespace.NL, + readswitch, + ] + + for c in ud.components: + ct = c.ipdltype + caselabel = CaseLabel(alias + "::" + c.enum()) + origenum = c.enum() + + writecase = StmtBlock() + wstmt = cls.checkedWrite( + c.ipdltype, + ExprCall(ExprSelect(cls.var, ".", c.getTypeName())), + cls.msgvar, + sentinelKey=c.enum(), + actor=cls.actor, + ) + if c.special: + # Report an error if the type is special and the side is wrong + wstmt = cls.ifsideis(c.side, wstmt, els=cls.fatalError("wrong side!")) + writecase.addstmts([wstmt, StmtReturn()]) + writeswitch.addcase(caselabel, writecase) + + readcase = StmtBlock() + if c.special: + # The type comes across flipped from what the actor will be on + # this side; i.e. child->parent messages will have PFooChild + # when received on the parent side. Report an error if the sides + # match, and handle c.other instead. + readcase.addstmt( + cls.ifsideis( + c.side, + StmtBlock([cls.fatalError("wrong side!"), StmtReturn.FALSE]), + ) + ) + c = c.other + tmpvar = ExprVar("tmp") + ct = c.bareType(fq=True) + readcase.addstmts( + [ + StmtDecl(Decl(ct, tmpvar.name), init=c.defaultValue(fq=True)), + StmtExpr(ExprAssn(ExprDeref(cls.var), ExprMove(tmpvar))), + cls._checkedRead( + c.ipdltype, + ExprAddrOf( + ExprCall(ExprSelect(cls.var, "->", c.getTypeName())) + ), + origenum, + "variant " + origenum + " of union " + uniontype.name(), + ), + StmtReturn.TRUE, + ] + ) + readswitch.addcase(caselabel, readcase) + + # Add the error default case + writeswitch.addcase( + DefaultLabel(), + StmtBlock([cls.fatalError("unknown union type"), StmtReturn()]), + ) + readswitch.addcase( + DefaultLabel(), + StmtBlock([cls.fatalError("unknown union type"), StmtReturn.FALSE]), + ) + + return cls.generateDecl(cxxtype, write, read) + + +# -------------------------------------------------- + + +class _ComputeTypeDeps(TypeVisitor): + """Pass that gathers the C++ types that a particular IPDL type + (recursively) depends on. There are three kinds of dependencies: (i) + types that need forward declaration; (ii) types that need a |using| + stmt; (iii) IPDL structs or unions which must be fully declared + before this struct. Some types generate multiple kinds.""" + + def __init__(self, fortype, unqualifiedTypedefs=False): + ipdl.type.TypeVisitor.__init__(self) + self.usingTypedefs = [] + self.forwardDeclStmts = [] + self.fullDeclTypes = [] + self.fortype = fortype + self.unqualifiedTypedefs = unqualifiedTypedefs + + def maybeTypedef(self, fqname, name, templateargs=[]): + if fqname != name or self.unqualifiedTypedefs: + self.usingTypedefs.append(Typedef(Type(fqname), name, templateargs)) + + 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()) + + # Mutually recursive fields in unions are behind indirection, so we only + # need a forward decl, and don't need a full type declaration. + if isinstance(self.fortype, UnionType) and self.fortype.mutuallyRecursiveWith( + su + ): + self.forwardDeclStmts.append(_makeForwardDecl(su)) + else: + self.fullDeclTypes.append(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 visitMaybeType(self, m): + return TypeVisitor.visitMaybeType(self, m) + + def visitShmemType(self, s): + if s in self.visited: + return + self.visited.add(s) + self.maybeTypedef("mozilla::ipc::Shmem", "Shmem") + + def visitByteBufType(self, s): + if s in self.visited: + return + self.visited.add(s) + self.maybeTypedef("mozilla::ipc::ByteBuf", "ByteBuf") + + def visitFDType(self, s): + if s in self.visited: + return + self.visited.add(s) + self.maybeTypedef("mozilla::ipc::FileDescriptor", "FileDescriptor") + + def visitEndpointType(self, s): + if s in self.visited: + return + self.visited.add(s) + self.maybeTypedef("mozilla::ipc::Endpoint", "Endpoint", ["FooSide"]) + self.visitActorType(s.actor) + + def visitManagedEndpointType(self, s): + if s in self.visited: + return + self.visited.add(s) + self.maybeTypedef( + "mozilla::ipc::ManagedEndpoint", "ManagedEndpoint", ["FooSide"] + ) + self.visitActorType(s.actor) + + def visitUniquePtrType(self, s): + if s in self.visited: + return + self.visited.add(s) + + def visitVoidType(self, v): + assert 0 + + def visitMessageType(self, v): + assert 0 + + def visitProtocolType(self, v): + assert 0 + + +def _fieldStaticAssertions(sd): + staticasserts = [] + for (size, fields) in itertools.groupby( + sd.fields_member_order(), lambda f: pod_size(f.ipdltype) + ): + if size == pod_size_sentinel: + continue + + fields = list(fields) + if len(fields) == 1: + continue + + staticasserts.append( + StmtCode( + """ + static_assert( + (offsetof(${struct}, ${last}) - offsetof(${struct}, ${first})) == ${expected}, + "Bad assumptions about field layout!"); + """, + struct=sd.name, + first=fields[0].memberVar(), + last=fields[-1].memberVar(), + expected=ExprLiteral.Int(size * (len(fields) - 1)), + ) + ) + + return staticasserts + + +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 + fulldecltypes = gettypedeps.fullDeclTypes + + struct = Class(sd.name, final=True) + struct.addstmts([Label.PRIVATE] + usingTypedefs + [Whitespace.NL, Label.PUBLIC]) + + constreftype = Type(sd.name, const=True, ref=True) + + def fieldsAsParamList(): + # FIXME Bug 1547019 inType() should do the right thing once + # _cxxTypeCanOnlyMove is replaced with + # _cxxTypeNeedsMoveForSend + return [ + Decl( + f.forceMoveType() if _cxxTypeCanOnlyMove(f.ipdltype) else f.inType(), + f.argVar().name, + ) + for f in sd.fields_ipdl_order() + ] + + # 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): + assert len(sd.fields) == len(sd.packed_field_order) + + # Struct() + defctor = ConstructorDefn(ConstructorDecl(sd.name, force_inline=True)) + + # We want to explicitly default-construct every member of the struct. + # This will initialize all primitives which wouldn't be initialized + # normally to their default values, and will initialize any actor member + # pointers to the correct default value of `nullptr`. Other C++ types + # with custom constructors must also provide a default constructor. + defctor.memberinits = [ + ExprMemberInit(f.memberVar()) for f in sd.fields_member_order() + ] + struct.addstmts([defctor, Whitespace.NL]) + + # Struct(const field1& _f1, ...) + valctor = ConstructorDefn( + ConstructorDecl(sd.name, params=fieldsAsParamList(), force_inline=True) + ) + valctor.memberinits = [] + for f in sd.fields_member_order(): + arg = f.argVar() + if _cxxTypeCanOnlyMove(f.ipdltype): + arg = ExprMove(arg) + valctor.memberinits.append(ExprMemberInit(f.memberVar(), args=[arg])) + + struct.addstmts([valctor, Whitespace.NL]) + + # The default copy, move, and assignment constructors, and the default + # destructor, will do the right thing. + + if sd.comparable: + # bool operator==(const Struct& _o) + ovar = ExprVar("_o") + opeqeq = MethodDefn( + MethodDecl( + "operator==", + params=[Decl(constreftype, ovar.name)], + ret=Type.BOOL, + const=True, + ) + ) + for f in sd.fields_ipdl_order(): + 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]) + + # bool operator!=(const Struct& _o) + opneq = MethodDefn( + MethodDecl( + "operator!=", + params=[Decl(constreftype, ovar.name)], + ret=Type.BOOL, + const=True, + ) + ) + opneq.addstmt(StmtReturn(ExprNot(ExprCall(ExprVar("operator=="), args=[ovar])))) + struct.addstmts([opneq, Whitespace.NL]) + + # field1& f1() + # const field1& f1() const + for f in sd.fields_ipdl_order(): + get = MethodDefn( + MethodDecl( + f.getMethod().name, params=[], ret=f.refType(), force_inline=True + ) + ) + get.addstmt(StmtReturn(f.refExpr())) + + getconstdecl = deepcopy(get.decl) + getconstdecl.ret = f.constRefType() + getconstdecl.const = True + getconst = MethodDefn(getconstdecl) + getconst.addstmt(StmtReturn(f.constRefExpr())) + + struct.addstmts([get, getconst, Whitespace.NL]) + + # private: + struct.addstmt(Label.PRIVATE) + + # Static assertions to ensure our assumptions about field layout match + # what the compiler is actually producing. We define this as a member + # function, rather than throwing the assertions in the constructor or + # similar, because we don't want to evaluate the static assertions every + # time the header file containing the structure is included. + staticasserts = _fieldStaticAssertions(sd) + if staticasserts: + method = MethodDefn( + MethodDecl("StaticAssertions", params=[], ret=Type.VOID, const=True) + ) + method.addstmts(staticasserts) + struct.addstmts([method]) + + # members + struct.addstmts( + [ + StmtDecl(Decl(_effectiveMemberType(f), f.memberVar().name)) + for f in sd.fields_member_order() + ] + ) + + return forwarddeclstmts, fulldecltypes, struct + + +def _effectiveMemberType(f): + effective_type = f.bareType() + # Structs must be copyable for backwards compatibility reasons, so we use + # CopyableTArray<T> as their member type for arrays. This is not exposed + # in the method signatures, these keep using nsTArray<T>, which is a base + # class of CopyableTArray<T>. + if effective_type.name == "nsTArray": + effective_type.name = "CopyableTArray" + return effective_type + + +# -------------------------------------------------- + + +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/move ctor, assignment operators, + # 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 + # - move ctor + # - case in generic copy ctor + # - copy operator= impl + # - move 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=True) + # const Union&, i.e., Union type with inparam semantics + inClsType = Type(ud.name, const=True, ref=True) + refClsType = Type(ud.name, ref=True) + rvalueRefClsType = Type(ud.name, rvalref=True) + 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 + + def voidCast(expr): + return ExprCast(expr, Type.VOID, static=True) + + # 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 + fulldecltypes = gettypedeps.fullDeclTypes + + # 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=True + ) + ) + getptr.addstmt(StmtReturn(c.ptrToSelfExpr())) + + getptrconst = MethodDefn( + MethodDecl( + c.getConstPtrName(), + params=[], + ret=c.constPtrToType(), + const=True, + force_inline=True, + ) + ) + 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=True, force_inline=True) + ) + 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=True, + force_inline=True, + ) + ) + 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=True), + memberinits=[ExprMemberInit(mtypevar, [tnonevar])], + ), + Whitespace.NL, + ] + ) + + # Union(const T&) copy & Union(T&&) move ctors + othervar = ExprVar("aOther") + for c in ud.components: + if not _cxxTypeCanOnlyMove(c.ipdltype): + 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]) + + if not _cxxTypeCanMove(c.ipdltype) or _cxxTypeNeedsMoveForSend(c.ipdltype): + continue + movector = ConstructorDefn( + ConstructorDecl(ud.name, params=[Decl(c.forceMoveType(), othervar.name)]) + ) + movector.addstmts( + [ + StmtExpr(c.callCtor(ExprMove(othervar))), + StmtExpr(ExprAssn(mtypevar, c.enumvar())), + ] + ) + cls.addstmts([movector, Whitespace.NL]) + + unionNeedsMove = any(_cxxTypeCanOnlyMove(c.ipdltype) for c in ud.components) + + # Union(const Union&) copy ctor + if not unionNeedsMove: + 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(Union&&) move ctor + movector = ConstructorDefn( + ConstructorDecl(ud.name, params=[Decl(rvalueRefClsType, othervar.name)]) + ) + othertypevar = ExprVar("t") + moveswitch = StmtSwitch(othertypevar) + for c in ud.components: + case = StmtBlock() + if c.recursive: + # This is sound as we set othervar.mTypeVar to T__None after the + # switch. The pointer in the union will be left dangling. + case.addstmts( + [ + # ptr_C() = other.ptr_C() + StmtExpr( + ExprAssn( + c.callGetPtr(), + ExprCall( + ExprSelect(othervar, ".", ExprVar(c.getPtrName())) + ), + ) + ) + ] + ) + else: + case.addstmts( + [ + # new ... (Move(other.get_C())) + StmtExpr( + c.callCtor( + ExprMove( + ExprCall(ExprSelect(othervar, ".", c.getTypeName())) + ) + ) + ), + # other.MaybeDestroy(T__None) + StmtExpr( + voidCast( + ExprCall( + ExprSelect(othervar, ".", maybedtorvar), args=[tnonevar] + ) + ) + ), + ] + ) + case.addstmts([StmtBreak()]) + moveswitch.addcase(CaseLabel(c.enum()), case) + moveswitch.addcase(CaseLabel(tnonevar.name), StmtBlock([StmtBreak()])) + moveswitch.addcase( + DefaultLabel(), StmtBlock([_logicError("unreached"), StmtReturn()]) + ) + movector.addstmts( + [ + StmtExpr(callAssertSanity(uvar=othervar)), + StmtDecl(Decl(typetype, othertypevar.name), init=ud.callType(othervar)), + moveswitch, + StmtExpr(ExprAssn(ExprSelect(othervar, ".", mtypevar), tnonevar)), + StmtExpr(ExprAssn(mtypevar, othertypevar)), + ] + ) + cls.addstmts([movector, Whitespace.NL]) + + # ~Union() + dtor = DestructorDefn(DestructorDecl(ud.name)) + # The void cast prevents Coverity from complaining about missing return + # value checks. + dtor.addstmt(StmtExpr(voidCast(callMaybeDestroy(tnonevar)))) + cls.addstmts([dtor, Whitespace.NL]) + + # type() + typemeth = MethodDefn( + MethodDecl("type", ret=typetype, const=True, force_inline=True) + ) + typemeth.addstmt(StmtReturn(mtypevar)) + cls.addstmts([typemeth, Whitespace.NL]) + + # Union& operator= methods + rhsvar = ExprVar("aRhs") + for c in ud.components: + if not _cxxTypeCanOnlyMove(c.ipdltype): + # Union& operator=(const T&) + 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=(T&&) + if not _cxxTypeCanMove(c.ipdltype) or _cxxTypeNeedsMoveForSend(c.ipdltype): + continue + + opeq = MethodDefn( + MethodDecl( + "operator=", + params=[Decl(c.forceMoveType(), rhsvar.name)], + ret=refClsType, + ) + ) + opeq.addstmts( + [ + # might need to placement-delete old value first + maybeReconstruct(c, c.enumvar()), + StmtExpr(c.callOperatorEq(ExprMove(rhsvar))), + StmtExpr(ExprAssn(mtypevar, c.enumvar())), + StmtReturn(ExprDeref(ExprVar.THIS)), + ] + ) + cls.addstmts([opeq, Whitespace.NL]) + + # Union& operator=(const Union&) + if not unionNeedsMove: + 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=True) + ), + 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]) + + # Union& operator=(Union&&) + opeq = MethodDefn( + MethodDecl( + "operator=", params=[Decl(rvalueRefClsType, rhsvar.name)], ret=refClsType + ) + ) + rhstypevar = ExprVar("t") + opeqswitch = StmtSwitch(rhstypevar) + for c in ud.components: + case = StmtBlock() + if c.recursive: + case.addstmts( + [ + StmtExpr(voidCast(callMaybeDestroy(tnonevar))), + StmtExpr( + ExprAssn( + c.callGetPtr(), + ExprCall(ExprSelect(rhsvar, ".", ExprVar(c.getPtrName()))), + ) + ), + ] + ) + else: + case.addstmts( + [ + maybeReconstruct(c, rhstypevar), + StmtExpr( + c.callOperatorEq( + ExprMove(ExprCall(ExprSelect(rhsvar, ".", c.getTypeName()))) + ) + ), + # other.MaybeDestroy(T__None) + StmtExpr( + voidCast( + ExprCall( + ExprSelect(rhsvar, ".", maybedtorvar), args=[tnonevar] + ) + ) + ), + ] + ) + case.addstmts([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(voidCast(callMaybeDestroy(rhstypevar))), 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(ExprSelect(rhsvar, ".", mtypevar), tnonevar)), + StmtExpr(ExprAssn(mtypevar, rhstypevar)), + StmtReturn(ExprDeref(ExprVar.THIS)), + ] + ) + cls.addstmts([opeq, Whitespace.NL]) + + if ud.comparable: + # bool operator==(const T&) + for c in ud.components: + opeqeq = MethodDefn( + MethodDecl( + "operator==", + params=[Decl(c.inType(), rhsvar.name)], + ret=Type.BOOL, + const=True, + ) + ) + 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=True, + ) + ) + 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=True) + ) + getvalue.addstmts( + [ + StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())), + StmtReturn(ExprDeref(c.callGetPtr())), + ] + ) + + getconstvalue = MethodDefn( + MethodDecl( + getConstValueVar.name, + ret=c.constRefType(), + const=True, + force_inline=True, + ) + ) + getconstvalue.addstmts( + [ + StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())), + StmtReturn(c.getConstValue()), + ] + ) + + cls.addstmts([getvalue, getconstvalue]) + + optype = MethodDefn(MethodDecl("", typeop=c.refType(), force_inline=True)) + optype.addstmt(StmtReturn(ExprCall(getValueVar))) + opconsttype = MethodDefn( + MethodDecl("", const=True, typeop=c.constRefType(), force_inline=True) + ) + opconsttype.addstmt(StmtReturn(ExprCall(getConstValueVar))) + + cls.addstmts([optype, opconsttype, Whitespace.NL]) + # private vars + cls.addstmts( + [ + Label.PRIVATE, + StmtDecl(Decl(valuetype, mvaluevar.name)), + StmtDecl(Decl(typetype, mtypevar.name)), + ] + ) + + return forwarddeclstmts, fulldecltypes, 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"), "IProtocol"), + Typedef(Type("IPC::Message"), "Message"), + Typedef(Type("base::ProcessHandle"), "ProcessHandle"), + Typedef(Type("mozilla::ipc::MessageChannel"), "MessageChannel"), + Typedef(Type("mozilla::ipc::SharedMemory"), "SharedMemory"), + ] + + 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] + ) + + actortype = ActorType(tu.protocol.decl.type) + traitsdecl, traitsdefn = _ParamTraits.actorPickling(actortype, self.side) + + self.hdrfile.addthings([traitsdecl, Whitespace.NL] + _includeGuardEnd(hf)) + + # make the .cpp file + if (self.protocol.name, self.side) not in VIRTUAL_CALL_CLASSES: + if (self.protocol.name, self.side) in DIRECT_CALL_OVERRIDES: + (_, header_file) = DIRECT_CALL_OVERRIDES[self.protocol.name, self.side] + else: + assert self.protocol.name.startswith("P") + header_file = "{}/{}{}.h".format( + "/".join(n.name for n in self.protocol.namespaces), + self.protocol.name[1:], + self.side.capitalize(), + ) + self.externalIncludes.add(header_file) + + cf.addthings( + [ + _DISCLAIMER, + Whitespace.NL, + CppDirective( + "include", + '"' + _protocolHeaderName(self.protocol, self.side) + '.h"', + ), + ] + + setToIncludes(self.externalIncludes) + ) + + cf.addthings( + ( + [Whitespace.NL] + + [ + CppDirective("include", '"%s.h"' % (inc)) + for inc in self.protocolCxxIncludes + ] + + [Whitespace.NL] + + [ + CppDirective("include", '"%s"' % filename) + for filename in ipdl.builtin.CppIncludes + ] + + [Whitespace.NL] + ) + ) + + cppns = makeNamespace(self.protocol, cf) + cppns.addstmts( + [Whitespace.NL, Whitespace.NL, clsdefn, Whitespace.NL, Whitespace.NL] + ) + + cf.addthing(traitsdefn) + + def visitUsingStmt(self, using): + if using.header is None: + return + + if using.canBeForwardDeclared() and not using.decl.type.isUniquePtr(): + 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.externalIncludes.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.addcode( + """ + #ifdef DEBUG + #include "prenv.h" + #endif // DEBUG + + #include "mozilla/Tainting.h" + #include "mozilla/ipc/MessageChannel.h" + #include "mozilla/ipc/ProtocolUtils.h" + """ + ) + + self.protocol = p + ptype = p.decl.type + toplevel = p.decl.type.toplevel() + + hasAsyncReturns = False + for md in p.messageDecls: + if md.hasAsyncReturns(): + hasAsyncReturns = True + break + + 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 == "parent": + self.hdrfile.addthings( + [_makeForwardDeclForQClass("nsIFile", []), Whitespace.NL] + ) + + self.cls = Class(self.clsname, inherits=inherits, abstract=True) + + self.cls.addstmt(Label.PRIVATE) + 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.addstmt( + FriendClassDecl(_actorName(friend.fullname(), self.prettyside)) + ) + + 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) + + if hasAsyncReturns: + self.cls.addstmt(Label.PUBLIC) + for md in p.messageDecls: + if self.sendsMessage(md) and md.hasAsyncReturns(): + self.cls.addstmt( + Typedef(_makePromise(md.returns, self.side), md.promiseName()) + ) + if self.receivesMessage(md) and md.hasAsyncReturns(): + self.cls.addstmt( + Typedef(_makeResolver(md.returns, self.side), md.resolverName()) + ) + self.cls.addstmt(Whitespace.NL) + + self.cls.addstmt(Label.PROTECTED) + # 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 + returnsems = "resolver" if md.decl.type.isAsync() else "out" + recvDecl = MethodDecl( + md.recvMethod(), + params=md.makeCxxParams( + paramsems="move", + returnsems=returnsems, + side=self.side, + implicit=implicit, + direction="recv", + ), + ret=Type("mozilla::ipc::IPCResult"), + methodspec=MethodSpec.VIRTUAL, + ) + + # These method implementations cause problems when trying to + # override them with different types in a direct call class. + # + # For the `isdtor` case there's a simple solution: it doesn't + # make much sense to specify arguments and then completely + # ignore them, and the no-arg case isn't a problem for + # overriding. + if isctor or (isdtor and not md.inParams): + defaultRecv = MethodDefn(recvDecl) + defaultRecv.addcode("return IPC_OK();\n") + self.cls.addstmt(defaultRecv) + elif (self.protocol.name, self.side) in VIRTUAL_CALL_CLASSES: + # If we're using virtual calls, we need the methods to be + # declared on the base class. + recvDecl.methodspec = MethodSpec.PURE + self.cls.addstmt(StmtDecl(recvDecl)) + + # If we're using virtual calls, we need the methods to be declared on + # the base class. + if (self.protocol.name, self.side) in VIRTUAL_CALL_CLASSES: + for md in p.messageDecls: + managed = md.decl.type.constructedType() + if not ptype.isManagerOf(managed) or md.decl.type.isDtor(): + continue + + # add the Alloc interface for managed actors + actortype = md.actorDecl().bareType(self.side) + + if managed.isRefcounted(): + if not self.receivesMessage(md): + continue + + actortype.ptr = False + actortype = _alreadyaddrefed(actortype) + + self.cls.addstmt( + StmtDecl( + MethodDecl( + _allocMethod(managed, self.side), + params=md.makeCxxParams( + side=self.side, implicit=False, direction="recv" + ), + ret=actortype, + methodspec=MethodSpec.PURE, + ) + ) + ) + + # add the Dealloc interface for all managed non-refcounted actors, + # even without ctors. This is useful for protocols which use + # ManagedEndpoint for construction. + for managed in ptype.manages: + if managed.isRefcounted(): + continue + + self.cls.addstmt( + StmtDecl( + MethodDecl( + _deallocMethod(managed, self.side), + params=[ + Decl(p.managedCxxType(managed, self.side), "aActor") + ], + ret=Type.BOOL, + methodspec=MethodSpec.PURE, + ) + ) + ) + + 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=True, ptr=True), "aReason"), + ], + methodspec=MethodSpec.OVERRIDE, + ) + ) + + # bool ShouldContinueFromReplyTimeout(); default to |true| + shouldcontinue = MethodDefn( + MethodDecl( + p.shouldContinueFromTimeoutVar().name, + ret=Type.BOOL, + methodspec=MethodSpec.OVERRIDE, + ) + ) + shouldcontinue.addcode("return true;\n") + + # void Entered*()/Exited*(); default to no-op + entered = MethodDefn( + MethodDecl(p.enteredCxxStackVar().name, methodspec=MethodSpec.OVERRIDE) + ) + exited = MethodDefn( + MethodDecl(p.exitedCxxStackVar().name, methodspec=MethodSpec.OVERRIDE) + ) + enteredcall = MethodDefn( + MethodDecl(p.enteredCallVar().name, methodspec=MethodSpec.OVERRIDE) + ) + exitedcall = MethodDefn( + MethodDecl(p.exitedCallVar().name, methodspec=MethodSpec.OVERRIDE) + ) + + 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(): + name = ExprLiteral.String(_actorName(p.name, self.side)) + ctor.memberinits = [ + ExprMemberInit( + ExprVar("mozilla::ipc::IToplevelProtocol"), + [name, _protocolId(ptype), side], + ) + ] + else: + ctor.memberinits = [ + ExprMemberInit( + ExprVar("mozilla::ipc::IProtocol"), [_protocolId(ptype), side] + ) + ] + + ctor.addcode("MOZ_COUNT_CTOR(${clsname});\n", clsname=self.clsname) + self.cls.addstmts([ctor, Whitespace.NL]) + + # ~Actor() + dtor = DestructorDefn( + DestructorDecl(self.clsname, methodspec=MethodSpec.VIRTUAL) + ) + dtor.addcode("MOZ_COUNT_DTOR(${clsname});\n", clsname=self.clsname) + + self.cls.addstmts([dtor, Whitespace.NL]) + + if ptype.isRefcounted(): + self.cls.addcode( + """ + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + """ + ) + self.cls.addstmt(Label.PROTECTED) + self.cls.addcode( + """ + void ActorAlloc() final { AddRef(); } + void ActorDealloc() final { Release(); } + """ + ) + + self.cls.addstmt(Label.PUBLIC) + if not ptype.isToplevel(): + if 1 == len(p.managers): + # manager() const + managertype = p.managerActorType(self.side, ptr=True) + managermeth = MethodDefn( + MethodDecl("Manager", ret=managertype, const=True) + ) + managermeth.addcode( + """ + return static_cast<${type}>(IProtocol::Manager()); + """, + type=managertype, + ) + + self.cls.addstmts([managermeth, Whitespace.NL]) + + def actorFromIter(itervar): + return ExprCode("${iter}.Get()->GetKey()", iter=itervar) + + def forLoopOverHashtable(hashtable, itervar, const=False): + itermeth = "ConstIter" if const else "Iter" + return StmtFor( + init=ExprCode( + "auto ${itervar} = ${hashtable}.${itermeth}()", + itervar=itervar, + hashtable=hashtable, + itermeth=itermeth, + ), + cond=ExprCode("!${itervar}.Done()", itervar=itervar), + update=ExprCode("${itervar}.Next()", itervar=itervar), + ) + + # Managed[T](Array& inout) const + # const Array<T>& Managed() const + for managed in ptype.manages: + container = p.managedVar(managed, self.side) + + meth = MethodDefn( + MethodDecl( + p.managedMethod(managed, self.side).name, + params=[ + Decl( + _cxxArrayType( + p.managedCxxType(managed, self.side), ref=True + ), + "aArr", + ) + ], + const=True, + ) + ) + meth.addcode("${container}.ToArray(aArr);\n", container=container) + + refmeth = MethodDefn( + MethodDecl( + p.managedMethod(managed, self.side).name, + params=[], + ret=p.managedVarType(managed, self.side, const=True, ref=True), + const=True, + ) + ) + refmeth.addcode("return ${container};\n", container=container) + + self.cls.addstmts([meth, refmeth, Whitespace.NL]) + + # AllManagedActors(Array& inout) const + arrvar = ExprVar("arr__") + managedmeth = MethodDefn( + MethodDecl( + "AllManagedActors", + params=[ + Decl( + _cxxArrayType(_refptr(_cxxLifecycleProxyType()), ref=True), + arrvar.name, + ) + ], + methodspec=MethodSpec.OVERRIDE, + const=True, + ) + ) + + # Count the number of managed actors, and allocate space in the output array. + managedmeth.addcode( + """ + uint32_t total = 0; + """ + ) + for managed in ptype.manages: + managedmeth.addcode( + """ + total += ${container}.Count(); + """, + container=p.managedVar(managed, self.side), + ) + managedmeth.addcode( + """ + arr__.SetCapacity(total); + + """ + ) + + for managed in ptype.manages: + managedmeth.addcode( + """ + for (auto it = ${container}.ConstIter(); !it.Done(); it.Next()) { + arr__.AppendElement(it.Get()->GetKey()->GetLifecycleProxy()); + } + + """, + container=p.managedVar(managed, self.side), + ) + + self.cls.addstmts([managedmeth, Whitespace.NL]) + + # OpenPEndpoint(...)/BindPEndpoint(...) + for managed in ptype.manages: + self.genManagedEndpoint(managed) + + # 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 = ExprCode("msg__.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) + + # add default cases + default = StmtCode( + """ + return MsgNotKnown; + """ + ) + self.asyncSwitch.addcase(DefaultLabel(), default) + if toplevel.isSync() or toplevel.isInterrupt(): + self.syncSwitch.addcase(DefaultLabel(), default) + if toplevel.isInterrupt(): + self.interruptSwitch.addcase(DefaultLabel(), default) + + self.cls.addstmts(self.implementManagerIface()) + + def makeHandlerMethod(name, switch, hasReply, dispatches=False): + params = [Decl(Type("Message", const=True, ref=True), msgvar.name)] + if hasReply: + params.append(Decl(Type("Message", ref=True, ptr=True), replyvar.name)) + + method = MethodDefn( + MethodDecl( + name, + methodspec=MethodSpec.OVERRIDE, + params=params, + ret=_Result.Type(), + ) + ) + + if not switch: + method.addcode( + """ + MOZ_ASSERT_UNREACHABLE("message protocol not supported"); + return MsgNotKnown; + """ + ) + return method + + if dispatches: + method.addcode( + """ + int32_t route__ = ${msgvar}.routing_id(); + if (MSG_ROUTING_CONTROL != route__) { + IProtocol* routed__ = Lookup(route__); + if (!routed__ || !routed__->GetLifecycleProxy()) { + ${logignored} + return MsgProcessed; + } + + RefPtr<mozilla::ipc::ActorLifecycleProxy> proxy__ = + routed__->GetLifecycleProxy(); + return proxy__->Get()->${name}($,{args}); + } + + """, + msgvar=msgvar, + logignored=self.logMessage( + None, ExprAddrOf(msgvar), "Ignored message for dead actor" + ), + name=name, + args=[p.name for p in params], + ) + + # 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=False, + dispatches=dispatches, + ), + Whitespace.NL, + ] + ) + self.cls.addstmts( + [ + makeHandlerMethod( + "OnMessageReceived", + self.syncSwitch, + hasReply=True, + dispatches=dispatches, + ), + Whitespace.NL, + ] + ) + self.cls.addstmts( + [ + makeHandlerMethod( + "OnCallReceived", + self.interruptSwitch, + hasReply=True, + dispatches=dispatches, + ), + Whitespace.NL, + ] + ) + + clearsubtreevar = ExprVar("ClearSubtree") + + if ptype.isToplevel(): + # OnChannelClose() + onclose = MethodDefn( + MethodDecl("OnChannelClose", methodspec=MethodSpec.OVERRIDE) + ) + onclose.addcode( + """ + DestroySubtree(NormalShutdown); + ClearSubtree(); + DeallocShmems(); + if (GetLifecycleProxy()) { + GetLifecycleProxy()->Release(); + } + """ + ) + self.cls.addstmts([onclose, Whitespace.NL]) + + # OnChannelError() + onerror = MethodDefn( + MethodDecl("OnChannelError", methodspec=MethodSpec.OVERRIDE) + ) + onerror.addcode( + """ + DestroySubtree(AbnormalShutdown); + ClearSubtree(); + DeallocShmems(); + if (GetLifecycleProxy()) { + GetLifecycleProxy()->Release(); + } + """ + ) + self.cls.addstmts([onerror, Whitespace.NL]) + + if ptype.isToplevel() and ptype.isInterrupt(): + processnative = MethodDefn( + MethodDecl("ProcessNativeEventsInInterruptCall", ret=Type.VOID) + ) + processnative.addcode( + """ + #ifdef OS_WIN + GetIPCChannel()->ProcessNativeEventsInInterruptCall(); + #else + FatalError("This method is Windows-only"); + #endif + """ + ) + + self.cls.addstmts([processnative, Whitespace.NL]) + + # private methods + self.cls.addstmt(Label.PRIVATE) + + # ClearSubtree() + clearsubtree = MethodDefn(MethodDecl(clearsubtreevar.name)) + for managed in ptype.manages: + clearsubtree.addcode( + """ + for (auto it = ${container}.Iter(); !it.Done(); it.Next()) { + it.Get()->GetKey()->ClearSubtree(); + } + for (auto it = ${container}.Iter(); !it.Done(); it.Next()) { + // Recursively releasing ${container} kids. + auto* proxy = it.Get()->GetKey()->GetLifecycleProxy(); + NS_IF_RELEASE(proxy); + } + ${container}.Clear(); + + """, + container=p.managedVar(managed, self.side), + ) + + # don't release our own IPC reference: either the manager will do it, + # or we're toplevel + self.cls.addstmts([clearsubtree, Whitespace.NL]) + + for managed in ptype.manages: + self.cls.addstmts( + [ + StmtDecl( + Decl( + p.managedVarType(managed, self.side), + p.managedVar(managed, self.side).name, + ) + ) + ] + ) + + def genManagedEndpoint(self, managed): + hereEp = "ManagedEndpoint<%s>" % _actorName(managed.name(), self.side) + thereEp = "ManagedEndpoint<%s>" % _actorName( + managed.name(), _otherSide(self.side) + ) + + actor = _HybridDecl(ipdl.type.ActorType(managed), "aActor") + + # ManagedEndpoint<PThere> OpenPEndpoint(PHere* aActor) + openmeth = MethodDefn( + MethodDecl( + "Open%sEndpoint" % managed.name(), + params=[ + Decl(self.protocol.managedCxxType(managed, self.side), actor.name) + ], + ret=Type(thereEp), + ) + ) + openmeth.addcode( + """ + $*{bind} + return ${thereEp}(mozilla::ipc::PrivateIPDLInterface(), aActor->Id()); + """, + bind=self.bindManagedActor(actor, errfn=ExprCall(ExprVar(thereEp))), + thereEp=thereEp, + ) + + # void BindPEndpoint(ManagedEndpoint<PHere>&& aEndpoint, PHere* aActor) + bindmeth = MethodDefn( + MethodDecl( + "Bind%sEndpoint" % managed.name(), + params=[ + Decl(Type(hereEp), "aEndpoint"), + Decl(self.protocol.managedCxxType(managed, self.side), actor.name), + ], + ret=Type.BOOL, + ) + ) + bindmeth.addcode( + """ + MOZ_RELEASE_ASSERT(aEndpoint.ActorId(), "Invalid Endpoint!"); + $*{bind} + return true; + """, + bind=self.bindManagedActor( + actor, errfn=ExprLiteral.FALSE, idexpr=ExprCode("*aEndpoint.ActorId()") + ), + ) + + self.cls.addstmts([openmeth, bindmeth, Whitespace.NL]) + + def implementManagerIface(self): + p = self.protocol + protocolbase = Type("IProtocol", ptr=True) + + methods = [] + + if p.decl.type.isToplevel(): + + # "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 + ) + + # 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), + ], + methodspec=MethodSpec.OVERRIDE, + ) + ) + + if not len(p.managesStmts): + removemanagee.addcode( + """ + FatalError("unreached"); + return; + """ + ) + else: + switchontype = StmtSwitch(pvar) + for managee in p.managesStmts: + manageeipdltype = managee.decl.type + manageecxxtype = _cxxBareType( + ipdl.type.ActorType(manageeipdltype), self.side + ) + case = ExprCode( + """ + { + ${manageecxxtype} actor = static_cast<${manageecxxtype}>(aListener); + auto& container = ${container}; + + // Use a temporary variable here so all the assertion expressions + // in the MOZ_RELEASE_ASSERT call below are textually identical; + // the linker can then merge the strings from the assertion macro(s). + MOZ_RELEASE_ASSERT(container.Contains(actor), + "actor not managed by this!"); + container.RemoveEntry(actor); + + auto* proxy = actor->GetLifecycleProxy(); + NS_IF_RELEASE(proxy); + return; + } + """, + manageecxxtype=manageecxxtype, + container=p.managedVar(manageeipdltype, self.side), + ) + switchontype.addcase(CaseLabel(_protocolId(manageeipdltype).name), case) + switchontype.addcase( + DefaultLabel(), + ExprCode( + """ + FatalError("unreached"); + return; + """ + ), + ) + removemanagee.addstmt(switchontype) + + # The `DeallocManagee` method is called for managed actors to trigger + # deallocation when ActorLifecycleProxy is freed. + deallocmanagee = MethodDefn( + MethodDecl( + p.deallocManageeMethod().name, + params=[ + Decl(_protocolIdType(), pvar.name), + Decl(protocolbase, listenervar.name), + ], + methodspec=MethodSpec.OVERRIDE, + ) + ) + + if not len(p.managesStmts): + deallocmanagee.addcode( + """ + FatalError("unreached"); + return; + """ + ) + else: + switchontype = StmtSwitch(pvar) + for managee in p.managesStmts: + manageeipdltype = managee.decl.type + # Reference counted actor types don't have corresponding + # `Dealloc` methods, as they are deallocated by releasing the + # IPDL-held reference. + if manageeipdltype.isRefcounted(): + continue + + case = StmtCode( + """ + ${concrete}->${dealloc}(static_cast<${type}>(aListener)); + return; + """, + concrete=self.concreteThis(), + dealloc=_deallocMethod(manageeipdltype, self.side), + type=_cxxBareType(ipdl.type.ActorType(manageeipdltype), self.side), + ) + switchontype.addcase(CaseLabel(_protocolId(manageeipdltype).name), case) + switchontype.addcase( + DefaultLabel(), + StmtCode( + """ + FatalError("unreached"); + return; + """ + ), + ) + deallocmanagee.addstmt(switchontype) + + return methods + [removemanagee, deallocmanagee, Whitespace.NL] + + def genShmemCreatedHandler(self): + assert self.protocol.decl.type.isToplevel() + + return StmtCode( + """ + { + if (!ShmemCreated(${msgvar})) { + return MsgPayloadError; + } + return MsgProcessed; + } + """, + msgvar=self.msgvar, + ) + + def genShmemDestroyedHandler(self): + assert self.protocol.decl.type.isToplevel() + + return StmtCode( + """ + { + if (!ShmemDestroyed(${msgvar})) { + return MsgPayloadError; + } + return MsgProcessed; + } + """, + msgvar=self.msgvar, + ) + + # ------------------------------------------------------------------------- + # 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 concreteThis(self): + if (self.protocol.name, self.side) in VIRTUAL_CALL_CLASSES: + return ExprVar.THIS + + if (self.protocol.name, self.side) in DIRECT_CALL_OVERRIDES: + (class_name, _) = DIRECT_CALL_OVERRIDES[self.protocol.name, self.side] + else: + assert self.protocol.name.startswith("P") + class_name = "{}{}".format(self.protocol.name[1:], self.side.capitalize()) + + return ExprCode("static_cast<${class_name}*>(this)", class_name=class_name) + + def thisCall(self, function, args): + return ExprCall(ExprSelect(self.concreteThis(), "->", function), args=args) + + def visitMessageDecl(self, md): + isctor = md.decl.type.isCtor() + isdtor = md.decl.type.isDtor() + decltype = md.decl.type + sendmethod = None + movesendmethod = None + promisesendmethod = 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() + + # NOTE: Don't generate helper ctors for refcounted types. + # + # Safety concerns around providing your own actor to a ctor (namely + # that the return value won't be checked, and the argument will be + # `delete`-ed) are less critical with refcounted actors, due to the + # actor being held alive by the callsite. + # + # This allows refcounted actors to not implement crashing AllocPFoo + # methods on the sending side. + if isctor and not md.decl.type.constructedType().isRefcounted(): + 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, + movesendmethod, + promisesendmethod, + (recvlbl, recvcase), + ) = self.genAsyncSendMethod(md) + else: + sendmethod, movesendmethod = 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 movesendmethod is not None: + self.cls.addstmts([movesendmethod, Whitespace.NL]) + if promisesendmethod is not None: + self.cls.addstmts([promisesendmethod, 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)) + + msgvar, stmts = self.makeMessage(md, errfnSendCtor) + sendok, sendstmts = self.sendAsync(md, msgvar) + + method.addcode( + """ + $*{bind} + + // Build our constructor message & verify it. + $*{stmts} + $*{verify} + + // Notify the other side about the newly created actor. This can + // fail if our manager has already been destroyed. + // + // NOTE: If the send call fails due to toplevel channel teardown, + // the `IProtocol::ChannelSend` wrapper absorbs the error for us, + // so we don't tear down actors unexpectedly. + $*{sendstmts} + + // Warn, destroy the actor, and return null if the message failed to + // send. Otherwise, return the successfully created actor reference. + if (!${sendok}) { + NS_WARNING("Error sending ${actorname} constructor"); + $*{destroy} + return nullptr; + } + return ${actor}; + """, + bind=self.bindManagedActor(actor), + stmts=stmts, + verify=self.genVerifyMessage( + md.decl.type.verify, md.params, errfnSendCtor, ExprVar("msg__") + ), + sendstmts=sendstmts, + sendok=sendok, + destroy=self.destroyActor( + md, actor.var(), why=_DestroyReason.FailedConstructor + ), + actor=actor.var(), + actorname=actor.ipdltype.protocol.name() + self.side.capitalize(), + ) + + 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)) + + msgvar, stmts = self.makeMessage(md, errfnSendCtor) + verify = self.genVerifyMessage( + md.decl.type.verify, md.params, errfnSendCtor, ExprVar("msg__") + ) + + replyvar = self.replyvar + sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar) + replystmts = self.deserializeReply( + md, + ExprAddrOf(replyvar), + self.side, + errfnSendCtor, + errfnSentinel(ExprLiteral.NULL), + ) + + method.addcode( + """ + $*{bind} + + // Build our constructor message & verify it. + $*{stmts} + $*{verify} + + // Synchronously send the constructor message to the other side. If + // the send fails, e.g. due to the remote side shutting down, the + // actor will be destroyed and potentially freed. + Message ${replyvar}; + $*{sendstmts} + + if (!(${sendok})) { + // Warn, destroy the actor and return null if the message + // failed to send. + NS_WARNING("Error sending constructor"); + $*{destroy} + return nullptr; + } + + $*{replystmts} + return ${actor}; + """, + bind=self.bindManagedActor(actor), + stmts=stmts, + verify=verify, + replyvar=replyvar, + sendstmts=sendstmts, + sendok=sendok, + destroy=self.destroyActor( + md, actor.var(), why=_DestroyReason.FailedConstructor + ), + replystmts=replystmts, + actor=actor.var(), + actorname=actor.ipdltype.protocol.name() + self.side.capitalize(), + ) + + return method + + def bindManagedActor(self, actordecl, errfn=ExprLiteral.NULL, idexpr=None): + actorproto = actordecl.ipdltype.protocol + + if idexpr is None: + setManagerArgs = [ExprVar.THIS] + else: + setManagerArgs = [ExprVar.THIS, idexpr] + + return [ + StmtCode( + """ + if (!${actor}) { + NS_WARNING("Cannot bind null ${actorname} actor"); + return ${errfn}; + } + + ${actor}->SetManagerAndRegister($,{setManagerArgs}); + ${container}.PutEntry(${actor}); + """, + actor=actordecl.var(), + actorname=actorproto.name() + self.side.capitalize(), + errfn=errfn, + setManagerArgs=setManagerArgs, + container=self.protocol.managedVar(actorproto, self.side), + ) + ] + + def genHelperCtor(self, md): + helperdecl = self.makeSendMethodDecl(md) + helperdecl.params = helperdecl.params[1:] + helper = MethodDefn(helperdecl) + + helper.addstmts( + [ + self.callAllocActor(md, retsems="out", side=self.side), + StmtReturn(ExprCall(ExprVar(helperdecl.name), args=md.makeCxxArgs())), + ] + ) + return helper + + def genAsyncDtor(self, md): + actor = md.actorDecl() + actorvar = actor.var() + method = MethodDefn(self.makeDtorMethodDecl(md)) + + method.addstmt(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.addstmt(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, errfnSentinel(), actorvar + ) + ifsendok = StmtIf(ExprLiteral.FALSE) + ifsendok.addifstmts(destmts) + ifsendok.addifstmts( + [Whitespace.NL, StmtExpr(ExprAssn(sendok, ExprLiteral.FALSE, "&="))] + ) + + method.addstmt(ifsendok) + + 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 [ + StmtCode( + """ + IProtocol* mgr = ${actor}->Manager(); + ${actor}->DestroySubtree(${why}); + ${actor}->ClearSubtree(); + mgr->RemoveManagee(${protoId}, ${actor}); + """, + actor=actorexpr, + why=why, + protoId=_protocolId(destroyedType), + ) + ] + + def dtorPrologue(self, actorexpr): + return StmtCode( + """ + if (!${actor} || !${actor}->CanSend()) { + NS_WARNING("Attempt to __delete__ missing or closed actor"); + return false; + } + """, + actor=actorexpr, + ) + + def dtorEpilogue(self, md, actorexpr): + return self.destroyActor(md, actorexpr) + + def genRecvAsyncReplyCase(self, md): + lbl = CaseLabel(md.pqReplyId()) + case = StmtBlock() + resolve, reason, prologue, desrej, desstmts = self.deserializeAsyncReply( + md, self.side, errfnRecv, errfnSentinel(_Result.ValuError) + ) + + if len(md.returns) > 1: + resolvetype = _tuple([d.bareType(self.side) for d in md.returns]) + resolvearg = ExprCall( + ExprVar("MakeTuple"), args=[ExprMove(p.var()) for p in md.returns] + ) + else: + resolvetype = md.returns[0].bareType(self.side) + resolvearg = ExprMove(md.returns[0].var()) + + case.addcode( + """ + $*{prologue} + + UniquePtr<MessageChannel::UntypedCallbackHolder> untypedCallback = + GetIPCChannel()->PopCallback(${msgvar}); + + typedef MessageChannel::CallbackHolder<${resolvetype}> CallbackHolder; + auto* callback = static_cast<CallbackHolder*>(untypedCallback.get()); + if (!callback) { + FatalError("Error unknown callback"); + return MsgProcessingError; + } + + if (${resolve}) { + $*{desstmts} + callback->Resolve(${resolvearg}); + } else { + $*{desrej} + callback->Reject(std::move(${reason})); + } + return MsgProcessed; + """, + prologue=prologue, + msgvar=self.msgvar, + resolve=resolve, + resolvetype=resolvetype, + desstmts=desstmts, + resolvearg=resolvearg, + desrej=desrej, + reason=reason, + ) + + return (lbl, case) + + def genAsyncSendMethod(self, md): + method = MethodDefn(self.makeSendMethodDecl(md)) + msgvar, stmts = self.makeMessage(md, errfnSend) + retvar, sendstmts = self.sendAsync(md, msgvar) + + method.addstmts( + stmts + + [Whitespace.NL] + + self.genVerifyMessage( + md.decl.type.verify, md.params, errfnSend, ExprVar("msg__") + ) + + sendstmts + + [StmtReturn(retvar)] + ) + + movemethod = None + + # Add the promise overload if we need one. + if md.returns: + promisemethod = MethodDefn(self.makeSendMethodDecl(md, promise=True)) + stmts = self.sendAsyncWithPromise(md) + promisemethod.addstmts(stmts) + + (lbl, case) = self.genRecvAsyncReplyCase(md) + else: + (promisemethod, lbl, case) = (None, None, None) + + return method, movemethod, promisemethod, (lbl, case) + + 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, errfnSentinel() + ) + + 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] + ) + + movemethod = None + + return method, movemethod + + def genCtorRecvCase(self, md): + lbl = CaseLabel(md.pqMsgId()) + case = StmtBlock() + actorhandle = self.handlevar + + stmts = self.deserializeMessage( + md, self.side, errfnRecv, errfnSent=errfnSentinel(_Result.ValuError) + ) + + idvar, saveIdStmts = self.saveActorId(md) + case.addstmts( + stmts + + [ + StmtDecl(Decl(r.bareType(self.side), r.var().name), initargs=[]) + for r in md.returns + ] + # alloc the actor, register it under the foreign ID + + [self.callAllocActor(md, retsems="in", side=self.side)] + + self.bindManagedActor( + md.actorDecl(), 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, errfnSent=errfnSentinel(_Result.ValuError) + ) + + idvar, saveIdStmts = self.saveActorId(md) + case.addstmts( + stmts + + [ + StmtDecl(Decl(r.bareType(self.side), r.var().name), initargs=[]) + for r in md.returns + ] + + self.invokeRecvHandler(md, implicit=False) + + [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, errfnSent=errfnSentinel(_Result.ValuError) + ) + + idvar, saveIdStmts = self.saveActorId(md) + declstmts = [ + StmtDecl(Decl(r.bareType(self.side), r.var().name), initargs=[]) + for r in md.returns + ] + if md.decl.type.isAsync() and md.returns: + declstmts = self.makeResolver(md, errfnRecv, routingId=idvar) + case.addstmts( + stmts + + saveIdStmts + + declstmts + + 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 makeMessage(self, md, errfn, fromActor=None): + msgvar = self.msgvar + routingId = self.protocol.routingId(fromActor) + this = ExprVar.THIS + if md.decl.type.isDtor(): + this = md.actorDecl().var() + + stmts = ( + [ + StmtDecl( + Decl(Type("IPC::Message", ptr=True), msgvar.name), + init=ExprCall(ExprVar(md.pqMsgCtorFunc()), args=[routingId]), + ) + ] + + [Whitespace.NL] + + [ + _ParamTraits.checkedWrite( + p.ipdltype, p.var(), msgvar, sentinelKey=p.name, actor=this + ) + for p in md.params + ] + + [Whitespace.NL] + + self.setMessageFlags(md, msgvar) + ) + return msgvar, stmts + + def makeResolver(self, md, errfn, routingId): + if routingId is None: + routingId = self.protocol.routingId() + if not md.decl.type.isAsync() or not md.hasReply(): + return [] + + selfvar = ExprVar("self__") + serializeParams = [ + StmtCode("bool resolve__ = true;\n"), + _ParamTraits.checkedWrite( + None, + ExprVar("resolve__"), + self.replyvar, + sentinelKey="resolve__", + actor=selfvar, + ), + ] + + def paramValue(idx): + assert idx < len(md.returns) + if len(md.returns) > 1: + return ExprCode("mozilla::Get<${idx}>(aParam)", idx=idx) + return ExprVar("aParam") + + serializeParams += [ + _ParamTraits.checkedWrite( + p.ipdltype, + paramValue(idx), + self.replyvar, + sentinelKey=p.name, + actor=selfvar, + ) + for idx, p in enumerate(md.returns) + ] + + return [ + StmtCode( + """ + int32_t seqno__ = ${msgvar}.seqno(); + RefPtr<mozilla::ipc::ActorLifecycleProxy> proxy__ = + GetLifecycleProxy(); + + ${resolvertype} resolver = [proxy__, seqno__, ${routingId}](${resolveType} aParam) { + if (!proxy__->Get()) { + NS_WARNING("Not resolving response because actor is dead."); + return; + } + ${actortype} self__ = static_cast<${actortype}>(proxy__->Get()); + + IPC::Message* ${replyvar} = ${replyCtor}(${routingId}); + ${replyvar}->set_seqno(seqno__); + + $*{serializeParams} + ${logSendingReply} + bool sendok__ = self__->ChannelSend(${replyvar}); + if (!sendok__) { + NS_WARNING("Error sending reply"); + } + }; + """, + msgvar=self.msgvar, + resolvertype=Type(md.resolverName()), + routingId=routingId, + resolveType=_resolveType(md.returns, self.side), + actortype=_cxxBareType(ActorType(self.protocol.decl.type), self.side), + replyvar=self.replyvar, + replyCtor=ExprVar(md.pqReplyCtorFunc()), + serializeParams=serializeParams, + logSendingReply=self.logMessage( + md, + self.replyvar, + "Sending reply ", + actor=selfvar, + ), + ) + ] + + 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 [] + if md.decl.type.isAsync() and md.decl.type.hasReply(): + return [] + + replyvar = self.replyvar + return ( + [ + StmtExpr( + ExprAssn( + replyvar, + ExprCall(ExprVar(md.pqReplyCtorFunc()), args=[routingId]), + ) + ), + Whitespace.NL, + ] + + [ + _ParamTraits.checkedWrite( + r.ipdltype, + r.var(), + replyvar, + sentinelKey=r.name, + actor=ExprVar.THIS, + ) + for r in md.returns + ] + + self.setMessageFlags(md, replyvar) + + [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=False), "msgverify__"), + init=ExprMove(ExprDeref(msgsrcVar)), + ) + ) + + stmts.extend( + ( + # PickleIterator msgverifyIter__(msgverify__); + [StmtDecl(Decl(_iterType(ptr=False), itervar.name), initargs=[msgvar])] + # declare varCopy for each variable to deserialize. + + [ + StmtDecl(Decl(p.bareType(side), p.var().name + "Copy"), initargs=[]) + for p in params + ] + + [Whitespace.NL] + # checked Read(&(varCopy), &(msgverify__), &(msgverifyIter__)) + + [ + _ParamTraits.checkedRead( + p.ipdltype, + ExprAddrOf(ExprVar(p.var().name + "Copy")), + msgexpr, + ExprAddrOf(itervar), + errfn, + p.ipdltype.name(), + sentinelKey=p.name, + errfnSentinel=errfnSentinel(), + actor=ExprVar.THIS, + ) + 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, seqno=None): + stmts = [] + + if seqno: + stmts.append( + StmtExpr(ExprCall(ExprSelect(var, "->", "set_seqno"), args=[seqno])) + ) + + return stmts + [Whitespace.NL] + + def deserializeMessage(self, md, side, errfn, errfnSent): + 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), initargs=[])] + reads = [ + _ParamTraits.checkedRead( + None, + ExprAddrOf(handlevar), + msgexpr, + ExprAddrOf(self.itervar), + errfn, + "'%s'" % handletype.name, + sentinelKey="actor", + errfnSentinel=errfnSent, + actor=ExprVar.THIS, + ) + ] + start = 1 + + decls.extend( + [ + StmtDecl( + Decl( + ( + Type("Tainted", T=p.bareType(side)) + if md.decl.type.tainted + else p.bareType(side) + ), + p.var().name, + ), + initargs=[], + ) + for p in md.params[start:] + ] + ) + reads.extend( + [ + _ParamTraits.checkedRead( + p.ipdltype, + ExprAddrOf(p.var()), + msgexpr, + ExprAddrOf(itervar), + errfn, + "'%s'" % p.ipdltype.name(), + sentinelKey=p.name, + errfnSentinel=errfnSent, + actor=ExprVar.THIS, + ) + for p in md.params[start:] + ] + ) + + stmts.extend( + ( + [ + StmtDecl( + Decl(_iterType(ptr=False), self.itervar.name), initargs=[msgvar] + ) + ] + + decls + + [Whitespace.NL] + + reads + + [self.endRead(msgvar, itervar)] + ) + ) + + return stmts + + def deserializeAsyncReply(self, md, side, errfn, errfnSent): + msgvar = self.msgvar + itervar = self.itervar + msgexpr = ExprAddrOf(msgvar) + isctor = md.decl.type.isCtor() + resolve = ExprVar("resolve__") + reason = ExprVar("reason__") + desresolve = [ + StmtDecl(Decl(Type.BOOL, resolve.name), init=ExprLiteral.FALSE), + _ParamTraits.checkedRead( + None, + ExprAddrOf(resolve), + msgexpr, + ExprAddrOf(itervar), + errfn, + "'%s'" % resolve.name, + sentinelKey=resolve.name, + errfnSentinel=errfnSent, + actor=ExprVar.THIS, + ), + ] + desrej = [ + StmtDecl(Decl(_ResponseRejectReason.Type(), reason.name), initargs=[]), + _ParamTraits.checkedRead( + None, + ExprAddrOf(reason), + msgexpr, + ExprAddrOf(itervar), + errfn, + "'%s'" % reason.name, + sentinelKey=reason.name, + errfnSentinel=errfnSent, + actor=ExprVar.THIS, + ), + self.endRead(msgvar, itervar), + ] + prologue = [ + self.logMessage(md, msgexpr, "Received ", receiving=True), + self.profilerLabel(md), + Whitespace.NL, + ] + + if not md.returns: + return prologue + + prologue.extend( + [StmtDecl(Decl(_iterType(ptr=False), itervar.name), initargs=[msgvar])] + + desresolve + ) + + 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), initargs=[])] + reads = [ + _ParamTraits.checkedRead( + None, + ExprAddrOf(handlevar), + msgexpr, + ExprAddrOf(itervar), + errfn, + "'%s'" % handletype.name, + sentinelKey="actor", + errfnSentinel=errfnSent, + actor=ExprVar.THIS, + ) + ] + start = 1 + + stmts = ( + decls + + [ + StmtDecl(Decl(p.bareType(side), p.var().name), initargs=[]) + for p in md.returns + ] + + [Whitespace.NL] + + reads + + [ + _ParamTraits.checkedRead( + p.ipdltype, + ExprAddrOf(p.var()), + msgexpr, + ExprAddrOf(itervar), + errfn, + "'%s'" % p.ipdltype.name(), + sentinelKey=p.name, + errfnSentinel=errfnSent, + actor=ExprVar.THIS, + ) + for p in md.returns[start:] + ] + + [self.endRead(msgvar, itervar)] + ) + + return resolve, reason, prologue, desrej, stmts + + def deserializeReply( + self, md, replyexpr, side, errfn, errfnSentinel, actor=None, decls=False + ): + stmts = [ + Whitespace.NL, + self.logMessage(md, replyexpr, "Received reply ", actor, receiving=True), + ] + if 0 == len(md.returns): + return stmts + + itervar = self.itervar + declstmts = [] + if decls: + declstmts = [ + StmtDecl(Decl(p.bareType(side), p.var().name), initargs=[]) + for p in md.returns + ] + stmts.extend( + [ + Whitespace.NL, + StmtDecl( + Decl(_iterType(ptr=False), itervar.name), initargs=[self.replyvar] + ), + ] + + declstmts + + [Whitespace.NL] + + [ + _ParamTraits.checkedRead( + r.ipdltype, + r.var(), + ExprAddrOf(self.replyvar), + ExprAddrOf(self.itervar), + errfn, + "'%s'" % r.ipdltype.name(), + sentinelKey=r.name, + errfnSentinel=errfnSentinel, + actor=ExprVar.THIS, + ) + for r in md.returns + ] + + [self.endRead(self.replyvar, itervar)] + ) + + return stmts + + def sendAsync(self, md, msgexpr, actor=None): + sendok = ExprVar("sendok__") + resolvefn = ExprVar("aResolve") + rejectfn = ExprVar("aReject") + + stmts = [ + Whitespace.NL, + self.logMessage(md, msgexpr, "Sending ", actor), + self.profilerLabel(md), + ] + stmts.append(Whitespace.NL) + + # Generate the actual call expression. + send = ExprVar("ChannelSend") + if actor is not None: + send = ExprSelect(actor, "->", send.name) + if md.returns: + stmts.append( + StmtExpr( + ExprCall( + send, args=[msgexpr, ExprMove(resolvefn), ExprMove(rejectfn)] + ) + ) + ) + retvar = None + else: + stmts.append( + StmtDecl( + Decl(Type.BOOL, sendok.name), init=ExprCall(send, args=[msgexpr]) + ) + ) + retvar = sendok + + return (retvar, stmts) + + def sendBlocking(self, md, msgexpr, replyexpr, actor=None): + send = ExprVar("ChannelSend") + if md.decl.type.isInterrupt(): + send = ExprVar("ChannelCall") + if actor is not None: + send = ExprSelect(actor, "->", send.name) + + sendok = ExprVar("sendok__") + return ( + sendok, + ( + [ + Whitespace.NL, + self.logMessage(md, msgexpr, "Sending ", actor), + self.profilerLabel(md), + ] + + [ + Whitespace.NL, + StmtDecl(Decl(Type.BOOL, sendok.name), init=ExprLiteral.FALSE), + StmtBlock( + [ + StmtExpr( + ExprAssn( + sendok, + ExprCall( + send, args=[msgexpr, ExprAddrOf(replyexpr)] + ), + ) + ), + ] + ), + ] + ), + ) + + def sendAsyncWithPromise(self, md): + # Create a new promise, and forward to the callback send overload. + promise = _makePromise(md.returns, self.side, resolver=True) + + if len(md.returns) > 1: + resolvetype = _tuple([d.bareType(self.side) for d in md.returns]) + else: + resolvetype = md.returns[0].bareType(self.side) + + resolve = ExprCode( + """ + [promise__](${resolvetype}&& aValue) { + promise__->Resolve(std::move(aValue), __func__); + } + """, + resolvetype=resolvetype, + ) + reject = ExprCode( + """ + [promise__](ResponseRejectReason&& aReason) { + promise__->Reject(std::move(aReason), __func__); + } + """, + resolvetype=resolvetype, + ) + + args = [ExprMove(p.var()) for p in md.params] + [resolve, reject] + stmt = StmtCode( + """ + RefPtr<${promise}> promise__ = new ${promise}(__func__); + promise__->UseDirectTaskDispatch(__func__); + ${send}($,{args}); + return promise__; + """, + promise=promise, + send=md.sendMethod(), + args=args, + ) + return [stmt] + + def callAllocActor(self, md, retsems, side): + actortype = md.actorDecl().bareType(self.side) + if md.decl.type.constructedType().isRefcounted(): + actortype.ptr = False + actortype = _refptr(actortype) + + callalloc = self.thisCall( + _allocMethod(md.decl.type.constructedType(), side), + args=md.makeCxxArgs(retsems=retsems, retcallsems="out", implicit=False), + ) + + return StmtDecl(Decl(actortype, md.actorDecl().var().name), init=callalloc) + + def invokeRecvHandler(self, md, implicit=True): + retsems = "in" + if md.decl.type.isAsync() and md.returns: + retsems = "resolver" + failif = StmtIf( + ExprNot( + self.thisCall( + md.recvMethod(), + md.makeCxxArgs( + paramsems="move", + retsems=retsems, + retcallsems="out", + implicit=implicit, + ), + ) + ) + ) + failif.addifstmts( + [ + _protocolErrorBreakpoint("Handler returned error code!"), + Whitespace( + "// Error handled in mozilla::ipc::IPCResult\n", indent=True + ), + StmtReturn(_Result.ProcessingError), + ] + ) + return [failif] + + def makeDtorMethodDecl(self, md): + decl = self.makeSendMethodDecl(md) + decl.methodspec = MethodSpec.STATIC + return decl + + def makeSendMethodDecl(self, md, promise=False, paramsems="in"): + implicit = md.decl.type.hasImplicitActorParam() + if md.decl.type.isAsync() and md.returns: + if promise: + returnsems = "promise" + rettype = _refptr(Type(md.promiseName())) + else: + returnsems = "callback" + rettype = Type.VOID + else: + assert not promise + returnsems = "out" + rettype = Type.BOOL + decl = MethodDecl( + md.sendMethod(), + params=md.makeCxxParams( + paramsems, + returnsems=returnsems, + side=self.side, + implicit=implicit, + direction="send", + ), + warn_unused=( + (self.side == "parent" and returnsems != "callback") + or (md.decl.type.isCtor() and not md.decl.type.isAsync()) + ), + ret=rettype, + ) + 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 StmtCode( + """ + if (mozilla::ipc::LoggingEnabledFor(${actorname})) { + mozilla::ipc::LogMessageForProtocol( + ${actorname}, + ${otherpid}, + ${pfx}, + ${msgptr}->type(), + mozilla::ipc::MessageDirection::${direction}); + } + """, + actorname=ExprLiteral.String(actorname), + otherpid=self.protocol.callOtherPid(actor), + pfx=ExprLiteral.String(pfx), + msgptr=msgptr, + direction="eReceiving" if receiving else "eSending", + ) + + def profilerLabel(self, md): + return StmtCode( + """ + AUTO_PROFILER_LABEL("${name}::${msgname}", OTHER); + """, + name=self.protocol.name, + msgname=md.prettyMsgName(), + ) + + 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 endRead(self, msgexpr, iterexpr): + return StmtCode( + """ + ${msg}.EndRead(${iter}, ${msg}.type()); + """, + msg=msgexpr, + iter=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 = _splitMethodDeclDefn(stmt, cls) + cls.stmts[i] = StmtDecl(decl) + if defn: + defns.addstmts([defn, Whitespace.NL]) + + return cls, defns + + +def _splitMethodDeclDefn(md, cls): + # Pure methods have decls but no defns. + if md.decl.methodspec == MethodSpec.PURE: + return md.decl, None + + saveddecl = deepcopy(md.decl) + md.decl.cls = cls + # Don't emit method specifiers on method defns. + md.decl.methodspec = MethodSpec.NONE + md.decl.warn_unused = False + 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.force_inline + return StmtDecl(fun.decl), fun diff --git a/ipc/ipdl/ipdl/parser.py b/ipc/ipdl/ipdl/parser.py new file mode 100644 index 0000000000..9ffe9e5bed --- /dev/null +++ b/ipc/ipdl/ipdl/parser.py @@ -0,0 +1,783 @@ +# 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 +from ply import lex, yacc + +from ipdl.ast import * + +# ----------------------------------------------------------------------------- + + +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=False): + 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 + + def parse(self, input, filename, includedirs): + assert os.path.isabs(filename) + + if self.tu.name in Parser.parsed: + priorTU = Parser.parsed[self.tu.name].tu + if priorTU.filename != filename: + _error( + Loc(filename), + "Trying to load `%s' from a file when we'd already seen it in file `%s'" + % (self.tu.name, priorTU.filename), + ) + + return priorTU + + self.lexer = lex.lex(debug=self.debug) + self.parser = yacc.yacc(debug=self.debug, write_tables=False) + self.filename = filename + self.includedirs = includedirs + self.tu.filename = filename + + Parser.parsed[self.tu.name] = self + Parser.parseStack.append(Parser.current) + Parser.current = self + + try: + ast = self.parser.parse(input=input, lexer=self.lexer, debug=self.debug) + finally: + 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( + ( + "async", + "both", + "child", + "class", + "comparable", + "compress", + "compressall", + "from", + "include", + "intr", + "manager", + "manages", + "namespace", + "nested", + "nullable", + "or", + "parent", + "prio", + "protocol", + "refcounted", + "moveonly", + "returns", + "struct", + "sync", + "tainted", + "union", + "UniquePtr", + "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) + p[0] = inc + + +def p_UsingKind(p): + """UsingKind : CLASS + | STRUCT + |""" + p[0] = p[1] if 2 == len(p) else None + + +def p_MaybeComparable(p): + """MaybeComparable : COMPARABLE + |""" + p[0] = 2 == len(p) + + +def p_MaybeRefcounted(p): + """MaybeRefcounted : REFCOUNTED + |""" + p[0] = 2 == len(p) + + +def p_MaybeMoveOnly(p): + """MaybeMoveOnly : MOVEONLY + |""" + p[0] = 2 == len(p) + + +def p_UsingStmt(p): + """UsingStmt : USING MaybeRefcounted MaybeMoveOnly UsingKind CxxType FROM STRING""" + p[0] = UsingStmt( + locFromTok(p, 1), + refcounted=p[2], + moveonly=p[3], + kind=p[4], + cxxTypeSpec=p[5], + cxxHeader=p[7], + ) + + +# -------------------- +# 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 : MaybeComparable STRUCT ID '{' StructFields '}' ';' + | MaybeComparable STRUCT ID '{' '}' ';'""" + if 8 == len(p): + p[0] = StructDecl(locFromTok(p, 2), p[3], p[5], p[1]) + else: + p[0] = StructDecl(locFromTok(p, 2), p[3], [], p[1]) + + +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 : MaybeComparable UNION ID '{' ComponentTypes '}' ';'""" + p[0] = UnionDecl(locFromTok(p, 2), p[3], p[5], p[1]) + + +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 MaybeRefcounted \ + PROTOCOL ID '{' ProtocolBody '}' ';'""" + protocol = p[6] + protocol.loc = locFromTok(p, 3) + protocol.name = p[4] + protocol.nested = p[1][0] + protocol.sendSemantics = p[1][1] + protocol.refcounted = p[2] + 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 : ManagersStmtOpt""" + p[0] = p[1] + + +# -------------------- +# 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 + |""" + if 1 == len(p): + # we fill in |loc| in the Protocol rule + p[0] = Protocol(None) + 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 : ID MessageInParams MessageOutParams OptionalMessageModifiers""" + # FIXME/cjones: need better loc info: use one of the quals + name = p[1] + msg = MessageDecl(locFromTok(p, 1)) + msg.name = name + msg.addInParams(p[2]) + msg.addOutParams(p[3]) + msg.addModifiers(p[4]) + + p[0] = msg + + +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 + | MessageTainted""" + 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] + + +def p_MessageTainted(p): + """MessageTainted : TAINTED""" + p[0] = 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, "input": 2, "high": 3, "mediumhigh": 4} + if p[1] not in kinds: + _error( + locFromTok(p, 1), "Expected normal, input, high or mediumhigh 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, 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 = p[4] + else: + mtype = p[1] + 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 : CxxID + | CxxID '[' ']' + | CxxID '?' + | CxxUniquePtrInst""" + # ID == CxxType; we forbid qnames here, + # in favor of the |using| declaration + if not isinstance(p[1], TypeSpec): + assert (len(p[1]) == 2) or (len(p[1]) == 3) + if 2 == len(p[1]): + # p[1] is CxxID. isunique = False + p[1] = p[1] + (False,) + loc, id, isunique = p[1] + p[1] = TypeSpec(loc, QualifiedId(loc, id)) + p[1].uniqueptr = isunique + if 4 == len(p): + p[1].array = True + if 3 == len(p): + p[1].maybe = True + p[0] = p[1] + + +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_CxxUniquePtrInst(p): + """CxxUniquePtrInst : UNIQUEPTR '<' ID '>'""" + p[0] = (locFromTok(p, 1), str(p[3]), True) + + +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 0000000000..652d0b3bbc --- /dev/null +++ b/ipc/ipdl/ipdl/type.py @@ -0,0 +1,1530 @@ +# 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/. + +from __future__ import print_function + +import os +import sys + +from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, StructDecl +from ipdl.ast import TypeSpec, UnionDecl, UsingStmt, Visitor +from ipdl.ast import ASYNC, SYNC, INTR +from ipdl.ast import IN, OUT, INOUT +from ipdl.ast import NOT_NESTED, INSIDE_SYNC_NESTED, INSIDE_CPOW_NESTED +import ipdl.builtin as builtin +from ipdl.util import hash_str + +_DELETE_MSG = "__delete__" + + +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 visitImportedCxxType(self, t, *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) + + 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 visitMaybeType(self, m, *args): + m.basetype.accept(self, *args) + + def visitUniquePtrType(self, m, *args): + m.basetype.accept(self, *args) + + def visitShmemType(self, s, *args): + pass + + def visitByteBufType(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 + + def visitManagedEndpointType(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_str(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 + + def isRefcounted(self): + return False + + def isUniquePtr(self): + return False + + def typename(self): + return self.__class__.__name__ + + def name(self): + raise NotImplementedError() + + def fullname(self): + raise NotImplementedError() + + 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 name(self): + return "void" + + def fullname(self): + return "void" + + +VOID = VoidType() + +# -------------------- + + +class ImportedCxxType(Type): + def __init__(self, qname, refcounted, moveonly): + assert isinstance(qname, QualifiedId) + self.loc = qname.loc + self.qname = qname + self.refcounted = refcounted + self.moveonly = moveonly + + def isCxx(self): + return True + + def isAtom(self): + return True + + def isRefcounted(self): + return self.refcounted + + def isMoveonly(self): + return self.moveonly + + def name(self): + return self.qname.baseid + + def fullname(self): + return str(self.qname) + + +# -------------------- + + +class IPDLType(Type): + def isIPDL(self): + return True + + 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 isMaybe(self): + return False + + def isAtom(self): + return True + + def isCompound(self): + return False + + def isShmem(self): + return False + + def isByteBuf(self): + return False + + def isFD(self): + return False + + def isEndpoint(self): + return False + + def isManagedEndpoint(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() + + def hasBaseType(self): + return False + + @classmethod + def convertsTo(cls, lesser, greater): + def _unwrap(nr): + if isinstance(nr, dict): + return _unwrap(nr["nested"]) + elif isinstance(nr, int): + return nr + else: + raise ValueError("Got unexpected nestedRange value: %s" % nr) + + lnr0, gnr0, lnr1, gnr1 = ( + _unwrap(lesser.nestedRange[0]), + _unwrap(greater.nestedRange[0]), + _unwrap(lesser.nestedRange[1]), + _unwrap(greater.nestedRange[1]), + ) + if lnr0 < gnr0 or lnr1 > gnr1: + 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 MessageType(IPDLType): + def __init__( + self, + nested, + prio, + sendSemantics, + direction, + ctor=False, + dtor=False, + cdtype=None, + compress=False, + tainted=False, + verify=False, + ): + assert not (ctor and dtor) + assert not (ctor or dtor) or cdtype 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.tainted = tainted + 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 hasReply(self): + return len(self.returns) or IPDLType.hasReply(self) + + def hasImplicitActorParam(self): + return self.isCtor() or self.isDtor() + + +class ProtocolType(IPDLType): + def __init__(self, qname, nested, sendSemantics, refcounted): + self.qname = qname + self.nestedRange = (NOT_NESTED, nested) + self.sendSemantics = sendSemantics + self.managers = [] # ProtocolType + self.manages = [] + self.hasDelete = False + self.refcounted = refcounted + + def isProtocol(self): + return True + + def isRefcounted(self): + return self.refcounted + + 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 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, nullable=False): + self.protocol = protocol + self.nullable = nullable + + def isActor(self): + return True + + def isRefcounted(self): + return self.protocol.isRefcounted() + + 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.hasBaseType(): + 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 hasBaseType(self): + return True + + def name(self): + return self.basetype.name() + "[]" + + def fullname(self): + return self.basetype.fullname() + "[]" + + +class MaybeType(IPDLType): + def __init__(self, basetype): + self.basetype = basetype + + def isAtom(self): + return False + + def isMaybe(self): + return True + + def hasBaseType(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 ByteBufType(IPDLType): + def __init__(self, qname): + self.qname = qname + + def isByteBuf(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, actor): + self.qname = qname + self.actor = actor + + def isEndpoint(self): + return True + + def name(self): + return self.qname.baseid + + def fullname(self): + return str(self.qname) + + +class ManagedEndpointType(IPDLType): + def __init__(self, qname, actor): + self.qname = qname + self.actor = actor + + def isManagedEndpoint(self): + return True + + def name(self): + return self.qname.baseid + + def fullname(self): + return str(self.qname) + + +class UniquePtrType(IPDLType): + def __init__(self, basetype): + self.basetype = basetype + + def isAtom(self): + return False + + def isUniquePtr(self): + return True + + def hasBaseType(self): + return True + + def name(self): + return "UniquePtr<" + self.basetype.name() + ">" + + def fullname(self): + return "mozilla::UniquePtr<" + self.basetype.fullname() + ">" + + +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.hasBaseType(): + 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 hasshmem(type): + """Return true iff |type| is shmem or has it buried within.""" + + class found(BaseException): + pass + + class findShmem(TypeVisitor): + def visitShmemType(self, s): + raise found() + + try: + type.accept(findShmem()) + 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.currentScope = self.scopes[0] + + def enterScope(self): + assert isinstance(self.scopes[0], dict) + assert isinstance(self.currentScope, dict) + + self.scopes.append({}) + self.currentScope = self.scopes[-1] + + def exitScope(self): + symtab = self.scopes.pop() + assert self.currentScope is symtab + + self.currentScope = self.scopes[-1] + + assert isinstance(self.scopes[0], dict) + 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 .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 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 + + return True + + def reportErrors(self, errout): + for error in self.errors: + print(error, file=errout) + + +class TcheckVisitor(Visitor): + def __init__(self, errors): + self.errors = errors + + def error(self, loc, fmt, *args): + self.errors.append(errormsg(loc, fmt, *args)) + + +class GatherDecls(TcheckVisitor): + def __init__(self, builtinUsing, errors): + TcheckVisitor.__init__(self, errors) + + # |self.symtab| is the symbol table for the translation unit + # currently being visited + self.symtab = None + self.builtinUsing = builtinUsing + + 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 + + def visitTranslationUnit(self, tu): + # all TranslationUnits declare symbols in global scope + if hasattr(tu, "visited"): + return + tu.visited = True + savedSymtab = self.symtab + self.symtab = SymbolTable(self.errors) + + # 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'", # NOQA: E501 + 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() + fullname = str(qname) + p.decl = self.declare( + loc=p.loc, + type=ProtocolType(qname, p.nested, p.sendSemantics, p.refcounted), + shortname=p.name, + fullname=None if 0 == len(qname.quals) else fullname, + ) + + p.parentEndpointDecl = self.declare( + loc=p.loc, + type=EndpointType( + QualifiedId( + p.loc, "Endpoint<" + fullname + "Parent>", ["mozilla", "ipc"] + ), + ActorType(p.decl.type), + ), + shortname="Endpoint<" + p.name + "Parent>", + ) + p.childEndpointDecl = self.declare( + loc=p.loc, + type=EndpointType( + QualifiedId( + p.loc, "Endpoint<" + fullname + "Child>", ["mozilla", "ipc"] + ), + ActorType(p.decl.type), + ), + shortname="Endpoint<" + p.name + "Child>", + ) + + p.parentManagedEndpointDecl = self.declare( + loc=p.loc, + type=ManagedEndpointType( + QualifiedId( + p.loc, + "ManagedEndpoint<" + fullname + "Parent>", + ["mozilla", "ipc"], + ), + ActorType(p.decl.type), + ), + shortname="ManagedEndpoint<" + p.name + "Parent>", + ) + p.childManagedEndpointDecl = self.declare( + loc=p.loc, + type=ManagedEndpointType( + QualifiedId( + p.loc, + "ManagedEndpoint<" + fullname + "Child>", + ["mozilla", "ipc"], + ), + ActorType(p.decl.type), + ), + shortname="ManagedEndpoint<" + 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) + + if tu.protocol: + # grab symbols in the protocol itself + p.accept(self) + + 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) + self.symtab.declare(inc.tu.protocol.parentManagedEndpointDecl) + self.symtab.declare(inc.tu.protocol.childManagedEndpointDecl) + 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, "visited"): + return + + stype = sd.decl.type + + self.symtab.enterScope() + sd.visited = True + + 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() + + 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) or using.type.uniqueptr: + # Prevent generation of typedefs. If basename == fullname then + # there is nothing to typedef. With UniquePtrs, basenames + # are generic so typedefs would be illegal. + fullname = None + if fullname == "mozilla::ipc::Shmem": + ipdltype = ShmemType(using.type.spec) + elif fullname == "mozilla::ipc::ByteBuf": + ipdltype = ByteBufType(using.type.spec) + elif fullname == "mozilla::ipc::FileDescriptor": + ipdltype = FDType(using.type.spec) + else: + ipdltype = ImportedCxxType( + using.type.spec, using.isRefcounted(), using.isMoveonly() + ) + existingType = self.symtab.lookup(ipdltype.fullname()) + if existingType and existingType.fullname == ipdltype.fullname(): + if ipdltype.isRefcounted() != existingType.type.isRefcounted(): + self.error( + using.loc, + "inconsistent refcounted status of type `%s`", + str(using.type), + ) + if ipdltype.isMoveonly() != existingType.type.isMoveonly(): + self.error( + using.loc, + "inconsistent moveonly status of type `%s`", + str(using.type), + ) + 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() + + 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 not (p.managers or p.messageDecls or p.managesStmts): + 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, + ) + + # 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() + + 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'", # NOQA: E501 + 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() + + msgtype = MessageType( + md.nested, + md.prio, + md.sendSemantics, + md.direction, + ctor=isctor, + dtor=isdtor, + cdtype=cdtype, + compress=md.compress, + tainted=md.tainted, + 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) + 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.decl = self.declare(loc=loc, type=msgtype, progname=msgname) + md.protocolDecl = self.currentProtocolDecl + md.decl._md = md + + def _canonicalType(self, itype, typespec): + loc = typespec.loc + if itype.isIPDL(): + if itype.isProtocol(): + itype = ActorType(itype, nullable=typespec.nullable) + + 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) + + if typespec.maybe: + itype = MaybeType(itype) + + if typespec.uniqueptr: + itype = UniquePtrType(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.hasBaseType(): + 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): + TcheckVisitor.__init__(self, 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 + + 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", # NOQA: E501 + pname, + mgrtype.name(), + ) + + if ptype.isToplevel(): + cycles = checkcycles(p.decl.type) + if cycles: + self.error( + p.decl.loc, + "cycle(s) detected in manager/manages hierarchy: %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 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'", # NOQA: E501 + pname, + mgsname, + ) + + def visitManager(self, mgr): + 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'", # NOQA: E501 + 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')", # NOQA: E501 + 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", # NOQA: E501 + mname, + pname, + ) + + if (mtype.isCtor() or mtype.isDtor()) and mtype.isAsync() and mtype.returns: + self.error( + loc, "asynchronous ctor/dtor message `%s' declares return values", mname + ) + + if mtype.compress and (not mtype.isAsync() or mtype.isCtor() or mtype.isDtor()): + + if mtype.isCtor() or mtype.isDtor(): + message_type = "constructor" if mtype.isCtor() else "destructor" + error_message = ( + "%s messages can't use compression (here, in protocol `%s')" + % (message_type, pname) + ) + else: + error_message = ( + "message `%s' in protocol `%s' requests compression but is not async" + % (mname, pname) # NOQA: E501 + ) + + self.error(loc, error_message) + + 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, + ) diff --git a/ipc/ipdl/ipdl/util.py b/ipc/ipdl/ipdl/util.py new file mode 100644 index 0000000000..60d9c904e2 --- /dev/null +++ b/ipc/ipdl/ipdl/util.py @@ -0,0 +1,12 @@ +# 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 zlib + + +# The built-in hash over the str type is non-deterministic, so we need to do +# this instead. +def hash_str(s): + assert isinstance(s, str) + return zlib.adler32(s.encode("utf-8")) diff --git a/ipc/ipdl/message-metadata.ini b/ipc/ipdl/message-metadata.ini new file mode 100644 index 0000000000..88c4715026 --- /dev/null +++ b/ipc/ipdl/message-metadata.ini @@ -0,0 +1,38 @@ +############################################################# +# # +# Any changes to this file must be reviewed by an IPC peer. # +# # +############################################################# + +[PContent::AccumulateChildKeyedHistograms] +segment_capacity = 16384 +[PContent::AccumulateChildHistograms] +segment_capacity = 16384 +[PLayerTransaction::Update] +segment_capacity = 8192 +[PContent::StoreAndBroadcastBlobURLRegistration] +segment_capacity = 8192 +[PHttpChannel::Redirect1Begin] +segment_capacity = 8192 +[PHttpBackgroundChannel::OnStartRequest] +segment_capacity = 8192 +[PHttpBackgroundChannel::OnTransportAndData] +segment_capacity = 8192 +[PNecko::PHttpChannelConstructor] +segment_capacity = 8192 +[PMessagePort::PostMessages] +segment_capacity = 12288 +[PMessagePort::ReceiveData] +segment_capacity = 12288 + +#------------------------------------------------------------ +# Small-size messages. +#------------------------------------------------------------ +[PCompositorBridge::DidComposite] +segment_capacity = 128 +[PBrowser::RealMouseMoveEvent] +segment_capacity = 192 +[PCompositorBridge::PTextureConstructor] +segment_capacity = 192 +[PHttpBackgroundChannel::OnStopRequest] +segment_capacity = 192 diff --git a/ipc/ipdl/moz.build b/ipc/ipdl/moz.build new file mode 100644 index 0000000000..b5f01c5267 --- /dev/null +++ b/ipc/ipdl/moz.build @@ -0,0 +1,21 @@ +# -*- 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 += ["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"] + +# Add libFuzzer configuration directives +include("/tools/fuzzing/libfuzzer-config.mozbuild") diff --git a/ipc/ipdl/msgtype-components b/ipc/ipdl/msgtype-components new file mode 100644 index 0000000000..35229182a2 --- /dev/null +++ b/ipc/ipdl/msgtype-components @@ -0,0 +1,14 @@ +#!/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/. + +from __future__ import print_function + +import sys + +msgid = int(sys.argv[1]) +protocol = (msgid >> 16) +msg = (msgid - (protocol << 16)) + +print('protocol', protocol, 'message', msg) diff --git a/ipc/ipdl/sync-messages.ini b/ipc/ipdl/sync-messages.ini new file mode 100644 index 0000000000..ed5a001a0c --- /dev/null +++ b/ipc/ipdl/sync-messages.ini @@ -0,0 +1,1124 @@ +############################################################# +# # +# AVOID ADDING NEW MESSAGES TO THIS FILE # +# # +# New sync IPC messages will only be approved in # +# exceptional circumstances, and must be reviewed by an IPC # +# peer. Please be prepared with a detailed justification # +# before requesting IPC peer review. # +# # +############################################################# + +# C++ unit tests +[PTestBadActorSub::__delete__] +description = Only used by C++ unit tests +[PTestCancel::Test1_1] +description = Only used by C++ unit tests +[PTestCancel::Test2_2] +description = Only used by C++ unit tests +[PTestCancel::Test2_1] +description = Only used by C++ unit tests +[PTestCancel::Test3_1] +description = Only used by C++ unit tests +[PTestCancel::Test3_2] +description = Only used by C++ unit tests +[PTestCancel::CheckChild] +description = Only used by C++ unit tests +[PTestCancel::CheckParent] +description = Only used by C++ unit tests +[PTestCrashCleanup::DIEDIEDIE] +description = Only used by C++ unit tests +[PTestDataStructures::Test1] +description = Only used by C++ unit tests +[PTestDataStructures::Test2] +description = Only used by C++ unit tests +[PTestDataStructures::Test3] +description = Only used by C++ unit tests +[PTestDataStructures::Test4] +description = Only used by C++ unit tests +[PTestDataStructures::Test5] +description = Only used by C++ unit tests +[PTestDataStructures::Test6] +description = Only used by C++ unit tests +[PTestDataStructures::Test7_0] +description = Only used by C++ unit tests +[PTestDataStructures::Test7] +description = Only used by C++ unit tests +[PTestDataStructures::Test8] +description = Only used by C++ unit tests +[PTestDataStructures::Test9] +description = Only used by C++ unit tests +[PTestDataStructures::Test10] +description = Only used by C++ unit tests +[PTestDataStructures::Test11] +description = Only used by C++ unit tests +[PTestDataStructures::Test12] +description = Only used by C++ unit tests +[PTestDataStructures::Test13] +description = Only used by C++ unit tests +[PTestDataStructures::Test14] +description = Only used by C++ unit tests +[PTestDataStructures::Test15] +description = Only used by C++ unit tests +[PTestDataStructures::Test16] +description = Only used by C++ unit tests +[PTestDataStructures::Test17] +description = Only used by C++ unit tests +[PTestDataStructures::Test18] +description = Only used by C++ unit tests +[PTestDataStructures::Dummy] +description = Only used by C++ unit tests +[PTestDataStructuresSub::__delete__] +description = Only used by C++ unit tests +[PTestDemon::HiPrioSyncMessage] +description = Only used by C++ unit tests +[PTestDemon::SyncMessage] +description = Only used by C++ unit tests +[PTestDemon::UrgentSyncMessage] +description = Only used by C++ unit tests +[PTestDesc::PTestDescSub] +description = Only used by C++ unit tests +[PTestDescSub::PTestDescSubsub] +description = Only used by C++ unit tests +[PTestDescSubsub::__delete__] +description = Only used by C++ unit tests +[PTestEndpointBridgeMainSub::HiRpc] +description = Only used by C++ unit tests +[PTestEndpointBridgeMainSub::HelloSync] +description = Only used by C++ unit tests +[PTestEndpointBridgeMainSub::HelloRpc] +description = Only used by C++ unit tests +[PTestEndpointOpensOpened::HiRpc] +description = Only used by C++ unit tests +[PTestEndpointOpensOpened::HelloSync] +description = Only used by C++ unit tests +[PTestEndpointOpensOpened::HelloRpc] +description = Only used by C++ unit tests +[PTestFailedCtor::PTestFailedCtorSub] +description = Only used by C++ unit tests +[PTestFailedCtorSub::Sync] +description = Only used by C++ unit tests +[PTestHangs::StackFrame] +description = Only used by C++ unit tests +[PTestHangs::Hang] +description = Only used by C++ unit tests +[PTestHighestPrio::Msg2] +description = Only used by C++ unit tests +[PTestHighestPrio::Msg4] +description = Only used by C++ unit tests +[PTestHighestPrio::StartInner] +description = Only used by C++ unit tests +[PTestIndirectProtocolParamFirst::Test] +description = Only used by C++ unit tests +[PTestInterruptErrorCleanup::Error] +description = Only used by C++ unit tests +[PTestInterruptErrorCleanup::__delete__] +description = Only used by C++ unit tests +[PTestInterruptRaces::Race] +description = Only used by C++ unit tests +[PTestInterruptRaces::StackFrame] +description = Only used by C++ unit tests +[PTestInterruptRaces::StackFrame3] +description = Only used by C++ unit tests +[PTestInterruptRaces::StartRace] +description = Only used by C++ unit tests +[PTestInterruptRaces::Parent] +description = Only used by C++ unit tests +[PTestInterruptRaces::GetAnsweredParent] +description = Only used by C++ unit tests +[PTestInterruptRaces::Child] +description = Only used by C++ unit tests +[PTestInterruptShutdownRace::StartDeath] +description = Only used by C++ unit tests +[PTestInterruptShutdownRace::Exit] +description = Only used by C++ unit tests +[PTestJSON::Test] +description = Only used by C++ unit tests +[PTestLatency::Rpc] +description = Only used by C++ unit tests +[PTestLatency::Synchro] +description = Only used by C++ unit tests +[PTestLatency::Synchro2] +description = Only used by C++ unit tests +[PTestNestedLoops::R] +description = Only used by C++ unit tests +[PTestPriority::PMsg2] +description = Only used by C++ unit tests +[PTestPriority::PMsg4] +description = Only used by C++ unit tests +[PTestPriority::PMsg6] +description = Only used by C++ unit tests +[PTestRPC::Test1_Start] +description = Only used by C++ unit tests +[PTestRPC::Test1_InnerEvent] +description = Only used by C++ unit tests +[PTestRPC::Test2_OutOfOrder] +description = Only used by C++ unit tests +[PTestRPC::Test1_InnerQuery] +description = Only used by C++ unit tests +[PTestRPC::Test1_NoReenter] +description = Only used by C++ unit tests +[PTestRPC::Test2_FirstUrgent] +description = Only used by C++ unit tests +[PTestRPC::Test2_SecondUrgent] +description = Only used by C++ unit tests +[PTestRaceDeadlock::Lose] +description = Only used by C++ unit tests +[PTestRaceDeadlock::Win] +description = Only used by C++ unit tests +[PTestRaceDeadlock::Rpc] +description = Only used by C++ unit tests +[PTestRaceDeferral::Lose] +description = Only used by C++ unit tests +[PTestRaceDeferral::Win] +description = Only used by C++ unit tests +[PTestRaceDeferral::Rpc] +description = Only used by C++ unit tests +[PTestRacyInterruptReplies::R_] +description = Only used by C++ unit tests +[PTestRacyInterruptReplies::_R] +description = Only used by C++ unit tests +[PTestRacyReentry::E] +description = Only used by C++ unit tests +[PTestRacyReentry::H] +description = Only used by C++ unit tests +[PTestRacyUndefer::Race] +description = Only used by C++ unit tests +[PTestRacyUndefer::Spam] +description = Only used by C++ unit tests +[PTestRacyUndefer::RaceWinTwice] +description = Only used by C++ unit tests +[PTestShutdown::Sync] +description = Only used by C++ unit tests +[PTestShutdownSub::StackFrame] +description = Only used by C++ unit tests +[PTestShutdownSub::__delete__] +description = Only used by C++ unit tests +[PTestShutdownSubsub::__delete__] +description = Only used by C++ unit tests +[PTestStackHooks::Sync] +description = Only used by C++ unit tests +[PTestStackHooks::Rpc] +description = Only used by C++ unit tests +[PTestStackHooks::StackFrame] +description = Only used by C++ unit tests +[PTestSyncError::Error] +description = Only used by C++ unit tests +[PTestSyncWakeup::StackFrame] +description = Only used by C++ unit tests +[PTestSyncWakeup::Sync1] +description = Only used by C++ unit tests +[PTestSyncWakeup::Sync2] +description = Only used by C++ unit tests +[PTestUrgency::Test1] +description = Only used by C++ unit tests +[PTestUrgency::Test3] +description = Only used by C++ unit tests +[PTestUrgency::FinalTest_Begin] +description = Only used by C++ unit tests +[PTestUrgency::Reply1] +description = Only used by C++ unit tests +[PTestUrgency::Reply2] +description = Only used by C++ unit tests +[PTestUrgentHangs::Test1_2] +description = Only used by C++ unit tests +[PTestUrgentHangs::TestInner] +description = Only used by C++ unit tests +[PTestUrgentHangs::TestInnerUrgent] +description = Only used by C++ unit tests +[PTestUrgentHangs::Test1_1] +description = Only used by C++ unit tests +[PTestUrgentHangs::Test1_3] +description = Only used by C++ unit tests +[PTestUrgentHangs::Test2] +description = Only used by C++ unit tests +[PTestUrgentHangs::Test3] +description = Only used by C++ unit tests +[PTestUrgentHangs::Test4_1] +description = Only used by C++ unit tests +[PTestUrgentHangs::Test5_1] +description = Only used by C++ unit tests +[PTestLayoutThread::SyncMessage] +description = Only used by C++ unit tests +[PTestPaintThread::FinishedPaint] +description = Only used by C++ unit tests + +# A11y code +[PDocAccessible::State] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::NativeState] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::Name] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::Value] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::Help] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::Description] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::Attributes] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::RelationByType] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::Relations] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::IsSearchbox] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::LandmarkRole] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ARIARoleAtom] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::GroupPosition] +description = Legacy a11y IPC. Platform accessibility APIs need to query content synchronously for information. +platform = notwin +[PDocAccessible::CaretLineNumber] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::CaretOffset] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::CharacterCount] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::SelectionCount] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TextSubstring] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::GetTextAfterOffset] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::GetTextAtOffset] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::GetTextBeforeOffset] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::CharAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TextAttributes] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::DefaultTextAttributes] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TextBounds] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::CharBounds] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::OffsetAtPoint] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::SelectionBoundsAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::SetSelectionBoundsAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::AddToSelection] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::RemoveFromSelection] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::Text] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ReplaceText] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::InsertText] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::CopyText] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::CutText] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::DeleteText] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::PasteText] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ImagePosition] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ImageSize] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::StartOffset] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::EndOffset] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::IsLinkValid] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::AnchorCount] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::AnchorURIAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::AnchorAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::LinkCount] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::LinkAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::LinkIndexOf] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::LinkIndexAtOffset] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableOfACell] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ColIdx] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::RowIdx] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::GetPosition] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ColExtent] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::RowExtent] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::GetColRowExtents] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ColHeaderCells] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::RowHeaderCells] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::IsCellSelected] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableCaption] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableSummary] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableColumnCount] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableRowCount] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableCellAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableCellIndexAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableColumnIndexAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableRowIndexAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableRowAndColumnIndicesAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableColumnExtentAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableRowExtentAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableColumnDescription] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableRowDescription] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableColumnSelected] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableRowSelected] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableCellSelected] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableSelectedCellCount] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableSelectedColumnCount] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableSelectedRowCount] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableSelectedCells] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableSelectedCellIndices] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableSelectedColumnIndices] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableSelectedRowIndices] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableSelectColumn] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableSelectRow] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableUnselectColumn] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableUnselectRow] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::TableIsProbablyForLayout] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::AtkTableColumnHeader] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::AtkTableRowHeader] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::SelectedItems] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::SelectedItemCount] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::GetSelectedItem] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::IsItemSelected] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::AddItemToSelection] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::RemoveItemFromSelection] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::SelectAll] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::UnselectAll] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::DoAction] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ActionCount] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ActionDescriptionAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ActionNameAt] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::AccessKey] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::KeyboardShortcut] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::AtkKeyBinding] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::CurValue] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::SetCurValue] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::MinValue] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::MaxValue] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::Step] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::FocusedChild] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::Language] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::DocType] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::Title] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::URL] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::MimeType] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::URLDocTypeMimeType] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ChildAtPoint] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::Extents] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::ExtentsInCSSPixels] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::DOMNodeID] +description = Legacy a11y IPC +platform = notwin +[PDocAccessible::GetWindowedPluginIAccessible] +description = Legacy a11y IPC +platform = win +[PDocAccessible::SyncTextChangeEvent] +description = Legacy a11y IPC +platform = win + +[PDocAccessiblePlatformExt::RangeAt] +description = Retrieve a text range given an offset and range type. Platform API is synchronous, so this needs to be too. +platform = mac +[PDocAccessiblePlatformExt::NextClusterAt] +description = Retrieve next character from offset. Platform API is synchronous, so this needs to be too. +platform = mac +[PDocAccessiblePlatformExt::PreviousClusterAt] +description = Retrieve previous character from offset. Platform API is synchronous, so this needs to be too. +platform = mac +[PDocAccessiblePlatformExt::TextForRange] +description = Retrieve flattened string for text range. Platform API is synchronous, so this needs to be too. +platform = mac +[PDocAccessiblePlatformExt::BoundsForRange] +description = Retrieve geometric bounds for text range. Platform API is synchronous, so this needs to be too. +platform = mac +[PDocAccessiblePlatformExt::LengthForRange] +description = Retrieve length of text range. Platform API is synchronous, so this needs to be too. +platform = mac +[PDocAccessiblePlatformExt::OffsetAtIndex] +description = Retrieve text offset at absolute index. Platform API is synchronous, so this needs to be too. +platform = mac +[PDocAccessiblePlatformExt::RangeOfChild] +description = Retrieve text range in parent of child. Platform API is synchronous, so this needs to be too. +platform = mac +[PDocAccessiblePlatformExt::LeafAtOffset] +description = Retrieve text leaf at given offset. Platform API is synchronous, so this needs to be too. +platform = mac +[PDocAccessiblePlatformExt::ApplyPostSearchFilter] +description = Filter a preliminary list of accessibles that match a predicate. Platform API is synchronous, so this needs to be too. +platform = mac + +# Plugins +[PPluginWidget::Create] +description = Legacy NPAPI IPC +[PPluginWidget::GetNativePluginPort] +description = Legacy NPAPI IPC +[PPluginWidget::SetNativeChildWindow] +description = Legacy NPAPI IPC +[PFunctionBroker::BrokerFunction] +description = Legacy NPAPI IPC +[PPluginInstance::CreateChildPluginWindow] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_SetWindow] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_GetValue_NPPVpluginWantsAllNetworkStreams] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_GetValue_NPPVpluginScriptableNPObject] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_SetValue_NPNVprivateModeBool] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_SetValue_NPNVCSSZoomFactor] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_SetValue_NPNVmuteAudioBool] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_HandleEvent] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_HandleEvent_Shmem] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_HandleEvent_IOSurface] +description = Legacy NPAPI IPC +[PPluginInstance::Paint] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_Destroy] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_GetValue_NPNVWindowNPObject] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_GetValue_NPNVPluginElementNPObject] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_GetValue_NPNVprivateModeBool] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_GetValue_NPNVnetscapeWindow] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_GetValue_NPNVdocumentOrigin] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_GetValue_DrawingModelSupport] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_GetValue_SupportsAsyncBitmapSurface] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_GetValue_SupportsAsyncDXGISurface] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_GetValue_PreferredDXGIAdapter] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_SetValue_NPPVpluginWindow] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_SetValue_NPPVpluginTransparent] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_SetValue_NPPVpluginUsesDOMForCursor] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_SetValue_NPPVpluginDrawingModel] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_SetValue_NPPVpluginEventModel] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_SetValue_NPPVpluginIsPlayingAudio] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_GetURL] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_PostURL] +description = Legacy NPAPI IPC +[PPluginInstance::PStreamNotify] +description = Legacy NPAPI IPC +[PPluginInstance::RevokeCurrentDirectSurface] +description = Legacy NPAPI IPC +[PPluginInstance::InitDXGISurface] +description = Legacy NPAPI IPC +[PPluginInstance::FinalizeDXGISurface] +description = Legacy NPAPI IPC +[PPluginInstance::ShowDirectBitmap] +description = Legacy NPAPI IPC +[PPluginInstance::ShowDirectDXGISurface] +description = Legacy NPAPI IPC +[PPluginInstance::Show] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_PushPopupsEnabledState] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_PopPopupsEnabledState] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_GetValueForURL] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_SetValueForURL] +description = Legacy NPAPI IPC +[PPluginInstance::NPN_ConvertPoint] +description = Legacy NPAPI IPC +[PPluginInstance::GetCompositionString] +description = Legacy NPAPI IPC +[PPluginInstance::NPP_NewStream] +description = Legacy NPAPI IPC +[PPluginInstance::PluginFocusChange] +description = Legacy NPAPI IPC +[PPluginInstance::SetPluginFocus] +description = Legacy NPAPI IPC +[PPluginInstance::UpdateWindow] +description = Legacy NPAPI IPC +[PPluginModule::ModuleSupportsAsyncRender] +description = Legacy NPAPI IPC +[PPluginModule::NP_GetEntryPoints] +description = Legacy NPAPI IPC +[PPluginModule::NP_Initialize] +description = Legacy NPAPI IPC +[PPluginModule::SyncNPP_New] +description = Legacy NPAPI IPC +[PPluginModule::NP_Shutdown] +description = Legacy NPAPI IPC +[PPluginModule::OptionalFunctionsSupported] +description = Legacy NPAPI IPC +[PPluginModule::ProcessSomeEvents] +description = Legacy NPAPI IPC +[PPluginModule::NPN_SetException] +description = Legacy NPAPI IPC +[PPluginModule::NPN_SetValue_NPPVpluginRequiresAudioDeviceChanges] +description = Legacy NPAPI IPC +[PPluginModule::InitCrashReporter] +description = Legacy NPAPI IPC +[PPluginScriptableObject::NPN_Evaluate] +description = Legacy NPAPI IPC +[PPluginScriptableObject::Invalidate] +description = Legacy NPAPI IPC +[PPluginScriptableObject::HasMethod] +description = Legacy NPAPI IPC +[PPluginScriptableObject::Invoke] +description = Legacy NPAPI IPC +[PPluginScriptableObject::InvokeDefault] +description = Legacy NPAPI IPC +[PPluginScriptableObject::HasProperty] +description = Legacy NPAPI IPC +[PPluginScriptableObject::SetProperty] +description = Legacy NPAPI IPC +[PPluginScriptableObject::RemoveProperty] +description = Legacy NPAPI IPC +[PPluginScriptableObject::Enumerate] +description = Legacy NPAPI IPC +[PPluginScriptableObject::Construct] +description = Legacy NPAPI IPC +[PPluginScriptableObject::GetParentProperty] +description = Legacy NPAPI IPC +[PPluginScriptableObject::GetChildProperty] +description = Legacy NPAPI IPC +[PPluginStream::NPN_Write] +description = Legacy NPAPI IPC +[PPluginStream::__delete__] +description = Legacy NPAPI IPC +# The rest +[PHeapSnapshotTempFileHelper::OpenHeapSnapshotTempFile] +description = legacy sync IPC - please add detailed description +[PBackgroundMutableFile::GetFileId] +description = legacy sync IPC - please add detailed description +[PBackgroundIndexedDBUtils::GetFileReferences] +description = legacy sync IPC - please add detailed description +[PBrowser::SyncMessage] +description = JS MessageManager implementation +[PBrowser::PPluginWidget] +description = Legacy NPAPI IPC +[PBrowser::DispatchFocusToTopLevelWindow] +description = legacy sync IPC - please add detailed description +[PBrowser::NotifyIMEMouseButtonEvent] +description = legacy sync IPC - please add detailed description +[PBrowser::RequestIMEToCommitComposition] +description = legacy sync IPC - please add detailed description +[PBrowser::GetInputContext] +description = legacy sync IPC - please add detailed description +[PBrowser::RequestNativeKeyBindings] +description = legacy sync IPC - please add detailed description +[PBrowser::DispatchWheelEvent] +description = legacy sync IPC - please add detailed description +[PBrowser::DispatchMouseEvent] +description = legacy sync IPC - please add detailed description +[PBrowser::DispatchKeyboardEvent] +description = legacy sync IPC - please add detailed description +[PBrowser::EnsureLayersConnected] +description = legacy sync IPC - please add detailed description +[PBrowser::SetSystemFont] +description = test only +[PBrowser::GetSystemFont] +description = test only +[PContent::SyncMessage] +description = JS MessageManager implementation +[PContent::LoadPlugin] +description = Legacy NPAPI IPC +[PContent::ConnectPluginBridge] +description = Legacy NPAPI IPC +[PContent::IsSecureURI] +description = legacy sync IPC - please add detailed description +[PContent::PURLClassifier] +description = legacy sync IPC - please add detailed description +[PContent::GetGfxVars] +description = legacy sync IPC - please add detailed description +[PContent::GetClipboard] +description = Legacy synchronous clipboard API +[PContent::ClipboardHasType] +description = Legacy synchronous clipboard API +[PContent::GetExternalClipboardFormats] +description = Retrieve supported clipboard formats synchronously +[PContent::GetIconForExtension] +description = legacy sync IPC - please add detailed description +[PContent::BeginDriverCrashGuard] +description = legacy sync IPC - please add detailed description +[PContent::EndDriverCrashGuard] +description = legacy sync IPC - please add detailed description +[PContent::GetGraphicsDeviceInitData] +description = Retrieve information needed to initialize the graphics device in the content process +[PContent::GetOutputColorProfileData] +description = Retrieve the contents of the output color profile file +[PContent::GetFontListShmBlock] +description = for bug 1514869 - layout code needs synchronous access to font list, but this is used only once per block, after which content directly reads the shared memory +[PContent::InitializeFamily] +description = for bug 1514869 - layout is blocked on needing sync access to a specific font family - used once per family, then the data is cached in shared memory +[PContent::InitOtherFamilyNames] +description = for bug 1514869 - layout is blocked on font lookup, needs complete family-name information - not used after loading is complete +[PContent::GetHyphDict] +description = for bug 1487212 - layout requires hyphenation data from a given omnijar resource - only called once per locale by a given content process +[PContent::UngrabPointer] +description = legacy sync IPC - please add detailed description +[PContent::RemovePermission] +description = legacy sync IPC - please add detailed description +[PContent::GetA11yContentId] +description = legacy sync IPC - please add detailed description +[PGMP::StartPlugin] +description = legacy sync IPC - please add detailed description +[PGMPService::LaunchGMP] +description = legacy sync IPC - please add detailed description +[PGMPService::GetGMPNodeId] +description = legacy sync IPC - please add detailed description +[PGMPVideoDecoder::NeedShmem] +description = legacy sync IPC - please add detailed description +[PGMPVideoEncoder::NeedShmem] +description = legacy sync IPC - please add detailed description +[PRemoteDecoderManager::Readback] +description = legacy sync IPC - please add detailed description +[PBackgroundStorage::Preload] +description = legacy sync IPC - please add detailed description +[PBackgroundLSDatabase::PBackgroundLSSnapshot] +description = See corresponding comment in PBackgroundLSDatabase.ipdl +[PBackgroundLSSnapshot::LoadValueAndMoreItems] +description = See corresponding comment in PBackgroundLSSnapshot.ipdl +[PBackgroundLSSnapshot::LoadKeys] +description = See corresponding comment in PBackgroundLSSnapshot.ipdl +[PBackgroundLSSnapshot::IncreasePeakUsage] +description = See corresponding comment in PBackgroundLSSnapshot.ipdl +[PBackgroundLSSnapshot::Ping] +description = See corresponding comment in PBackgroundLSSnapshot.ipdl +[PBackgroundSessionStorageCache::Load] +description = See corresponding comment in PBackgroundSessionStorageCache.ipdl +[PRemoteSpellcheckEngine::CheckAndSuggest] +description = legacy sync IPC - please add detailed description +[PRemoteSpellcheckEngine::SetDictionary] +description = legacy sync IPC - please add detailed description +[PGPU::AddLayerTreeIdMapping] +description = legacy sync IPC - please add detailed description +[PGPU::GetDeviceStatus] +description = legacy sync IPC - please add detailed description +[PGPU::SimulateDeviceReset] +description = legacy sync IPC - please add detailed description +[PAPZInputBridge::ReceiveMultiTouchInputEvent] +description = legacy sync IPC - please add detailed description +[PAPZInputBridge::ReceiveMouseInputEvent] +description = legacy sync IPC - please add detailed description +[PAPZInputBridge::ReceivePanGestureInputEvent] +description = legacy sync IPC - please add detailed description +[PAPZInputBridge::ReceivePinchGestureInputEvent] +description = legacy sync IPC - please add detailed description +[PAPZInputBridge::ReceiveTapGestureInputEvent] +description = legacy sync IPC - please add detailed description +[PAPZInputBridge::ReceiveScrollWheelInputEvent] +description = legacy sync IPC - please add detailed description +[PAPZInputBridge::ProcessUnhandledEvent] +description = legacy sync IPC - please add detailed description +[PAPZInputBridge::ReceiveKeyboardInputEvent] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::Initialize] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::WillClose] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::Pause] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::Resume] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::NotifyChildCreated] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::MapAndNotifyChildCreated] +description = bug 1350660 +[PCompositorBridge::NotifyChildRecreated] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::MakeSnapshot] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::FlushRendering] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::WaitOnTransactionProcessed] +description = bug 1364626 +[PCompositorBridge::StartFrameTimeRecording] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::StopFrameTimeRecording] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::SyncWithCompositor] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::CheckContentOnlyTDR] +description = legacy sync IPC - please add detailed description +[PCompositorWidget::Initialize] +description = Fallable initialization for the widget. Must be called right after construction before any other messages are sent +platform = win +[PCompositorWidget::EnterPresentLock] +description = Obtain exclusive access to the widget surface. Used to ensure events don't change the surface while it's being used as a render target +platform = win +[PCompositorWidget::LeavePresentLock] +description = Release the exclusive lock that EnterPresentLock obtained +platform = win +[PCompositorBridge::SupportsAsyncDXGISurface] +description = legacy sync IPC - please add detailed description +[PCompositorBridge::PreferredDXGIAdapter] +description = legacy sync IPC - please add detailed description +[PCompositorWidget::ClearTransparentWindow] +description = Synchronously clear the widget surface. Does not rely on (Enter|Leave)PresentLock. When call returns, window surface has been fully updated with cleared pixel values. +platform = win +[PImageBridge::NewCompositable] +description = legacy sync IPC - please add detailed description +[PImageBridge::MakeAsyncPluginSurfaces] +description = When plugin asks content to synchronously make surfaces, content needs to synchronously get those surfaces from the compositor process since sandboxing forbids content to make them itself. +[PImageBridge::ReadbackAsyncPluginSurface] +description = When content needs to synchronously readback a plugin surface, the compositor process needs to synchronously perform the operation since sandboxing forbids content to perform graphics operations. +[PLayerTransaction::SetTestSampleTime] +description = legacy sync IPC - please add detailed description +[PLayerTransaction::LeaveTestMode] +description = legacy sync IPC - please add detailed description +[PLayerTransaction::GetAnimationValue] +description = test only +[PLayerTransaction::GetTransform] +description = test only +[PLayerTransaction::SetAsyncScrollOffset] +description = legacy sync IPC - please add detailed description +[PLayerTransaction::SetAsyncZoom] +description = legacy sync IPC - please add detailed description +[PLayerTransaction::GetAPZTestData] +description = legacy sync IPC - please add detailed description +[PLayerTransaction::GetFrameUniformity] +description = test only +[PLayerTransaction::RequestProperty] +description = legacy sync IPC - please add detailed description +[PLayerTransaction::GetTextureFactoryIdentifier] +description = bug 1350634 +[PUiCompositorController::Pause] +description = legacy sync IPC - please add detailed description +[PUiCompositorController::Resume] +description = legacy sync IPC - please add detailed description +[PUiCompositorController::ResumeAndResize] +description = legacy sync IPC - please add detailed description +[PWebRenderBridge::EnsureConnected] +description = legacy sync IPC - please add detailed description +[PWebRenderBridge::GetSnapshot] +description = legacy sync IPC - please add detailed description +[PWebRenderBridge::SetTestSampleTime] +description = test only +[PWebRenderBridge::LeaveTestMode] +description = test only +[PWebRenderBridge::GetAnimationValue] +description = test only +[PWebRenderBridge::SetAsyncScrollOffset] +description = test only +[PWebRenderBridge::SetAsyncZoom] +description = test only +[PWebRenderBridge::GetAPZTestData] +description = test only +[PWebRenderBridge::GetFrameUniformity] +description = test only +[PWebRenderBridge::ShutdownSync] +description = bug 1377024 +[PWebRenderBridge::SyncWithCompositor] +description = WebRender equivalent for PCompositorBridge::SyncWithCompositor +[PHal::GetCurrentBatteryInformation] +description = legacy sync IPC - please add detailed description +[PHal::GetCurrentNetworkInformation] +description = legacy sync IPC - please add detailed description +[PHal::GetWakeLockInfo] +description = legacy sync IPC - please add detailed description +[PHal::LockScreenOrientation] +description = legacy sync IPC - please add detailed description +[PPrinting::SavePrintSettings] +description = legacy sync IPC - please add detailed description +[PHandlerService::FillHandlerInfo] +description = legacy sync IPC - please add detailed description +[PHandlerService::GetMIMEInfoFromOS] +description = Lets unprivileged child processes synchronously get MIME type/handler information from the OS +[PHandlerService::ExistsForProtocolOS] +description = bug 1382323 +[PHandlerService::ExistsForProtocol] +description = legacy sync IPC - please add detailed description +[PHandlerService::Exists] +description = legacy sync IPC - please add detailed description +[PHandlerService::GetTypeFromExtension] +description = legacy sync IPC - please add detailed description +[PHandlerService::GetApplicationDescription] +description = Lets unprivileged child processes synchronously get a description of the app that handles a given protocol scheme +[PLayerTransaction::ShutdownSync] +description = bug 1363126 +[PClientSource::WorkerSyncPing] +description = Synchronous ping allowing worker thread to confirm actor is created. Necessary to avoid racing with ClientHandle actors on main thread. +[PRemoteSandboxBroker::LaunchApp] +description = Synchronous launch of a child process that in turn launches and sandboxes another process. Called on a dedicated thread and targets a dedicated process, so this shouldn't block anything. +# WebGL internals +[PWebGL::Initialize] +description = Initialization of WebGL contexts is synchronous by spec. +[PWebGL::GetFrontBuffer] +description = Publishing a WebGL frame for compositing is synchronous for now to ensure DOM transaction atomicity. +[PWebGL::OnMemoryPressure] +description = Synchronous to ensure immediate memory pressure relief. +# WebGL spec-synchronous functions +[PWebGL::CheckFramebufferStatus] +description = Checking framebuffer completenss must ask the driver. +[PWebGL::ClientWaitSync] +description = Checking fence-sync completenss must ask the driver. +[PWebGL::CreateOpaqueFramebuffer] +description = Must synchronously check for allocation success. +[PWebGL::DrawingBufferSize] +description = The returned size might be smaller than requested due to allocation failure. +[PWebGL::Finish] +description = Synchronous by spec, but not generally used. +[PWebGL::GetBufferSubData] +description = Retrieving buffer contents is synchronous in the worst case. +[PWebGL::GetFrontBufferSnapshot] +description = Retrieving canvas contents is synchronous. +[PWebGL::ReadPixels] +description = Retrieving WebGL framebuffer contents is synchronous. +# WebGL reflection functions +[PWebGL::GetBufferParameter] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetCompileResult] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetError] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetFragDataLocation] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetFramebufferAttachmentParameter] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetIndexedParameter] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetInternalformatParameter] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetLinkResult] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetNumber] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetQueryParameter] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetRenderbufferParameter] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetSamplerParameter] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetShaderPrecisionFormat] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetString] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetTexParameter] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetUniform] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::GetVertexAttrib] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::IsEnabled] +description = Reflection is cold code, but synchronous by spec. +[PWebGL::ValidateProgram] +description = Reflection is cold code, but synchronous by spec. +# - +[PSocketProcess::GetTLSClientCert] +description = Synchronously get client certificate and key from parent process. Once bug 696976 has been fixed, this can be removed. + +############################################################# +# AVOID ADDING NEW MESSAGES TO THIS FILE # +# see comment at top of file # +############################################################# diff --git a/ipc/ipdl/test/README.txt b/ipc/ipdl/test/README.txt new file mode 100644 index 0000000000..8c5eb04110 --- /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 0000000000..d2a1f9872a --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp @@ -0,0 +1,26 @@ +/* -*- 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(int aArgc, char* aArgv[]) { + IPDLUnitTestChildInit(IOThreadChild::TakeChannel(), ParentPid(), + IOThreadChild::message_loop()); + + 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 0000000000..bae927cf24 --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h @@ -0,0 +1,29 @@ +/* -*- 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(int aArgc, char* aArgv[]) override; +}; + +} // 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 0000000000..660e5928d3 --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp @@ -0,0 +1,19 @@ +/* -*- 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 0000000000..384cf25ce9 --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h @@ -0,0 +1,34 @@ +/* -*- 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(); + + /** + * Asynchronously launch the plugin process. + */ + // Could override parent Launch, but don't need to here + // bool Launch(); + + private: + ~IPDLUnitTestSubprocess(); + + 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 0000000000..7d20c77bf0 --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h @@ -0,0 +1,43 @@ +/* -*- 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 _ipdltest +} // namespace mozilla + +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)); + } +}; +} // namespace IPC + +#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 0000000000..a5d2345654 --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTestUtils.h @@ -0,0 +1,29 @@ + +#ifndef mozilla__ipdltest_IPDLUnitTestUtils +#define mozilla__ipdltest_IPDLUnitTestUtils 1 + +#include "ipc/IPCMessageUtils.h" + +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 0000000000..28fc4ee372 --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTests.h @@ -0,0 +1,85 @@ +/* -*- 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 "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); + + MOZ_CRASH("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 0000000000..30eea42838 --- /dev/null +++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp @@ -0,0 +1,347 @@ +// +// 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" + +// clang-format off +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${INCLUDES} +//----------------------------------------------------------------------------- +// clang-format on + +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, + + // clang-format off +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${ENUM_VALUES} + + LastTest = ${LAST_ENUM} +//----------------------------------------------------------------------------- +//clang-format on +}; + +IPDLUnitTestType IPDLUnitTestFromString(const char* const aString) { + if (!aString) return static_cast<IPDLUnitTestType>(0); +// clang-format off +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${STRING_TO_ENUMS} +//----------------------------------------------------------------------------- + // clang-format on + else return static_cast<IPDLUnitTestType>(0); +} + +IPDLUnitTestType IPDLUnitTest() { + return IPDLUnitTestFromString(::mozilla::_ipdltest::IPDLUnitTestName()); +} + +} // namespace + +//----------------------------------------------------------------------------- +// 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); + MOZ_CRASH("can't continue"); + } + gIPDLUnitTestName = testString; + + // Check whether this test is enabled for processes: + switch (test) { + // clang-format off +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${PARENT_ENABLED_CASES_PROC} +//----------------------------------------------------------------------------- + // clang-format on + + 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) { + // clang-format off +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${PARENT_MAIN_CASES_PROC} +//----------------------------------------------------------------------------- + // clang-format on + + 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); + MOZ_CRASH("can't continue"); + } + gIPDLUnitTestName = testString; + + // Check whether this test is enabled for threads: + switch (test) { + // clang-format off +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${PARENT_ENABLED_CASES_THREAD} +//----------------------------------------------------------------------------- + // clang-format on + + 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) { + // clang-format off +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${PARENT_MAIN_CASES_THREAD} +//----------------------------------------------------------------------------- + // clang-format on + + default: + fail("not reached"); + return; // unreached + } +} + +void DeleteParentActor() { + if (!gParentActor) return; + + switch (IPDLUnitTest()) { + // clang-format off +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${PARENT_DELETE_CASES} +//----------------------------------------------------------------------------- + // clang-format on + 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 + gSubprocess->Destroy(); + gSubprocess = nullptr; + uiLoop->PostTask(NewRunnableFunction("QuitXPCOM", QuitXPCOM)); +} + +void DeferredParentShutdown() { + // ping to DeleteSubprocess + XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction( + "DeleteSubprocess", 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", TryThreadedShutdown)); + } else { + // defer "real" shutdown to avoid *Channel::Close() racing with the + // deletion of the subprocess + MessageLoop::current()->PostTask( + NewRunnableFunction("DeferredParentShutdown", DeferredParentShutdown)); + } +} + +static void ChildDie() { + DeleteChildActor(); + XRE_ShutdownChildProcess(); +} + +void QuitChild() { + if (gChildThread) { // Threaded-mode test + gParentMessageLoop->PostTask( + NewRunnableFunction("ChildCompleted", ChildCompleted)); + } else { // Process-mode test + MessageLoop::current()->PostTask(NewRunnableFunction("ChildDie", ChildDie)); + } +} + +} // namespace _ipdltest +} // namespace mozilla + +//----------------------------------------------------------------------------- +// child process only + +namespace mozilla { +namespace _ipdltest { + +void DeleteChildActor() { + if (!gChildActor) return; + + switch (IPDLUnitTest()) { + // clang-format off +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${CHILD_DELETE_CASES} +//----------------------------------------------------------------------------- + // clang-format on + default: + ::mozilla::_ipdltest::fail("???"); + } +} + +void IPDLUnitTestChildInit(IPC::Channel* transport, base::ProcessId parentPid, + MessageLoop* worker) { + switch (IPDLUnitTest()) { + // clang-format off +//----------------------------------------------------------------------------- +//===== TEMPLATED ===== +${CHILD_INIT_CASES} +//----------------------------------------------------------------------------- + // clang-format on + + 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 0000000000..b32533a648 --- /dev/null +++ b/ipc/ipdl/test/cxx/Makefile.in @@ -0,0 +1,45 @@ +# 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 = \ + 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) + $(PYTHON3) $(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 0000000000..5868f379e4 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestActorPunning.ipdl @@ -0,0 +1,26 @@ + +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__(); +}; + +} // 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 0000000000..a6b875920e --- /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 0000000000..1441219c38 --- /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/PTestAsyncReturns.ipdl b/ipc/ipdl/test/cxx/PTestAsyncReturns.ipdl new file mode 100644 index 0000000000..723e973b32 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestAsyncReturns.ipdl @@ -0,0 +1,17 @@ +namespace mozilla { +namespace _ipdltest { + + +protocol PTestAsyncReturns { + +child: + async Ping() returns (bool one); + async NoReturn() returns (bool unused); + +parent: + async Pong() returns (uint32_t param1, uint32_t param2); +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestBadActor.ipdl b/ipc/ipdl/test/cxx/PTestBadActor.ipdl new file mode 100644 index 0000000000..841d89ff63 --- /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 0000000000..99c78f4ac9 --- /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/PTestCancel.ipdl b/ipc/ipdl/test/cxx/PTestCancel.ipdl new file mode 100644 index 0000000000..3a6b46b437 --- /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 0000000000..7d8f8f6614 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl @@ -0,0 +1,17 @@ +// 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__(); +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestDataStructures.ipdl b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl new file mode 100644 index 0000000000..301cf251f7 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl @@ -0,0 +1,107 @@ +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); +}; + +} // 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 0000000000..39d7f482b3 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh @@ -0,0 +1,107 @@ +include protocol PTestDataStructuresSub; + +using struct mozilla::null_t from "mozilla/ipc/IPCCore.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 0000000000..7a4e87d83a --- /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 0000000000..25fa5d0f92 --- /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 0000000000..9091237e68 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestDesc.ipdl @@ -0,0 +1,21 @@ +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); +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestDescSub.ipdl b/ipc/ipdl/test/cxx/PTestDescSub.ipdl new file mode 100644 index 0000000000..67d4fe1660 --- /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 0000000000..c449f27441 --- /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 0000000000..51ac5f615c --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestEndpointBridgeMain.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; +include protocol PTestEndpointBridgeSub; + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestEndpointBridgeMain { + +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 0000000000..7364057acc --- /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 0000000000..0bc09b70e1 --- /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 0000000000..7be99ddd2b --- /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 0000000000..d0da59d0ce --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.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: */ +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__(); +}; + + +} // 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 0000000000..1f27d7ce66 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl @@ -0,0 +1,14 @@ +include protocol PTestFailedCtorSub; + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestFailedCtor { + manages PTestFailedCtorSub; +child: + intr PTestFailedCtorSub(); + async __delete__(); +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl b/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl new file mode 100644 index 0000000000..b1d18a05fd --- /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 0000000000..654170d976 --- /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 0000000000..aad92bae18 --- /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 0000000000..e2c34c2a72 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestHangs.ipdl @@ -0,0 +1,19 @@ + +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestHangs { +both: + intr StackFrame(); + +parent: + async Nonce(); + +child: + async Start(); + intr Hang(); + async __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 0000000000..0192f59b21 --- /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 0000000000..a81fcdee46 --- /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 0000000000..228ec04e88 --- /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 0000000000..db7c828a22 --- /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 0000000000..ed21f58f88 --- /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 0000000000..95f933bba7 --- /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 0000000000..b37faa13aa --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl @@ -0,0 +1,24 @@ +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__(); +}; + +} // 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 0000000000..19c06353b3 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl @@ -0,0 +1,16 @@ +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestInterruptShutdownRace { +parent: + sync StartDeath(); + async Orphan(); + +child: + async Start(); + intr Exit(); + async __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 0000000000..558512eb13 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestJSON.ipdl @@ -0,0 +1,46 @@ +include protocol PTestHandle; + +using struct mozilla::void_t from "mozilla/ipc/IPCCore.h"; +using struct mozilla::null_t from "mozilla/ipc/IPCCore.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__(); +}; + +} // 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 0000000000..45429c3314 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestLatency.ipdl @@ -0,0 +1,26 @@ + +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(); +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestLayoutThread.ipdl b/ipc/ipdl/test/cxx/PTestLayoutThread.ipdl new file mode 100644 index 0000000000..a2153ee462 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestLayoutThread.ipdl @@ -0,0 +1,19 @@ +include protocol PTestPaintThread; + +namespace mozilla { +namespace _ipdltest { + +// This is supposed to be analagous to PLayerTransaction. +sync protocol PTestLayoutThread +{ +parent: + async FinishedLayout(uint64_t aTxnId); + async AsyncMessage(uint64_t aTxnId); + sync SyncMessage(uint64_t aTxnId); + async EndTest(); +child: + async StartTest(Endpoint<PTestPaintThreadChild> endpoint); +}; + +} // 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 0000000000..767af85a20 --- /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 0000000000..e3d9c98df9 --- /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 0000000000..5bc9abf986 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl @@ -0,0 +1,22 @@ +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__(); +}; + +} // 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 0000000000..0c51d68414 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl @@ -0,0 +1,15 @@ +include protocol PTestMultiMgrsLeft; +include protocol PTestMultiMgrsRight; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestMultiMgrsBottom { + manager PTestMultiMgrsLeft or PTestMultiMgrsRight; + +child: + async __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 0000000000..d1bab6262a --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl @@ -0,0 +1,18 @@ +include protocol PTestMultiMgrs; +include protocol PTestMultiMgrsBottom; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestMultiMgrsLeft { + manager PTestMultiMgrs; + + manages PTestMultiMgrsBottom; + +child: + async PTestMultiMgrsBottom(); + async __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 0000000000..eef6a2af75 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl @@ -0,0 +1,18 @@ +include protocol PTestMultiMgrs; +include protocol PTestMultiMgrsBottom; + +namespace mozilla { +namespace _ipdltest { + +protocol PTestMultiMgrsRight { + manager PTestMultiMgrs; + + manages PTestMultiMgrsBottom; + +child: + async PTestMultiMgrsBottom(); + async __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 0000000000..0123a40691 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl @@ -0,0 +1,19 @@ + +namespace mozilla { +namespace _ipdltest { + + +intr protocol PTestNestedLoops { + +child: + async Start(); + intr R(); + async __delete__(); + +parent: + async Nonce(); +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestPaintThread.ipdl b/ipc/ipdl/test/cxx/PTestPaintThread.ipdl new file mode 100644 index 0000000000..0a37fda1be --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestPaintThread.ipdl @@ -0,0 +1,13 @@ + +namespace mozilla { +namespace _ipdltest { + +// This is supposed to be analagous to PPaintingBridge. +sync protocol PTestPaintThread +{ +parent: + sync FinishedPaint(uint64_t aTxnId); +}; + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/PTestPriority.ipdl b/ipc/ipdl/test/cxx/PTestPriority.ipdl new file mode 100644 index 0000000000..c4a2c93c39 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestPriority.ipdl @@ -0,0 +1,20 @@ +namespace mozilla { +namespace _ipdltest { + +sync protocol PTestPriority { +parent: + prio(input) async PMsg1(); + prio(input) sync PMsg2(); + prio(high) async PMsg3(); + prio(high) sync PMsg4(); + prio(mediumhigh) async PMsg5(); + prio(mediumhigh) sync PMsg6(); + +child: + prio(input) async CMsg1(); + prio(high) async CMsg2(); + prio(mediumhigh) async CMsg3(); +}; + +} // 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 0000000000..f51ee2735d --- /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 0000000000..1e4f574509 --- /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 0000000000..6a5a84c559 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl @@ -0,0 +1,19 @@ +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 +}; + +} // 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 0000000000..48921ed697 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl @@ -0,0 +1,17 @@ +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_(); +}; + +} // 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 0000000000..4dd5fe54fb --- /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 0000000000..8863d61795 --- /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 0000000000..c4dbec4076 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSanity.ipdl @@ -0,0 +1,18 @@ + +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); +}; + + +} // 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 0000000000..2cd4375abd --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSelfManage.ipdl @@ -0,0 +1,18 @@ +include protocol PTestSelfManageRoot; + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestSelfManage { + manager PTestSelfManageRoot or PTestSelfManage; + manages PTestSelfManage; + +child: + async PTestSelfManage(); + async __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 0000000000..ec86783ea7 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl @@ -0,0 +1,17 @@ +include protocol PTestSelfManage; + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestSelfManageRoot { + manages PTestSelfManage; + +child: + async PTestSelfManage(); + async __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 0000000000..262e34fabe --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestShmem.ipdl @@ -0,0 +1,14 @@ +namespace mozilla { +namespace _ipdltest { + +protocol PTestShmem { +child: + async Give(Shmem mem, Shmem unsafe, uint32_t expectedSize); + +parent: + async Take(Shmem mem, Shmem unsafe, uint32_t expectedSize); + async __delete__(); +}; + +} +} diff --git a/ipc/ipdl/test/cxx/PTestShutdown.ipdl b/ipc/ipdl/test/cxx/PTestShutdown.ipdl new file mode 100644 index 0000000000..2cfa489f8d --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestShutdown.ipdl @@ -0,0 +1,26 @@ +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__(); +}; + +} // 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 0000000000..c58b30e159 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl @@ -0,0 +1,20 @@ +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__(); +}; + +} // 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 0000000000..8300792bb4 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl @@ -0,0 +1,14 @@ +include protocol PTestShutdownSub; + +namespace mozilla { +namespace _ipdltest { + +sync protocol PTestShutdownSubsub { + manager PTestShutdownSub; + +parent: + sync __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 0000000000..e576f8634f --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestStackHooks.ipdl @@ -0,0 +1,26 @@ + +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__(); +}; + + +} // 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 0000000000..9a9e0db748 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSyncError.ipdl @@ -0,0 +1,18 @@ + +namespace mozilla { +namespace _ipdltest { + + +sync protocol PTestSyncError { + +child: + async Start(); + +parent: + sync Error(); + async __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 0000000000..49489aaa9c --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSyncHang.ipdl @@ -0,0 +1,14 @@ + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestSyncHang { + +child: + async UnusedMessage(); +}; + + +} // 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 0000000000..4f23153340 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl @@ -0,0 +1,20 @@ +namespace mozilla { +namespace _ipdltest { + +intr protocol PTestSyncWakeup { +both: + intr StackFrame(); + +child: + async Start(); + async Note1(); + async Note2(); + +parent: + sync Sync1(); + sync Sync2(); + async __delete__(); +}; + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/PTestUniquePtrIPC.ipdl b/ipc/ipdl/test/cxx/PTestUniquePtrIPC.ipdl new file mode 100644 index 0000000000..6c09c7174c --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestUniquePtrIPC.ipdl @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+namespace mozilla {
+namespace _ipdltest {
+
+struct DummyStruct {
+ int x;
+};
+
+protocol PTestUniquePtrIPC
+{
+child:
+ async TestMessage(UniquePtr<int> a1, UniquePtr<DummyStruct> a2,
+ DummyStruct a3, UniquePtr<int> a4);
+ async TestSendReference(UniquePtr<DummyStruct> a);
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestUrgency.ipdl b/ipc/ipdl/test/cxx/PTestUrgency.ipdl new file mode 100644 index 0000000000..79b479ca2e --- /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 0000000000..8ce8d17473 --- /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 0000000000..0fe6c07320 --- /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 0000000000..c969ef60eb --- /dev/null +++ b/ipc/ipdl/test/cxx/TestActorPunning.cpp @@ -0,0 +1,126 @@ +#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"); +} + +mozilla::ipc::IPCResult TestActorPunningParent::RecvPun( + PTestActorPunningSubParent* a, const Bad& bad) { + if (a->SendBad()) fail("bad!"); + fail("shouldn't have received this message in the first place"); + return IPC_OK(); +} + +// By default, fatal errors kill the parent process, but this makes it +// hard to test, so instead we use the previous behavior and kill the +// child process. +void TestActorPunningParent::HandleFatalError(const char* aErrorMsg) const { + if (!!strcmp(aErrorMsg, "Error deserializing 'PTestActorPunningSubParent'")) { + fail("wrong fatal error"); + } + + ipc::ScopedProcessHandle otherProcessHandle; + if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) { + fail("couldn't open child process"); + } else { + if (!base::KillProcess(otherProcessHandle, 0, false)) { + fail("terminating child process"); + } + } +} + +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; +} + +mozilla::ipc::IPCResult TestActorPunningChild::RecvStart() { + SendPTestActorPunningSubConstructor(); + SendPTestActorPunningPunnedConstructor(); + PTestActorPunningSubChild* a = SendPTestActorPunningSubConstructor(); + // We can't assert whether this succeeds or fails, due to race + // conditions. + SendPun(a, Bad()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestActorPunningSubChild::RecvBad() { + fail("things are going really badly right now"); + return IPC_OK(); +} + +} // 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 0000000000..9c19a0e0dd --- /dev/null +++ b/ipc/ipdl/test/cxx/TestActorPunning.h @@ -0,0 +1,100 @@ +#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 { + friend class PTestActorPunningParent; + + public: + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + + protected: + PTestActorPunningPunnedParent* AllocPTestActorPunningPunnedParent(); + bool DeallocPTestActorPunningPunnedParent(PTestActorPunningPunnedParent* a); + + PTestActorPunningSubParent* AllocPTestActorPunningSubParent(); + bool DeallocPTestActorPunningSubParent(PTestActorPunningSubParent* a); + + mozilla::ipc::IPCResult RecvPun(PTestActorPunningSubParent* a, + const Bad& bad); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown == why) fail("should have died from error!"); + passed("ok"); + QuitParent(); + } + + virtual void HandleFatalError(const char* aErrorMsg) const override; +}; + +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 { + friend class PTestActorPunningChild; + + public: + TestActorPunningChild() {} + virtual ~TestActorPunningChild() {} + + protected: + PTestActorPunningPunnedChild* AllocPTestActorPunningPunnedChild(); + bool DeallocPTestActorPunningPunnedChild(PTestActorPunningPunnedChild* a); + + PTestActorPunningSubChild* AllocPTestActorPunningSubChild(); + bool DeallocPTestActorPunningSubChild(PTestActorPunningSubChild* a); + + mozilla::ipc::IPCResult RecvStart(); + + 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() {} + + mozilla::ipc::IPCResult RecvBad(); +}; + +} // namespace _ipdltest +} // namespace mozilla + +#endif // ifndef mozilla__ipdltest_TestActorPunning_h diff --git a/ipc/ipdl/test/cxx/TestAsyncReturns.cpp b/ipc/ipdl/test/cxx/TestAsyncReturns.cpp new file mode 100644 index 0000000000..081222395d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestAsyncReturns.cpp @@ -0,0 +1,101 @@ +#include "TestAsyncReturns.h" + +#include "IPDLUnitTests.h" // fail etc. + +#include "mozilla/AbstractThread.h" +#include "mozilla/Unused.h" + +namespace mozilla { +namespace _ipdltest { + +static uint32_t sMagic1 = 0x105b59fb; +static uint32_t sMagic2 = 0x09b6f5e3; + +//----------------------------------------------------------------------------- +// parent + +TestAsyncReturnsParent::TestAsyncReturnsParent() { + MOZ_COUNT_CTOR(TestAsyncReturnsParent); +} + +TestAsyncReturnsParent::~TestAsyncReturnsParent() { + MOZ_COUNT_DTOR(TestAsyncReturnsParent); +} + +void TestAsyncReturnsParent::Main() { + SendNoReturn()->Then( + MessageLoop::current()->SerialEventTarget(), __func__, + [](bool unused) { fail("resolve handler should not be called"); }, + [](ResponseRejectReason&& aReason) { + // MozPromise asserts in debug build if the + // handler is not called + if (aReason != ResponseRejectReason::ChannelClosed) { + fail("reject with wrong reason"); + } + passed("reject handler called on channel close"); + }); + SendPing()->Then( + MessageLoop::current()->SerialEventTarget(), __func__, + [this](bool one) { + if (one) { + passed("take one argument"); + } else { + fail("get one argument but has wrong value"); + } + + // Also try with the callback-based API. + SendPing( + [this](bool one) { + if (one) { + passed("take one argument"); + } else { + fail("get one argument but has wrong value"); + } + Close(); + }, + [](ResponseRejectReason&& aReason) { fail("sending Ping"); }); + }, + [](ResponseRejectReason&& aReason) { fail("sending Ping"); }); +} + +mozilla::ipc::IPCResult TestAsyncReturnsParent::RecvPong( + PongResolver&& aResolve) { + aResolve(Tuple<const uint32_t&, const uint32_t&>(sMagic1, sMagic2)); + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +TestAsyncReturnsChild::TestAsyncReturnsChild() { + MOZ_COUNT_CTOR(TestAsyncReturnsChild); +} + +TestAsyncReturnsChild::~TestAsyncReturnsChild() { + MOZ_COUNT_DTOR(TestAsyncReturnsChild); +} + +mozilla::ipc::IPCResult TestAsyncReturnsChild::RecvNoReturn( + NoReturnResolver&& aResolve) { + // Not resolving the promise intentionally + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestAsyncReturnsChild::RecvPing( + PingResolver&& aResolve) { + SendPong()->Then( + MessageLoop::current()->SerialEventTarget(), __func__, + [aResolve](const Tuple<uint32_t, uint32_t>& aParam) { + if (Get<0>(aParam) == sMagic1 && Get<1>(aParam) == sMagic2) { + passed("take two arguments"); + } else { + fail("get two argument but has wrong value"); + } + aResolve(true); + }, + [](ResponseRejectReason&& aReason) { fail("sending Pong"); }); + return IPC_OK(); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestAsyncReturns.h b/ipc/ipdl/test/cxx/TestAsyncReturns.h new file mode 100644 index 0000000000..5dad3da0ab --- /dev/null +++ b/ipc/ipdl/test/cxx/TestAsyncReturns.h @@ -0,0 +1,54 @@ +#ifndef mozilla__ipdltest_TestAsyncReturns_h +#define mozilla__ipdltest_TestAsyncReturns_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestAsyncReturnsParent.h" +#include "mozilla/_ipdltest/PTestAsyncReturnsChild.h" + +namespace mozilla { +namespace _ipdltest { + +class TestAsyncReturnsParent : public PTestAsyncReturnsParent { + friend class PTestAsyncReturnsParent; + + public: + TestAsyncReturnsParent(); + virtual ~TestAsyncReturnsParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvPong(PongResolver&& aResolve); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + +class TestAsyncReturnsChild : public PTestAsyncReturnsChild { + friend class PTestAsyncReturnsChild; + + public: + TestAsyncReturnsChild(); + virtual ~TestAsyncReturnsChild(); + + protected: + mozilla::ipc::IPCResult RecvPing(PingResolver&& aResolve); + mozilla::ipc::IPCResult RecvNoReturn(NoReturnResolver&& aResolve); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + QuitChild(); + } +}; + +} // namespace _ipdltest +} // namespace mozilla + +#endif // ifndef mozilla__ipdltest_TestAsyncReturns_h diff --git a/ipc/ipdl/test/cxx/TestBadActor.cpp b/ipc/ipdl/test/cxx/TestBadActor.cpp new file mode 100644 index 0000000000..42a0faf19d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestBadActor.cpp @@ -0,0 +1,59 @@ +#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); +} + +// By default, fatal errors kill the parent process, but this makes it +// hard to test, so instead we use the previous behavior and kill the +// child process. +void TestBadActorParent::HandleFatalError(const char* aErrorMsg) const { + if (!!strcmp(aErrorMsg, "incoming message racing with actor deletion")) { + fail("wrong fatal error"); + } + + ipc::ScopedProcessHandle otherProcessHandle; + if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) { + fail("couldn't open child process"); + } else { + if (!base::KillProcess(otherProcessHandle, 0, false)) { + fail("terminating child process"); + } + } +} + +PTestBadActorSubParent* TestBadActorParent::AllocPTestBadActorSubParent() { + return new TestBadActorSubParent(); +} + +mozilla::ipc::IPCResult TestBadActorSubParent::RecvPing() { + fail("Shouldn't have received ping."); + return IPC_FAIL_NO_REASON(this); +} + +PTestBadActorSubChild* TestBadActorChild::AllocPTestBadActorSubChild() { + return new TestBadActorSubChild(); +} + +mozilla::ipc::IPCResult TestBadActorChild::RecvPTestBadActorSubConstructor( + PTestBadActorSubChild* actor) { + if (!actor->SendPing()) { + fail("Couldn't send ping to an actor which supposedly isn't dead yet."); + } + return IPC_OK(); +} + +} // 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 0000000000..0157b898f4 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestBadActor.h @@ -0,0 +1,84 @@ +#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 { + friend class 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 void HandleFatalError(const char* aErrorMsg) const override; + + PTestBadActorSubParent* AllocPTestBadActorSubParent(); + + bool DeallocPTestBadActorSubParent(PTestBadActorSubParent* actor) { + delete actor; + return true; + } +}; + +class TestBadActorSubParent : public PTestBadActorSubParent { + friend class PTestBadActorSubParent; + + public: + TestBadActorSubParent() {} + virtual ~TestBadActorSubParent() {} + + protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} + mozilla::ipc::IPCResult RecvPing(); +}; + +class TestBadActorChild : public PTestBadActorChild { + friend class PTestBadActorChild; + + public: + TestBadActorChild() {} + virtual ~TestBadActorChild() {} + + protected: + virtual PTestBadActorSubChild* AllocPTestBadActorSubChild(); + + virtual bool DeallocPTestBadActorSubChild(PTestBadActorSubChild* actor) { + delete actor; + return true; + } + + virtual mozilla::ipc::IPCResult 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/TestCancel.cpp b/ipc/ipdl/test/cxx/TestCancel.cpp new file mode 100644 index 0000000000..2f97aa2e93 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCancel.cpp @@ -0,0 +1,115 @@ +#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"); +} + +mozilla::ipc::IPCResult TestCancelParent::RecvDone1() { + if (!SendStart2()) fail("sending Start2"); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestCancelParent::RecvTest2_1() { + if (SendTest2_2()) fail("sending Test2_2"); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestCancelParent::RecvTest3_2() { + GetIPCChannel()->CancelCurrentTransaction(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestCancelParent::RecvDone() { + MessageLoop::current()->PostTask(NewNonOwningRunnableMethod( + "ipc::IToplevelProtocol::Close", this, &TestCancelParent::Close)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestCancelParent::RecvCheckParent(uint32_t* reply) { + *reply = 12; + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestCancelChild::RecvStart2() { + if (!SendTest2_1()) fail("sending Test2_1"); + + if (!SendStart3()) fail("sending Start3"); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestCancelChild::RecvTest2_2() { + GetIPCChannel()->CancelCurrentTransaction(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestCancelChild::RecvCheckChild(uint32_t* reply) { + *reply = 12; + return IPC_OK(); +} + +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 0000000000..7d944d6a69 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCancel.h @@ -0,0 +1,54 @@ +#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(); + + mozilla::ipc::IPCResult RecvDone1(); + mozilla::ipc::IPCResult RecvTest2_1(); + mozilla::ipc::IPCResult RecvStart3(); + mozilla::ipc::IPCResult RecvTest3_2(); + mozilla::ipc::IPCResult RecvDone(); + + mozilla::ipc::IPCResult RecvCheckParent(uint32_t* reply); + + virtual void ActorDestroy(ActorDestroyReason why) override { + passed("ok"); + QuitParent(); + } +}; + +class TestCancelChild : public PTestCancelChild { + public: + TestCancelChild(); + virtual ~TestCancelChild(); + + mozilla::ipc::IPCResult RecvTest1_1(); + mozilla::ipc::IPCResult RecvStart2(); + mozilla::ipc::IPCResult RecvTest2_2(); + mozilla::ipc::IPCResult RecvTest3_1(); + + mozilla::ipc::IPCResult RecvCheckChild(uint32_t* reply); + + 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 0000000000..6807bd296d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp @@ -0,0 +1,100 @@ +#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); + + gSubprocess->Destroy(); + 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", 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 + +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", DeleteTheWorld)); + + if (CallDIEDIEDIE()) fail("expected an error!"); + + Close(); + + MessageLoop::current()->PostTask(NewRunnableFunction("Done", Done)); +} + +//----------------------------------------------------------------------------- +// child + +TestCrashCleanupChild::TestCrashCleanupChild() { + MOZ_COUNT_CTOR(TestCrashCleanupChild); +} + +TestCrashCleanupChild::~TestCrashCleanupChild() { + MOZ_COUNT_DTOR(TestCrashCleanupChild); +} + +mozilla::ipc::IPCResult TestCrashCleanupChild::AnswerDIEDIEDIE() { + _exit(0); + MOZ_CRASH("unreached"); +} + +} // 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 0000000000..30371264e6 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCrashCleanup.h @@ -0,0 +1,49 @@ +#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 { + friend class PTestCrashCleanupChild; + + public: + TestCrashCleanupChild(); + virtual ~TestCrashCleanupChild(); + + protected: + mozilla::ipc::IPCResult AnswerDIEDIEDIE(); + + 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 0000000000..5b1cabfe7d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDataStructures.cpp @@ -0,0 +1,888 @@ +#include "TestDataStructures.h" + +#include "mozilla/Unused.h" + +#include "IPDLUnitTests.h" // fail etc. + +typedef nsTArray<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 nsTArray<T>& a, const nsTArray<T>& b) { + test_assert(a == b, "arrays equal"); +} + +inline static TestDataStructuresSub& Cast(PTestDataStructuresSubParent* 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; +} + +mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest1(nsTArray<int>&& ia, + nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest2( + nsTArray<PTestDataStructuresSubParent*>&& i1, + nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest4( + nsTArray<IntDouble>&& i1, nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 nsTArray<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 nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest6( + nsTArray<IntDoubleArrays>&& i1, nsTArray<IntDoubleArrays>* o1) { + test_assert(3 == i1.Length(), "wrong length"); + + IntDoubleArrays id1(i1[0]); + test_assert(42 == id1.get_int(), "wrong value"); + + nsTArray<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"); + + nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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"); + + nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest8( + nsTArray<Actors>&& i1, nsTArray<Actors>* o1) { + test_assert(3 == i1.Length(), "wrong length"); + test_assert(42 == i1[0].get_int(), "wrong value"); + + const nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 nsTArray<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 nsTArray<PTestDataStructuresSubParent*>& i4a = + i4.get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubParent(); + assert_arrays_equal(mKids, i4a); + + *o1 = i1; + *o2 = i2; + *o3 = i3; + *o4 = i4; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest10( + nsTArray<Unions>&& i1, nsTArray<Unions>* o1) { + test_assert(42 == i1[0].get_int(), "wrong value"); + + const nsTArray<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 nsTArray<PTestDataStructuresSubParent*>& i4a = + i1[3].get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubParent(); + assert_arrays_equal(mKids, i4a); + + *o1 = i1; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest12( + const SIntDoubleArrays& i, SIntDoubleArrays* o) { + nsTArray<int> ai; + ai.AppendElement(1); + ai.AppendElement(2); + ai.AppendElement(3); + + nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest13(const SActors& i, + SActors* o) { + nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest14(const Structs& i, + Structs* o) { + nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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) { + nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest16( + const WithUnions& i, WithUnions* o) { + test_assert(i.i() == 42, "wrong value"); + + nsTArray<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 nsTArray<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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestDataStructuresParent::RecvTest17( + nsTArray<Op>&& sa) { + test_assert(sa.Length() == 1 && Op::TSetAttrs == sa[0].type(), "wrong value"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +TestDataStructuresChild::TestDataStructuresChild() { + MOZ_COUNT_CTOR(TestDataStructuresChild); +} + +TestDataStructuresChild::~TestDataStructuresChild() { + MOZ_COUNT_DTOR(TestDataStructuresChild); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +void TestDataStructuresChild::Test1() { + nsTArray<int> ia; + + for (int i = 0; i < 5; ++i) ia.AppendElement(i); + + nsTArray<int> oa; + if (!SendTest1(ia, &oa)) fail("can't send Test1"); + + assert_arrays_equal(ia, oa); + + printf(" passed %s\n", __FUNCTION__); +} + +void TestDataStructuresChild::Test2() { + nsTArray<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() { + nsTArray<IntDouble> i1; + i1.AppendElement(IntDouble(int(1))); + i1.AppendElement(IntDouble(2.0)); + i1.AppendElement(IntDouble(int(3))); + i1.AppendElement(IntDouble(4.0)); + + nsTArray<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)); + nsTArray<int> i2; + i2.AppendElement(1); + i2.AppendElement(2); + i2.AppendElement(3); + nsTArray<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)); + nsTArray<int> id2; + id2.AppendElement(1); + id2.AppendElement(2); + id2.AppendElement(3); + nsTArray<double> id3; + id3.AppendElement(1.0); + id3.AppendElement(2.0); + id3.AppendElement(3.0); + + nsTArray<IntDoubleArrays> i1; + i1.AppendElement(id1); + i1.AppendElement(IntDoubleArrays(id2)); + i1.AppendElement(IntDoubleArrays(id3)); + + nsTArray<IntDoubleArrays> o1; + if (!SendTest6(i1, &o1)) fail("can't send Test6"); + + test_assert(3 == o1.Length(), "wrong length"); + IntDoubleArrays od1(o1[0]); + nsTArray<int> od2(o1[1].get_ArrayOfint()); + nsTArray<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); + nsTArray<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); + nsTArray<int> i2a; + i2a.AppendElement(1); + i2a.AppendElement(2); + i2a.AppendElement(3); + + nsTArray<Actors> i1; + i1.AppendElement(i1e); + i1.AppendElement(i2a); + i1.AppendElement(mKids); + + nsTArray<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)); + + nsTArray<int> i2a; + i2a.AppendElement(1); + i2a.AppendElement(2); + i2a.AppendElement(3); + + nsTArray<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)); + + nsTArray<int> i2a; + i2a.AppendElement(1); + i2a.AppendElement(2); + i2a.AppendElement(3); + + nsTArray<Actors> i4a; + i4a.AppendElement(mKids); + + nsTArray<Unions> i1; + i1.AppendElement(i1a); + i1.AppendElement(Unions(i2a)); + i1.AppendElement(Unions(mKids)); + i1.AppendElement(Unions(i4a)); + + nsTArray<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() { + nsTArray<int> ai; + ai.AppendElement(1); + ai.AppendElement(2); + ai.AppendElement(3); + + nsTArray<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() { + nsTArray<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() { + nsTArray<int> ai; + ai.AppendElement(1); + ai.AppendElement(2); + ai.AppendElement(3); + + SActors ia; + ia.i() = 42; + ia.ai() = ai; + ia.apChild() = mKids; + nsTArray<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() { + nsTArray<int> ai; + ai.AppendElement(1); + ai.AppendElement(2); + ai.AppendElement(3); + + SActors ia; + ia.i() = 42; + ia.ai() = ai; + ia.apChild() = mKids; + nsTArray<SActors> iaa; + iaa.AppendElement(ia); + + Structs is; + is.i() = 42; + is.ai() = ai; + is.apChild() = mKids; + is.aa() = iaa; + nsTArray<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; + + nsTArray<int> ai; + ai.AppendElement(1); + ai.AppendElement(2); + ai.AppendElement(3); + i.ai() = ai; + + i.apChild() = mKids; + + nsTArray<Actors> iaa; + iaa.AppendElement(mKids); + i.aa() = iaa; + + nsTArray<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 nsTArray<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); + + nsTArray<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 0000000000..dc25b5282c --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDataStructures.h @@ -0,0 +1,180 @@ +#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 { + friend class PTestDataStructuresParent; + + public: + TestDataStructuresParent(); + virtual ~TestDataStructuresParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + PTestDataStructuresSubParent* AllocPTestDataStructuresSubParent( + const int& i) { + PTestDataStructuresSubParent* actor = new TestDataStructuresSub(i); + mKids.AppendElement(actor); + return actor; + } + + bool DeallocPTestDataStructuresSubParent(PTestDataStructuresSubParent* actor); + + mozilla::ipc::IPCResult RecvTest1(nsTArray<int>&& i1, nsTArray<int>* o1); + + mozilla::ipc::IPCResult RecvTest2( + nsTArray<PTestDataStructuresSubParent*>&& i1, + nsTArray<PTestDataStructuresSubParent*>* o1); + + mozilla::ipc::IPCResult RecvTest3(const IntDouble& i1, const IntDouble& i2, + IntDouble* o1, IntDouble* o2); + + mozilla::ipc::IPCResult RecvTest4(nsTArray<IntDouble>&& i1, + nsTArray<IntDouble>* o1); + + mozilla::ipc::IPCResult RecvTest5(const IntDoubleArrays& i1, + const IntDoubleArrays& i2, + const IntDoubleArrays& i3, + IntDoubleArrays* o1, IntDoubleArrays* o2, + IntDoubleArrays* o3); + + mozilla::ipc::IPCResult RecvTest6(nsTArray<IntDoubleArrays>&& i1, + nsTArray<IntDoubleArrays>* o1); + + mozilla::ipc::IPCResult RecvTest7_0(const ActorWrapper& i1, ActorWrapper* o1); + + mozilla::ipc::IPCResult RecvTest7(const Actors& i1, const Actors& i2, + const Actors& i3, Actors* o1, Actors* o2, + Actors* o3); + + mozilla::ipc::IPCResult RecvTest8(nsTArray<Actors>&& i1, + nsTArray<Actors>* o1); + + mozilla::ipc::IPCResult RecvTest9(const Unions& i1, const Unions& i2, + const Unions& i3, const Unions& i4, + Unions* o1, Unions* o2, Unions* o3, + Unions* o4); + + mozilla::ipc::IPCResult RecvTest10(nsTArray<Unions>&& i1, + nsTArray<Unions>* o1); + + mozilla::ipc::IPCResult RecvTest11(const SIntDouble& i, SIntDouble* o); + + mozilla::ipc::IPCResult RecvTest12(const SIntDoubleArrays& i, + SIntDoubleArrays* o); + + mozilla::ipc::IPCResult RecvTest13(const SActors& i, SActors* o); + + mozilla::ipc::IPCResult RecvTest14(const Structs& i, Structs* o); + + mozilla::ipc::IPCResult 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); + + mozilla::ipc::IPCResult RecvTest16(const WithUnions& i, WithUnions* o); + + mozilla::ipc::IPCResult RecvTest17(nsTArray<Op>&& sa); + + mozilla::ipc::IPCResult RecvTest18(nsTArray<nsIntRegion>&& ra); + + mozilla::ipc::IPCResult RecvDummy(const ShmemUnion& su, ShmemUnion* rsu) { + *rsu = su; + return IPC_OK(); + } + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + + private: + nsTArray<PTestDataStructuresSubParent*> mKids; +}; + +class TestDataStructuresChild : public PTestDataStructuresChild { + friend class PTestDataStructuresChild; + + public: + TestDataStructuresChild(); + virtual ~TestDataStructuresChild(); + + protected: + PTestDataStructuresSubChild* AllocPTestDataStructuresSubChild(const int& i) { + PTestDataStructuresSubChild* actor = new TestDataStructuresSub(i); + mKids.AppendElement(actor); + return actor; + } + + bool DeallocPTestDataStructuresSubChild(PTestDataStructuresSubChild* actor) { + delete actor; + return true; + } + + mozilla::ipc::IPCResult RecvStart(); + + 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(); + + nsTArray<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 0000000000..811f6c3314 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDemon.cpp @@ -0,0 +1,362 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 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 + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestDemonParent::RecvHiPrioSyncMessage() { + DEMON_LOG("Start RecvHiPrioSyncMessage"); + RunLimitedSequence(); + DEMON_LOG("End RecvHiPrioSyncMessage"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +void TestDemonParent::RunUnlimitedSequence() { + if (mDone) { + return; + } + + gFlushStack = false; + DoAction(); + + MessageLoop::current()->PostTask(NewNonOwningRunnableMethod( + "_ipdltest::TestDemonParent::RunUnlimitedSequence", 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); } + +mozilla::ipc::IPCResult TestDemonChild::RecvStart() { +#ifdef OS_POSIX + srandom(time(nullptr)); +#else + srand(time(nullptr)); +#endif + + DEMON_LOG("RecvStart"); + + RunUnlimitedSequence(); + return IPC_OK(); +} + +#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 + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestDemonChild::RecvHiPrioSyncMessage() { + DEMON_LOG("Start RecvHiPrioSyncMessage"); + RunLimitedSequence(); + DEMON_LOG("End RecvHiPrioSyncMessage"); + return IPC_OK(); +} + +void TestDemonChild::RunUnlimitedSequence() { + gFlushStack = false; + DoAction(); + + MessageLoop::current()->PostTask(NewNonOwningRunnableMethod( + "_ipdltest::TestDemonChild::RunUnlimitedSequence", 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 0000000000..ba5b003436 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDemon.h @@ -0,0 +1,91 @@ +#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 + + mozilla::ipc::IPCResult RecvAsyncMessage(const int& n); + mozilla::ipc::IPCResult RecvHiPrioSyncMessage(); + + mozilla::ipc::IPCResult RecvSyncMessage(const int& n); + mozilla::ipc::IPCResult RecvUrgentAsyncMessage(const int& n); + mozilla::ipc::IPCResult RecvUrgentSyncMessage(const int& n); + + 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(); + + mozilla::ipc::IPCResult RecvStart(); + +#ifdef DEBUG + bool NeedArtificialSleep() override { return true; } + void ArtificialSleep() override; +#endif + + mozilla::ipc::IPCResult RecvAsyncMessage(const int& n); + mozilla::ipc::IPCResult RecvHiPrioSyncMessage(); + + 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 0000000000..2ae199457d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDesc.cpp @@ -0,0 +1,78 @@ +#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"); +} + +mozilla::ipc::IPCResult TestDescParent::RecvOk(PTestDescSubsubParent* a) { + if (!a) fail("didn't receive Subsub"); + + if (!PTestDescSubsubParent::Call__delete__(a)) fail("deleting Subsub"); + + Close(); + + return IPC_OK(); +} + +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 + +mozilla::ipc::IPCResult TestDescChild::RecvTest(PTestDescSubsubChild* a) { + if (!a) fail("didn't receive Subsub"); + if (!SendOk(a)) fail("couldn't send Ok()"); + return IPC_OK(); +} + +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 0000000000..c9fa04f89c --- /dev/null +++ b/ipc/ipdl/test/cxx/TestDesc.h @@ -0,0 +1,115 @@ +#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 { + friend class PTestDescParent; + + public: + TestDescParent() {} + virtual ~TestDescParent() {} + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + mozilla::ipc::IPCResult RecvOk(PTestDescSubsubParent* a); + + protected: + PTestDescSubParent* AllocPTestDescSubParent(PTestDescSubsubParent*); + bool DeallocPTestDescSubParent(PTestDescSubParent* actor); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + +class TestDescChild : public PTestDescChild { + friend class PTestDescChild; + + public: + TestDescChild() {} + virtual ~TestDescChild() {} + + protected: + PTestDescSubChild* AllocPTestDescSubChild(PTestDescSubsubChild*); + + bool DeallocPTestDescSubChild(PTestDescSubChild* actor); + + mozilla::ipc::IPCResult RecvTest(PTestDescSubsubChild* a); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + QuitChild(); + } +}; + +//----------------------------------------------------------------------------- +// First descendent +// +class TestDescSubParent : public PTestDescSubParent { + friend class PTestDescSubParent; + + public: + TestDescSubParent() {} + virtual ~TestDescSubParent() {} + + protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} + PTestDescSubsubParent* AllocPTestDescSubsubParent(); + bool DeallocPTestDescSubsubParent(PTestDescSubsubParent* actor); +}; + +class TestDescSubChild : public PTestDescSubChild { + friend class PTestDescSubChild; + + public: + TestDescSubChild() {} + virtual ~TestDescSubChild() {} + + protected: + PTestDescSubsubChild* AllocPTestDescSubsubChild(); + bool DeallocPTestDescSubsubChild(PTestDescSubsubChild* actor); +}; + +//----------------------------------------------------------------------------- +// 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() {} +}; + +} // namespace _ipdltest +} // namespace mozilla + +#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 0000000000..4a4366f0ba --- /dev/null +++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp @@ -0,0 +1,226 @@ +/* -*- 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"); + } +} + +mozilla::ipc::IPCResult TestEndpointBridgeMainParent::RecvBridged( + Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint) { + TestEndpointBridgeMainSubParent* a = new TestEndpointBridgeMainSubParent(); + if (!endpoint.Bind(a)) { + fail("Bind failed"); + } + return IPC_OK(); +} + +void TestEndpointBridgeMainParent::ActorDestroy(ActorDestroyReason why) { + if (NormalShutdown != why) { + fail("unexpected destruction!"); + } + passed("ok"); + QuitParent(); +} + +mozilla::ipc::IPCResult TestEndpointBridgeMainSubParent::RecvHello() { + if (!SendHi()) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestEndpointBridgeMainSubParent::RecvHelloSync() { + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestEndpointBridgeMainSubParent::AnswerHelloRpc() { + if (!CallHiRpc()) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +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; +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +void TestEndpointBridgeMainChild::ActorDestroy(ActorDestroyReason why) { + if (NormalShutdown != why) { + fail("unexpected destruction!"); + } + // NB: this is kosher because QuitChild() joins with the IO thread + mSubprocess->Destroy(); + mSubprocess = nullptr; + QuitChild(); +} + +void TestEndpointBridgeSubParent::Main() { + if (!SendPing()) { + fail("sending Ping"); + } +} + +mozilla::ipc::IPCResult 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(std::move(parent))) { + fail("SendBridge failed for parent"); + } + if (!SendBridged(std::move(child))) { + fail("SendBridge failed for child"); + } + + return IPC_OK(); +} + +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; +} + +mozilla::ipc::IPCResult TestEndpointBridgeSubChild::RecvPing() { + if (!SendBridgeEm()) { + fail("sending BridgeEm"); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestEndpointBridgeSubChild::RecvBridged( + Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint) { + TestEndpointBridgeMainSubChild* a = new TestEndpointBridgeMainSubChild(); + + if (!endpoint.Bind(a)) { + fail("failed to Bind"); + } + + if (!a->SendHello()) { + fail("sending Hello"); + } + + return IPC_OK(); +} + +void TestEndpointBridgeSubChild::ActorDestroy(ActorDestroyReason why) { + if (NormalShutdown != why) { + fail("unexpected destruction!"); + } + QuitChild(); +} + +mozilla::ipc::IPCResult 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("ipc::IToplevelProtocol::Close", this, + &TestEndpointBridgeMainSubChild::Close)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestEndpointBridgeMainSubChild::AnswerHiRpc() { + mGotHi = true; // d00d + return IPC_OK(); +} + +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 _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h new file mode 100644 index 0000000000..88ac12d71b --- /dev/null +++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h @@ -0,0 +1,132 @@ +/* -*- 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 { + friend class PTestEndpointBridgeMainParent; + + public: + TestEndpointBridgeMainParent() {} + virtual ~TestEndpointBridgeMainParent() {} + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvBridged( + mozilla::ipc::Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint); + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +class TestEndpointBridgeMainSubParent + : public PTestEndpointBridgeMainSubParent { + friend class PTestEndpointBridgeMainSubParent; + + public: + explicit TestEndpointBridgeMainSubParent() {} + virtual ~TestEndpointBridgeMainSubParent() {} + + protected: + mozilla::ipc::IPCResult RecvHello(); + mozilla::ipc::IPCResult RecvHelloSync(); + mozilla::ipc::IPCResult AnswerHelloRpc(); + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +//----------------------------------------------------------------------------- +// "Sub" process --- child of "main" +// +class TestEndpointBridgeSubParent; + +class TestEndpointBridgeMainChild : public PTestEndpointBridgeMainChild { + friend class PTestEndpointBridgeMainChild; + + public: + TestEndpointBridgeMainChild(); + virtual ~TestEndpointBridgeMainChild() {} + + protected: + mozilla::ipc::IPCResult RecvStart(); + + virtual void ActorDestroy(ActorDestroyReason why) override; + + IPDLUnitTestSubprocess* mSubprocess; +}; + +class TestEndpointBridgeSubParent : public PTestEndpointBridgeSubParent { + friend class PTestEndpointBridgeSubParent; + + public: + TestEndpointBridgeSubParent() {} + virtual ~TestEndpointBridgeSubParent() {} + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvBridgeEm(); + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +//----------------------------------------------------------------------------- +// "Subsub" process --- child of "sub" +// +class TestEndpointBridgeSubChild : public PTestEndpointBridgeSubChild { + friend class PTestEndpointBridgeSubChild; + + public: + TestEndpointBridgeSubChild(); + virtual ~TestEndpointBridgeSubChild() {} + + protected: + mozilla::ipc::IPCResult RecvPing(); + + mozilla::ipc::IPCResult RecvBridged( + Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint); + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +class TestEndpointBridgeMainSubChild : public PTestEndpointBridgeMainSubChild { + friend class PTestEndpointBridgeMainSubChild; + + public: + explicit TestEndpointBridgeMainSubChild() : mGotHi(false) {} + virtual ~TestEndpointBridgeMainSubChild() {} + + protected: + mozilla::ipc::IPCResult RecvHi(); + mozilla::ipc::IPCResult AnswerHiRpc(); + + 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 0000000000..ca141f7b58 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp @@ -0,0 +1,242 @@ +/* -*- 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"); + } +} + +mozilla::ipc::IPCResult 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", OpenParent, a, std::move(endpoint))); + + return IPC_OK(); +} + +void TestEndpointOpensParent::ActorDestroy(ActorDestroyReason why) { + // Stops the thread and joins it + delete gParentThread; + + if (NormalShutdown != why) { + fail("unexpected destruction A!"); + } + passed("ok"); + QuitParent(); +} + +mozilla::ipc::IPCResult TestEndpointOpensOpenedParent::RecvHello() { + AssertNotMainThread(); + if (!SendHi()) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestEndpointOpensOpenedParent::RecvHelloSync() { + AssertNotMainThread(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestEndpointOpensOpenedParent::AnswerHelloRpc() { + AssertNotMainThread(); + if (!CallHiRpc()) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +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", + 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"); + } +} + +mozilla::ipc::IPCResult 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", OpenChild, a, std::move(child))); + + if (!SendStartSubprotocol(std::move(parent))) { + fail("send StartSubprotocol"); + } + + return IPC_OK(); +} + +void TestEndpointOpensChild::ActorDestroy(ActorDestroyReason why) { + // Stops the thread and joins it + delete gChildThread; + + if (NormalShutdown != why) { + fail("unexpected destruction C!"); + } + QuitChild(); +} + +mozilla::ipc::IPCResult 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("ipc::IToplevelProtocol::Close", this, + &TestEndpointOpensOpenedChild::Close)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestEndpointOpensOpenedChild::AnswerHiRpc() { + AssertNotMainThread(); + + mGotHi = true; // d00d + return IPC_OK(); +} + +static void ShutdownTestEndpointOpensOpenedChild( + TestEndpointOpensOpenedChild* child, Transport* transport) { + delete child; + + // Kick off main-thread shutdown. + gMainThread->PostTask( + NewNonOwningRunnableMethod("ipc::IToplevelProtocol::Close", 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", + 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 0000000000..6721274937 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestEndpointOpens.h @@ -0,0 +1,102 @@ +/* -*- 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 { + friend class PTestEndpointOpensParent; + + public: + TestEndpointOpensParent() {} + virtual ~TestEndpointOpensParent() {} + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvStartSubprotocol( + mozilla::ipc::Endpoint<PTestEndpointOpensOpenedParent>&& endpoint); + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +} // namespace _ipdltest + +namespace _ipdltest2 { + +class TestEndpointOpensOpenedParent : public PTestEndpointOpensOpenedParent { + friend class PTestEndpointOpensOpenedParent; + + public: + explicit TestEndpointOpensOpenedParent() {} + virtual ~TestEndpointOpensOpenedParent() {} + + protected: + mozilla::ipc::IPCResult RecvHello(); + mozilla::ipc::IPCResult RecvHelloSync(); + mozilla::ipc::IPCResult AnswerHelloRpc(); + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +} // namespace _ipdltest2 + +// child process + +namespace _ipdltest { + +class TestEndpointOpensChild : public PTestEndpointOpensChild { + friend class PTestEndpointOpensChild; + + public: + TestEndpointOpensChild(); + virtual ~TestEndpointOpensChild() {} + + protected: + mozilla::ipc::IPCResult RecvStart(); + + virtual void ActorDestroy(ActorDestroyReason why) override; +}; + +} // namespace _ipdltest + +namespace _ipdltest2 { + +class TestEndpointOpensOpenedChild : public PTestEndpointOpensOpenedChild { + friend class PTestEndpointOpensOpenedChild; + + public: + explicit TestEndpointOpensOpenedChild() : mGotHi(false) {} + virtual ~TestEndpointOpensOpenedChild() {} + + protected: + mozilla::ipc::IPCResult RecvHi(); + mozilla::ipc::IPCResult AnswerHiRpc(); + + 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 0000000000..2f08d2eeb0 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestFailedCtor.cpp @@ -0,0 +1,112 @@ +#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(); +} + +mozilla::ipc::IPCResult +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 IPC_FAIL_NO_REASON(this); +} + +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 0000000000..6fac4eb278 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestFailedCtor.h @@ -0,0 +1,127 @@ +#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 { + friend class 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: + PTestFailedCtorSubParent* AllocPTestFailedCtorSubParent(); + bool DeallocPTestFailedCtorSubParent(PTestFailedCtorSubParent* actor); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (AbnormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + +class TestFailedCtorChild : public PTestFailedCtorChild { + friend class PTestFailedCtorChild; + + public: + TestFailedCtorChild() {} + virtual ~TestFailedCtorChild() {} + + protected: + PTestFailedCtorSubChild* AllocPTestFailedCtorSubChild(); + + mozilla::ipc::IPCResult AnswerPTestFailedCtorSubConstructor( + PTestFailedCtorSubChild* actor) override; + + bool DeallocPTestFailedCtorSubChild(PTestFailedCtorSubChild* actor); + + 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 { + friend class PTestFailedCtorSubParent; + + public: + TestFailedCtorSubParent() : mOne(nullptr), mTwo(nullptr), mThree(nullptr) {} + virtual ~TestFailedCtorSubParent(); + + protected: + PTestFailedCtorSubsubParent* AllocPTestFailedCtorSubsubParent(); + + bool DeallocPTestFailedCtorSubsubParent(PTestFailedCtorSubsubParent* actor); + mozilla::ipc::IPCResult RecvSync() { return IPC_OK(); } + + virtual void ActorDestroy(ActorDestroyReason why) override; + + TestFailedCtorSubsub* mOne; + TestFailedCtorSubsub* mTwo; + TestFailedCtorSubsub* mThree; +}; + +class TestFailedCtorSubChild : public PTestFailedCtorSubChild { + friend class PTestFailedCtorSubChild; + + public: + TestFailedCtorSubChild() {} + virtual ~TestFailedCtorSubChild() {} + + protected: + PTestFailedCtorSubsubChild* AllocPTestFailedCtorSubsubChild(); + bool DeallocPTestFailedCtorSubsubChild(PTestFailedCtorSubsubChild* actor); + + 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; +}; + +} // namespace _ipdltest +} // namespace mozilla + +#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 0000000000..2694cbfdab --- /dev/null +++ b/ipc/ipdl/test/cxx/TestHangs.cpp @@ -0,0 +1,127 @@ +#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), mNumAnswerStackFrame(0) { + 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( + "_ipdltest::TestHangsParent::CleanUp", this, &TestHangsParent::CleanUp)); + + GetIPCChannel()->CloseWithTimeout(); + + return false; +} + +mozilla::ipc::IPCResult TestHangsParent::AnswerStackFrame() { + ++mNumAnswerStackFrame; + + if (mNumAnswerStackFrame == 1) { + if (CallStackFrame()) { + fail("should have timed out!"); + } + } else if (mNumAnswerStackFrame == 2) { + // 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!"); + } else { + fail("unexpected state"); + } + + return IPC_OK(); +} + +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); } + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +} // 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 0000000000..a3dfb991f5 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestHangs.h @@ -0,0 +1,75 @@ +#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 { + friend class 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; + + mozilla::ipc::IPCResult RecvNonce() { return IPC_OK(); } + + mozilla::ipc::IPCResult AnswerStackFrame(); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (AbnormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + + void CleanUp(); + + bool mDetectedHang; + int32_t mNumAnswerStackFrame; +}; + +class TestHangsChild : public PTestHangsChild { + friend class PTestHangsChild; + + public: + TestHangsChild(); + virtual ~TestHangsChild(); + + protected: + mozilla::ipc::IPCResult RecvStart() { + if (!SendNonce()) fail("sending Nonce"); + return IPC_OK(); + } + + mozilla::ipc::IPCResult AnswerStackFrame() { + if (CallStackFrame()) fail("should have failed"); + return IPC_OK(); + } + + mozilla::ipc::IPCResult AnswerHang(); + + 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 0000000000..e6715fba8e --- /dev/null +++ b/ipc/ipdl/test/cxx/TestHighestPrio.cpp @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=2 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"); +} + +mozilla::ipc::IPCResult TestHighestPrioParent::RecvMsg1() { + MOZ_ASSERT(msg_num_ == 0); + msg_num_ = 1; + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestHighestPrioParent::RecvMsg2() { + MOZ_ASSERT(msg_num_ == 1); + msg_num_ = 2; + + if (!SendStartInner()) fail("sending StartInner"); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestHighestPrioParent::RecvMsg3() { + MOZ_ASSERT(msg_num_ == 2); + msg_num_ = 3; + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestHighestPrioParent::RecvMsg4() { + MOZ_ASSERT(msg_num_ == 3); + msg_num_ = 4; + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +TestHighestPrioChild::TestHighestPrioChild() { + MOZ_COUNT_CTOR(TestHighestPrioChild); +} + +TestHighestPrioChild::~TestHighestPrioChild() { + MOZ_COUNT_DTOR(TestHighestPrioChild); +} + +mozilla::ipc::IPCResult TestHighestPrioChild::RecvStart() { + if (!SendMsg1()) fail("sending Msg1"); + + if (!SendMsg2()) fail("sending Msg2"); + + Close(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestHighestPrioChild::RecvStartInner() { + if (!SendMsg3()) fail("sending Msg3"); + + if (!SendMsg4()) fail("sending Msg4"); + + return IPC_OK(); +} + +} // 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 0000000000..34563d7708 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestHighestPrio.h @@ -0,0 +1,55 @@ +#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(); + + mozilla::ipc::IPCResult RecvMsg1(); + mozilla::ipc::IPCResult RecvMsg2(); + mozilla::ipc::IPCResult RecvMsg3(); + mozilla::ipc::IPCResult RecvMsg4(); + + 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(); + + mozilla::ipc::IPCResult RecvStart(); + mozilla::ipc::IPCResult RecvStartInner(); + + 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 0000000000..30a90e8928 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp @@ -0,0 +1,138 @@ +#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); + + gSubprocess->Destroy(); + 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", 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 + +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", 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", 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); +} + +mozilla::ipc::IPCResult TestInterruptErrorCleanupChild::AnswerError() { + _exit(0); + MOZ_CRASH("unreached"); +} + +} // 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 0000000000..9ae0493cf0 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h @@ -0,0 +1,52 @@ +#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 { + friend class PTestInterruptErrorCleanupChild; + + public: + TestInterruptErrorCleanupChild(); + virtual ~TestInterruptErrorCleanupChild(); + + protected: + mozilla::ipc::IPCResult AnswerError(); + + 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 0000000000..02b42cfbc3 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptRaces.cpp @@ -0,0 +1,169 @@ +#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()"); +} + +mozilla::ipc::IPCResult TestInterruptRacesParent::RecvStartRace() { + MessageLoop::current()->PostTask(NewNonOwningRunnableMethod( + "_ipdltest::TestInterruptRacesParent::OnRaceTime", this, + &TestInterruptRacesParent::OnRaceTime)); + return IPC_OK(); +} + +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("_ipdltest::TestInterruptRacesParent::Test2", + this, &TestInterruptRacesParent::Test2)); +} + +mozilla::ipc::IPCResult TestInterruptRacesParent::AnswerRace(bool* hasReply) { + if (mHasReply) fail("apparently the parent won the Interrupt race!"); + *hasReply = hasReply; + return IPC_OK(); +} + +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("_ipdltest::TestInterruptRacesParent::Test3", + this, &TestInterruptRacesParent::Test3)); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +void TestInterruptRacesParent::Test3() { + puts("Test 3"); + + if (!CallStackFrame3()) fail("can't set up a stack frame"); + + puts(" passed"); + + Close(); +} + +mozilla::ipc::IPCResult TestInterruptRacesParent::AnswerStackFrame3() { + if (!SendWakeup3()) fail("can't wake up the child"); + + if (!CallChild()) fail("can't set up race condition"); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestInterruptRacesParent::AnswerParent() { + mAnsweredParent = true; + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestInterruptRacesParent::RecvGetAnsweredParent( + bool* answeredParent) { + *answeredParent = mAnsweredParent; + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child +mozilla::ipc::IPCResult TestInterruptRacesChild::RecvStart() { + puts("Test 1"); + + if (!SendStartRace()) fail("problem sending StartRace()"); + + bool dontcare; + if (!CallRace(&dontcare)) fail("problem calling Race()"); + + mHasReply = true; + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestInterruptRacesChild::AnswerRace(bool* hasReply) { + if (!mHasReply) fail("apparently the child lost the Interrupt race!"); + + *hasReply = mHasReply; + + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestInterruptRacesChild::RecvWakeup() { + bool dontcare; + if (!CallRace(&dontcare)) fail("can't set up race condition"); + + mHasReply = true; + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestInterruptRacesChild::AnswerStackFrame3() { + if (!CallStackFrame3()) fail("can't set up stack frame"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestInterruptRacesChild::RecvWakeup3() { + if (!CallParent()) fail("can't set up race condition"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +} // 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 0000000000..6fa184da1a --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptRaces.h @@ -0,0 +1,104 @@ +#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 { + friend class PTestInterruptRacesParent; + + public: + TestInterruptRacesParent() + : mHasReply(false), mChildHasReply(false), mAnsweredParent(false) {} + virtual ~TestInterruptRacesParent() {} + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvStartRace(); + + mozilla::ipc::IPCResult AnswerRace(bool* hasRace); + + mozilla::ipc::IPCResult AnswerStackFrame(); + + mozilla::ipc::IPCResult AnswerStackFrame3(); + + mozilla::ipc::IPCResult AnswerParent(); + + mozilla::ipc::IPCResult RecvGetAnsweredParent(bool* answeredParent); + + 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 { + friend class PTestInterruptRacesChild; + + public: + TestInterruptRacesChild() : mHasReply(false) {} + virtual ~TestInterruptRacesChild() {} + + protected: + mozilla::ipc::IPCResult RecvStart(); + + mozilla::ipc::IPCResult AnswerRace(bool* hasRace); + + mozilla::ipc::IPCResult AnswerStackFrame(); + + mozilla::ipc::IPCResult AnswerStackFrame3(); + + mozilla::ipc::IPCResult RecvWakeup(); + + mozilla::ipc::IPCResult RecvWakeup3(); + + mozilla::ipc::IPCResult AnswerChild(); + + 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 0000000000..5482893995 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp @@ -0,0 +1,111 @@ +#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() { + gSubprocess->Destroy(); + gSubprocess = nullptr; +} + +void Done() { + passed(__FILE__); + QuitParent(); +} + +} // namespace + +TestInterruptShutdownRaceParent::TestInterruptShutdownRaceParent() { + MOZ_COUNT_CTOR(TestInterruptShutdownRaceParent); +} + +TestInterruptShutdownRaceParent::~TestInterruptShutdownRaceParent() { + MOZ_COUNT_DTOR(TestInterruptShutdownRaceParent); +} + +void TestInterruptShutdownRaceParent::Main() { + if (!SendStart()) fail("sending Start"); +} + +mozilla::ipc::IPCResult TestInterruptShutdownRaceParent::RecvStartDeath() { + // this will be ordered before the OnMaybeDequeueOne event of + // Orphan in the queue + MessageLoop::current()->PostTask(NewNonOwningRunnableMethod( + "_ipdltest::TestInterruptShutdownRaceParent::StartShuttingDown", this, + &TestInterruptShutdownRaceParent::StartShuttingDown)); + return IPC_OK(); +} + +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", DeleteSubprocess)); + + // this is ordered after the OnMaybeDequeueOne event in the queue + MessageLoop::current()->PostTask(NewRunnableFunction("Done", Done)); + + // |this| has been deleted, be mindful +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +TestInterruptShutdownRaceChild::TestInterruptShutdownRaceChild() { + MOZ_COUNT_CTOR(TestInterruptShutdownRaceChild); +} + +TestInterruptShutdownRaceChild::~TestInterruptShutdownRaceChild() { + MOZ_COUNT_DTOR(TestInterruptShutdownRaceChild); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestInterruptShutdownRaceChild::AnswerExit() { + _exit(0); + MOZ_CRASH("unreached"); +} + +} // 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 0000000000..17425fc06c --- /dev/null +++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.h @@ -0,0 +1,56 @@ +#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(); + + mozilla::ipc::IPCResult RecvStartDeath(); + + mozilla::ipc::IPCResult RecvOrphan(); + + protected: + void StartShuttingDown(); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (AbnormalShutdown != why) fail("unexpected destruction!"); + } +}; + +class TestInterruptShutdownRaceChild : public PTestInterruptShutdownRaceChild { + friend class PTestInterruptShutdownRaceChild; + + public: + TestInterruptShutdownRaceChild(); + virtual ~TestInterruptShutdownRaceChild(); + + protected: + mozilla::ipc::IPCResult RecvStart(); + + mozilla::ipc::IPCResult AnswerExit(); + + 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 0000000000..42fac919d1 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestJSON.cpp @@ -0,0 +1,113 @@ +#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(nsTArray<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 ] + // } + // ] + // + nsTArray<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); + + nsTArray<JSONVariant> tmp; + Array123(tmp); + outer.AppendElement(tmp); + + nsTArray<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)); + nsTArray<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"); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +} // 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 0000000000..eaf7516525 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestJSON.h @@ -0,0 +1,93 @@ +#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 { + friend class PTestJSONParent; + + public: + TestJSONParent() {} + virtual ~TestJSONParent() {} + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvTest(const JSONVariant& i, JSONVariant* o); + + PTestHandleParent* AllocPTestHandleParent() { + return mKid = new TestHandleParent(); + } + + bool DeallocPTestHandleParent(PTestHandleParent* actor) { + 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 { + friend class PTestJSONChild; + + public: + TestJSONChild() {} + virtual ~TestJSONChild() {} + + protected: + mozilla::ipc::IPCResult RecvStart(); + + PTestHandleChild* AllocPTestHandleChild() { + return mKid = new TestHandleChild(); + } + + bool DeallocPTestHandleChild(PTestHandleChild* actor) { + 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 0000000000..f88d8ea644 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestLatency.cpp @@ -0,0 +1,210 @@ +#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), + mWhichPong5(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()) + MOZ_CRASH( + "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()"); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestLatencyParent::RecvPong5() { + ++mWhichPong5; + + if (mWhichPong5 < 5) { + return IPC_OK(); + } + + mWhichPong5 = 0; + + 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 IPC_OK(); +} + +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), mWhichPing5(0) { + MOZ_COUNT_CTOR(TestLatencyChild); +} + +TestLatencyChild::~TestLatencyChild() { MOZ_COUNT_DTOR(TestLatencyChild); } + +mozilla::ipc::IPCResult TestLatencyChild::RecvPing() { + SendPong(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestLatencyChild::RecvPing5() { + ++mWhichPing5; + + if (mWhichPing5 < 5) { + return IPC_OK(); + } + + mWhichPing5 = 0; + + if (!SendPong5() || !SendPong5() || !SendPong5() || !SendPong5() || + !SendPong5()) + fail("sending Pong5()"); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestLatencyChild::AnswerRpc() { return IPC_OK(); } + +mozilla::ipc::IPCResult TestLatencyChild::RecvSpam() { + // no-op + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestLatencyChild::AnswerSynchro() { return IPC_OK(); } + +mozilla::ipc::IPCResult TestLatencyChild::RecvCompressedSpam( + const uint32_t& seqno) { + if (seqno <= mLastSeqno) + fail("compressed seqnos must monotonically increase"); + + mLastSeqno = seqno; + ++mNumProcessedCompressedSpams; + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestLatencyChild::AnswerSynchro2( + uint32_t* lastSeqno, uint32_t* numMessagesDispatched) { + *lastSeqno = mLastSeqno; + *numMessagesDispatched = mNumProcessedCompressedSpams; + return IPC_OK(); +} + +} // 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 0000000000..687b107808 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestLatency.h @@ -0,0 +1,107 @@ +#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 { + friend class 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: + mozilla::ipc::IPCResult RecvPong(); + mozilla::ipc::IPCResult RecvPong5(); + + 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; + uint32_t mWhichPong5; +}; + +class TestLatencyChild : public PTestLatencyChild { + friend class PTestLatencyChild; + + public: + TestLatencyChild(); + virtual ~TestLatencyChild(); + + protected: + mozilla::ipc::IPCResult RecvPing(); + mozilla::ipc::IPCResult RecvPing5(); + mozilla::ipc::IPCResult AnswerRpc(); + mozilla::ipc::IPCResult RecvSpam(); + mozilla::ipc::IPCResult AnswerSynchro(); + mozilla::ipc::IPCResult RecvCompressedSpam(const uint32_t& seqno); + mozilla::ipc::IPCResult AnswerSynchro2(uint32_t* lastSeqno, + uint32_t* numMessagesDispatched); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + QuitChild(); + } + + uint32_t mLastSeqno; + uint32_t mNumProcessedCompressedSpams; + uint32_t mWhichPing5; +}; + +} // 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 0000000000..77d5786cfa --- /dev/null +++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp @@ -0,0 +1,83 @@ +#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()"); +} + +mozilla::ipc::IPCResult TestManyChildAllocsParent::RecvDone() { + // explicitly *not* cleaning up, so we can sanity-check IPDL's + // auto-shutdown/cleanup handling + Close(); + + return IPC_OK(); +} + +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); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +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 0000000000..3da6461810 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.h @@ -0,0 +1,82 @@ +#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 { + friend class PTestManyChildAllocsParent; + + public: + TestManyChildAllocsParent(); + virtual ~TestManyChildAllocsParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvDone(); + bool DeallocPTestManyChildAllocsSubParent(PTestManyChildAllocsSubParent* __a); + PTestManyChildAllocsSubParent* AllocPTestManyChildAllocsSubParent(); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + +class TestManyChildAllocsChild : public PTestManyChildAllocsChild { + friend class PTestManyChildAllocsChild; + + public: + TestManyChildAllocsChild(); + virtual ~TestManyChildAllocsChild(); + + protected: + mozilla::ipc::IPCResult RecvGo(); + bool DeallocPTestManyChildAllocsSubChild(PTestManyChildAllocsSubChild* __a); + PTestManyChildAllocsSubChild* AllocPTestManyChildAllocsSubChild(); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + QuitChild(); + } +}; + +// do-nothing sub-protocol actors + +class TestManyChildAllocsSubParent : public PTestManyChildAllocsSubParent { + friend class PTestManyChildAllocsSubParent; + + public: + TestManyChildAllocsSubParent() {} + virtual ~TestManyChildAllocsSubParent() {} + + protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} + mozilla::ipc::IPCResult RecvHello() { return IPC_OK(); } +}; + +class TestManyChildAllocsSubChild : public PTestManyChildAllocsSubChild { + public: + TestManyChildAllocsSubChild() {} + virtual ~TestManyChildAllocsSubChild() {} +}; + +} // namespace _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 0000000000..f09bb9ca7b --- /dev/null +++ b/ipc/ipdl/test/cxx/TestMultiMgrs.cpp @@ -0,0 +1,83 @@ +#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"); +} + +mozilla::ipc::IPCResult TestMultiMgrsParent::RecvOK() { + Close(); + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +mozilla::ipc::IPCResult +TestMultiMgrsLeftChild::RecvPTestMultiMgrsBottomConstructor( + PTestMultiMgrsBottomChild* actor) { + static_cast<TestMultiMgrsChild*>(Manager())->mBottomL = actor; + return IPC_OK(); +} + +mozilla::ipc::IPCResult +TestMultiMgrsRightChild::RecvPTestMultiMgrsBottomConstructor( + PTestMultiMgrsBottomChild* actor) { + static_cast<TestMultiMgrsChild*>(Manager())->mBottomR = actor; + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +} // 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 0000000000..71826d42a7 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestMultiMgrs.h @@ -0,0 +1,222 @@ +#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 { + friend class PTestMultiMgrsLeftParent; + + public: + TestMultiMgrsLeftParent() {} + virtual ~TestMultiMgrsLeftParent() {} + + bool HasChild(TestMultiMgrsBottomParent* c) { + return ManagedPTestMultiMgrsBottomParent().Contains(c); + } + + protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} + + PTestMultiMgrsBottomParent* AllocPTestMultiMgrsBottomParent() { + return new TestMultiMgrsBottomParent(); + } + + bool DeallocPTestMultiMgrsBottomParent(PTestMultiMgrsBottomParent* actor) { + delete actor; + return true; + } +}; + +class TestMultiMgrsRightParent : public PTestMultiMgrsRightParent { + friend class PTestMultiMgrsRightParent; + + public: + TestMultiMgrsRightParent() {} + virtual ~TestMultiMgrsRightParent() {} + + bool HasChild(TestMultiMgrsBottomParent* c) { + return ManagedPTestMultiMgrsBottomParent().Contains(c); + } + + protected: + virtual void ActorDestroy(ActorDestroyReason why) override {} + + PTestMultiMgrsBottomParent* AllocPTestMultiMgrsBottomParent() { + return new TestMultiMgrsBottomParent(); + } + + bool DeallocPTestMultiMgrsBottomParent(PTestMultiMgrsBottomParent* actor) { + delete actor; + return true; + } +}; + +class TestMultiMgrsParent : public PTestMultiMgrsParent { + friend class PTestMultiMgrsParent; + + public: + TestMultiMgrsParent() {} + virtual ~TestMultiMgrsParent() {} + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvOK(); + + PTestMultiMgrsLeftParent* AllocPTestMultiMgrsLeftParent() { + return new TestMultiMgrsLeftParent(); + } + + bool DeallocPTestMultiMgrsLeftParent(PTestMultiMgrsLeftParent* actor) { + delete actor; + return true; + } + + PTestMultiMgrsRightParent* AllocPTestMultiMgrsRightParent() { + return new TestMultiMgrsRightParent(); + } + + bool DeallocPTestMultiMgrsRightParent(PTestMultiMgrsRightParent* actor) { + 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 { + friend class PTestMultiMgrsLeftChild; + + public: + TestMultiMgrsLeftChild() {} + virtual ~TestMultiMgrsLeftChild() {} + + bool HasChild(PTestMultiMgrsBottomChild* c) { + return ManagedPTestMultiMgrsBottomChild().Contains(c); + } + + protected: + virtual mozilla::ipc::IPCResult RecvPTestMultiMgrsBottomConstructor( + PTestMultiMgrsBottomChild* actor) override; + + PTestMultiMgrsBottomChild* AllocPTestMultiMgrsBottomChild() { + return new TestMultiMgrsBottomChild(); + } + + bool DeallocPTestMultiMgrsBottomChild(PTestMultiMgrsBottomChild* actor) { + delete actor; + return true; + } +}; + +class TestMultiMgrsRightChild : public PTestMultiMgrsRightChild { + friend class PTestMultiMgrsRightChild; + + public: + TestMultiMgrsRightChild() {} + virtual ~TestMultiMgrsRightChild() {} + + bool HasChild(PTestMultiMgrsBottomChild* c) { + return ManagedPTestMultiMgrsBottomChild().Contains(c); + } + + protected: + virtual mozilla::ipc::IPCResult RecvPTestMultiMgrsBottomConstructor( + PTestMultiMgrsBottomChild* actor) override; + + PTestMultiMgrsBottomChild* AllocPTestMultiMgrsBottomChild() { + return new TestMultiMgrsBottomChild(); + } + + bool DeallocPTestMultiMgrsBottomChild(PTestMultiMgrsBottomChild* actor) { + delete actor; + return true; + } +}; + +class TestMultiMgrsChild : public PTestMultiMgrsChild { + friend class PTestMultiMgrsChild; + + public: + TestMultiMgrsChild() {} + virtual ~TestMultiMgrsChild() {} + + void Main(); + + PTestMultiMgrsBottomChild* mBottomL; + PTestMultiMgrsBottomChild* mBottomR; + + protected: + mozilla::ipc::IPCResult RecvCheck(); + + PTestMultiMgrsLeftChild* AllocPTestMultiMgrsLeftChild() { + return new TestMultiMgrsLeftChild(); + } + + bool DeallocPTestMultiMgrsLeftChild(PTestMultiMgrsLeftChild* actor) { + delete actor; + return true; + } + + PTestMultiMgrsRightChild* AllocPTestMultiMgrsRightChild() { + return new TestMultiMgrsRightChild(); + } + + bool DeallocPTestMultiMgrsRightChild(PTestMultiMgrsRightChild* actor) { + 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 0000000000..3658d7dfb6 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestNestedLoops.cpp @@ -0,0 +1,78 @@ +#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(); +} + +mozilla::ipc::IPCResult 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( + "_ipdltest::TestNestedLoopsParent::BreakNestedLoop", 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 IPC_OK(); +} + +void TestNestedLoopsParent::BreakNestedLoop() { mBreakNestedLoop = true; } + +//----------------------------------------------------------------------------- +// child + +TestNestedLoopsChild::TestNestedLoopsChild() { + MOZ_COUNT_CTOR(TestNestedLoopsChild); +} + +TestNestedLoopsChild::~TestNestedLoopsChild() { + MOZ_COUNT_DTOR(TestNestedLoopsChild); +} + +mozilla::ipc::IPCResult TestNestedLoopsChild::RecvStart() { + if (!SendNonce()) fail("sending Nonce"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestNestedLoopsChild::AnswerR() { return IPC_OK(); } + +} // 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 0000000000..001b571494 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestNestedLoops.h @@ -0,0 +1,59 @@ +#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 { + friend class PTestNestedLoopsParent; + + public: + TestNestedLoopsParent(); + virtual ~TestNestedLoopsParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvNonce(); + + void BreakNestedLoop(); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + + bool mBreakNestedLoop; +}; + +class TestNestedLoopsChild : public PTestNestedLoopsChild { + friend class PTestNestedLoopsChild; + + public: + TestNestedLoopsChild(); + virtual ~TestNestedLoopsChild(); + + protected: + mozilla::ipc::IPCResult RecvStart(); + + mozilla::ipc::IPCResult AnswerR(); + + 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/TestOffMainThreadPainting.cpp b/ipc/ipdl/test/cxx/TestOffMainThreadPainting.cpp new file mode 100644 index 0000000000..7a1bf2cf87 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestOffMainThreadPainting.cpp @@ -0,0 +1,237 @@ +#include "TestOffMainThreadPainting.h" + +#include "IPDLUnitTests.h" // fail etc. +#include "mozilla/Unused.h" +#include <prinrval.h> +#include <prthread.h> + +namespace mozilla { +namespace _ipdltest { + +TestOffMainThreadPaintingParent::TestOffMainThreadPaintingParent() + : mAsyncMessages(0), mSyncMessages(0) {} + +TestOffMainThreadPaintingParent::~TestOffMainThreadPaintingParent() {} + +void TestOffMainThreadPaintingParent::Main() { + ipc::Endpoint<PTestPaintThreadParent> parentPipe; + ipc::Endpoint<PTestPaintThreadChild> childPipe; + nsresult rv = PTestPaintThread::CreateEndpoints( + base::GetCurrentProcId(), OtherPid(), &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + fail("create pipes"); + } + + mPaintActor = new TestPaintThreadParent(this); + if (!mPaintActor->Bind(std::move(parentPipe))) { + fail("bind parent pipe"); + } + + if (!SendStartTest(std::move(childPipe))) { + fail("sending Start"); + } +} + +ipc::IPCResult TestOffMainThreadPaintingParent::RecvFinishedLayout( + const uint64_t& aTxnId) { + if (!mPaintedTxn || mPaintedTxn.value() != aTxnId) { + fail("received transaction before receiving paint"); + } + mPaintedTxn = Nothing(); + mCompletedTxn = Some(aTxnId); + return IPC_OK(); +} + +void TestOffMainThreadPaintingParent::NotifyFinishedPaint( + const uint64_t& aTxnId) { + if (mCompletedTxn && mCompletedTxn.value() >= aTxnId) { + fail("received paint after receiving transaction"); + } + if (mPaintedTxn) { + fail("painted again before completing previous transaction"); + } + mPaintedTxn = Some(aTxnId); +} + +ipc::IPCResult TestOffMainThreadPaintingParent::RecvAsyncMessage( + const uint64_t& aTxnId) { + if (!mCompletedTxn || mCompletedTxn.value() != aTxnId) { + fail("sync message received out of order"); + return IPC_FAIL_NO_REASON(this); + } + mAsyncMessages++; + return IPC_OK(); +} + +ipc::IPCResult TestOffMainThreadPaintingParent::RecvSyncMessage( + const uint64_t& aTxnId) { + if (!mCompletedTxn || mCompletedTxn.value() != aTxnId) { + fail("sync message received out of order"); + return IPC_FAIL_NO_REASON(this); + } + if (mSyncMessages >= mAsyncMessages) { + fail("sync message received before async message"); + return IPC_FAIL_NO_REASON(this); + } + mSyncMessages++; + return IPC_OK(); +} + +ipc::IPCResult TestOffMainThreadPaintingParent::RecvEndTest() { + if (!mCompletedTxn || mCompletedTxn.value() != 1) { + fail("expected to complete a transaction"); + } + if (mAsyncMessages != 1) { + fail("expected to get 1 async message"); + } + if (mSyncMessages != 1) { + fail("expected to get 1 sync message"); + } + + passed("ok"); + + mPaintActor->Close(); + Close(); + return IPC_OK(); +} + +void TestOffMainThreadPaintingParent::ActorDestroy(ActorDestroyReason aWhy) { + if (aWhy != NormalShutdown) { + fail("child process aborted"); + } + QuitParent(); +} + +/************************** + * PTestLayoutThreadChild * + **************************/ + +TestOffMainThreadPaintingChild::TestOffMainThreadPaintingChild() + : mNextTxnId(1) {} + +TestOffMainThreadPaintingChild::~TestOffMainThreadPaintingChild() {} + +ipc::IPCResult TestOffMainThreadPaintingChild::RecvStartTest( + ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint) { + mPaintThread = MakeUnique<base::Thread>("PaintThread"); + if (!mPaintThread->Start()) { + return IPC_FAIL_NO_REASON(this); + } + + mPaintActor = new TestPaintThreadChild(GetIPCChannel()); + RefPtr<Runnable> task = + NewRunnableMethod<ipc::Endpoint<PTestPaintThreadChild>&&>( + "TestPaintthreadChild::Bind", mPaintActor, + &TestPaintThreadChild::Bind, std::move(aEndpoint)); + mPaintThread->message_loop()->PostTask(task.forget()); + + IssueTransaction(); + return IPC_OK(); +} + +void TestOffMainThreadPaintingChild::ActorDestroy(ActorDestroyReason aWhy) { + RefPtr<Runnable> task = NewRunnableMethod( + "TestPaintThreadChild::Close", mPaintActor, &TestPaintThreadChild::Close); + mPaintThread->message_loop()->PostTask(task.forget()); + mPaintThread = nullptr; + + QuitChild(); +} + +void TestOffMainThreadPaintingChild::ProcessingError(Result aCode, + const char* aReason) { + MOZ_CRASH("Aborting child due to IPC error"); +} + +void TestOffMainThreadPaintingChild::IssueTransaction() { + GetIPCChannel()->BeginPostponingSends(); + + uint64_t txnId = mNextTxnId++; + + // Start painting before we send the message. + RefPtr<Runnable> task = NewRunnableMethod<uint64_t>( + "TestPaintThreadChild::BeginPaintingForTxn", mPaintActor, + &TestPaintThreadChild::BeginPaintingForTxn, txnId); + mPaintThread->message_loop()->PostTask(task.forget()); + + // Simulate some gecko main thread stuff. + SendFinishedLayout(txnId); + SendAsyncMessage(txnId); + SendSyncMessage(txnId); + SendEndTest(); +} + +/************************** + * PTestPaintThreadParent * + **************************/ + +TestPaintThreadParent::TestPaintThreadParent( + TestOffMainThreadPaintingParent* aMainBridge) + : mMainBridge(aMainBridge) {} + +TestPaintThreadParent::~TestPaintThreadParent() {} + +bool TestPaintThreadParent::Bind( + ipc::Endpoint<PTestPaintThreadParent>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + return false; + } + + AddRef(); + return true; +} + +ipc::IPCResult TestPaintThreadParent::RecvFinishedPaint( + const uint64_t& aTxnId) { + mMainBridge->NotifyFinishedPaint(aTxnId); + return IPC_OK(); +} + +void TestPaintThreadParent::ActorDestroy(ActorDestroyReason aWhy) {} + +void TestPaintThreadParent::DeallocPTestPaintThreadParent() { Release(); } + +/************************* + * PTestPaintThreadChild * + *************************/ + +TestPaintThreadChild::TestPaintThreadChild(MessageChannel* aMainChannel) + : mCanSend(false), mMainChannel(aMainChannel) {} + +TestPaintThreadChild::~TestPaintThreadChild() {} + +void TestPaintThreadChild::Bind( + ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + MOZ_CRASH("could not bind paint child endpoint"); + } + AddRef(); + mCanSend = true; +} + +void TestPaintThreadChild::BeginPaintingForTxn(uint64_t aTxnId) { + MOZ_RELEASE_ASSERT(!NS_IsMainThread()); + + // Sleep for some time to simulate painting being slow. + PR_Sleep(PR_MillisecondsToInterval(500)); + SendFinishedPaint(aTxnId); + + mMainChannel->StopPostponingSends(); +} + +void TestPaintThreadChild::ActorDestroy(ActorDestroyReason aWhy) { + mCanSend = false; +} + +void TestPaintThreadChild::Close() { + MOZ_RELEASE_ASSERT(!NS_IsMainThread()); + + if (mCanSend) { + PTestPaintThreadChild::Close(); + } +} + +void TestPaintThreadChild::DeallocPTestPaintThreadChild() { Release(); } + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestOffMainThreadPainting.h b/ipc/ipdl/test/cxx/TestOffMainThreadPainting.h new file mode 100644 index 0000000000..7cb935a2ee --- /dev/null +++ b/ipc/ipdl/test/cxx/TestOffMainThreadPainting.h @@ -0,0 +1,109 @@ +#ifndef mozilla__ipdltest_TestOffMainThreadPainting_h +#define mozilla__ipdltest_TestOffMainThreadPainting_h + +#include "mozilla/Maybe.h" +#include "mozilla/_ipdltest/IPDLUnitTests.h" +#include "mozilla/_ipdltest/PTestLayoutThreadChild.h" +#include "mozilla/_ipdltest/PTestLayoutThreadParent.h" +#include "mozilla/_ipdltest/PTestPaintThreadChild.h" +#include "mozilla/_ipdltest/PTestPaintThreadParent.h" +#include "base/thread.h" + +namespace mozilla { +namespace _ipdltest { + +class TestPaintThreadChild; +class TestPaintThreadParent; + +// Analagous to LayerTransactionParent. +class TestOffMainThreadPaintingParent final : public PTestLayoutThreadParent { + public: + static bool RunTestInThreads() { return false; } + static bool RunTestInProcesses() { return true; } + + void Main(); + + MOZ_IMPLICIT TestOffMainThreadPaintingParent(); + ~TestOffMainThreadPaintingParent() override; + + ipc::IPCResult RecvFinishedLayout(const uint64_t& aTxnId); + ipc::IPCResult RecvAsyncMessage(const uint64_t& aTxnId); + ipc::IPCResult RecvSyncMessage(const uint64_t& aTxnId); + ipc::IPCResult RecvEndTest(); + void ActorDestroy(ActorDestroyReason aWhy) override; + + void NotifyFinishedPaint(const uint64_t& aTxnId); + + private: + RefPtr<TestPaintThreadParent> mPaintActor; + Maybe<uint64_t> mCompletedTxn; + Maybe<uint64_t> mPaintedTxn; + uint32_t mAsyncMessages; + uint32_t mSyncMessages; +}; + +// Analagous to LayerTransactionChild. +class TestOffMainThreadPaintingChild final : public PTestLayoutThreadChild { + public: + TestOffMainThreadPaintingChild(); + ~TestOffMainThreadPaintingChild() override; + + ipc::IPCResult RecvStartTest( + ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint); + void ActorDestroy(ActorDestroyReason aWhy) override; + void ProcessingError(Result aCode, const char* aReason) override; + + private: + void IssueTransaction(); + + private: + UniquePtr<base::Thread> mPaintThread; + RefPtr<TestPaintThreadChild> mPaintActor; + uint64_t mNextTxnId; +}; + +/**************** + * Paint Actors * + ****************/ + +class TestPaintThreadParent final : public PTestPaintThreadParent { + public: + explicit TestPaintThreadParent(TestOffMainThreadPaintingParent* aMainBridge); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestPaintThreadParent); + + bool Bind(ipc::Endpoint<PTestPaintThreadParent>&& aEndpoint); + ipc::IPCResult RecvFinishedPaint(const uint64_t& aTxnId); + void ActorDestroy(ActorDestroyReason aWhy) override; + void DeallocPTestPaintThreadParent() override; + + private: + ~TestPaintThreadParent() override; + + private: + TestOffMainThreadPaintingParent* mMainBridge; +}; + +class TestPaintThreadChild final : public PTestPaintThreadChild { + public: + explicit TestPaintThreadChild(MessageChannel* aOtherChannel); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestPaintThreadChild); + + void Bind(ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint); + void BeginPaintingForTxn(uint64_t aTxnId); + void ActorDestroy(ActorDestroyReason aWhy) override; + void DeallocPTestPaintThreadChild() override; + void Close(); + + private: + ~TestPaintThreadChild() override; + + bool mCanSend; + MessageChannel* mMainChannel; +}; + +} // namespace _ipdltest +} // namespace mozilla + +#endif // ifndef mozilla__ipdltest_TestOffMainThreadPainting_h diff --git a/ipc/ipdl/test/cxx/TestRPC.cpp b/ipc/ipdl/test/cxx/TestRPC.cpp new file mode 100644 index 0000000000..404de01aa8 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRPC.cpp @@ -0,0 +1,107 @@ +#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"); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +TestRPCChild::TestRPCChild() { MOZ_COUNT_CTOR(TestRPCChild); } + +TestRPCChild::~TestRPCChild() { MOZ_COUNT_DTOR(TestRPCChild); } + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestRPCChild::RecvTest1_NoReenter(uint32_t* aResult) { + *aResult = 400; + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestRPCChild::RecvTest2_FirstUrgent() { + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestRPCChild::RecvTest2_SecondUrgent() { + return IPC_OK(); +} + +} // 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 0000000000..907d3fde36 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRPC.h @@ -0,0 +1,60 @@ +#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(); + + mozilla::ipc::IPCResult RecvTest1_Start(uint32_t* aResult); + mozilla::ipc::IPCResult RecvTest1_InnerEvent(uint32_t* aResult); + mozilla::ipc::IPCResult RecvTest2_Start(); + mozilla::ipc::IPCResult RecvTest2_OutOfOrder(); + + 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(); + + mozilla::ipc::IPCResult RecvStart(); + mozilla::ipc::IPCResult RecvTest1_InnerQuery(uint32_t* aResult); + mozilla::ipc::IPCResult RecvTest1_NoReenter(uint32_t* aResult); + mozilla::ipc::IPCResult RecvTest2_FirstUrgent(); + mozilla::ipc::IPCResult RecvTest2_SecondUrgent(); + + 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 0000000000..76defe97a5 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp @@ -0,0 +1,102 @@ +#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"); + } +} + +mozilla::ipc::IPCResult TestRaceDeadlockParent::AnswerLose() { + return IPC_OK(); +} + +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); +} + +mozilla::ipc::IPCResult TestRaceDeadlockParent::RecvStartRace() { + if (!CallWin()) { + fail("calling Win"); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestRaceDeadlockChild::RecvStartRace() { + if (!SendStartRace()) { + fail("calling SendStartRace"); + } + if (!CallLose()) { + fail("calling Lose"); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestRaceDeadlockChild::AnswerWin() { return IPC_OK(); } + +mozilla::ipc::IPCResult TestRaceDeadlockChild::AnswerRpc() { return IPC_OK(); } + +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 0000000000..2c5617130d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRaceDeadlock.h @@ -0,0 +1,68 @@ +#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 { + friend class PTestRaceDeadlockParent; + + public: + TestRaceDeadlockParent(); + virtual ~TestRaceDeadlockParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + virtual bool ShouldContinueFromReplyTimeout() override; + + void Test1(); + + mozilla::ipc::IPCResult RecvStartRace(); + mozilla::ipc::IPCResult AnswerLose(); + + 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 { + friend class PTestRaceDeadlockChild; + + public: + TestRaceDeadlockChild(); + virtual ~TestRaceDeadlockChild(); + + protected: + mozilla::ipc::IPCResult RecvStartRace(); + + mozilla::ipc::IPCResult AnswerWin(); + + mozilla::ipc::IPCResult AnswerRpc(); + + 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 0000000000..c327b57c16 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRaceDeferral.cpp @@ -0,0 +1,84 @@ +#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"); +} + +mozilla::ipc::IPCResult TestRaceDeferralParent::AnswerLose() { + if (mProcessedLose) fail("processed Lose twice"); + mProcessedLose = true; + return IPC_OK(); +} + +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); +} + +mozilla::ipc::IPCResult TestRaceDeferralChild::RecvStartRace() { + if (!CallLose()) fail("calling Lose"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestRaceDeferralChild::AnswerWin() { return IPC_OK(); } + +mozilla::ipc::IPCResult TestRaceDeferralChild::AnswerRpc() { return IPC_OK(); } + +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 0000000000..b30264fb64 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRaceDeferral.h @@ -0,0 +1,67 @@ +#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 { + friend class PTestRaceDeferralParent; + + public: + TestRaceDeferralParent(); + virtual ~TestRaceDeferralParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + void Test1(); + + mozilla::ipc::IPCResult AnswerLose(); + + 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 { + friend class PTestRaceDeferralChild; + + public: + TestRaceDeferralChild(); + virtual ~TestRaceDeferralChild(); + + protected: + mozilla::ipc::IPCResult RecvStartRace(); + + mozilla::ipc::IPCResult AnswerWin(); + + mozilla::ipc::IPCResult AnswerRpc(); + + 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 0000000000..cda950b1a1 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp @@ -0,0 +1,94 @@ +#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"); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestRacyInterruptRepliesParent::Answer_R( + int* replyNum) { + *replyNum = ++mReplyNum; + + if (1 == *replyNum) + if (!Send_A()) fail("sending _A()"); + + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +TestRacyInterruptRepliesChild::TestRacyInterruptRepliesChild() : mReplyNum(0) { + MOZ_COUNT_CTOR(TestRacyInterruptRepliesChild); +} + +TestRacyInterruptRepliesChild::~TestRacyInterruptRepliesChild() { + MOZ_COUNT_DTOR(TestRacyInterruptRepliesChild); +} + +mozilla::ipc::IPCResult TestRacyInterruptRepliesChild::AnswerR_(int* replyNum) { + *replyNum = ++mReplyNum; + + if (1 == *replyNum) SendA_(); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +} // 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 0000000000..182c07f314 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.h @@ -0,0 +1,65 @@ +#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 { + friend class PTestRacyInterruptRepliesParent; + + public: + TestRacyInterruptRepliesParent(); + virtual ~TestRacyInterruptRepliesParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvA_(); + + mozilla::ipc::IPCResult Answer_R(int* replyNum); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + + private: + int mReplyNum; +}; + +class TestRacyInterruptRepliesChild : public PTestRacyInterruptRepliesChild { + friend class PTestRacyInterruptRepliesChild; + + public: + TestRacyInterruptRepliesChild(); + virtual ~TestRacyInterruptRepliesChild(); + + protected: + mozilla::ipc::IPCResult AnswerR_(int* replyNum); + + mozilla::ipc::IPCResult RecvChildTest(); + + mozilla::ipc::IPCResult Recv_A(); + + 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 0000000000..4224018e11 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyReentry.cpp @@ -0,0 +1,63 @@ +#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"); +} + +mozilla::ipc::IPCResult TestRacyReentryParent::AnswerE() { + if (!mRecvdE) { + mRecvdE = true; + return IPC_OK(); + } + + if (!CallH()) fail("calling H"); + + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +TestRacyReentryChild::TestRacyReentryChild() { + MOZ_COUNT_CTOR(TestRacyReentryChild); +} + +TestRacyReentryChild::~TestRacyReentryChild() { + MOZ_COUNT_DTOR(TestRacyReentryChild); +} + +mozilla::ipc::IPCResult TestRacyReentryChild::RecvStart() { + if (!CallE()) fail("calling E"); + + Close(); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestRacyReentryChild::RecvN() { + if (!CallE()) fail("calling E"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestRacyReentryChild::AnswerH() { return IPC_OK(); } + +} // 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 0000000000..e31f90c0e3 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyReentry.h @@ -0,0 +1,59 @@ +#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 { + friend class PTestRacyReentryParent; + + public: + TestRacyReentryParent(); + virtual ~TestRacyReentryParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult AnswerE(); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } + + bool mRecvdE; +}; + +class TestRacyReentryChild : public PTestRacyReentryChild { + friend class PTestRacyReentryChild; + + public: + TestRacyReentryChild(); + virtual ~TestRacyReentryChild(); + + protected: + mozilla::ipc::IPCResult RecvStart(); + + mozilla::ipc::IPCResult RecvN(); + + mozilla::ipc::IPCResult AnswerH(); + + 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 0000000000..a46db5618e --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyUndefer.cpp @@ -0,0 +1,83 @@ +#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"); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestRacyUndeferParent::AnswerRaceWinTwice() { + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestRacyUndeferParent::RecvDone() { + Close(); + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +TestRacyUndeferChild::TestRacyUndeferChild() { + MOZ_COUNT_CTOR(TestRacyUndeferChild); +} + +TestRacyUndeferChild::~TestRacyUndeferChild() { + MOZ_COUNT_DTOR(TestRacyUndeferChild); +} + +mozilla::ipc::IPCResult TestRacyUndeferChild::RecvStart() { + if (!CallSpam()) fail("calling Spam"); + + if (!SendDone()) fail("sending Done"); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestRacyUndeferChild::RecvAwakenSpam() { + if (!CallSpam()) fail("calling Spam"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestRacyUndeferChild::RecvAwakenRaceWinTwice() { + if (!CallRaceWinTwice()) fail("calling RaceWinTwice"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestRacyUndeferChild::AnswerRace() { return IPC_OK(); } + +} // 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 0000000000..1e157a541b --- /dev/null +++ b/ipc/ipdl/test/cxx/TestRacyUndefer.h @@ -0,0 +1,62 @@ +#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 { + friend class PTestRacyUndeferParent; + + public: + TestRacyUndeferParent(); + virtual ~TestRacyUndeferParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult AnswerSpam(); + + mozilla::ipc::IPCResult AnswerRaceWinTwice(); + + mozilla::ipc::IPCResult RecvDone(); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + +class TestRacyUndeferChild : public PTestRacyUndeferChild { + friend class PTestRacyUndeferChild; + + public: + TestRacyUndeferChild(); + virtual ~TestRacyUndeferChild(); + + protected: + mozilla::ipc::IPCResult RecvStart(); + + mozilla::ipc::IPCResult RecvAwakenSpam(); + mozilla::ipc::IPCResult RecvAwakenRaceWinTwice(); + + mozilla::ipc::IPCResult AnswerRace(); + + 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 0000000000..ba1920fdcc --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSanity.cpp @@ -0,0 +1,52 @@ +#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"); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +TestSanityChild::TestSanityChild() { MOZ_COUNT_CTOR(TestSanityChild); } + +TestSanityChild::~TestSanityChild() { MOZ_COUNT_DTOR(TestSanityChild); } + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +} // 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 0000000000..ca29d67672 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSanity.h @@ -0,0 +1,55 @@ +#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 { + friend class PTestSanityParent; + + public: + TestSanityParent(); + virtual ~TestSanityParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvPong(const int& one, const float& zeroPtTwoFive, + const uint8_t& dummy); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + +class TestSanityChild : public PTestSanityChild { + friend class PTestSanityChild; + + public: + TestSanityChild(); + virtual ~TestSanityChild(); + + protected: + mozilla::ipc::IPCResult RecvPing(const int& zero, const float& zeroPtFive, + const int8_t& dummy); + + 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 0000000000..5e09aa4d7e --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp @@ -0,0 +1,54 @@ +#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 0000000000..5cea09b10d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSelfManageRoot.h @@ -0,0 +1,117 @@ +#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 { + friend class PTestSelfManageParent; + + public: + MOZ_COUNTED_DEFAULT_CTOR(TestSelfManageParent) + MOZ_COUNTED_DTOR_OVERRIDE(TestSelfManageParent) + + ActorDestroyReason mWhy; + + protected: + PTestSelfManageParent* AllocPTestSelfManageParent() { + return new TestSelfManageParent(); + } + + bool DeallocPTestSelfManageParent(PTestSelfManageParent* a) { return true; } + + virtual void ActorDestroy(ActorDestroyReason why) override { mWhy = why; } +}; + +class TestSelfManageRootParent : public PTestSelfManageRootParent { + friend class PTestSelfManageRootParent; + + public: + MOZ_COUNTED_DEFAULT_CTOR(TestSelfManageRootParent) + virtual ~TestSelfManageRootParent() { + MOZ_COUNT_DTOR(TestSelfManageRootParent); + } + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + PTestSelfManageParent* AllocPTestSelfManageParent() { + return new TestSelfManageParent(); + } + + bool DeallocPTestSelfManageParent(PTestSelfManageParent* a) { return true; } + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + +//----------------------------------------------------------------------------- +// Child side + +class TestSelfManageChild : public PTestSelfManageChild { + friend class PTestSelfManageChild; + + public: + MOZ_COUNTED_DEFAULT_CTOR(TestSelfManageChild) + MOZ_COUNTED_DTOR_OVERRIDE(TestSelfManageChild) + + protected: + PTestSelfManageChild* AllocPTestSelfManageChild() { + return new TestSelfManageChild(); + } + + bool DeallocPTestSelfManageChild(PTestSelfManageChild* a) { + delete a; + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override {} +}; + +class TestSelfManageRootChild : public PTestSelfManageRootChild { + friend class PTestSelfManageRootChild; + + public: + MOZ_COUNTED_DEFAULT_CTOR(TestSelfManageRootChild) + virtual ~TestSelfManageRootChild() { + MOZ_COUNT_DTOR(TestSelfManageRootChild); + } + + void Main(); + + protected: + PTestSelfManageChild* AllocPTestSelfManageChild() { + return new TestSelfManageChild(); + } + + bool DeallocPTestSelfManageChild(PTestSelfManageChild* a) { + 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 0000000000..dcf7ae2f21 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestShmem.cpp @@ -0,0 +1,108 @@ +#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(std::move(mem), std::move(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; +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +//----------------------------------------------------------------------------- +// Child + +mozilla::ipc::IPCResult 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(std::move(mem), std::move(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 IPC_OK(); +} + +} // 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 0000000000..4ef1f4fd7b --- /dev/null +++ b/ipc/ipdl/test/cxx/TestShmem.h @@ -0,0 +1,55 @@ +#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 { + friend class PTestShmemParent; + + public: + TestShmemParent() {} + virtual ~TestShmemParent() {} + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvTake(Shmem&& mem, Shmem&& unsafe, + const size_t& expectedSize); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + +class TestShmemChild : public PTestShmemChild { + friend class PTestShmemChild; + + public: + TestShmemChild() {} + virtual ~TestShmemChild() {} + + protected: + mozilla::ipc::IPCResult RecvGive(Shmem&& mem, Shmem&& unsafe, + const size_t& expectedSize); + + 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 0000000000..502695bd88 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestShutdown.cpp @@ -0,0 +1,185 @@ +#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 + +mozilla::ipc::IPCResult 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!"); +} + +mozilla::ipc::IPCResult TestShutdownSubChild::AnswerStackFrame() { + if (!PTestShutdownSubChild::Send__delete__(this)) + fail("problem sending dtor"); + + // WATCH OUT! |this| has just deleted + + return IPC_OK(); +} + +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 0000000000..224e42496d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestShutdown.h @@ -0,0 +1,168 @@ +#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 { + friend class PTestShutdownSubParent; + + public: + explicit TestShutdownSubParent(bool expectCrash) + : mExpectCrash(expectCrash), mDeletedCount(0) {} + + virtual ~TestShutdownSubParent() { + if (2 != mDeletedCount) fail("managees outliving manager!"); + } + + protected: + mozilla::ipc::IPCResult AnswerStackFrame() { + if (!CallStackFrame()) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); + } + + PTestShutdownSubsubParent* AllocPTestShutdownSubsubParent( + const bool& expectParentDelete) { + return new TestShutdownSubsubParent(expectParentDelete); + } + + bool DeallocPTestShutdownSubsubParent(PTestShutdownSubsubParent* actor) { + delete actor; + ++mDeletedCount; + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override; + + private: + bool mExpectCrash; + int mDeletedCount; +}; + +class TestShutdownParent : public PTestShutdownParent { + friend class 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: + mozilla::ipc::IPCResult RecvSync() { return IPC_OK(); } + + PTestShutdownSubParent* AllocPTestShutdownSubParent(const bool& expectCrash) { + return new TestShutdownSubParent(expectCrash); + } + + bool DeallocPTestShutdownSubParent(PTestShutdownSubParent* actor) { + 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 { + friend class PTestShutdownSubChild; + + public: + explicit TestShutdownSubChild(bool expectCrash) : mExpectCrash(expectCrash) {} + + virtual ~TestShutdownSubChild() {} + + protected: + mozilla::ipc::IPCResult AnswerStackFrame(); + + PTestShutdownSubsubChild* AllocPTestShutdownSubsubChild( + const bool& expectParentDelete) { + return new TestShutdownSubsubChild(expectParentDelete); + } + + bool DeallocPTestShutdownSubsubChild(PTestShutdownSubsubChild* actor) { + delete actor; + return true; + } + + virtual void ActorDestroy(ActorDestroyReason why) override; + + private: + bool mExpectCrash; +}; + +class TestShutdownChild : public PTestShutdownChild { + friend class PTestShutdownChild; + + public: + TestShutdownChild() {} + virtual ~TestShutdownChild() {} + + protected: + mozilla::ipc::IPCResult RecvStart(); + + PTestShutdownSubChild* AllocPTestShutdownSubChild(const bool& expectCrash) { + return new TestShutdownSubChild(expectCrash); + } + + bool DeallocPTestShutdownSubChild(PTestShutdownSubChild* actor) { + 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 0000000000..9800ac54e7 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestStackHooks.cpp @@ -0,0 +1,122 @@ +#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()"); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +TestStackHooksChild::TestStackHooksChild() + : mOnStack(false), + mEntered(0), + mExited(0), + mIncallDepth(0), + mNumAnswerStackFrame(0) { + MOZ_COUNT_CTOR(TestStackHooksChild); +} + +TestStackHooksChild::~TestStackHooksChild() { + MOZ_COUNT_DTOR(TestStackHooksChild); +} + +namespace { +void RunTestsFn() { + static_cast<TestStackHooksChild*>(gChildActor)->RunTests(); +} +} // namespace + +mozilla::ipc::IPCResult 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", RunTestsFn)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestStackHooksChild::AnswerStackFrame() { + ++mNumAnswerStackFrame; + + if (!mOnStack) fail("missed stack notification"); + + if (1 != mIncallDepth) fail("missed EnteredCall or ExitedCall hook"); + + if (mNumAnswerStackFrame == 1) { + if (!SendAsync()) fail("sending Async()"); + } else if (mNumAnswerStackFrame == 2) { + if (!SendSync()) fail("sending Sync()"); + } else { + fail("unexpected state"); + } + + if (!mOnStack) fail("bad stack exit notification"); + + return IPC_OK(); +} + +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 0000000000..315a3c1ae5 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestStackHooks.h @@ -0,0 +1,107 @@ +#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 { + friend class PTestStackHooksParent; + + public: + TestStackHooksParent(); + virtual ~TestStackHooksParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvAsync() { + if (!mOnStack) fail("not on C++ stack?!"); + return IPC_OK(); + } + + mozilla::ipc::IPCResult RecvSync() { + if (!mOnStack) fail("not on C++ stack?!"); + return IPC_OK(); + } + + mozilla::ipc::IPCResult AnswerRpc() { + if (!mOnStack) fail("not on C++ stack?!"); + return IPC_OK(); + } + + mozilla::ipc::IPCResult AnswerStackFrame(); + + 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 { + friend class PTestStackHooksChild; + + public: + TestStackHooksChild(); + virtual ~TestStackHooksChild(); + + void RunTests(); + + protected: + mozilla::ipc::IPCResult RecvStart(); + + mozilla::ipc::IPCResult AnswerStackFrame(); + + 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; + int32_t mNumAnswerStackFrame; +}; + +} // 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 0000000000..6c21590a87 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncError.cpp @@ -0,0 +1,45 @@ +#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"); +} + +mozilla::ipc::IPCResult TestSyncErrorParent::RecvError() { + return IPC_FAIL_NO_REASON(this); +} + +//----------------------------------------------------------------------------- +// child + +TestSyncErrorChild::TestSyncErrorChild() { MOZ_COUNT_CTOR(TestSyncErrorChild); } + +TestSyncErrorChild::~TestSyncErrorChild() { + MOZ_COUNT_DTOR(TestSyncErrorChild); +} + +mozilla::ipc::IPCResult TestSyncErrorChild::RecvStart() { + if (SendError()) fail("Error() should have return false"); + + Close(); + + return IPC_OK(); +} + +} // 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 0000000000..8b84ed5e0b --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncError.h @@ -0,0 +1,61 @@ +#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 { + friend class PTestSyncErrorParent; + + public: + TestSyncErrorParent(); + virtual ~TestSyncErrorParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult RecvError(); + + 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 { + friend class PTestSyncErrorChild; + + public: + TestSyncErrorChild(); + virtual ~TestSyncErrorChild(); + + protected: + mozilla::ipc::IPCResult RecvStart(); + + 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 0000000000..f53054787b --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncHang.cpp @@ -0,0 +1,62 @@ +#include "TestSyncHang.h" +#include "base/task.h" +#include "mozilla/ipc/GeckoChildProcessHost.h" + +#include "IPDLUnitTests.h" // fail etc. + +using std::string; +using std::vector; + +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) { + gSyncHangSubprocess->Destroy(); + gSyncHangSubprocess = nullptr; +} + +void DeferredSyncHangParentShutdown() { + // ping to DeleteSubprocess + XRE_GetIOMessageLoop()->PostTask( + NewRunnableFunction("DeleteSyncHangSubprocess", 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", DeferredSyncHangParentShutdown)); + Close(); +} + +//----------------------------------------------------------------------------- +// child + +TestSyncHangChild::TestSyncHangChild() { MOZ_COUNT_CTOR(TestSyncHangChild); } + +TestSyncHangChild::~TestSyncHangChild() { MOZ_COUNT_DTOR(TestSyncHangChild); } + +mozilla::ipc::IPCResult TestSyncHangChild::RecvUnusedMessage() { + return IPC_OK(); +} + +} // 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 0000000000..911f05cd31 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncHang.h @@ -0,0 +1,50 @@ +#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 { + friend class PTestSyncHangChild; + + public: + TestSyncHangChild(); + virtual ~TestSyncHangChild(); + + protected: + mozilla::ipc::IPCResult RecvUnusedMessage(); + + 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 0000000000..4c99aa1bab --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncWakeup.cpp @@ -0,0 +1,106 @@ +#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()"); +} + +mozilla::ipc::IPCResult TestSyncWakeupParent::AnswerStackFrame() { + if (!CallStackFrame()) fail("calling StackFrame()"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestSyncWakeupParent::RecvSync2() { + if (!SendNote2()) fail("sending Note2()"); + +#if defined(OS_POSIX) + // see above + sleep(5); + puts(" (sleeping for 5 seconds. sorry!)"); +#endif + + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +TestSyncWakeupChild::TestSyncWakeupChild() : mDone(false) { + MOZ_COUNT_CTOR(TestSyncWakeupChild); +} + +TestSyncWakeupChild::~TestSyncWakeupChild() { + MOZ_COUNT_DTOR(TestSyncWakeupChild); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestSyncWakeupChild::AnswerStackFrame() { + if (!SendSync2()) fail("sending Sync()"); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestSyncWakeupChild::RecvNote2() { + mDone = true; + return IPC_OK(); +} + +} // 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 0000000000..5666307929 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncWakeup.h @@ -0,0 +1,66 @@ +#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 { + friend class PTestSyncWakeupParent; + + public: + TestSyncWakeupParent(); + virtual ~TestSyncWakeupParent(); + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return true; } + + void Main(); + + protected: + mozilla::ipc::IPCResult AnswerStackFrame(); + + mozilla::ipc::IPCResult RecvSync1(); + + mozilla::ipc::IPCResult RecvSync2(); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + +class TestSyncWakeupChild : public PTestSyncWakeupChild { + friend class PTestSyncWakeupChild; + + public: + TestSyncWakeupChild(); + virtual ~TestSyncWakeupChild(); + + protected: + mozilla::ipc::IPCResult RecvStart(); + + mozilla::ipc::IPCResult RecvNote1(); + + mozilla::ipc::IPCResult AnswerStackFrame(); + + mozilla::ipc::IPCResult RecvNote2(); + + 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/TestUniquePtrIPC.cpp b/ipc/ipdl/test/cxx/TestUniquePtrIPC.cpp new file mode 100644 index 0000000000..7d0e8dd6ca --- /dev/null +++ b/ipc/ipdl/test/cxx/TestUniquePtrIPC.cpp @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "TestUniquePtrIPC.h" + +namespace mozilla { +namespace _ipdltest { + +// --------------------------------------------------------------------------- +// PARENT PROCESS +// --------------------------------------------------------------------------- + +void TestUniquePtrIPCParent::Main() { + UniquePtr<int> a1 = MakeUnique<int>(1); + UniquePtr<DummyStruct> a2 = MakeUnique<DummyStruct>(2); + DummyStruct a3(3); + UniquePtr<int> a4; + + if (!SendTestMessage(std::move(a1), std::move(a2), a3, std::move(a4))) { + fail("failed sending UniquePtr items"); + } + + if (a1 || a2) { + fail("did not move TestMessage items in parent"); + } + + if (a4) { + fail("somehow turned null ptr into non-null by sending it"); + } + + // Pass UniquePtr by reference + UniquePtr<DummyStruct> b = MakeUnique<DummyStruct>(1); + + if (!SendTestSendReference(std::move(b))) { + fail("failed sending UniquePtr by reference"); + } + if (b) { + fail("did not move UniquePtr sent by reference"); + } +} + +// --------------------------------------------------------------------------- +// CHILD PROCESS +// --------------------------------------------------------------------------- + +mozilla::ipc::IPCResult TestUniquePtrIPCChild::RecvTestMessage( + UniquePtr<int>&& aA1, UniquePtr<DummyStruct>&& aA2, const DummyStruct& aA3, + UniquePtr<int>&& aA4) { + if ((!aA1) || (!aA2)) { + fail("TestMessage received NULL items in child"); + } + + if (aA4) { + fail("TestMessage received non-NULL when expecting NULL"); + } + + if ((*aA1 != 1) || (aA2->x() != 2) || (aA3.x() != 3)) { + fail("TestMessage received incorrect items in child"); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestUniquePtrIPCChild::RecvTestSendReference( + UniquePtr<DummyStruct>&& aA) { + if (!aA) { + fail("TestSendReference received NULL item in child"); + } + + if (*aA != 1) { + fail("TestSendReference received incorrect item in child"); + } + + Close(); + return IPC_OK(); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestUniquePtrIPC.h b/ipc/ipdl/test/cxx/TestUniquePtrIPC.h new file mode 100644 index 0000000000..3c9de33df3 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestUniquePtrIPC.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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_TestUniquePtrIPC_h +#define mozilla_TestUniquePtrIPC_h + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestUniquePtrIPCParent.h" +#include "mozilla/_ipdltest/PTestUniquePtrIPCChild.h" + +namespace mozilla { +namespace _ipdltest { + +class TestUniquePtrIPCParent : public PTestUniquePtrIPCParent { + public: + MOZ_COUNTED_DEFAULT_CTOR(TestUniquePtrIPCParent) + MOZ_COUNTED_DTOR_OVERRIDE(TestUniquePtrIPCParent) + + static bool RunTestInProcesses() { return true; } + static bool RunTestInThreads() { return false; } + + void Main(); + + bool ShouldContinueFromReplyTimeout() override { return false; } + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) { + fail("Abnormal shutdown of parent"); + } + passed("ok"); + QuitParent(); + } +}; + +class TestUniquePtrIPCChild : public PTestUniquePtrIPCChild { + public: + MOZ_COUNTED_DEFAULT_CTOR(TestUniquePtrIPCChild) + MOZ_COUNTED_DTOR_OVERRIDE(TestUniquePtrIPCChild) + + mozilla::ipc::IPCResult RecvTestMessage(UniquePtr<int>&& aA1, + UniquePtr<DummyStruct>&& aA2, + const DummyStruct& aA3, + UniquePtr<int>&& aA4); + + mozilla::ipc::IPCResult RecvTestSendReference(UniquePtr<DummyStruct>&& aA); + + virtual void ActorDestroy(ActorDestroyReason why) override { + if (NormalShutdown != why) { + fail("Abnormal shutdown of child"); + } + QuitChild(); + } +}; + +} // namespace _ipdltest +} // namespace mozilla + +#endif // mozilla_TestUniquePtrIPC_h diff --git a/ipc/ipdl/test/cxx/TestUrgency.cpp b/ipc/ipdl/test/cxx/TestUrgency.cpp new file mode 100644 index 0000000000..5845f8210d --- /dev/null +++ b/ipc/ipdl/test/cxx/TestUrgency.cpp @@ -0,0 +1,117 @@ +#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"); +} + +mozilla::ipc::IPCResult TestUrgencyParent::RecvTest1(uint32_t* value) { + if (!SendReply1(value)) fail("sending Reply1"); + if (*value != 99) fail("bad value"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestUrgencyParent::RecvTest2() { + uint32_t value; + inreply_ = true; + if (!SendReply2(&value)) fail("sending Reply2"); + inreply_ = false; + if (value != 500) fail("bad value"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestUrgencyParent::RecvTest3(uint32_t* value) { + if (inreply_) fail("nested non-urgent on top of urgent rpc"); + *value = 1000; + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestUrgencyParent::RecvFinalTest_Begin() { + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +enum { + kFirstTestBegin = 1, + kFirstTestGotReply, + kSecondTestBegin, + kSecondTestGotReply, +}; + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestUrgencyChild::RecvReply1(uint32_t* reply) { + if (test_ != kFirstTestBegin) fail("wrong test # in RecvReply1"); + + *reply = 99; + test_ = kFirstTestGotReply; + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +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 0000000000..6af73177ee --- /dev/null +++ b/ipc/ipdl/test/cxx/TestUrgency.h @@ -0,0 +1,57 @@ +#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(); + + mozilla::ipc::IPCResult RecvTest1(uint32_t* value); + mozilla::ipc::IPCResult RecvTest2(); + mozilla::ipc::IPCResult RecvTest3(uint32_t* value); + mozilla::ipc::IPCResult RecvTest4_Begin(); + mozilla::ipc::IPCResult RecvTest4_NestedSync(); + mozilla::ipc::IPCResult 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(); + + mozilla::ipc::IPCResult RecvStart(); + mozilla::ipc::IPCResult RecvReply1(uint32_t* reply); + mozilla::ipc::IPCResult 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 0000000000..e6139e38c5 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp @@ -0,0 +1,174 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=2 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( + "_ipdltest::TestUrgentHangsParent::SecondStage", 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("_ipdltest::TestUrgentHangsParent::ThirdStage", + 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("ipc::IToplevelProtocol::Close", this, + &TestUrgentHangsParent::Close), + 3000); +} + +mozilla::ipc::IPCResult TestUrgentHangsParent::RecvTest1_2() { + if (!SendTest1_3()) fail("sending Test1_3"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestUrgentHangsParent::RecvTestInner() { + mInnerCount++; + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestUrgentHangsParent::RecvTestInnerUrgent() { + mInnerUrgentCount++; + return IPC_OK(); +} + +//----------------------------------------------------------------------------- +// child + +mozilla::ipc::IPCResult TestUrgentHangsChild::RecvTest1_1() { + if (!SendTest1_2()) fail("sending Test1_2"); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestUrgentHangsChild::RecvTest1_3() { + PR_Sleep(PR_SecondsToInterval(2)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestUrgentHangsChild::RecvTest2() { + PR_Sleep(PR_SecondsToInterval(2)); + + // Should fail because of the timeout. + if (SendTestInner()) fail("sending TestInner"); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult TestUrgentHangsChild::RecvTest3() { + fail("RecvTest3 should never be called"); + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult TestUrgentHangsChild::RecvTest4_1() { + // This should fail because Test4_1 timed out and hasn't gotten a response + // yet. + if (SendTestInner()) fail("sending TestInner"); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +mozilla::ipc::IPCResult 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 IPC_OK(); +} + +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 0000000000..facda522bf --- /dev/null +++ b/ipc/ipdl/test/cxx/TestUrgentHangs.h @@ -0,0 +1,64 @@ +#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(); + + mozilla::ipc::IPCResult RecvTest1_2(); + mozilla::ipc::IPCResult RecvTestInner(); + mozilla::ipc::IPCResult 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(); + + mozilla::ipc::IPCResult RecvTest1_1(); + mozilla::ipc::IPCResult RecvTest1_3(); + mozilla::ipc::IPCResult RecvTest2(); + mozilla::ipc::IPCResult RecvTest3(); + mozilla::ipc::IPCResult RecvTest4(); + mozilla::ipc::IPCResult RecvTest4_1(); + mozilla::ipc::IPCResult RecvTest5(); + mozilla::ipc::IPCResult 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/TestIPDL.cpp b/ipc/ipdl/test/cxx/app/TestIPDL.cpp new file mode 100644 index 0000000000..3891aead62 --- /dev/null +++ b/ipc/ipdl/test/cxx/app/TestIPDL.cpp @@ -0,0 +1,24 @@ +/* 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/. */ + +#define MOZ_IPDL_TESTS +#include "mozilla/Bootstrap.h" + +#if defined(XP_WIN) +# include <windows.h> +# include "nsWindowsWMain.cpp" +#endif + +using namespace mozilla; + +int main(int argc, char** argv) { + // the first argument specifies which IPDL test case/suite to load + if (argc < 2) return 1; + + Bootstrap::UniquePtr bootstrap = GetBootstrap(); + if (!bootstrap) { + return 2; + } + return bootstrap->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 0000000000..909360062a --- /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["CC_TYPE"] == "clang-cl": + 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 0000000000..3636ec0416 --- /dev/null +++ b/ipc/ipdl/test/cxx/genIPDLUnitTests.py @@ -0,0 +1,195 @@ +# 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/. + +from __future__ import print_function, unicode_literals + +import string +import sys + + +def usage(): + print( + """ +%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]), + file=sys.stderr, + ) + 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", encoding="utf-8") + 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 0000000000..591fa269a9 --- /dev/null +++ b/ipc/ipdl/test/cxx/moz.build @@ -0,0 +1,173 @@ +# -*- 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", + "TestActorPunning.h", + "TestAsyncReturns.h", + "TestBadActor.h", + "TestCancel.h", + "TestCrashCleanup.h", + "TestDataStructures.h", + "TestDemon.h", + "TestDesc.h", + "TestEndpointBridgeMain.h", + "TestEndpointOpens.h", + "TestFailedCtor.h", + "TestHangs.h", + "TestHighestPrio.h", + "TestInterruptErrorCleanup.h", + "TestInterruptRaces.h", + "TestInterruptShutdownRace.h", + "TestJSON.h", + "TestLatency.h", + "TestManyChildAllocs.h", + "TestMultiMgrs.h", + "TestNestedLoops.h", + "TestOffMainThreadPainting.h", + "TestRaceDeadlock.h", + "TestRaceDeferral.h", + "TestRacyInterruptReplies.h", + "TestRacyReentry.h", + "TestRacyUndefer.h", + "TestRPC.h", + "TestSanity.h", + "TestSelfManageRoot.h", + "TestShmem.h", + "TestShutdown.h", + "TestStackHooks.h", + "TestSyncError.h", + "TestSyncHang.h", + "TestSyncWakeup.h", + "TestUniquePtrIPC.h", + "TestUrgency.h", + "TestUrgentHangs.h", +] + +SOURCES += [ + "TestActorPunning.cpp", + "TestAsyncReturns.cpp", + "TestBadActor.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", + "TestOffMainThreadPainting.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", + "TestUniquePtrIPC.cpp", + "TestUrgency.cpp", + "TestUrgentHangs.cpp", +] + +SOURCES += [ + "!IPDLUnitTests.cpp", + "IPDLUnitTestProcessChild.cpp", + "IPDLUnitTestSubprocess.cpp", +] + +IPDL_SOURCES += [ + "PTestActorPunning.ipdl", + "PTestActorPunningPunned.ipdl", + "PTestActorPunningSub.ipdl", + "PTestAsyncReturns.ipdl", + "PTestBadActor.ipdl", + "PTestBadActorSub.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", + "PTestLayoutThread.ipdl", + "PTestManyChildAllocs.ipdl", + "PTestManyChildAllocsSub.ipdl", + "PTestMultiMgrs.ipdl", + "PTestMultiMgrsBottom.ipdl", + "PTestMultiMgrsLeft.ipdl", + "PTestMultiMgrsRight.ipdl", + "PTestNestedLoops.ipdl", + "PTestPaintThread.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", + "PTestUniquePtrIPC.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 0000000000..af45a70faa --- /dev/null +++ b/ipc/ipdl/test/ipdl/IPDLCompile.py @@ -0,0 +1,78 @@ +import copy +import re +import os +import subprocess +import 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.specfilename| 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, + universal_newlines=True, + ) + 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, expectedError): + """Return True iff compiling self.specstring resulted in an + IPDL compiler error.""" + assert self.completed() + + errorRe = re.compile(re.escape(expectedError)) + return None is not re.search(errorRe, 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("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 0000000000..67f241775b --- /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:: + @$(PYTHON3) $(srcdir)/runtests.py \ + $(srcdir)/ok $(srcdir)/error \ + $(PYTHON3) $(topsrcdir)/ipc/ipdl/ipdl.py \ + --sync-msg-list=$(srcdir)/sync-messages.ini \ + --msg-metadata=$(srcdir)/message-metadata.ini \ + OKTESTS $(OKTESTS) \ + ERRORTESTS $(ERRORTESTS) diff --git a/ipc/ipdl/test/ipdl/error/AsyncCtorReturns.ipdl b/ipc/ipdl/test/ipdl/error/AsyncCtorReturns.ipdl new file mode 100644 index 0000000000..831d4d1159 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/AsyncCtorReturns.ipdl @@ -0,0 +1,10 @@ +//error: asynchronous ctor/dtor message `AsyncCtorReturnsManageeConstructor' declares return values + +include protocol AsyncCtorReturnsManagee; + +protocol AsyncCtorReturns { + manages AsyncCtorReturnsManagee; + +child: + async AsyncCtorReturnsManagee() returns (bool unused); +}; diff --git a/ipc/ipdl/test/ipdl/error/AsyncCtorReturnsManagee.ipdl b/ipc/ipdl/test/ipdl/error/AsyncCtorReturnsManagee.ipdl new file mode 100644 index 0000000000..f38e2bd0ea --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/AsyncCtorReturnsManagee.ipdl @@ -0,0 +1,10 @@ +//error: asynchronous ctor/dtor message `AsyncCtorReturnsManageeConstructor' declares return values + +include protocol AsyncCtorReturns; + +protocol AsyncCtorReturnsManagee { + manager AsyncCtorReturns; + +parent: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/AsyncInsideSync.ipdl b/ipc/ipdl/test/ipdl/error/AsyncInsideSync.ipdl new file mode 100644 index 0000000000..d5358d260e --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/AsyncInsideSync.ipdl @@ -0,0 +1,9 @@ +// inside_sync nested messages must be sync + +//error: inside_sync nested messages must be sync (here, message `Msg' in protocol `AsyncInsideSync') +//error: message `Msg' requires more powerful send semantics than its protocol `AsyncInsideSync' provides + +protocol AsyncInsideSync { +child: + nested(inside_sync) async Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/ForgottenManagee.ipdl b/ipc/ipdl/test/ipdl/error/ForgottenManagee.ipdl new file mode 100644 index 0000000000..35f6f02e4d --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/ForgottenManagee.ipdl @@ -0,0 +1,12 @@ +//error: |manager| declaration in protocol `ForgottenManagee' does not match any |manages| declaration in protocol `ManagerForgot' + +include protocol ManagerForgot; + +// This protocol says ManagerForgot manages it, +// but ManagerForgot does not manage it. + +protocol ForgottenManagee { + manager ManagerForgot; +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/ForgottenManager.ipdl b/ipc/ipdl/test/ipdl/error/ForgottenManager.ipdl new file mode 100644 index 0000000000..4335dc8dc7 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/ForgottenManager.ipdl @@ -0,0 +1,11 @@ +//error: |manages| declaration in protocol `ForgottenManager' does not match any |manager| declaration in protocol `ManageeForgot' + +include protocol ManageeForgot; + +// ManageeForgot should have this protocol as its manager. + +protocol ForgottenManager { + manages ManageeForgot; +child: + async ManageeForgot(); +}; diff --git a/ipc/ipdl/test/ipdl/error/InsideCpowToChild.ipdl b/ipc/ipdl/test/ipdl/error/InsideCpowToChild.ipdl new file mode 100644 index 0000000000..a28ab2c6d7 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/InsideCpowToChild.ipdl @@ -0,0 +1,9 @@ +// inside_cpow nested parent-to-child messages are verboten + +//error: inside_cpow nested parent-to-child messages are verboten (here, message `Msg' in protocol `InsideCpowToChild') +//error: message `Msg' requires more powerful send semantics than its protocol `InsideCpowToChild' provides + +protocol InsideCpowToChild { +child: + nested(inside_cpow) sync Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/IntrAsyncManagee.ipdl b/ipc/ipdl/test/ipdl/error/IntrAsyncManagee.ipdl new file mode 100644 index 0000000000..527c007ec4 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/IntrAsyncManagee.ipdl @@ -0,0 +1,9 @@ +//error: protocol `IntrAsyncManagee' requires more powerful send semantics than its manager `IntrAsyncManager' provides + +include protocol IntrAsyncManager; + +intr protocol IntrAsyncManagee { + manager IntrAsyncManager; +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/IntrAsyncManager.ipdl b/ipc/ipdl/test/ipdl/error/IntrAsyncManager.ipdl new file mode 100644 index 0000000000..7bf413ef61 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/IntrAsyncManager.ipdl @@ -0,0 +1,9 @@ +//error: protocol `IntrAsyncManagee' requires more powerful send semantics than its manager `IntrAsyncManager' provides + +include protocol IntrAsyncManagee; + +async protocol IntrAsyncManager { + manages IntrAsyncManagee; +parent: + async IntrAsyncManagee(); +}; diff --git a/ipc/ipdl/test/ipdl/error/IntrSyncManagee.ipdl b/ipc/ipdl/test/ipdl/error/IntrSyncManagee.ipdl new file mode 100644 index 0000000000..d0b1462e86 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/IntrSyncManagee.ipdl @@ -0,0 +1,9 @@ +//error: protocol `IntrSyncManagee' requires more powerful send semantics than its manager `IntrSyncManager' provides + +include protocol IntrSyncManager; + +intr protocol IntrSyncManagee { + manager IntrSyncManager; +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/IntrSyncManager.ipdl b/ipc/ipdl/test/ipdl/error/IntrSyncManager.ipdl new file mode 100644 index 0000000000..80a82ab6d8 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/IntrSyncManager.ipdl @@ -0,0 +1,9 @@ +//error: protocol `IntrSyncManagee' requires more powerful send semantics than its manager `IntrSyncManager' provides + +include protocol IntrSyncManagee; + +sync protocol IntrSyncManager { + manages IntrSyncManagee; +parent: + async IntrSyncManagee(); +}; diff --git a/ipc/ipdl/test/ipdl/error/ManageeForgot.ipdl b/ipc/ipdl/test/ipdl/error/ManageeForgot.ipdl new file mode 100644 index 0000000000..babe2f0658 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/ManageeForgot.ipdl @@ -0,0 +1,11 @@ +//error: |manages| declaration in protocol `ForgottenManager' does not match any |manager| declaration in protocol `ManageeForgot' + +include protocol ForgottenManager; + +// See ForgottenManager. This includes ForgottenManager to ensure that +// loading this file fails. + +protocol ManageeForgot { +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/ManagerForgot.ipdl b/ipc/ipdl/test/ipdl/error/ManagerForgot.ipdl new file mode 100644 index 0000000000..00c08ebe87 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/ManagerForgot.ipdl @@ -0,0 +1,11 @@ +//error: |manager| declaration in protocol `ForgottenManagee' does not match any |manages| declaration in protocol `ManagerForgot' + +include protocol ForgottenManagee; + +// See ForgottenManagee.ipdl. This includes ForgottenManagee to +// ensure that loading this file fails. + +protocol ManagerForgot { +child: + async Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/Nullable.ipdl b/ipc/ipdl/test/ipdl/error/Nullable.ipdl new file mode 100644 index 0000000000..e1dd6fd55e --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/Nullable.ipdl @@ -0,0 +1,6 @@ +//error: `nullable' qualifier for type `int' makes no sense + +protocol PNullable { +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 0000000000..41c8b190b7 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/Nullable2.ipdl @@ -0,0 +1,10 @@ +//error: `nullable' qualifier for type `int' makes no sense + +union Union { + nullable int; +}; + +protocol Nullable2 { +child: + async Msg(Union i); +}; diff --git a/ipc/ipdl/test/ipdl/error/PBadArrayBase.ipdl b/ipc/ipdl/test/ipdl/error/PBadArrayBase.ipdl new file mode 100644 index 0000000000..4afdcb4c8e --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/PBadArrayBase.ipdl @@ -0,0 +1,7 @@ +//error: argument typename `X' of message `Test' has not been declared + +protocol PBadArrayBase { +child: + async Test(X[] x); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/PBadNestedManagee.ipdl b/ipc/ipdl/test/ipdl/error/PBadNestedManagee.ipdl new file mode 100644 index 0000000000..43ee446bd7 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/PBadNestedManagee.ipdl @@ -0,0 +1,9 @@ +//error: protocol `PBadNestedManagee' requires more powerful send semantics than its manager `PBadNestedManager' provides + +include protocol PBadNestedManager; + +nested(upto inside_sync) async protocol PBadNestedManagee { + manager PBadNestedManager; +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/PBadNestedManager.ipdl b/ipc/ipdl/test/ipdl/error/PBadNestedManager.ipdl new file mode 100644 index 0000000000..84a4fe491e --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/PBadNestedManager.ipdl @@ -0,0 +1,9 @@ +//error: protocol `PBadNestedManagee' requires more powerful send semantics than its manager `PBadNestedManager' provides + +include protocol PBadNestedManagee; + +nested(upto not) async protocol PBadNestedManager { + manages PBadNestedManagee; +parent: + async PBadNestedManagee(); +}; diff --git a/ipc/ipdl/test/ipdl/error/PBadUniquePtrBase.ipdl b/ipc/ipdl/test/ipdl/error/PBadUniquePtrBase.ipdl new file mode 100644 index 0000000000..b93b20742d --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/PBadUniquePtrBase.ipdl @@ -0,0 +1,7 @@ +//error: argument typename `UndeclaredType' of message `Test' has not been declared + +protocol PBadUniquePtrBase { +child: + async Test(UniquePtr<UndeclaredType> x); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/PDouble.ipdl b/ipc/ipdl/test/ipdl/error/PDouble.ipdl new file mode 100644 index 0000000000..e08ff1d063 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/PDouble.ipdl @@ -0,0 +1,11 @@ +//error: Trying to load `PDouble' from a file when we'd already seen it in file + +// This will load extra/PDouble.ipdl because extra/ is earlier +// in the list of include directories than the current working +// directory. Loading the same protocol from two files is +// obviously bad. +include protocol PDouble; + +protocol PDouble { +child: async Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/PInconsistentMoveOnly.ipdl b/ipc/ipdl/test/ipdl/error/PInconsistentMoveOnly.ipdl new file mode 100644 index 0000000000..0efd3c840c --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/PInconsistentMoveOnly.ipdl @@ -0,0 +1,9 @@ +//error: inconsistent moveonly status of type `mozilla::ipc::SomeMoveonlyType` + +using moveonly class mozilla::ipc::SomeMoveonlyType from "SomeFile.h"; +using class mozilla::ipc::SomeMoveonlyType from "SomeFile.h"; + +protocol PInconsistentMoveOnly { +child: + async SomeMessage(); +}; diff --git a/ipc/ipdl/test/ipdl/error/PUniquePtrRecursive.ipdl b/ipc/ipdl/test/ipdl/error/PUniquePtrRecursive.ipdl new file mode 100644 index 0000000000..83c9459ec6 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/PUniquePtrRecursive.ipdl @@ -0,0 +1,5 @@ +//error: bad syntax near `UniquePtr' + +protocol PUniquePtrRecursive { +child: async Msg( UniquePtr< UniquePtr<int> > aa); +}; diff --git a/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecStruct.ipdl b/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecStruct.ipdl new file mode 100644 index 0000000000..c9a54b22b2 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecStruct.ipdl @@ -0,0 +1,9 @@ +//error: struct `X' is only partially defined + +struct X { + UniquePtr<X> x; +}; + +protocol PUniquePtrSelfRecStruct { +child: async Msg(UniquePtr<X> aa); +}; diff --git a/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecUnion.ipdl b/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecUnion.ipdl new file mode 100644 index 0000000000..5dba38263f --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/PUniquePtrSelfRecUnion.ipdl @@ -0,0 +1,7 @@ +//error: union `X' is only partially defined + +union X { UniquePtr<X>; }; + +protocol PUniquePtrSelfRecUnion { +child: async Msg(UniquePtr<X> aa); +}; diff --git a/ipc/ipdl/test/ipdl/error/PasyncMessageListed.ipdl b/ipc/ipdl/test/ipdl/error/PasyncMessageListed.ipdl new file mode 100644 index 0000000000..6a3cdad0ee --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/PasyncMessageListed.ipdl @@ -0,0 +1,6 @@ +//error: IPC message PasyncMessageListed::Msg is async, can be delisted + +protocol PasyncMessageListed { +parent: + async Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/SyncAsyncManagee.ipdl b/ipc/ipdl/test/ipdl/error/SyncAsyncManagee.ipdl new file mode 100644 index 0000000000..166881ae1e --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/SyncAsyncManagee.ipdl @@ -0,0 +1,9 @@ +//error: protocol `SyncAsyncManagee' requires more powerful send semantics than its manager `SyncAsyncManager' provides + +include protocol SyncAsyncManager; + +sync protocol SyncAsyncManagee { + manager SyncAsyncManager; +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/SyncAsyncManager.ipdl b/ipc/ipdl/test/ipdl/error/SyncAsyncManager.ipdl new file mode 100644 index 0000000000..9dbc7a6318 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/SyncAsyncManager.ipdl @@ -0,0 +1,9 @@ +//error: protocol `SyncAsyncManagee' requires more powerful send semantics than its manager `SyncAsyncManager' provides + +include protocol SyncAsyncManagee; + +async protocol SyncAsyncManager { + manages SyncAsyncManagee; +parent: + async SyncAsyncManagee(); +}; 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 0000000000..f5027e2299 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/array_Recursive.ipdl @@ -0,0 +1,5 @@ +//error: bad syntax near `[' + +protocol array_Recursive { +child: async 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 0000000000..7a45d8066c --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl @@ -0,0 +1,9 @@ +//error: can't locate include file `IDONTEXIST.ipdl' + +include protocol IDONTEXIST; + +// error: nonexistent protocol ^^^ + +protocol badProtocolInclude { +child: Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/compressCtor.ipdl b/ipc/ipdl/test/ipdl/error/compressCtor.ipdl new file mode 100644 index 0000000000..cc8d7e3d7c --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/compressCtor.ipdl @@ -0,0 +1,11 @@ +//error: destructor messages can't use compression (here, in protocol `compressCtorManagee') +//error: constructor messages can't use compression (here, in protocol `compressCtor') + +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 0000000000..23586fe104 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl @@ -0,0 +1,11 @@ +//error: constructor messages can't use compression (here, in protocol `compressCtor') +//error: destructor messages can't use compression (here, in protocol `compressCtorManagee') + +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 0000000000..b223dab783 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl @@ -0,0 +1,9 @@ +//error: ctor for protocol `conflictProtocolMsg', which is not managed by protocol `conflictProtocolMsg' + +protocol conflictProtocolMsg { + + // it's an error to re-use the protocol name as a message ID; these + // are reserved +child: async 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 0000000000..dd27586d8b --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl @@ -0,0 +1,15 @@ +//error: cycle(s) detected in manager/manages hierarchy: `cyclecheck_Parent -> cyclecheck_Child -> cyclecheck_Grandchild -> cyclecheck_Parent' +//error: |manages| declaration in protocol `cyclecheck_Grandchild' does not match any |manager| declaration in protocol `cyclecheck_Parent' + +include protocol cyclecheck_Parent; +include protocol cyclecheck_Grandchild; + +protocol cyclecheck_Child { + manager cyclecheck_Parent; + manages cyclecheck_Grandchild; + +child: + async 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 0000000000..9152a56a16 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl @@ -0,0 +1,15 @@ +//error: cycle(s) detected in manager/manages hierarchy: `cyclecheck_Parent -> cyclecheck_Child -> cyclecheck_Grandchild -> cyclecheck_Parent' +//error: |manages| declaration in protocol `cyclecheck_Grandchild' does not match any |manager| declaration in protocol `cyclecheck_Parent' + +include protocol cyclecheck_Child; +include protocol cyclecheck_Parent; + +protocol cyclecheck_Grandchild { + manager cyclecheck_Child; + manages cyclecheck_Parent; + +child: + async 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 0000000000..f2eb060641 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl @@ -0,0 +1,12 @@ +//error: cycle(s) detected in manager/manages hierarchy: `cyclecheck_Parent -> cyclecheck_Child -> cyclecheck_Grandchild -> cyclecheck_Parent' + +include protocol cyclecheck_Child; + +protocol cyclecheck_Parent { + manages cyclecheck_Child; + +child: + async 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 0000000000..51833489be --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl @@ -0,0 +1,10 @@ +//error: lexically invalid characters `~SomeMsg(); + +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 0000000000..c6249c280b --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/empty.ipdl @@ -0,0 +1 @@ +//error: bad syntax near `???'
\ No newline at end of file diff --git a/ipc/ipdl/test/ipdl/error/extra/PDouble.ipdl b/ipc/ipdl/test/ipdl/error/extra/PDouble.ipdl new file mode 100644 index 0000000000..2efae8fff4 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/extra/PDouble.ipdl @@ -0,0 +1,5 @@ +// This is a valid file. + +protocol PDouble { +child: async Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/inconsistentRC.ipdl b/ipc/ipdl/test/ipdl/error/inconsistentRC.ipdl new file mode 100644 index 0000000000..8ce0618a83 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/inconsistentRC.ipdl @@ -0,0 +1,9 @@ +//error: inconsistent refcounted status of type `mozilla::ipc::SomeRefcountedType` + +using refcounted class mozilla::ipc::SomeRefcountedType from "SomeFile.h"; +using class mozilla::ipc::SomeRefcountedType from "SomeFile.h"; + +protocol inconsistentRC { +child: + async SomeMessage(); +}; diff --git a/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl b/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl new file mode 100644 index 0000000000..f76a0620df --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl @@ -0,0 +1,9 @@ +//error: message `foo' in protocol `intrMessageCompress' requests compression but is not async +//error: message `bar' in protocol `intrMessageCompress' requests compression but is not async + +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 0000000000..a00629cbca --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/lex1.ipdl @@ -0,0 +1,2 @@ +//error: bad syntax near `slkdjfl' +slkdjfl*&^* diff --git a/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl b/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl new file mode 100644 index 0000000000..4540c2c2f6 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl @@ -0,0 +1,10 @@ +//error: top-level protocol `manageSelfToplevel' cannot manage itself + +protocol manageSelfToplevel { + manager manageSelfToplevel; + manages manageSelfToplevel; + +child: + async manageSelfToplevel(); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl b/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl new file mode 100644 index 0000000000..41bbd76689 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl @@ -0,0 +1,7 @@ +//error: destructor declaration `__delete__(...)' required for managed protocol `managedNoDtor' +include protocol managerNoDtor; + +protocol managedNoDtor { + manager managerNoDtor; + // empty +}; diff --git a/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl b/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl new file mode 100644 index 0000000000..0417cc5ace --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl @@ -0,0 +1,11 @@ +//error: destructor declaration `__delete__(...)' required for managed protocol `managedNoDtor' + +include protocol managedNoDtor; + +protocol managerNoDtor { + manages managedNoDtor; + +parent: + async managedNoDtor(); + // error: no ctor defined +}; diff --git a/ipc/ipdl/test/ipdl/error/maybe_Recursive.ipdl b/ipc/ipdl/test/ipdl/error/maybe_Recursive.ipdl new file mode 100644 index 0000000000..b039924199 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/maybe_Recursive.ipdl @@ -0,0 +1,5 @@ +//error: bad syntax near `?' + +protocol maybe_Recursive { +child: async Msg(int?? aa); +}; diff --git a/ipc/ipdl/test/ipdl/error/maybe_SelfRecStruct.ipdl b/ipc/ipdl/test/ipdl/error/maybe_SelfRecStruct.ipdl new file mode 100644 index 0000000000..a13cdc3647 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/maybe_SelfRecStruct.ipdl @@ -0,0 +1,9 @@ +//error: struct `X' is only partially defined + +struct X { + X? x; +}; + +protocol maybe_SelfRecStruct { +child: async Msg(X? aa); +}; diff --git a/ipc/ipdl/test/ipdl/error/maybe_SelfRecUnion.ipdl b/ipc/ipdl/test/ipdl/error/maybe_SelfRecUnion.ipdl new file mode 100644 index 0000000000..3cd5cde22a --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/maybe_SelfRecUnion.ipdl @@ -0,0 +1,7 @@ +//error: union `X' is only partially defined + +union X { X?; }; + +protocol maybe_SelfRecUnion { +child: async Msg(X? aa); +}; diff --git a/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl b/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl new file mode 100644 index 0000000000..199bb089c3 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl @@ -0,0 +1,5 @@ +//error: missing message direction + +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 0000000000..805a9f905e --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl @@ -0,0 +1,10 @@ +//error: manager `multimanDupMgrsMgr' appears multiple times + +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 0000000000..ff96fe91a3 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl @@ -0,0 +1,11 @@ +//error: manager `multimanDupMgrsMgr' appears multiple times + +include protocol multimanDupMgrs; + +protocol multimanDupMgrsMgr { + manages multimanDupMgrs; + +child: + async 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 0000000000..24dafb8163 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl @@ -0,0 +1,10 @@ +//error: protocol `Starsky' referenced as |manager| of `multimanNonexistentMgrs' has not been declared +//error: protocol `Hutch' referenced as |manager| of `multimanNonexistentMgrs' has not been declared + +protocol multimanNonexistentMgrs { + 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 0000000000..9df16543bf --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl @@ -0,0 +1,24 @@ +//error: struct `X' is only partially defined +//error: struct `Y' is only partially defined +//error: struct `Z' is only partially defined + +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 0000000000..cd193f9f29 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl @@ -0,0 +1,24 @@ +//error: struct `X' is only partially defined +//error: union `Y' is only partially defined +//error: struct `Z' is only partially defined + +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 0000000000..bc36cead42 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl @@ -0,0 +1,7 @@ +//error: top-level protocol `noEmptyToplevel' cannot be empty + +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 0000000000..8351adf4cc --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh @@ -0,0 +1,6 @@ +//error: can't define a protocol in a header. Do it in a protocol spec instead. + +protocol noProtocolInHeader { +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl b/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl new file mode 100644 index 0000000000..cb17c60a40 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl @@ -0,0 +1,7 @@ +//error: bad syntax near `Foo.ipdl' + +include protocol "Foo.ipdl"; + +protocol oldIncludeSyntax { +child: async __delete__(X x); +}; diff --git a/ipc/ipdl/test/ipdl/error/parser.ipdl b/ipc/ipdl/test/ipdl/error/parser.ipdl new file mode 100644 index 0000000000..8bfd63546a --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/parser.ipdl @@ -0,0 +1,3 @@ +//error: bad syntax near `???' + +protocol parser { diff --git a/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl b/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl new file mode 100644 index 0000000000..1247aa41cf --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl @@ -0,0 +1,12 @@ +//error: message name `Msg' already declared as `MessageType' +//error: redeclaration of symbol `Msg', first declared at + +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 0000000000..ccdcbbe0d1 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl @@ -0,0 +1,9 @@ +//error: redeclaration of symbol `f', first declared at + +sync protocol redeclParamReturn { + + // it's an error to name a parameter with the same id as a return + +parent: async Msg(int f) returns (bool f); + +}; diff --git a/ipc/ipdl/test/ipdl/error/redeclScope.ipdlh b/ipc/ipdl/test/ipdl/error/redeclScope.ipdlh new file mode 100644 index 0000000000..a64439b599 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/redeclScope.ipdlh @@ -0,0 +1,12 @@ +//error: redeclaration of symbol `Foo', first declared at + +struct Foo { + bool b; +}; + +struct Bar { + // This should produce an error saying that Foo is a redeclaration, + // even though the initial declaration was in a different frame of + // the symbol table. + bool Foo; +}; diff --git a/ipc/ipdl/test/ipdl/error/shmem.ipdl b/ipc/ipdl/test/ipdl/error/shmem.ipdl new file mode 100644 index 0000000000..b2dda1adf7 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/shmem.ipdl @@ -0,0 +1,8 @@ +//error: redeclaration of symbol `Shmem', first declared at +//error: redeclaration of symbol `mozilla::ipc::Shmem', first declared at + +using class mozilla::ipc::Shmem from "mozilla/ipc/Shmem.h"; // redeclaration + +protocol shmem { +child: async Msg(Shmem s); +}; diff --git a/ipc/ipdl/test/ipdl/error/structRedecl.ipdl b/ipc/ipdl/test/ipdl/error/structRedecl.ipdl new file mode 100644 index 0000000000..03a5557877 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/structRedecl.ipdl @@ -0,0 +1,10 @@ +//error: redeclaration of symbol `a', first declared at + +struct Redecl { + int a; + double a; +}; + +protocol structRedecl { +child: async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl b/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl new file mode 100644 index 0000000000..ac92edee23 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl @@ -0,0 +1,9 @@ +//error: field `i' of struct `S' has unknown type `Foobers' + +struct S { + Foobers i; +}; + +protocol structUnknownField { +child: async __delete__(S s); +}; diff --git a/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl b/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl new file mode 100644 index 0000000000..72c9ef40ee --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl @@ -0,0 +1,6 @@ +//error: message `foo' in protocol `syncMessageCompress' requests compression but is not async + +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 0000000000..bd6c8bc5b1 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl @@ -0,0 +1,8 @@ +//error: sync parent-to-child messages are verboten (here, message `Msg' in protocol `syncParentToChild') + +intr protocol syncParentToChild { + + // can't declare sync parent-to-child messages +child: sync Msg(); + +}; diff --git a/ipc/ipdl/test/ipdl/error/tooWeakIntrAsync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakIntrAsync.ipdl new file mode 100644 index 0000000000..b85b5af600 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/tooWeakIntrAsync.ipdl @@ -0,0 +1,9 @@ +//error: message `Msg' requires more powerful send semantics than its protocol `tooWeakIntrAsync' provides + +protocol tooWeakIntrAsync { + + // it's an error to declare an async protocol with an intr message + +parent: intr Msg(); + +}; diff --git a/ipc/ipdl/test/ipdl/error/tooWeakIntrSync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakIntrSync.ipdl new file mode 100644 index 0000000000..9137586c90 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/tooWeakIntrSync.ipdl @@ -0,0 +1,8 @@ +//error: message `Msg' requires more powerful send semantics than its protocol `tooWeakIntrSync' provides + +sync protocol tooWeakIntrSync { + + // it's an error to declare a sync protocol with an interrupt message +parent: + intr Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl b/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl new file mode 100644 index 0000000000..49e4d3b75b --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl @@ -0,0 +1,9 @@ +//error: message `Msg' requires more powerful send semantics than its protocol `tooWeakSyncAsync' provides + +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/twoprotocols.ipdl b/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl new file mode 100644 index 0000000000..7e2a51a61a --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl @@ -0,0 +1,11 @@ +// it's an error to define two protocols in the same file + +//error: only one protocol definition per file + +protocol p1 { +child: async Msg(); +}; + +protocol p2 { +child: async Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl b/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl new file mode 100644 index 0000000000..3f93e747e1 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl @@ -0,0 +1,7 @@ +//error: argument typename `FARGLEGARGLE' of message `Msg' has not been declared + +protocol undeclParamType { + +child: async 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 0000000000..cddf57267f --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl @@ -0,0 +1,8 @@ +//error: protocol `undeclared', managed by `undeclProtocol', has not been declared + +protocol undeclProtocol { + manages undeclared; + +child: + async undeclared(); +}; diff --git a/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl b/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl new file mode 100644 index 0000000000..2f353a1957 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl @@ -0,0 +1,7 @@ +//error: argument typename `FARGLEGARGLE' of message `Msg' has not been declared + +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 0000000000..5ede1d8299 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl @@ -0,0 +1,11 @@ +//error: struct `X' is only partially defined +//error: struct `Y' is only partially defined +//error: struct `Z' is only partially defined + +struct X { Y y; }; +struct Y { Z z; }; +struct Z { X x; }; + +protocol undefMutualRecStruct { +child: async __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 0000000000..0e88c126e0 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl @@ -0,0 +1,11 @@ +//error: struct `X' is only partially defined +//error: union `Y' is only partially defined +//error: struct `Z' is only partially defined + +struct X { Y y; }; +union Y { Z; }; +struct Z { X x; }; + +protocol undefMutualRecStructUnion { +child: async __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 0000000000..3fc99bbb95 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl @@ -0,0 +1,11 @@ +//error: union `X' is only partially defined +//error: union `Y' is only partially defined +//error: union `Z' is only partially defined + +union X { Y; }; +union Y { Z; }; +union Z { X; }; + +protocol undefMutualRecUnion { +child: async __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 0000000000..15fc45abcd --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl @@ -0,0 +1,7 @@ +//error: struct `X' is only partially defined + +struct X { X x; }; + +protocol undefSelfRecStruct { +child: async __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 0000000000..951f916c57 --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl @@ -0,0 +1,7 @@ +//error: union `X' is only partially defined + +union X { X; }; + +protocol undefSelfRecUnion { +child: async __delete__(X x); +}; diff --git a/ipc/ipdl/test/ipdl/error/unknownIntrMessage.ipdl b/ipc/ipdl/test/ipdl/error/unknownIntrMessage.ipdl new file mode 100644 index 0000000000..6d668f01ea --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/unknownIntrMessage.ipdl @@ -0,0 +1,6 @@ +//error: Unknown sync IPC message unknownIntrMessage::Msg + +intr protocol unknownIntrMessage { +parent: + intr Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/error/unknownSyncMessage.ipdl b/ipc/ipdl/test/ipdl/error/unknownSyncMessage.ipdl new file mode 100644 index 0000000000..be2cfad0cc --- /dev/null +++ b/ipc/ipdl/test/ipdl/error/unknownSyncMessage.ipdl @@ -0,0 +1,6 @@ +//error: Unknown sync IPC message unknownSyncMessage::Msg + +sync protocol unknownSyncMessage { +parent: + sync Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/message-metadata.ini b/ipc/ipdl/test/ipdl/message-metadata.ini new file mode 100644 index 0000000000..02ec1a992f --- /dev/null +++ b/ipc/ipdl/test/ipdl/message-metadata.ini @@ -0,0 +1,2 @@ +# The IPDL parser expects this file to exist, even though we don't +# need it for parsing tests, so here's an empty file.
\ No newline at end of file diff --git a/ipc/ipdl/test/ipdl/moz.build b/ipc/ipdl/test/ipdl/moz.build new file mode 100644 index 0000000000..568f361a54 --- /dev/null +++ b/ipc/ipdl/test/ipdl/moz.build @@ -0,0 +1,5 @@ +# -*- 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/MutRecHeader1.ipdlh b/ipc/ipdl/test/ipdl/ok/MutRecHeader1.ipdlh new file mode 100644 index 0000000000..56135b2197 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/MutRecHeader1.ipdlh @@ -0,0 +1,34 @@ +include MutRecHeader2; + +/* MutRecHeader1 (H1) includes MutRecHeader2 (H2), and uses a struct from H2. + H2 includes MutRecHeader3 (H3). + H3 includes H1. + +When type checking H1, GatherDecls::visitInclude will recursively +cause us to first check H2, which in turn will cause us to first check +H3. + +H3 only includes H1, so when we check it, we do not have any +declarations from H2 in the context. There used to be code in +GatherDecls::visitTranslationUnit that would, as part of the "second +pass", check the validity of all included structures. This would check +Struct1, and fail, because Struct2 is not declared. + +Fundamentally, it doesn't make sense to check anything declared in an +included file in the context of the file that included it. + +Note that this error did not show up when either H2 or H3 was +checked. This is because in those cases we are not in the middle of +checking H1 when we check H3, so we end up fully checking H1 before we +get to the end of checking H3. This means the "visited" tag gets put +on Struct1 before we get to the end of that troublesome block of code +in visitTranslationUnit, and visitStructDecl doesn't do anything if +that tag is set, so we don't end up actually checking H1 in the +context of H3. + +*/ + +struct Struct1 +{ + Struct2 b; +}; diff --git a/ipc/ipdl/test/ipdl/ok/MutRecHeader2.ipdlh b/ipc/ipdl/test/ipdl/ok/MutRecHeader2.ipdlh new file mode 100644 index 0000000000..142979dc45 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/MutRecHeader2.ipdlh @@ -0,0 +1,8 @@ +include MutRecHeader3; + +// See MutRecHeader1.ipdlh for explanation. + +struct Struct2 +{ + bool b; +}; diff --git a/ipc/ipdl/test/ipdl/ok/MutRecHeader3.ipdlh b/ipc/ipdl/test/ipdl/ok/MutRecHeader3.ipdlh new file mode 100644 index 0000000000..2e303ae444 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/MutRecHeader3.ipdlh @@ -0,0 +1,8 @@ +include MutRecHeader1; + +// See MutRecHeader1.ipdlh for explanation. + +struct Struct3 +{ + bool b; +}; diff --git a/ipc/ipdl/test/ipdl/ok/PAsyncReturn.ipdl b/ipc/ipdl/test/ipdl/ok/PAsyncReturn.ipdl new file mode 100644 index 0000000000..89c2a7c647 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PAsyncReturn.ipdl @@ -0,0 +1,8 @@ +// Async messages are not allowed to return values. + +//error: asynchronous message `Msg' declares return values + +protocol PAsyncReturn { +child: + async Msg() returns(int32_t aNumber); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PDelete.ipdl b/ipc/ipdl/test/ipdl/ok/PDelete.ipdl new file mode 100644 index 0000000000..f2f85fd7fa --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PDelete.ipdl @@ -0,0 +1,8 @@ +include protocol PDeleteSub; + +sync protocol PDelete { + manages PDeleteSub; + +child: + async PDeleteSub(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PDeleteSub.ipdl b/ipc/ipdl/test/ipdl/ok/PDeleteSub.ipdl new file mode 100644 index 0000000000..12b4c677eb --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PDeleteSub.ipdl @@ -0,0 +1,10 @@ +include protocol PDelete; + +sync protocol PDeleteSub { + manager PDelete; + +parent: + sync __delete__(int x) returns (double d); + +}; + diff --git a/ipc/ipdl/test/ipdl/ok/PEndpointDecl.ipdl b/ipc/ipdl/test/ipdl/ok/PEndpointDecl.ipdl new file mode 100644 index 0000000000..25f58f3f63 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PEndpointDecl.ipdl @@ -0,0 +1,18 @@ + +// Basic test that Endpoint types are declared for protocols, within +// that protocol. + +struct Whatever { + Endpoint<PEndpointDeclParent> par; + Endpoint<PEndpointDeclChild> chi; +}; + +namespace mozilla { + +protocol PEndpointDecl { + child: + async Message(Endpoint<PEndpointDeclParent> aEndpointParent, + Endpoint<PEndpointDeclChild> aEndpointChild); +}; + +} diff --git a/ipc/ipdl/test/ipdl/ok/PEndpointUse.ipdl b/ipc/ipdl/test/ipdl/ok/PEndpointUse.ipdl new file mode 100644 index 0000000000..0c776b359c --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PEndpointUse.ipdl @@ -0,0 +1,9 @@ +include protocol PEndpointDecl; + +// Basic test that Endpoint types are declared for included protocols. + +protocol PEndpointUse { + child: + async Message(Endpoint<PEndpointDeclParent> aEndpointParent, + Endpoint<PEndpointDeclChild> aEndpointChild); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PManagedEndpointDecl.ipdl b/ipc/ipdl/test/ipdl/ok/PManagedEndpointDecl.ipdl new file mode 100644 index 0000000000..13eb10fcc4 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PManagedEndpointDecl.ipdl @@ -0,0 +1,23 @@ +include protocol PManagedEndpointManager; + +// Basic test that ManagedEndpoint types are declared for protocols, within +// that protocol. + +struct ManagedWhatever { + ManagedEndpoint<PManagedEndpointDeclParent> par; + ManagedEndpoint<PManagedEndpointDeclChild> chi; +}; + +namespace mozilla { + +protocol PManagedEndpointDecl { + manager PManagedEndpointManager; + +child: + async Message(ManagedEndpoint<PManagedEndpointDeclParent> aEndpointParent, + ManagedEndpoint<PManagedEndpointDeclChild> aEndpointChild); + + async __delete__(); +}; + +} diff --git a/ipc/ipdl/test/ipdl/ok/PManagedEndpointManager.ipdl b/ipc/ipdl/test/ipdl/ok/PManagedEndpointManager.ipdl new file mode 100644 index 0000000000..4c95b7880c --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PManagedEndpointManager.ipdl @@ -0,0 +1,14 @@ +include protocol PManagedEndpointDecl; + +namespace mozilla { + +protocol PManagedEndpointManager { + manages PManagedEndpointDecl; + +child: + async Message(ManagedEndpoint<PManagedEndpointDeclParent> aEndpointParent, + ManagedEndpoint<PManagedEndpointDeclChild> aEndpointChild); +}; + +} + diff --git a/ipc/ipdl/test/ipdl/ok/PMessageTainted.ipdl b/ipc/ipdl/test/ipdl/ok/PMessageTainted.ipdl new file mode 100644 index 0000000000..d6791fd693 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PMessageTainted.ipdl @@ -0,0 +1,4 @@ +intr protocol PMessageTainted { +child: + async foo() tainted; +}; diff --git a/ipc/ipdl/test/ipdl/ok/PNullable.ipdl b/ipc/ipdl/test/ipdl/ok/PNullable.ipdl new file mode 100644 index 0000000000..bd83c0c436 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PNullable.ipdl @@ -0,0 +1,11 @@ +union Union { + nullable PNullable; + nullable PNullable[]; +}; + +protocol PNullable { +child: + async Msg(nullable PNullable n); + async Msg2(nullable PNullable[] N); + async Msg3(Union u); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PRefcounted.ipdl b/ipc/ipdl/test/ipdl/ok/PRefcounted.ipdl new file mode 100644 index 0000000000..678f2ccf86 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PRefcounted.ipdl @@ -0,0 +1,4 @@ +async refcounted protocol PRefcounted { +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PStruct.ipdl b/ipc/ipdl/test/ipdl/ok/PStruct.ipdl new file mode 100644 index 0000000000..c670a83b0b --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PStruct.ipdl @@ -0,0 +1,10 @@ +struct S { + int i; + double d; +}; + +sync protocol PStruct { +parent: + sync test(S s) returns (S ss); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PStructComparable.ipdl b/ipc/ipdl/test/ipdl/ok/PStructComparable.ipdl new file mode 100644 index 0000000000..dfc1a2bf67 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PStructComparable.ipdl @@ -0,0 +1,10 @@ +comparable struct S { + int i; + double d; +}; + +sync protocol PStructComparable { +parent: + sync test(S s) returns (S ss); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PSyncSyncManagee.ipdl b/ipc/ipdl/test/ipdl/ok/PSyncSyncManagee.ipdl new file mode 100644 index 0000000000..458f0c83c0 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PSyncSyncManagee.ipdl @@ -0,0 +1,7 @@ +include protocol PSyncSyncManager; + +sync protocol PSyncSyncManagee { + manager PSyncSyncManager; +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PSyncSyncManager.ipdl b/ipc/ipdl/test/ipdl/ok/PSyncSyncManager.ipdl new file mode 100644 index 0000000000..40ed425a36 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PSyncSyncManager.ipdl @@ -0,0 +1,11 @@ +include protocol PSyncSyncManagee; + +/* The main reason for this test is that it would have caught a bug + * in the Rust IPDL parser that was treating "sync" like "async" in the + * nested case. + */ +nested(upto not) sync protocol PSyncSyncManager { + manages PSyncSyncManagee; +parent: + async PSyncSyncManagee(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PUniquePtrBasic.ipdl b/ipc/ipdl/test/ipdl/ok/PUniquePtrBasic.ipdl new file mode 100644 index 0000000000..caffd0dbbc --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PUniquePtrBasic.ipdl @@ -0,0 +1,4 @@ +protocol PUniquePtrBasic { +child: + async Msg(UniquePtr<int> maybe); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActors.ipdl b/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActors.ipdl new file mode 100644 index 0000000000..ce67f23c68 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActors.ipdl @@ -0,0 +1,10 @@ +include protocol PUniquePtrOfActorsSub; + +protocol PUniquePtrOfActors { + manages PUniquePtrOfActorsSub; + +child: + async Msg(UniquePtr<PUniquePtrOfActorsSub> p); + + async PUniquePtrOfActorsSub(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActorsSub.ipdl b/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActorsSub.ipdl new file mode 100644 index 0000000000..a60c8e475f --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PUniquePtrOfActorsSub.ipdl @@ -0,0 +1,7 @@ +include protocol PUniquePtrOfActors; + +protocol PUniquePtrOfActorsSub { + manager PUniquePtrOfActors; + +child: async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PUniquePtrRecUnion.ipdl b/ipc/ipdl/test/ipdl/ok/PUniquePtrRecUnion.ipdl new file mode 100644 index 0000000000..4a0b9e7299 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PUniquePtrRecUnion.ipdl @@ -0,0 +1,21 @@ +union X { + int; + Y[]; + UniquePtr<Y>; +}; + +union Y { + X; + Z; +}; + +union Z { + double; + X; +}; + +protocol PUniquePtrRecUnion { +child: + async Test(X x, Y y, Z z); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PUniquePtrUnion.ipdl b/ipc/ipdl/test/ipdl/ok/PUniquePtrUnion.ipdl new file mode 100644 index 0000000000..d756339980 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PUniquePtrUnion.ipdl @@ -0,0 +1,10 @@ +union UniquePtrUnion { + int[]; + int; + double; +}; + +sync protocol PUniquePtrUnion { +parent: + async Msg(UniquePtrUnion u, UniquePtr<UniquePtrUnion> au) returns (UniquePtrUnion r); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Pactorparam.ipdl b/ipc/ipdl/test/ipdl/ok/Pactorparam.ipdl new file mode 100644 index 0000000000..0cba299cf5 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pactorparam.ipdl @@ -0,0 +1,5 @@ +protocol Pactorparam { + +child: async Msg(Pactorparam p); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/Pactorreturn.ipdl b/ipc/ipdl/test/ipdl/ok/Pactorreturn.ipdl new file mode 100644 index 0000000000..99d0d36225 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pactorreturn.ipdl @@ -0,0 +1,6 @@ +sync protocol Pactorreturn { + +parent: + sync Msg(Pactorreturn p) returns (Pactorreturn r); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/Parray_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/Parray_Basic.ipdl new file mode 100644 index 0000000000..811c01f324 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Parray_Basic.ipdl @@ -0,0 +1,4 @@ +protocol Parray_Basic { +child: + async Msg(int[] array); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Parray_OfActors.ipdl b/ipc/ipdl/test/ipdl/ok/Parray_OfActors.ipdl new file mode 100644 index 0000000000..b084c2c464 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Parray_OfActors.ipdl @@ -0,0 +1,10 @@ +include protocol Parray_OfActorsSub; + +protocol Parray_OfActors { + manages Parray_OfActorsSub; + +child: + async Msg(Parray_OfActorsSub[] p); + + async Parray_OfActorsSub(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Parray_OfActorsSub.ipdl b/ipc/ipdl/test/ipdl/ok/Parray_OfActorsSub.ipdl new file mode 100644 index 0000000000..6769e98abb --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Parray_OfActorsSub.ipdl @@ -0,0 +1,7 @@ +include protocol Parray_OfActors; + +protocol Parray_OfActorsSub { + manager Parray_OfActors; + +child: async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Parray_Union.ipdl b/ipc/ipdl/test/ipdl/ok/Parray_Union.ipdl new file mode 100644 index 0000000000..33c94e7ae5 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Parray_Union.ipdl @@ -0,0 +1,10 @@ +union Union { + int[]; + int; + double; +}; + +sync protocol Parray_Union { +parent: + sync Msg(Union u, Union[] au) returns (Union r); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PbasicUsing.ipdl b/ipc/ipdl/test/ipdl/ok/PbasicUsing.ipdl new file mode 100644 index 0000000000..2de73dab14 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PbasicUsing.ipdl @@ -0,0 +1,36 @@ +using SomeType from "SomeFile.h"; +using class SomeClass from "SomeFile.h"; +using struct SomeStruct from "SomeFile.h"; + +using refcounted SomeRefcountedType from "SomeFile.h"; +using refcounted class SomeRefcountedClass from "SomeFile.h"; +using refcounted struct SomeRefcountedStruct from "SomeFile.h"; + +using moveonly SomeMoveonlyType from "SomeFile.h"; +using moveonly class SomeMoveonlyClass from "SomeFile.h"; +using moveonly struct SomeMoveonlyStruct from "SomeFile.h"; + +using refcounted moveonly SomeRefcountedMoveonlyType from "SomeFile.h"; +using refcounted moveonly class SomeRefcountedMoveonlyClass from "SomeFile.h"; +using refcounted moveonly struct SomeRefcountedMoveonlyStruct from "SomeFile.h"; + +union SomeUnion +{ + SomeType; + SomeClass; + SomeStruct; + SomeRefcountedType; + SomeRefcountedClass; + SomeRefcountedStruct; + SomeMoveonlyType; + SomeMoveonlyClass; + SomeMoveonlyStruct; + SomeRefcountedMoveonlyType; + SomeRefcountedMoveonlyClass; + SomeRefcountedMoveonlyStruct; +}; + +protocol PbasicUsing { +child: + async Msg(SomeUnion foo); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Pbuiltins.ipdl b/ipc/ipdl/test/ipdl/ok/Pbuiltins.ipdl new file mode 100644 index 0000000000..4085dc6d95 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pbuiltins.ipdl @@ -0,0 +1,21 @@ +protocol Pbuiltins { + + // sanity-check that "essential" builtins are being declared + +child: async 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/Pbytebuf.ipdl b/ipc/ipdl/test/ipdl/ok/Pbytebuf.ipdl new file mode 100644 index 0000000000..1e7dd49f8d --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pbytebuf.ipdl @@ -0,0 +1,13 @@ +union Foo { + int; + ByteBuf; +}; + +intr protocol Pbytebuf { +parent: + async Msg(ByteBuf s, Foo f); + sync SyncMsg(ByteBuf s, Foo f) + returns (ByteBuf t, Foo g); + intr InterruptMsg(ByteBuf s, Foo f) + returns (ByteBuf t, Foo g); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Pempty.ipdl b/ipc/ipdl/test/ipdl/ok/Pempty.ipdl new file mode 100644 index 0000000000..61b511bec6 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pempty.ipdl @@ -0,0 +1,3 @@ +protocol Pempty { +child: async Msg(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PemptyStruct.ipdl b/ipc/ipdl/test/ipdl/ok/PemptyStruct.ipdl new file mode 100644 index 0000000000..a067bf71da --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PemptyStruct.ipdl @@ -0,0 +1,3 @@ +struct empty { }; + +protocol PemptyStruct { child: async __delete__(); }; diff --git a/ipc/ipdl/test/ipdl/ok/PheaderProto.ipdl b/ipc/ipdl/test/ipdl/ok/PheaderProto.ipdl new file mode 100644 index 0000000000..7ae285042e --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PheaderProto.ipdl @@ -0,0 +1,10 @@ +include header; + +namespace c { + +protocol PheaderProto { +child: + async __delete__(foo a, baz b, Inner1 c, Inner2 d, X x); +}; + +} diff --git a/ipc/ipdl/test/ipdl/ok/PintrProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/PintrProtocol.ipdl new file mode 100644 index 0000000000..190d30abf5 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PintrProtocol.ipdl @@ -0,0 +1,13 @@ +intr protocol PintrProtocol { + + // 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/Pjetpack.ipdl b/ipc/ipdl/test/ipdl/ok/Pjetpack.ipdl new file mode 100644 index 0000000000..190732485d --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pjetpack.ipdl @@ -0,0 +1,5 @@ +sync protocol Pjetpack { +child: + async __delete__(); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmanageSelf.ipdl b/ipc/ipdl/test/ipdl/ok/PmanageSelf.ipdl new file mode 100644 index 0000000000..992f374a58 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmanageSelf.ipdl @@ -0,0 +1,10 @@ +include protocol PmanageSelf_Toplevel; + +protocol PmanageSelf { + manager PmanageSelf_Toplevel or PmanageSelf; + manages PmanageSelf; + +child: + async PmanageSelf(); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmanageSelf_Toplevel.ipdl b/ipc/ipdl/test/ipdl/ok/PmanageSelf_Toplevel.ipdl new file mode 100644 index 0000000000..70b5fe8a9b --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmanageSelf_Toplevel.ipdl @@ -0,0 +1,9 @@ +include protocol PmanageSelf; + +protocol PmanageSelf_Toplevel { + manages PmanageSelf; + +child: + async PmanageSelf(); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmanagedProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/PmanagedProtocol.ipdl new file mode 100644 index 0000000000..cab07d06e7 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmanagedProtocol.ipdl @@ -0,0 +1,8 @@ +include protocol PmanagerProtocol; + +protocol PmanagedProtocol { + manager PmanagerProtocol; + +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmanagerProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/PmanagerProtocol.ipdl new file mode 100644 index 0000000000..40d1de31b0 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmanagerProtocol.ipdl @@ -0,0 +1,11 @@ +include protocol PmanagedProtocol; + +// sanity check of managed/manager protocols + +protocol PmanagerProtocol { + manages PmanagedProtocol; + +parent: + async PmanagedProtocol(int i); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/Pmaybe_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/Pmaybe_Basic.ipdl new file mode 100644 index 0000000000..a0bbccd874 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pmaybe_Basic.ipdl @@ -0,0 +1,4 @@ +protocol Pmaybe_Basic { +child: + async Msg(int? maybe); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActors.ipdl b/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActors.ipdl new file mode 100644 index 0000000000..430faa9ae7 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActors.ipdl @@ -0,0 +1,10 @@ +include protocol Pmaybe_OfActorsSub; + +protocol Pmaybe_OfActors { + manages Pmaybe_OfActorsSub; + +child: + async Msg(Pmaybe_OfActorsSub? p); + + async Pmaybe_OfActorsSub(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActorsSub.ipdl b/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActorsSub.ipdl new file mode 100644 index 0000000000..6a4759953f --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pmaybe_OfActorsSub.ipdl @@ -0,0 +1,7 @@ +include protocol Pmaybe_OfActors; + +protocol Pmaybe_OfActorsSub { + manager Pmaybe_OfActors; + +child: async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Pmaybe_Union.ipdl b/ipc/ipdl/test/ipdl/ok/Pmaybe_Union.ipdl new file mode 100644 index 0000000000..67f24cb794 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pmaybe_Union.ipdl @@ -0,0 +1,10 @@ +union MaybeUnion { + int[]; + int; + double; +}; + +sync protocol Pmaybe_Union { +parent: + async Msg(MaybeUnion u, MaybeUnion? au) returns (MaybeUnion r); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Pmedia.ipdl b/ipc/ipdl/test/ipdl/ok/Pmedia.ipdl new file mode 100644 index 0000000000..0a8076a433 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pmedia.ipdl @@ -0,0 +1,5 @@ +sync protocol Pmedia { +child: + async __delete__(); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmessageCompress.ipdl b/ipc/ipdl/test/ipdl/ok/PmessageCompress.ipdl new file mode 100644 index 0000000000..d74c5626bd --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmessageCompress.ipdl @@ -0,0 +1,4 @@ +intr protocol PmessageCompress { +child: + async foo() compress; +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmessageVerify.ipdl b/ipc/ipdl/test/ipdl/ok/PmessageVerify.ipdl new file mode 100644 index 0000000000..ee7adfe8d0 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmessageVerify.ipdl @@ -0,0 +1,14 @@ +include protocol PmessageVerifyTopLevel; + +intr protocol PmessageVerify { + manager PmessageVerifyTopLevel; + +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/PmessageVerifyTopLevel.ipdl b/ipc/ipdl/test/ipdl/ok/PmessageVerifyTopLevel.ipdl new file mode 100644 index 0000000000..72115a0338 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmessageVerifyTopLevel.ipdl @@ -0,0 +1,7 @@ +include protocol PmessageVerify; + +intr protocol PmessageVerifyTopLevel{ + manages PmessageVerify; + parent: + sync PmessageVerify(uint32_t aParam1) returns (double rv1) verify; +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmultiManaged.ipdl b/ipc/ipdl/test/ipdl/ok/PmultiManaged.ipdl new file mode 100644 index 0000000000..d8c47fc9e3 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmultiManaged.ipdl @@ -0,0 +1,9 @@ +include protocol PmultiManager1; +include protocol PmultiManager2; + +protocol PmultiManaged { + manager PmultiManager1 or PmultiManager2; + +child: + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmultiManager1.ipdl b/ipc/ipdl/test/ipdl/ok/PmultiManager1.ipdl new file mode 100644 index 0000000000..b0654a8c4f --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmultiManager1.ipdl @@ -0,0 +1,8 @@ +include protocol PmultiManaged; + +protocol PmultiManager1 { + manages PmultiManaged; + +child: + async PmultiManaged(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmultiManager2.ipdl b/ipc/ipdl/test/ipdl/ok/PmultiManager2.ipdl new file mode 100644 index 0000000000..c03f05a173 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmultiManager2.ipdl @@ -0,0 +1,8 @@ +include protocol PmultiManaged; + +protocol PmultiManager2 { + manages PmultiManaged; + +child: + async PmultiManaged(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmultipleUsingCxxTypes.ipdl b/ipc/ipdl/test/ipdl/ok/PmultipleUsingCxxTypes.ipdl new file mode 100644 index 0000000000..fa63b5b4b0 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmultipleUsingCxxTypes.ipdl @@ -0,0 +1,7 @@ +using struct mozilla::void_t from "mozilla/ipc/IPCCore.h"; +using struct mozilla::void_t from "mozilla/ipc/IPCCore.h"; + +protocol PmultipleUsingCxxTypes { +child: + async Msg(void_t foo); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmutualRecStructUnion.ipdl b/ipc/ipdl/test/ipdl/ok/PmutualRecStructUnion.ipdl new file mode 100644 index 0000000000..0fe5501a09 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmutualRecStructUnion.ipdl @@ -0,0 +1,21 @@ +struct X { + int i; + Y[] y; +}; + +union Y { + double; + X; + Z; +}; + +struct Z { + X x; + Y y; +}; + +protocol PmutualRecStructUnion { +child: + async Test(X x, Y y, Z z); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PmutualRecUnion.ipdl b/ipc/ipdl/test/ipdl/ok/PmutualRecUnion.ipdl new file mode 100644 index 0000000000..304847e003 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PmutualRecUnion.ipdl @@ -0,0 +1,21 @@ +union X { + int; + Y[]; + Y?; +}; + +union Y { + X; + Z; +}; + +union Z { + double; + X; +}; + +protocol PmutualRecUnion { +child: + async Test(X x, Y y, Z z); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Pnamespace_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/Pnamespace_Basic.ipdl new file mode 100644 index 0000000000..4f996d9744 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pnamespace_Basic.ipdl @@ -0,0 +1,12 @@ +namespace basic { + +// sanity check of namespaced protocols + +protocol Pnamespace_Basic { + +child: + async Msg(); + +}; + +} // namespace basic diff --git a/ipc/ipdl/test/ipdl/ok/PnoRedeclCrossMessage.ipdl b/ipc/ipdl/test/ipdl/ok/PnoRedeclCrossMessage.ipdl new file mode 100644 index 0000000000..d353a8c07c --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PnoRedeclCrossMessage.ipdl @@ -0,0 +1,9 @@ +protocol PnoRedeclCrossMessage { + + // 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/Pplugin.ipdl b/ipc/ipdl/test/ipdl/ok/Pplugin.ipdl new file mode 100644 index 0000000000..29c001e67b --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pplugin.ipdl @@ -0,0 +1,5 @@ +intr protocol Pplugin { +child: + async __delete__(); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/Prio.ipdl b/ipc/ipdl/test/ipdl/ok/Prio.ipdl new file mode 100644 index 0000000000..d2690935e9 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Prio.ipdl @@ -0,0 +1,8 @@ +async protocol Prio +{ +child: + prio(normal) async NormalPrio(); + prio(high) async HighPrio(); + prio(input) async InputPrio(); + prio(mediumhigh) async MediumHighPrio(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/PselfRecUnion.ipdl b/ipc/ipdl/test/ipdl/ok/PselfRecUnion.ipdl new file mode 100644 index 0000000000..7d76c34542 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PselfRecUnion.ipdl @@ -0,0 +1,11 @@ +union R { + int; + double; + R; +}; + +protocol PselfRecUnion { +child: + async Test(R r); + async __delete__(); +}; diff --git a/ipc/ipdl/test/ipdl/ok/Pshmem.ipdl b/ipc/ipdl/test/ipdl/ok/Pshmem.ipdl new file mode 100644 index 0000000000..326fb60c97 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Pshmem.ipdl @@ -0,0 +1,13 @@ +union Foo { + int; + Shmem; +}; + +intr protocol Pshmem { +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/PsyncProtocol.ipdl b/ipc/ipdl/test/ipdl/ok/PsyncProtocol.ipdl new file mode 100644 index 0000000000..377671b2d6 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PsyncProtocol.ipdl @@ -0,0 +1,11 @@ +sync protocol PsyncProtocol { + + // sanity check of sync protocols + +child: + async AsyncMsg(); + +parent: + sync SyncMsg() returns (int i); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/PthreeDirections.ipdl b/ipc/ipdl/test/ipdl/ok/PthreeDirections.ipdl new file mode 100644 index 0000000000..81c8cf6041 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/PthreeDirections.ipdl @@ -0,0 +1,13 @@ +protocol PthreeDirections { + + // 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/Punion_Basic.ipdl b/ipc/ipdl/test/ipdl/ok/Punion_Basic.ipdl new file mode 100644 index 0000000000..697b86e088 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Punion_Basic.ipdl @@ -0,0 +1,11 @@ +union Basic { + int; + double; +}; + +sync protocol Punion_Basic { + +parent: + sync Msg(Basic p) returns (Basic r); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/Punion_Comparable.ipdl b/ipc/ipdl/test/ipdl/ok/Punion_Comparable.ipdl new file mode 100644 index 0000000000..b63043f377 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Punion_Comparable.ipdl @@ -0,0 +1,11 @@ +comparable union Basic { + int; + double; +}; + +sync protocol Punion_Comparable { + +parent: + sync Msg(Basic p) returns (Basic r); + +}; diff --git a/ipc/ipdl/test/ipdl/ok/Punion_Namespaced.ipdl b/ipc/ipdl/test/ipdl/ok/Punion_Namespaced.ipdl new file mode 100644 index 0000000000..9453a561c8 --- /dev/null +++ b/ipc/ipdl/test/ipdl/ok/Punion_Namespaced.ipdl @@ -0,0 +1,18 @@ +namespace kitties { + +union Socks { + int; + double; +}; + +} // namespace kitties + + +namespace puppies { + +protocol Punion_Namespaced { +child: + async Msg(Socks s); +}; + +} // namespace puppies diff --git a/ipc/ipdl/test/ipdl/ok/header.ipdlh b/ipc/ipdl/test/ipdl/ok/header.ipdlh new file mode 100644 index 0000000000..fc3f8c8279 --- /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/runtests.py b/ipc/ipdl/test/ipdl/runtests.py new file mode 100644 index 0000000000..7338c7087a --- /dev/null +++ b/ipc/ipdl/test/ipdl/runtests.py @@ -0,0 +1,116 @@ +import os +import 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 mkCustomMsg(self, msg): + return """ +### Command: %s +### %s +### stderr: +%s""" % ( + " ".join(self.compile.argv), + msg, + self.compile.stderr, + ) + + 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) + + # Look for expected errors in the input file. + f = open(filename, "r") + self.expectedErrorMessage = [] + for l in f: + if l.startswith("//error:"): + self.expectedErrorMessage.append(l[2:-1]) + f.close() + + def checkPassed(self): + self.assertNotEqual( + self.expectedErrorMessage, + [], + self.mkCustomMsg( + "Error test should contain at least " + + "one line starting with //error: " + + "that indicates the expected failure." + ), + ) + + for e in self.expectedErrorMessage: + self.assertTrue( + self.compile.error(e), + self.mkCustomMsg('Did not see expected error "' + e + '"'), + ) + + +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 = False, False + for arg in sys.argv[3:]: + if errortests: + # The extra subdirectory is used for non-failing files we want + # to include from failing files. + errorIncludes = ["-I", os.path.join(errordir, "extra"), "-I", errordir] + errorsuite.addTest(ErrorTestCase(ipdlargv + errorIncludes, arg)) + elif oktests: + if "ERRORTESTS" == arg: + errortests = True + continue + oksuite.addTest(OkTestCase(ipdlargv + ["-I", okdir], arg)) + else: + if "OKTESTS" == arg: + oktests = True + continue + ipdlargv.append(arg) + + (unittest.TextTestRunner()).run(unittest.TestSuite([oksuite, errorsuite])) diff --git a/ipc/ipdl/test/ipdl/sync-messages.ini b/ipc/ipdl/test/ipdl/sync-messages.ini new file mode 100644 index 0000000000..f7034efe08 --- /dev/null +++ b/ipc/ipdl/test/ipdl/sync-messages.ini @@ -0,0 +1,52 @@ +[Pactorreturn::Msg] +description = test only +[Parray_Union::Msg] +description = test only +[Punion_Basic::Msg] +description = test only +[PStruct::test] +description = test only +[PStructComparable::test] +description = test only +[Punion_Comparable::Msg] +description = test only +[PintrProtocol::SyncMsg] +description = test only +[PintrProtocol::InterruptMsg] +description = test only +[Pshmem::SyncMsg] +description = test only +[Pshmem::InterruptMsg] +description = test only +[Pbytebuf::SyncMsg] +description = test only +[Pbytebuf::InterruptMsg] +description = test only +[PsyncProtocol::SyncMsg] +description = test only +[PmessageVerify::__delete__] +description = test only +[PmessageVerify::msg3] +description = test only +[PmessageVerify::msg4] +description = test only +[PmessageVerifyTopLevel::PmessageVerify] +description = test only +[PDeleteSub::__delete__] +description = test only +[PintrMessageCompress::foo] +description = test only +[PintrMessageCompress::bar] +description = test only +[PsyncMessageCompress::foo] +description = test only +[PsyncParentToChild::Msg] +description = test only +[PtooWeakIntrSync::Msg] +description = test only +[PtooWeakSyncAsync::Msg] +description = test only +[PundeclReturnType::Msg] +description = test only +[PasyncMessageListed::Msg] +description = test only diff --git a/ipc/ipdl/test/moz.build b/ipc/ipdl/test/moz.build new file mode 100644 index 0000000000..7c0986edb1 --- /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"] |