1# Copyright (C) 2010 Apple Inc. All rights reserved. 2# 3# Redistribution and use in source and binary forms, with or without 4# modification, are permitted provided that the following conditions 5# are met: 6# 1. Redistributions of source code must retain the above copyright 7# notice, this list of conditions and the following disclaimer. 8# 2. Redistributions in binary form must reproduce the above copyright 9# notice, this list of conditions and the following disclaimer in the 10# documentation and/or other materials provided with the distribution. 11# 12# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND 13# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR 16# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 19# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 20# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 21# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 23import collections 24import itertools 25import re 26 27 28_license_header = """/* 29 * Copyright (C) 2010 Apple Inc. All rights reserved. 30 * 31 * Redistribution and use in source and binary forms, with or without 32 * modification, are permitted provided that the following conditions 33 * are met: 34 * 1. Redistributions of source code must retain the above copyright 35 * notice, this list of conditions and the following disclaimer. 36 * 2. Redistributions in binary form must reproduce the above copyright 37 * notice, this list of conditions and the following disclaimer in the 38 * documentation and/or other materials provided with the distribution. 39 * 40 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND 41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 42 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 43 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR 44 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 46 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 47 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 48 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 49 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50 */ 51 52""" 53 54class MessageReceiver(object): 55 def __init__(self, name, messages, condition): 56 self.name = name 57 self.messages = messages 58 self.condition = condition 59 60 def iterparameters(self): 61 return itertools.chain((parameter for message in self.messages for parameter in message.parameters), 62 (reply_parameter for message in self.messages if message.reply_parameters for reply_parameter in message.reply_parameters)) 63 64 @classmethod 65 def parse(cls, file): 66 destination = None 67 messages = [] 68 condition = None 69 master_condition = None 70 for line in file: 71 match = re.search(r'messages -> ([A-Za-z_0-9]+) {', line) 72 if match: 73 if condition: 74 master_condition = condition 75 condition = None 76 destination = match.group(1) 77 continue 78 if line.startswith('#'): 79 if line.startswith('#if '): 80 condition = line.rstrip()[4:] 81 elif line.startswith('#endif'): 82 condition = None 83 continue 84 match = re.search(r'([A-Za-z_0-9]+)\((.*?)\)(?:(?:\s+->\s+)\((.*?)\)(?:\s+(.*))?)?', line) 85 if match: 86 name, parameters_string, reply_parameters_string, attributes_string = match.groups() 87 if parameters_string: 88 parameters = parse_parameter_string(parameters_string) 89 else: 90 parameters = [] 91 92 for parameter in parameters: 93 parameter.condition = condition 94 95 if attributes_string: 96 attributes = frozenset(attributes_string.split()) 97 is_delayed = "Delayed" in attributes 98 dispatch_on_connection_queue = "DispatchOnConnectionQueue" in attributes 99 else: 100 is_delayed = False 101 dispatch_on_connection_queue = False 102 103 if reply_parameters_string: 104 reply_parameters = parse_parameter_string(reply_parameters_string) 105 elif reply_parameters_string == '': 106 reply_parameters = [] 107 else: 108 reply_parameters = None 109 110 messages.append(Message(name, parameters, reply_parameters, is_delayed, dispatch_on_connection_queue, condition)) 111 return MessageReceiver(destination, messages, master_condition) 112 113 114class Message(object): 115 def __init__(self, name, parameters, reply_parameters, is_delayed, dispatch_on_connection_queue, condition): 116 self.name = name 117 self.parameters = parameters 118 self.reply_parameters = reply_parameters 119 if self.reply_parameters is not None: 120 self.is_delayed = is_delayed 121 self.dispatch_on_connection_queue = dispatch_on_connection_queue 122 self.condition = condition 123 if len(self.parameters) != 0: 124 self.is_variadic = parameter_type_is_variadic(self.parameters[-1].type) 125 else: 126 self.is_variadic = False 127 128 def id(self): 129 return '%sID' % self.name 130 131 132class Parameter(object): 133 def __init__(self, type, name, condition=None): 134 self.type = type 135 self.name = name 136 self.condition = condition 137 138 139def parse_parameter_string(parameter_string): 140 return [Parameter(*type_and_name.rsplit(' ', 1)) for type_and_name in parameter_string.split(', ')] 141 142 143def messages_header_filename(receiver): 144 return '%sMessages.h' % receiver.name 145 146 147def surround_in_condition(string, condition): 148 if not condition: 149 return string 150 return '#if %s\n%s#endif\n' % (condition, string) 151 152 153def messages_to_kind_enum(messages): 154 result = [] 155 result.append('enum Kind {\n') 156 result += [surround_in_condition(' %s,\n' % message.id(), message.condition) for message in messages] 157 result.append('};\n') 158 return ''.join(result) 159 160 161def parameter_type_is_variadic(type): 162 variadic_types = frozenset([ 163 'WebKit::InjectedBundleUserMessageEncoder', 164 'WebKit::WebContextUserMessageEncoder', 165 ]) 166 167 return type in variadic_types 168 169def function_parameter_type(type): 170 # Don't use references for built-in types. 171 builtin_types = frozenset([ 172 'bool', 173 'float', 174 'double', 175 'uint8_t', 176 'uint16_t', 177 'uint32_t', 178 'uint64_t', 179 'int8_t', 180 'int16_t', 181 'int32_t', 182 'int64_t', 183 ]) 184 185 if type in builtin_types: 186 return type 187 188 return 'const %s&' % type 189 190 191def reply_parameter_type(type): 192 return '%s&' % type 193 194 195def arguments_type(parameters, parameter_type_function): 196 arguments_type = 'CoreIPC::Arguments%d' % len(parameters) 197 if len(parameters): 198 arguments_type = '%s<%s>' % (arguments_type, ', '.join(parameter_type_function(parameter.type) for parameter in parameters)) 199 return arguments_type 200 201 202def base_class(message): 203 return arguments_type(message.parameters, function_parameter_type) 204 205 206def reply_type(message): 207 return arguments_type(message.reply_parameters, reply_parameter_type) 208 209 210def decode_type(message): 211 if message.is_variadic: 212 return arguments_type(message.parameters[:-1], reply_parameter_type) 213 return base_class(message) 214 215 216def delayed_reply_type(message): 217 return arguments_type(message.reply_parameters, function_parameter_type) 218 219 220def message_to_struct_declaration(message): 221 result = [] 222 function_parameters = [(function_parameter_type(x.type), x.name) for x in message.parameters] 223 result.append('struct %s : %s' % (message.name, base_class(message))) 224 result.append(' {\n') 225 result.append(' static const Kind messageID = %s;\n' % message.id()) 226 if message.reply_parameters != None: 227 if message.is_delayed: 228 send_parameters = [(function_parameter_type(x.type), x.name) for x in message.reply_parameters] 229 result.append(' struct DelayedReply : public ThreadSafeRefCounted<DelayedReply> {\n') 230 result.append(' DelayedReply(PassRefPtr<CoreIPC::Connection>, PassOwnPtr<CoreIPC::ArgumentEncoder>);\n') 231 result.append(' ~DelayedReply();\n') 232 result.append('\n') 233 result.append(' bool send(%s);\n' % ', '.join([' '.join(x) for x in send_parameters])) 234 result.append('\n') 235 result.append(' private:\n') 236 result.append(' RefPtr<CoreIPC::Connection> m_connection;\n') 237 result.append(' OwnPtr<CoreIPC::ArgumentEncoder> m_arguments;\n') 238 result.append(' };\n\n') 239 240 result.append(' typedef %s Reply;\n' % reply_type(message)) 241 242 result.append(' typedef %s DecodeType;\n' % decode_type(message)) 243 if len(function_parameters): 244 result.append(' %s%s(%s)' % (len(function_parameters) == 1 and 'explicit ' or '', message.name, ', '.join([' '.join(x) for x in function_parameters]))) 245 result.append('\n : %s(%s)\n' % (base_class(message), ', '.join([x[1] for x in function_parameters]))) 246 result.append(' {\n') 247 result.append(' }\n') 248 result.append('};\n') 249 return surround_in_condition(''.join(result), message.condition) 250 251 252def struct_or_class(namespace, type): 253 structs = frozenset([ 254 'WebCore::EditorCommandsForKeyEvent', 255 'WebCore::CompositionUnderline', 256 'WebCore::GrammarDetail', 257 'WebCore::KeypressCommand', 258 'WebCore::PluginInfo', 259 'WebCore::PrintInfo', 260 'WebCore::ViewportArguments', 261 'WebCore::WindowFeatures', 262 'WebKit::AttributedString', 263 'WebKit::ContextMenuState', 264 'WebKit::DictionaryPopupInfo', 265 'WebKit::DrawingAreaInfo', 266 'WebKit::EditorState', 267 'WebKit::PlatformPopupMenuData', 268 'WebKit::PluginProcessCreationParameters', 269 'WebKit::PrintInfo', 270 'WebKit::SecurityOriginData', 271 'WebKit::TextCheckerState', 272 'WebKit::WebNavigationDataStore', 273 'WebKit::WebOpenPanelParameters::Data', 274 'WebKit::WebPageCreationParameters', 275 'WebKit::WebPreferencesStore', 276 'WebKit::WebProcessCreationParameters', 277 ]) 278 279 qualified_name = '%s::%s' % (namespace, type) 280 if qualified_name in structs: 281 return 'struct %s' % type 282 283 return 'class %s' % type 284 285def forward_declarations_for_namespace(namespace, types): 286 result = [] 287 result.append('namespace %s {\n' % namespace) 288 result += [' %s;\n' % struct_or_class(namespace, x) for x in types] 289 result.append('}\n') 290 return ''.join(result) 291 292 293def forward_declarations_and_headers(receiver): 294 types_by_namespace = collections.defaultdict(set) 295 296 headers = set([ 297 '"Arguments.h"', 298 '"MessageID.h"', 299 ]) 300 301 for message in receiver.messages: 302 if message.reply_parameters != None and message.is_delayed: 303 headers.add('<wtf/ThreadSafeRefCounted.h>') 304 types_by_namespace['CoreIPC'].update(['ArgumentEncoder', 'Connection']) 305 306 for parameter in receiver.iterparameters(): 307 type = parameter.type 308 309 if type.find('<') != -1: 310 # Don't forward declare class templates. 311 headers.update(headers_for_type(type)) 312 continue 313 314 split = type.split('::') 315 316 if len(split) == 2: 317 namespace = split[0] 318 inner_type = split[1] 319 types_by_namespace[namespace].add(inner_type) 320 elif len(split) > 2: 321 # We probably have a nested struct, which means we can't forward declare it. 322 # Include its header instead. 323 headers.update(headers_for_type(type)) 324 325 forward_declarations = '\n'.join([forward_declarations_for_namespace(namespace, types) for (namespace, types) in sorted(types_by_namespace.items())]) 326 headers = ['#include %s\n' % header for header in sorted(headers)] 327 328 return (forward_declarations, headers) 329 330def generate_messages_header(file): 331 receiver = MessageReceiver.parse(file) 332 header_guard = messages_header_filename(receiver).replace('.', '_') 333 334 result = [] 335 336 result.append(_license_header) 337 338 result.append('#ifndef %s\n' % header_guard) 339 result.append('#define %s\n\n' % header_guard) 340 341 if receiver.condition: 342 result.append('#if %s\n\n' % receiver.condition) 343 344 forward_declarations, headers = forward_declarations_and_headers(receiver) 345 346 result += headers 347 result.append('\n') 348 349 result.append(forward_declarations) 350 result.append('\n') 351 352 result.append('namespace Messages {\n\nnamespace %s {\n\n' % receiver.name) 353 result.append(messages_to_kind_enum(receiver.messages)) 354 result.append('\n') 355 result.append('\n'.join([message_to_struct_declaration(x) for x in receiver.messages])) 356 result.append('\n} // namespace %s\n\n} // namespace Messages\n' % receiver.name) 357 358 result.append('\nnamespace CoreIPC {\n\n') 359 result.append('template<> struct MessageKindTraits<Messages::%s::Kind> {\n' % receiver.name) 360 result.append(' static const MessageClass messageClass = MessageClass%s;\n' % receiver.name) 361 result.append('};\n') 362 result.append('\n} // namespace CoreIPC\n') 363 364 if receiver.condition: 365 result.append('\n#endif // %s\n' % receiver.condition) 366 367 result.append('\n#endif // %s\n' % header_guard) 368 369 return ''.join(result) 370 371 372def handler_function(receiver, message): 373 if message.name.find('URL') == 0: 374 return '%s::%s' % (receiver.name, 'url' + message.name[3:]) 375 return '%s::%s' % (receiver.name, message.name[0].lower() + message.name[1:]) 376 377 378def async_case_statement(receiver, message): 379 dispatch_function = 'handleMessage' 380 if message.is_variadic: 381 dispatch_function += 'Variadic' 382 383 result = [] 384 result.append(' case Messages::%s::%s:\n' % (receiver.name, message.id())) 385 result.append(' CoreIPC::%s<Messages::%s::%s>(arguments, this, &%s);\n' % (dispatch_function, receiver.name, message.name, handler_function(receiver, message))) 386 result.append(' return;\n') 387 return surround_in_condition(''.join(result), message.condition) 388 389 390def sync_case_statement(receiver, message): 391 dispatch_function = 'handleMessage' 392 if message.is_delayed: 393 dispatch_function += 'Delayed' 394 if message.is_variadic: 395 dispatch_function += 'Variadic' 396 397 result = [] 398 result.append(' case Messages::%s::%s:\n' % (receiver.name, message.id())) 399 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))) 400 401 if message.is_delayed: 402 result.append(' return CoreIPC::ManualReply;\n') 403 else: 404 result.append(' return CoreIPC::AutomaticReply;\n') 405 406 return surround_in_condition(''.join(result), message.condition) 407 408 409def argument_coder_headers_for_type(type): 410 # Check for Vector. 411 match = re.search(r'Vector<(.+)>', type) 412 if match: 413 element_type = match.groups()[0].strip() 414 return ['"ArgumentCoders.h"'] + argument_coder_headers_for_type(element_type) 415 416 special_cases = { 417 'WTF::String': '"ArgumentCoders.h"', 418 'WebKit::InjectedBundleUserMessageEncoder': '"InjectedBundleUserMessageCoders.h"', 419 'WebKit::WebContextUserMessageEncoder': '"WebContextUserMessageCoders.h"', 420 } 421 422 if type in special_cases: 423 return [special_cases[type]] 424 425 split = type.split('::') 426 if len(split) < 2: 427 return [] 428 if split[0] == 'WebCore': 429 return ['"WebCoreArgumentCoders.h"'] 430 431 return [] 432 433 434def headers_for_type(type): 435 # Check for Vector. 436 match = re.search(r'Vector<(.+)>', type) 437 if match: 438 element_type = match.groups()[0].strip() 439 return ['<wtf/Vector.h>'] + headers_for_type(element_type) 440 441 special_cases = { 442 'WTF::String': '<wtf/text/WTFString.h>', 443 'WebCore::CompositionUnderline': '<WebCore/Editor.h>', 444 'WebCore::GrammarDetail': '<WebCore/TextCheckerClient.h>', 445 'WebCore::KeypressCommand': '<WebCore/KeyboardEvent.h>', 446 'WebCore::PluginInfo': '<WebCore/PluginData.h>', 447 'WebCore::TextCheckingResult': '<WebCore/TextCheckerClient.h>', 448 'WebKit::WebGestureEvent': '"WebEvent.h"', 449 'WebKit::WebKeyboardEvent': '"WebEvent.h"', 450 'WebKit::WebMouseEvent': '"WebEvent.h"', 451 'WebKit::WebTouchEvent': '"WebEvent.h"', 452 'WebKit::WebWheelEvent': '"WebEvent.h"', 453 } 454 if type in special_cases: 455 return [special_cases[type]] 456 457 # We assume that we must include a header for a type iff it has a scope 458 # resolution operator (::). 459 split = type.split('::') 460 if len(split) < 2: 461 return [] 462 if split[0] == 'WebKit' or split[0] == 'CoreIPC': 463 return ['"%s.h"' % split[1]] 464 return ['<%s/%s.h>' % tuple(split)] 465 466 467def generate_message_handler(file): 468 receiver = MessageReceiver.parse(file) 469 headers = { 470 '"%s"' % messages_header_filename(receiver): None, 471 '"HandleMessage.h"': None, 472 '"ArgumentDecoder.h"': None, 473 } 474 475 type_conditions = {} 476 for parameter in receiver.iterparameters(): 477 type = parameter.type 478 condition = parameter.condition 479 480 if type in type_conditions: 481 if not type_conditions[type]: 482 condition = type_conditions[type] 483 else: 484 if not condition: 485 type_conditions[type] = condition 486 else: 487 type_conditions[type] = condition 488 489 argument_encoder_headers = argument_coder_headers_for_type(parameter.type) 490 if argument_encoder_headers: 491 for header in argument_encoder_headers: 492 headers[header] = condition 493 continue 494 495 type_headers = headers_for_type(type) 496 for header in type_headers: 497 headers[header] = condition 498 499 for message in receiver.messages: 500 if message.reply_parameters is not None: 501 for reply_parameter in message.reply_parameters: 502 type = reply_parameter.type 503 argument_encoder_headers = argument_coder_headers_for_type(type) 504 if argument_encoder_headers: 505 for header in argument_encoder_headers: 506 headers[header] = message.condition 507 continue 508 509 type_headers = headers_for_type(type) 510 for header in type_headers: 511 headers[header] = message.condition 512 513 result = [] 514 515 result.append(_license_header) 516 result.append('#include "config.h"\n') 517 result.append('\n') 518 519 if receiver.condition: 520 result.append('#if %s\n\n' % receiver.condition) 521 522 result.append('#include "%s.h"\n\n' % receiver.name) 523 for headercondition in sorted(headers): 524 if headers[headercondition]: 525 result.append('#if %s\n' % headers[headercondition]) 526 result += ['#include %s\n' % headercondition] 527 result.append('#endif\n') 528 else: 529 result += ['#include %s\n' % headercondition] 530 result.append('\n') 531 532 sync_delayed_messages = [] 533 for message in receiver.messages: 534 if message.reply_parameters != None and message.is_delayed: 535 sync_delayed_messages.append(message) 536 537 if sync_delayed_messages: 538 result.append('namespace Messages {\n\nnamespace %s {\n\n' % receiver.name) 539 540 for message in sync_delayed_messages: 541 send_parameters = [(function_parameter_type(x.type), x.name) for x in message.reply_parameters] 542 543 if message.condition: 544 result.append('#if %s\n\n' % message.condition) 545 546 result.append('%s::DelayedReply::DelayedReply(PassRefPtr<CoreIPC::Connection> connection, PassOwnPtr<CoreIPC::ArgumentEncoder> arguments)\n' % message.name) 547 result.append(' : m_connection(connection)\n') 548 result.append(' , m_arguments(arguments)\n') 549 result.append('{\n') 550 result.append('}\n') 551 result.append('\n') 552 result.append('%s::DelayedReply::~DelayedReply()\n' % message.name) 553 result.append('{\n') 554 result.append(' ASSERT(!m_connection);\n') 555 result.append('}\n') 556 result.append('\n') 557 result.append('bool %s::DelayedReply::send(%s)\n' % (message.name, ', '.join([' '.join(x) for x in send_parameters]))) 558 result.append('{\n') 559 result.append(' ASSERT(m_arguments);\n') 560 result += [' m_arguments->encode(%s);\n' % x.name for x in message.reply_parameters] 561 result.append(' bool result = m_connection->sendSyncReply(m_arguments.release());\n') 562 result.append(' m_connection = nullptr;\n') 563 result.append(' return result;\n') 564 result.append('}\n') 565 result.append('\n') 566 567 if message.condition: 568 result.append('#endif\n\n') 569 570 result.append('} // namespace %s\n\n} // namespace Messages\n\n' % receiver.name) 571 572 result.append('namespace WebKit {\n\n') 573 574 async_messages = [] 575 sync_messages = [] 576 for message in receiver.messages: 577 if message.reply_parameters is not None: 578 sync_messages.append(message) 579 else: 580 async_messages.append(message) 581 582 if async_messages: 583 result.append('void %s::didReceive%sMessage(CoreIPC::Connection*, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments)\n' % (receiver.name, receiver.name)) 584 result.append('{\n') 585 result.append(' switch (messageID.get<Messages::%s::Kind>()) {\n' % receiver.name) 586 result += [async_case_statement(receiver, message) for message in async_messages] 587 result.append(' default:\n') 588 result.append(' break;\n') 589 result.append(' }\n\n') 590 result.append(' ASSERT_NOT_REACHED();\n') 591 result.append('}\n') 592 593 if sync_messages: 594 result.append('\n') 595 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 '')) 596 result.append('{\n') 597 result.append(' switch (messageID.get<Messages::%s::Kind>()) {\n' % receiver.name) 598 result += [sync_case_statement(receiver, message) for message in sync_messages] 599 result.append(' default:\n') 600 result.append(' break;\n') 601 result.append(' }\n\n') 602 result.append(' ASSERT_NOT_REACHED();\n') 603 result.append(' return CoreIPC::AutomaticReply;\n') 604 result.append('}\n') 605 606 result.append('\n} // namespace WebKit\n') 607 608 if receiver.condition: 609 result.append('\n#endif // %s\n' % receiver.condition) 610 611 return ''.join(result) 612