Reviewed by Anders Carlsson and Darin Adler.
[WebKit-https.git] / Source / WebKit2 / Scripts / webkit2 / messages.py
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
23 import collections
24 import itertools
25 import 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 class MessageReceiver(object):
54     def __init__(self, name, messages, condition):
55         self.name = name
56         self.messages = messages
57         self.condition = condition
58
59     def iterparameters(self):
60         return itertools.chain((parameter for message in self.messages for parameter in message.parameters),
61             (reply_parameter for message in self.messages if message.reply_parameters for reply_parameter in message.reply_parameters))
62
63     @classmethod
64     def parse(cls, file):
65         destination = None
66         messages = []
67         condition = None
68         master_condition = None
69         for line in file:
70             match = re.search(r'messages -> ([A-Za-z_0-9]+) {', line)
71             if match:
72                 if condition:
73                     master_condition = condition
74                     condition = None
75                 destination = match.group(1)
76                 continue
77             if line.startswith('#'):
78                 if line.startswith('#if '):
79                     condition = line.rstrip()[4:]
80                 elif line.startswith('#endif'):
81                     condition = None
82                 continue
83             match = re.search(r'([A-Za-z_0-9]+)\((.*?)\)(?:(?:\s+->\s+)\((.*?)\)(?:\s+(delayed))?)?', line)
84             if match:
85                 name, parameters_string, reply_parameters_string, delayed_string = match.groups()
86                 if parameters_string:
87                     parameters = parse_parameter_string(parameters_string)
88                 else:
89                     parameters = []
90
91                 delayed = delayed_string == 'delayed'
92
93                 if reply_parameters_string:
94                     reply_parameters = parse_parameter_string(reply_parameters_string)
95                 elif reply_parameters_string == '':
96                     reply_parameters = []
97                 else:
98                     reply_parameters = None
99
100                 messages.append(Message(name, parameters, reply_parameters, delayed, condition))
101         return MessageReceiver(destination, messages, master_condition)
102
103
104 class Message(object):
105     def __init__(self, name, parameters, reply_parameters, delayed, condition):
106         self.name = name
107         self.parameters = parameters
108         self.reply_parameters = reply_parameters
109         if self.reply_parameters is not None:
110             self.delayed = delayed
111         self.condition = condition
112         if len(self.parameters) != 0:
113             self.is_variadic = parameter_type_is_variadic(self.parameters[-1].type)
114         else:
115             self.is_variadic = False
116
117     def id(self):
118         return '%sID' % self.name
119
120
121 class Parameter(object):
122     def __init__(self, type, name):
123         self.type = type
124         self.name = name
125
126
127 def parse_parameter_string(parameter_string):
128     return [Parameter(*type_and_name.rsplit(' ', 1)) for type_and_name in parameter_string.split(', ')]
129
130
131 def messages_header_filename(receiver):
132     return '%sMessages.h' % receiver.name
133
134
135 def surround_in_condition(string, condition):
136     if not condition:
137         return string
138     return '#if %s\n%s#endif\n' % (condition, string)
139
140
141 def messages_to_kind_enum(messages):
142     result = []
143     result.append('enum Kind {\n')
144     result += [surround_in_condition('    %s,\n' % message.id(), message.condition) for message in messages]
145     result.append('};\n')
146     return ''.join(result)
147
148
149 def parameter_type_is_variadic(type):
150     variadic_types = frozenset([
151         'WebKit::InjectedBundleUserMessageEncoder',
152         'WebKit::WebContextUserMessageEncoder',
153     ])
154
155     return type in variadic_types
156
157 def function_parameter_type(type):
158     # Don't use references for built-in types.
159     builtin_types = frozenset([
160         'bool',
161         'float',
162         'double',
163         'uint8_t',
164         'uint16_t',
165         'uint32_t',
166         'uint64_t',
167         'int8_t',
168         'int16_t',
169         'int32_t',
170         'int64_t',
171     ])
172
173     if type in builtin_types:
174         return type
175
176     return 'const %s&' % type
177
178
179 def reply_parameter_type(type):
180     return '%s&' % type
181
182
183 def arguments_type(parameters, parameter_type_function):
184     arguments_type = 'CoreIPC::Arguments%d' % len(parameters)
185     if len(parameters):
186         arguments_type = '%s<%s>' % (arguments_type, ', '.join(parameter_type_function(parameter.type) for parameter in parameters))
187     return arguments_type
188
189
190 def base_class(message):
191     return arguments_type(message.parameters, function_parameter_type)
192
193
194 def reply_type(message):
195     return arguments_type(message.reply_parameters, reply_parameter_type)
196
197
198 def decode_type(message):
199     if message.is_variadic:
200         return arguments_type(message.parameters[:-1], reply_parameter_type)
201     return base_class(message)
202
203
204 def delayed_reply_type(message):
205     return arguments_type(message.reply_parameters, function_parameter_type)
206
207
208 def message_to_struct_declaration(message):
209     result = []
210     function_parameters = [(function_parameter_type(x.type), x.name) for x in message.parameters]
211     result.append('struct %s : %s' % (message.name, base_class(message)))
212     result.append(' {\n')
213     result.append('    static const Kind messageID = %s;\n' % message.id())
214     if message.reply_parameters != None:
215         if message.delayed:
216             send_parameters = [(function_parameter_type(x.type), x.name) for x in message.reply_parameters]
217             result.append('    struct DelayedReply {\n')
218             result.append('        DelayedReply(PassRefPtr<CoreIPC::Connection> connection, PassOwnPtr<CoreIPC::ArgumentDecoder> arguments)\n')
219             result.append('            : m_connection(connection)\n')
220             result.append('            , m_arguments(arguments)\n')
221             result.append('        {\n')
222             result.append('        }\n')
223             result.append('\n')
224             result.append('        bool send(%s)\n' % ', '.join([' '.join(x) for x in send_parameters]))
225             result.append('        {\n')
226             result.append('            ASSERT(m_arguments);\n')
227             result += ['            m_arguments->encode(%s);\n' % x.name for x in message.reply_parameters]
228             result.append('            bool result = m_connection->sendSyncReply(m_arguments.release());\n')
229             result.append('            m_connection = nullptr;\n')
230             result.append('            return result;\n')
231             result.append('        }\n')
232             result.append('\n')
233             result.append('    private:\n')
234             result.append('        RefPtr<CoreIPC::Connection> m_connection;\n')
235             result.append('        OwnPtr<CoreIPC::ArgumentDecoder> m_arguments;\n')
236             result.append('    };\n\n')
237         else:
238             result.append('    typedef %s Reply;\n' % reply_type(message))
239
240     result.append('    typedef %s DecodeType;\n' % decode_type(message))
241     if len(function_parameters):
242         result.append('    %s%s(%s)' % (len(function_parameters) == 1 and 'explicit ' or '', message.name, ', '.join([' '.join(x) for x in function_parameters])))
243         result.append('\n        : %s(%s)\n' % (base_class(message), ', '.join([x[1] for x in function_parameters])))
244         result.append('    {\n')
245         result.append('    }\n')
246     result.append('};\n')
247     return surround_in_condition(''.join(result), message.condition)
248
249
250 def struct_or_class(namespace, type):
251     structs = frozenset([
252         'WebCore::CompositionUnderline',
253         'WebCore::KeypressCommand',
254         'WebCore::PluginInfo',
255         'WebCore::PrintInfo',
256         'WebCore::ViewportArguments',
257         'WebCore::WindowFeatures',
258         'WebKit::DrawingAreaInfo',
259         'WebKit::PlatformPopupMenuData',
260         'WebKit::PluginProcessCreationParameters',
261         'WebKit::WebNavigationDataStore',
262         'WebKit::WebOpenPanelParameters::Data',
263         'WebKit::WebPageCreationParameters',
264         'WebKit::WebPreferencesStore',
265         'WebKit::WebProcessCreationParameters',
266     ])
267
268     qualified_name = '%s::%s' % (namespace, type)
269     if qualified_name in structs:
270         return 'struct %s' % type
271
272     return 'class %s' % type
273
274 def forward_declarations_for_namespace(namespace, types):
275     result = []
276     result.append('namespace %s {\n' % namespace)
277     result += ['    %s;\n' % struct_or_class(namespace, x) for x in types]
278     result.append('}\n')
279     return ''.join(result)
280
281
282 def forward_declarations_and_headers(receiver):
283     types_by_namespace = collections.defaultdict(set)
284
285     headers = set([
286         '"Arguments.h"',
287         '"MessageID.h"',
288     ])
289
290     for parameter in receiver.iterparameters():
291         type = parameter.type
292
293         if type.find('<') != -1:
294             # Don't forward declare class templates.
295             headers.update(headers_for_type(type))
296             continue
297
298         split = type.split('::')
299
300         if len(split) == 2:
301             namespace = split[0]
302             inner_type = split[1]
303             types_by_namespace[namespace].add(inner_type)
304         elif len(split) > 2:
305             # We probably have a nested struct, which means we can't forward declare it.
306             # Include its header instead.
307             headers.update(headers_for_type(type))
308
309     forward_declarations = '\n'.join([forward_declarations_for_namespace(namespace, types) for (namespace, types) in sorted(types_by_namespace.iteritems())])
310     headers = ['#include %s\n' % header for header in sorted(headers)]
311
312     return (forward_declarations, headers)
313
314 def generate_messages_header(file):
315     receiver = MessageReceiver.parse(file)
316     header_guard = messages_header_filename(receiver).replace('.', '_')
317
318     result = []
319
320     result.append(_license_header)
321     result.append('\n')
322
323     result.append('#ifndef %s\n' % header_guard)
324     result.append('#define %s\n\n' % header_guard)
325
326     if receiver.condition:
327         result.append('#if %s\n\n' % receiver.condition)
328
329     forward_declarations, headers = forward_declarations_and_headers(receiver)
330
331     result += headers
332     result.append('\n')
333
334     result.append(forward_declarations)
335     result.append('\n')
336
337     result.append('namespace Messages {\n\nnamespace %s {\n\n' % receiver.name)
338     result.append(messages_to_kind_enum(receiver.messages))
339     result.append('\n')
340     result.append('\n'.join([message_to_struct_declaration(x) for x in receiver.messages]))
341     result.append('\n} // namespace %s\n\n} // namespace Messages\n' % receiver.name)
342
343     result.append('\nnamespace CoreIPC {\n\n')
344     result.append('template<> struct MessageKindTraits<Messages::%s::Kind> {\n' % receiver.name)
345     result.append('    static const MessageClass messageClass = MessageClass%s;\n' % receiver.name)
346     result.append('};\n')
347     result.append('\n} // namespace CoreIPC\n')
348
349     if receiver.condition:
350         result.append('\n#endif // %s\n' % receiver.condition)
351
352     result.append('\n#endif // %s\n' % header_guard)
353
354     return ''.join(result)
355
356
357 def handler_function(receiver, message):
358     return '%s::%s' % (receiver.name, message.name[0].lower() + message.name[1:])
359
360
361 def async_case_statement(receiver, message):
362     dispatch_function = 'handleMessage'
363     if message.is_variadic:
364         dispatch_function += 'Variadic'
365
366     result = []
367     result.append('    case Messages::%s::%s:\n' % (receiver.name, message.id()))
368     result.append('        CoreIPC::%s<Messages::%s::%s>(arguments, this, &%s);\n' % (dispatch_function, receiver.name, message.name, handler_function(receiver, message)))
369     result.append('        return;\n')
370     return surround_in_condition(''.join(result), message.condition)
371
372
373 def sync_case_statement(receiver, message):
374     result = []
375     result.append('    case Messages::%s::%s:\n' % (receiver.name, message.id()))
376     result.append('        CoreIPC::handleMessage<Messages::%s::%s>(arguments, reply, this, &%s);\n' % (receiver.name, message.name, handler_function(receiver, message)))
377     # FIXME: Handle delayed replies
378     result.append('        return CoreIPC::AutomaticReply;\n')
379     return surround_in_condition(''.join(result), message.condition)
380
381
382 def argument_coder_headers_for_type(type):
383     # Check for Vector.
384     match = re.search(r'Vector<(.+)>', type)
385     if match:
386         element_type = match.groups()[0].strip()
387         return ['"ArgumentCoders.h"'] + argument_coder_headers_for_type(element_type)
388
389     special_cases = {
390         'WTF::String': '"ArgumentCoders.h"',
391         'WebKit::InjectedBundleUserMessageEncoder': '"InjectedBundleUserMessageCoders.h"',
392         'WebKit::WebContextUserMessageEncoder': '"WebContextUserMessageCoders.h"',
393     }
394
395     if type in special_cases:
396         return [special_cases[type]]
397
398     split = type.split('::')
399     if len(split) < 2:
400         return []
401     if split[0] == 'WebCore':
402         return ['"WebCoreArgumentCoders.h"']
403
404     return []
405
406
407 def headers_for_type(type):
408     # Check for Vector.
409     match = re.search(r'Vector<(.+)>', type)
410     if match:
411         element_type = match.groups()[0].strip()
412         return ['<wtf/Vector.h>'] + headers_for_type(element_type)
413
414     special_cases = {
415         'WTF::String': '<wtf/text/WTFString.h>',
416         'WebCore::CompositionUnderline': '<WebCore/Editor.h>',
417         'WebCore::KeypressCommand': '<WebCore/KeyboardEvent.h>',
418         'WebCore::PluginInfo': '<WebCore/PluginData.h>',
419         'WebCore::TextCheckingResult': '<WebCore/EditorClient.h>',
420         'WebKit::WebKeyboardEvent': '"WebEvent.h"',
421         'WebKit::WebMouseEvent': '"WebEvent.h"',
422         'WebKit::WebTouchEvent': '"WebEvent.h"',
423         'WebKit::WebWheelEvent': '"WebEvent.h"',
424     }
425     if type in special_cases:
426         return [special_cases[type]]
427
428     # We assume that we must include a header for a type iff it has a scope
429     # resolution operator (::).
430     split = type.split('::')
431     if len(split) < 2:
432         return []
433     if split[0] == 'WebKit' or split[0] == 'CoreIPC':
434         return ['"%s.h"' % split[1]]
435     return ['<%s/%s.h>' % tuple(split)]
436
437
438 def generate_message_handler(file):
439     receiver = MessageReceiver.parse(file)
440     headers = set([
441         '"%s"' % messages_header_filename(receiver),
442         '"HandleMessage.h"',
443         '"ArgumentDecoder.h"',
444     ])
445
446     for parameter in receiver.iterparameters():
447         type = parameter.type
448         argument_encoder_headers = argument_coder_headers_for_type(parameter.type)
449         if argument_encoder_headers:
450             headers.update(argument_encoder_headers)
451             continue
452
453         type_headers = headers_for_type(type)
454         headers.update(type_headers)
455
456     for message in receiver.messages:
457         if message.reply_parameters is not None:
458             for reply_parameter in message.reply_parameters:
459                 type = reply_parameter.type
460                 argument_encoder_headers = argument_coder_headers_for_type(type)
461                 if argument_encoder_headers:
462                     headers.update(argument_encoder_headers)
463                     continue
464
465                 type_headers = headers_for_type(type)
466                 headers.update(type_headers)
467
468     result = []
469
470     result.append(_license_header)
471     result.append('\n')
472
473     if receiver.condition:
474         result.append('#if %s\n\n' % receiver.condition)
475
476     result.append('#include "%s.h"\n\n' % receiver.name)
477     result += ['#include %s\n' % header for header in sorted(headers)]
478     result.append('\n')
479
480     result.append('namespace WebKit {\n\n')
481
482     async_messages = []
483     sync_messages = []
484     for message in receiver.messages:
485         if message.reply_parameters is not None:
486             sync_messages.append(message)
487         else:
488             async_messages.append(message)
489
490     if async_messages:
491         result.append('void %s::didReceive%sMessage(CoreIPC::Connection*, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments)\n' % (receiver.name, receiver.name))
492         result.append('{\n')
493         result.append('    switch (messageID.get<Messages::%s::Kind>()) {\n' % receiver.name)
494         result += [async_case_statement(receiver, message) for message in async_messages]
495         result.append('    default:\n')
496         result.append('        break;\n')
497         result.append('    }\n\n')
498         result.append('    ASSERT_NOT_REACHED();\n')
499         result.append('}\n')
500
501     if sync_messages:
502         result.append('\n')
503         result.append('CoreIPC::SyncReplyMode %s::didReceiveSync%sMessage(CoreIPC::Connection*, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, CoreIPC::ArgumentEncoder* reply)\n' % (receiver.name, receiver.name))
504         result.append('{\n')
505         result.append('    switch (messageID.get<Messages::%s::Kind>()) {\n' % receiver.name)
506         result += [sync_case_statement(receiver, message) for message in sync_messages]
507         result.append('    default:\n')
508         result.append('        break;\n')
509         result.append('    }\n\n')
510         result.append('    ASSERT_NOT_REACHED();\n')
511         result.append('    return CoreIPC::AutomaticReply;\n')
512         result.append('}\n')
513
514     result.append('\n} // namespace WebKit\n')
515
516     if receiver.condition:
517         result.append('\n#endif // %s\n' % receiver.condition)
518
519     return ''.join(result)