blob: 433658de69e5da7dd583641670da28f3d0bc5a2f [file] [log] [blame]
# 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<DelayedReply> {\n')
result.append(' DelayedReply(PassRefPtr<CoreIPC::Connection>, PassOwnPtr<CoreIPC::ArgumentEncoder>);\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<CoreIPC::Connection> m_connection;\n')
result.append(' OwnPtr<CoreIPC::ArgumentEncoder> 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('<wtf/ThreadSafeRefCounted.h>')
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<Messages::%s::Kind> {\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<Messages::%s::%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<Messages::%s::%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 ['<wtf/Vector.h>'] + headers_for_type(element_type)
special_cases = {
'WTF::String': '<wtf/text/WTFString.h>',
'WebCore::CompositionUnderline': '<WebCore/Editor.h>',
'WebCore::GrammarDetail': '<WebCore/TextCheckerClient.h>',
'WebCore::KeypressCommand': '<WebCore/KeyboardEvent.h>',
'WebCore::PluginInfo': '<WebCore/PluginData.h>',
'WebCore::TextCheckingResult': '<WebCore/TextCheckerClient.h>',
'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<CoreIPC::Connection> connection, PassOwnPtr<CoreIPC::ArgumentEncoder> 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<Messages::%s::Kind>()) {\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<Messages::%s::Kind>()) {\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)