diff options
Diffstat (limited to 'ipc/ipdl/ipdl.py')
-rw-r--r-- | ipc/ipdl/ipdl.py | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/ipc/ipdl/ipdl.py b/ipc/ipdl/ipdl.py new file mode 100644 index 0000000000..1770416aaa --- /dev/null +++ b/ipc/ipdl/ipdl.py @@ -0,0 +1,336 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +import optparse +import os +import sys +from configparser import RawConfigParser +from io import StringIO + +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 DATA_PIPE_CLOSED_MESSAGE_TYPE: + return "DATA_PIPE_CLOSED_MESSAGE"; + case DATA_PIPE_BYTES_CONSUMED_MESSAGE_TYPE: + return "DATA_PIPE_BYTES_CONSUMED_MESSAGE"; + case ACCEPT_INVITE_MESSAGE_TYPE: + return "ACCEPT_INVITE_MESSAGE"; + case REQUEST_INTRODUCTION_MESSAGE_TYPE: + return "REQUEST_INTRODUCTION_MESSAGE"; + case INTRODUCE_MESSAGE_TYPE: + return "INTRODUCE_MESSAGE"; + case BROADCAST_MESSAGE_TYPE: + return "BROADCAST_MESSAGE"; + case EVENT_MESSAGE_TYPE: + return "EVENT_MESSAGE"; + case IMPENDING_SHUTDOWN_MESSAGE_TYPE: + return "IMPENDING_SHUTDOWN"; + case BUILD_IDS_MATCH_MESSAGE_TYPE: + return "BUILD_IDS_MATCH_MESSAGE"; + case BUILD_ID_MESSAGE_TYPE: + return "BUILD_ID_MESSAGE"; + 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) |