# Copyright (C) 2010 Apple Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import collections import itertools import re _license_header = """/* * Copyright (C) 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ """ class MessageReceiver(object): def __init__(self, name, messages, condition): self.name = name self.messages = messages self.condition = condition def iterparameters(self): return itertools.chain((parameter for message in self.messages for parameter in message.parameters), (reply_parameter for message in self.messages if message.reply_parameters for reply_parameter in message.reply_parameters)) @classmethod def parse(cls, file): destination = None messages = [] condition = None master_condition = None for line in file: match = re.search(r'messages -> ([A-Za-z_0-9]+) {', line) if match: if condition: master_condition = condition condition = None destination = match.group(1) continue if line.startswith('#'): if line.startswith('#if '): condition = line.rstrip()[4:] elif line.startswith('#endif'): condition = None continue match = re.search(r'([A-Za-z_0-9]+)\((.*?)\)(?:(?:\s+->\s+)\((.*?)\)(?:\s+(.*))?)?', line) if match: name, parameters_string, reply_parameters_string, attributes_string = match.groups() if parameters_string: parameters = parse_parameter_string(parameters_string) else: parameters = [] for parameter in parameters: parameter.condition = condition if attributes_string: attributes = frozenset(attributes_string.split()) is_delayed = "Delayed" in attributes dispatch_on_connection_queue = "DispatchOnConnectionQueue" in attributes else: is_delayed = False dispatch_on_connection_queue = False if reply_parameters_string: reply_parameters = parse_parameter_string(reply_parameters_string) elif reply_parameters_string == '': reply_parameters = [] else: reply_parameters = None messages.append(Message(name, parameters, reply_parameters, is_delayed, dispatch_on_connection_queue, condition)) return MessageReceiver(destination, messages, master_condition) class Message(object): def __init__(self, name, parameters, reply_parameters, is_delayed, dispatch_on_connection_queue, condition): self.name = name self.parameters = parameters self.reply_parameters = reply_parameters if self.reply_parameters is not None: self.is_delayed = is_delayed self.dispatch_on_connection_queue = dispatch_on_connection_queue self.condition = condition if len(self.parameters) != 0: self.is_variadic = parameter_type_is_variadic(self.parameters[-1].type) else: self.is_variadic = False def id(self): return '%sID' % self.name class Parameter(object): def __init__(self, type, name, condition=None): self.type = type self.name = name self.condition = condition def parse_parameter_string(parameter_string): return [Parameter(*type_and_name.rsplit(' ', 1)) for type_and_name in parameter_string.split(', ')] def messages_header_filename(receiver): return '%sMessages.h' % receiver.name def surround_in_condition(string, condition): if not condition: return string return '#if %s\n%s#endif\n' % (condition, string) def messages_to_kind_enum(messages): result = [] result.append('enum Kind {\n') result += [surround_in_condition(' %s,\n' % message.id(), message.condition) for message in messages] result.append('};\n') return ''.join(result) def parameter_type_is_variadic(type): variadic_types = frozenset([ 'WebKit::InjectedBundleUserMessageEncoder', 'WebKit::WebContextUserMessageEncoder', ]) return type in variadic_types def function_parameter_type(type): # Don't use references for built-in types. builtin_types = frozenset([ 'bool', 'float', 'double', 'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t', 'int8_t', 'int16_t', 'int32_t', 'int64_t', ]) if type in builtin_types: return type return 'const %s&' % type def reply_parameter_type(type): return '%s&' % type def arguments_type(parameters, parameter_type_function): arguments_type = 'CoreIPC::Arguments%d' % len(parameters) if len(parameters): arguments_type = '%s<%s>' % (arguments_type, ', '.join(parameter_type_function(parameter.type) for parameter in parameters)) return arguments_type def base_class(message): return arguments_type(message.parameters, function_parameter_type) def reply_type(message): return arguments_type(message.reply_parameters, reply_parameter_type) def decode_type(message): if message.is_variadic: return arguments_type(message.parameters[:-1], reply_parameter_type) return base_class(message) def delayed_reply_type(message): return arguments_type(message.reply_parameters, function_parameter_type) def message_to_struct_declaration(message): result = [] function_parameters = [(function_parameter_type(x.type), x.name) for x in message.parameters] result.append('struct %s : %s' % (message.name, base_class(message))) result.append(' {\n') result.append(' static const Kind messageID = %s;\n' % message.id()) if message.reply_parameters != None: if message.is_delayed: send_parameters = [(function_parameter_type(x.type), x.name) for x in message.reply_parameters] result.append(' struct DelayedReply : public ThreadSafeRefCounted {\n') result.append(' DelayedReply(PassRefPtr, PassOwnPtr);\n') result.append(' ~DelayedReply();\n') result.append('\n') result.append(' bool send(%s);\n' % ', '.join([' '.join(x) for x in send_parameters])) result.append('\n') result.append(' private:\n') result.append(' RefPtr m_connection;\n') result.append(' OwnPtr m_arguments;\n') result.append(' };\n\n') result.append(' typedef %s Reply;\n' % reply_type(message)) result.append(' typedef %s DecodeType;\n' % decode_type(message)) if len(function_parameters): result.append(' %s%s(%s)' % (len(function_parameters) == 1 and 'explicit ' or '', message.name, ', '.join([' '.join(x) for x in function_parameters]))) result.append('\n : %s(%s)\n' % (base_class(message), ', '.join([x[1] for x in function_parameters]))) result.append(' {\n') result.append(' }\n') result.append('};\n') return surround_in_condition(''.join(result), message.condition) def struct_or_class(namespace, type): structs = frozenset([ 'WebCore::EditorCommandsForKeyEvent', 'WebCore::CompositionUnderline', 'WebCore::GrammarDetail', 'WebCore::KeypressCommand', 'WebCore::PluginInfo', 'WebCore::PrintInfo', 'WebCore::ViewportArguments', 'WebCore::WindowFeatures', 'WebKit::AttributedString', 'WebKit::ContextMenuState', 'WebKit::DictionaryPopupInfo', 'WebKit::DrawingAreaInfo', 'WebKit::EditorState', 'WebKit::PlatformPopupMenuData', 'WebKit::PluginProcessCreationParameters', 'WebKit::PrintInfo', 'WebKit::SecurityOriginData', 'WebKit::TextCheckerState', 'WebKit::WebNavigationDataStore', 'WebKit::WebOpenPanelParameters::Data', 'WebKit::WebPageCreationParameters', 'WebKit::WebPreferencesStore', 'WebKit::WebProcessCreationParameters', ]) qualified_name = '%s::%s' % (namespace, type) if qualified_name in structs: return 'struct %s' % type return 'class %s' % type def forward_declarations_for_namespace(namespace, types): result = [] result.append('namespace %s {\n' % namespace) result += [' %s;\n' % struct_or_class(namespace, x) for x in types] result.append('}\n') return ''.join(result) def forward_declarations_and_headers(receiver): types_by_namespace = collections.defaultdict(set) headers = set([ '"Arguments.h"', '"MessageID.h"', ]) for message in receiver.messages: if message.reply_parameters != None and message.is_delayed: headers.add('') types_by_namespace['CoreIPC'].update(['ArgumentEncoder', 'Connection']) for parameter in receiver.iterparameters(): type = parameter.type if type.find('<') != -1: # Don't forward declare class templates. headers.update(headers_for_type(type)) continue split = type.split('::') if len(split) == 2: namespace = split[0] inner_type = split[1] types_by_namespace[namespace].add(inner_type) elif len(split) > 2: # We probably have a nested struct, which means we can't forward declare it. # Include its header instead. headers.update(headers_for_type(type)) forward_declarations = '\n'.join([forward_declarations_for_namespace(namespace, types) for (namespace, types) in sorted(types_by_namespace.items())]) headers = ['#include %s\n' % header for header in sorted(headers)] return (forward_declarations, headers) def generate_messages_header(file): receiver = MessageReceiver.parse(file) header_guard = messages_header_filename(receiver).replace('.', '_') result = [] result.append(_license_header) result.append('#ifndef %s\n' % header_guard) result.append('#define %s\n\n' % header_guard) if receiver.condition: result.append('#if %s\n\n' % receiver.condition) forward_declarations, headers = forward_declarations_and_headers(receiver) result += headers result.append('\n') result.append(forward_declarations) result.append('\n') result.append('namespace Messages {\n\nnamespace %s {\n\n' % receiver.name) result.append(messages_to_kind_enum(receiver.messages)) result.append('\n') result.append('\n'.join([message_to_struct_declaration(x) for x in receiver.messages])) result.append('\n} // namespace %s\n\n} // namespace Messages\n' % receiver.name) result.append('\nnamespace CoreIPC {\n\n') result.append('template<> struct MessageKindTraits {\n' % receiver.name) result.append(' static const MessageClass messageClass = MessageClass%s;\n' % receiver.name) result.append('};\n') result.append('\n} // namespace CoreIPC\n') if receiver.condition: result.append('\n#endif // %s\n' % receiver.condition) result.append('\n#endif // %s\n' % header_guard) return ''.join(result) def handler_function(receiver, message): if message.name.find('URL') == 0: return '%s::%s' % (receiver.name, 'url' + message.name[3:]) return '%s::%s' % (receiver.name, message.name[0].lower() + message.name[1:]) def async_case_statement(receiver, message): dispatch_function = 'handleMessage' if message.is_variadic: dispatch_function += 'Variadic' result = [] result.append(' case Messages::%s::%s:\n' % (receiver.name, message.id())) result.append(' CoreIPC::%s(arguments, this, &%s);\n' % (dispatch_function, receiver.name, message.name, handler_function(receiver, message))) result.append(' return;\n') return surround_in_condition(''.join(result), message.condition) def sync_case_statement(receiver, message): dispatch_function = 'handleMessage' if message.is_delayed: dispatch_function += 'Delayed' if message.is_variadic: dispatch_function += 'Variadic' result = [] result.append(' case Messages::%s::%s:\n' % (receiver.name, message.id())) result.append(' CoreIPC::%s(%sarguments, reply, this, &%s);\n' % (dispatch_function, receiver.name, message.name, 'connection, ' if message.is_delayed else '', handler_function(receiver, message))) if message.is_delayed: result.append(' return CoreIPC::ManualReply;\n') else: result.append(' return CoreIPC::AutomaticReply;\n') return surround_in_condition(''.join(result), message.condition) def argument_coder_headers_for_type(type): # Check for Vector. match = re.search(r'Vector<(.+)>', type) if match: element_type = match.groups()[0].strip() return ['"ArgumentCoders.h"'] + argument_coder_headers_for_type(element_type) special_cases = { 'WTF::String': '"ArgumentCoders.h"', 'WebKit::InjectedBundleUserMessageEncoder': '"InjectedBundleUserMessageCoders.h"', 'WebKit::WebContextUserMessageEncoder': '"WebContextUserMessageCoders.h"', } if type in special_cases: return [special_cases[type]] split = type.split('::') if len(split) < 2: return [] if split[0] == 'WebCore': return ['"WebCoreArgumentCoders.h"'] return [] def headers_for_type(type): # Check for Vector. match = re.search(r'Vector<(.+)>', type) if match: element_type = match.groups()[0].strip() return [''] + headers_for_type(element_type) special_cases = { 'WTF::String': '', 'WebCore::CompositionUnderline': '', 'WebCore::GrammarDetail': '', 'WebCore::KeypressCommand': '', 'WebCore::PluginInfo': '', 'WebCore::TextCheckingResult': '', 'WebKit::WebGestureEvent': '"WebEvent.h"', 'WebKit::WebKeyboardEvent': '"WebEvent.h"', 'WebKit::WebMouseEvent': '"WebEvent.h"', 'WebKit::WebTouchEvent': '"WebEvent.h"', 'WebKit::WebWheelEvent': '"WebEvent.h"', } if type in special_cases: return [special_cases[type]] # We assume that we must include a header for a type iff it has a scope # resolution operator (::). split = type.split('::') if len(split) < 2: return [] if split[0] == 'WebKit' or split[0] == 'CoreIPC': return ['"%s.h"' % split[1]] return ['<%s/%s.h>' % tuple(split)] def generate_message_handler(file): receiver = MessageReceiver.parse(file) headers = { '"%s"' % messages_header_filename(receiver): None, '"HandleMessage.h"': None, '"ArgumentDecoder.h"': None, } type_conditions = {} for parameter in receiver.iterparameters(): type = parameter.type condition = parameter.condition if type in type_conditions: if not type_conditions[type]: condition = type_conditions[type] else: if not condition: type_conditions[type] = condition else: type_conditions[type] = condition argument_encoder_headers = argument_coder_headers_for_type(parameter.type) if argument_encoder_headers: for header in argument_encoder_headers: headers[header] = condition continue type_headers = headers_for_type(type) for header in type_headers: headers[header] = condition for message in receiver.messages: if message.reply_parameters is not None: for reply_parameter in message.reply_parameters: type = reply_parameter.type argument_encoder_headers = argument_coder_headers_for_type(type) if argument_encoder_headers: for header in argument_encoder_headers: headers[header] = message.condition continue type_headers = headers_for_type(type) for header in type_headers: headers[header] = message.condition result = [] result.append(_license_header) result.append('#include "config.h"\n') result.append('\n') if receiver.condition: result.append('#if %s\n\n' % receiver.condition) result.append('#include "%s.h"\n\n' % receiver.name) for headercondition in sorted(headers): if headers[headercondition]: result.append('#if %s\n' % headers[headercondition]) result += ['#include %s\n' % headercondition] result.append('#endif\n') else: result += ['#include %s\n' % headercondition] result.append('\n') sync_delayed_messages = [] for message in receiver.messages: if message.reply_parameters != None and message.is_delayed: sync_delayed_messages.append(message) if sync_delayed_messages: result.append('namespace Messages {\n\nnamespace %s {\n\n' % receiver.name) for message in sync_delayed_messages: send_parameters = [(function_parameter_type(x.type), x.name) for x in message.reply_parameters] if message.condition: result.append('#if %s\n\n' % message.condition) result.append('%s::DelayedReply::DelayedReply(PassRefPtr connection, PassOwnPtr arguments)\n' % message.name) result.append(' : m_connection(connection)\n') result.append(' , m_arguments(arguments)\n') result.append('{\n') result.append('}\n') result.append('\n') result.append('%s::DelayedReply::~DelayedReply()\n' % message.name) result.append('{\n') result.append(' ASSERT(!m_connection);\n') result.append('}\n') result.append('\n') result.append('bool %s::DelayedReply::send(%s)\n' % (message.name, ', '.join([' '.join(x) for x in send_parameters]))) result.append('{\n') result.append(' ASSERT(m_arguments);\n') result += [' m_arguments->encode(%s);\n' % x.name for x in message.reply_parameters] result.append(' bool result = m_connection->sendSyncReply(m_arguments.release());\n') result.append(' m_connection = nullptr;\n') result.append(' return result;\n') result.append('}\n') result.append('\n') if message.condition: result.append('#endif\n\n') result.append('} // namespace %s\n\n} // namespace Messages\n\n' % receiver.name) result.append('namespace WebKit {\n\n') async_messages = [] sync_messages = [] for message in receiver.messages: if message.reply_parameters is not None: sync_messages.append(message) else: async_messages.append(message) if async_messages: result.append('void %s::didReceive%sMessage(CoreIPC::Connection*, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments)\n' % (receiver.name, receiver.name)) result.append('{\n') result.append(' switch (messageID.get()) {\n' % receiver.name) result += [async_case_statement(receiver, message) for message in async_messages] result.append(' default:\n') result.append(' break;\n') result.append(' }\n\n') result.append(' ASSERT_NOT_REACHED();\n') result.append('}\n') if sync_messages: result.append('\n') result.append('CoreIPC::SyncReplyMode %s::didReceiveSync%sMessage(CoreIPC::Connection*%s, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, CoreIPC::ArgumentEncoder* reply)\n' % (receiver.name, receiver.name, ' connection' if sync_delayed_messages else '')) result.append('{\n') result.append(' switch (messageID.get()) {\n' % receiver.name) result += [sync_case_statement(receiver, message) for message in sync_messages] result.append(' default:\n') result.append(' break;\n') result.append(' }\n\n') result.append(' ASSERT_NOT_REACHED();\n') result.append(' return CoreIPC::AutomaticReply;\n') result.append('}\n') result.append('\n} // namespace WebKit\n') if receiver.condition: result.append('\n#endif // %s\n' % receiver.condition) return ''.join(result)