57b842bcf94edcb72e61339c711d76332bb5517e
[WebKit-https.git] / Source / JavaScriptCore / inspector / scripts / codegen / generate_cpp_protocol_types_implementation.py
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2014, 2016 Apple Inc. All rights reserved.
4 # Copyright (c) 2014 University of Washington. All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 #    notice, this list of conditions and the following disclaimer in the
13 #    documentation and/or other materials provided with the distribution.
14 #
15 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25 # THE POSSIBILITY OF SUCH DAMAGE.
26
27
28 import logging
29 import string
30 from string import Template
31 from operator import methodcaller
32
33 from cpp_generator import CppGenerator
34 from cpp_generator_templates import CppGeneratorTemplates as CppTemplates
35 from generator import Generator, ucfirst
36 from models import AliasedType, ArrayType, EnumType, ObjectType
37
38 log = logging.getLogger('global')
39
40
41 class CppProtocolTypesImplementationGenerator(CppGenerator):
42     def __init__(self, model, input_filepath):
43         CppGenerator.__init__(self, model, input_filepath)
44
45     def output_filename(self):
46         return "%sProtocolObjects.cpp" % self.protocol_name()
47
48     def generate_output(self):
49         domains = self.domains_to_generate()
50         self.calculate_types_requiring_shape_assertions(domains)
51
52         secondary_headers = [
53             '<wtf/Optional.h>',
54             '<wtf/text/CString.h>',
55         ]
56
57         header_args = {
58             'primaryInclude': '"%sProtocolObjects.h"' % self.protocol_name(),
59             'secondaryIncludes': "\n".join(['#include %s' % header for header in secondary_headers]),
60         }
61
62         sections = []
63         sections.append(self.generate_license())
64         sections.append(Template(CppTemplates.ImplementationPrelude).substitute(None, **header_args))
65         sections.append('namespace Protocol {')
66         sections.extend(self._generate_enum_mapping_and_conversion_methods(domains))
67         sections.append(self._generate_open_field_names())
68         builder_sections = map(self._generate_builders_for_domain, domains)
69         sections.extend(filter(lambda section: len(section) > 0, builder_sections))
70         sections.append('} // namespace Protocol')
71         sections.append(Template(CppTemplates.ImplementationPostlude).substitute(None, **header_args))
72
73         return "\n\n".join(sections)
74
75     # Private methods.
76
77     def _generate_enum_mapping(self):
78         if not self.assigned_enum_values():
79             return []
80
81         lines = []
82         lines.append('static const char* const enum_constant_values[] = {')
83         lines.extend(['    "%s",' % enum_value for enum_value in self.assigned_enum_values()])
84         lines.append('};')
85         lines.append('')
86         lines.append('String getEnumConstantValue(int code) {')
87         lines.append('    return enum_constant_values[code];')
88         lines.append('}')
89         return ['\n'.join(lines)]
90
91     def _generate_enum_conversion_methods_for_domain(self, domain):
92
93         def type_member_is_anonymous_enum_type(type_member):
94             return isinstance(type_member.type, EnumType) and type_member.type.is_anonymous
95
96         def generate_conversion_method_body(enum_type, cpp_protocol_type):
97             body_lines = []
98             body_lines.extend([
99                 'template<>',
100                 'Optional<%s> parseEnumValueFromString<%s>(const String& protocolString)' % (cpp_protocol_type, cpp_protocol_type),
101                 '{',
102                 '    static const size_t constantValues[] = {',
103             ])
104
105             enum_values = enum_type.enum_values()
106             for enum_value in enum_values:
107                 body_lines.append('        (size_t)%s::%s,' % (cpp_protocol_type, Generator.stylized_name_for_enum_value(enum_value)))
108
109             body_lines.extend([
110                 '    };',
111                 '    for (size_t i = 0; i < %d; ++i)' % len(enum_values),
112                 '        if (protocolString == enum_constant_values[constantValues[i]])',
113                 '            return (%s)constantValues[i];' % cpp_protocol_type,
114                 '',
115                 '    return Nullopt;',
116                 '}',
117                 '',
118             ])
119             return body_lines
120
121         declaration_types = [decl.type for decl in domain.type_declarations]
122         object_types = filter(lambda _type: isinstance(_type, ObjectType), declaration_types)
123         enum_types = filter(lambda _type: isinstance(_type, EnumType), declaration_types)
124         if len(object_types) + len(enum_types) == 0:
125             return ''
126
127         sorted(object_types, key=methodcaller('raw_name'))
128         sorted(enum_types, key=methodcaller('raw_name'))
129
130         lines = []
131         lines.append("// Enums in the '%s' Domain" % domain.domain_name)
132         for enum_type in enum_types:
133             cpp_protocol_type = CppGenerator.cpp_protocol_type_for_type(enum_type)
134             lines.extend(generate_conversion_method_body(enum_type, cpp_protocol_type))
135
136         for object_type in object_types:
137             for enum_member in filter(type_member_is_anonymous_enum_type, object_type.members):
138                 cpp_protocol_type = CppGenerator.cpp_protocol_type_for_type_member(enum_member, object_type.declaration())
139                 lines.extend(generate_conversion_method_body(enum_member.type, cpp_protocol_type))
140
141         if len(lines) == 1:
142             return ''  # No real declarations to emit, just the domain comment.
143
144         return self.wrap_with_guard_for_domain(domain, '\n'.join(lines))
145
146     def _generate_enum_mapping_and_conversion_methods(self, domains):
147         sections = []
148         sections.append('namespace %s {' % self.helpers_namespace())
149         sections.extend(self._generate_enum_mapping())
150         enum_parser_sections = map(self._generate_enum_conversion_methods_for_domain, domains)
151         sections.extend(filter(lambda section: len(section) > 0, enum_parser_sections))
152         if len(sections) == 1:
153             return []  # No declarations to emit, just the namespace.
154
155         sections.append('} // namespace %s' % self.helpers_namespace())
156         return sections
157
158     def _generate_open_field_names(self):
159         lines = []
160         for domain in self.domains_to_generate():
161             for type_declaration in filter(lambda decl: Generator.type_has_open_fields(decl.type), domain.type_declarations):
162                 for type_member in sorted(type_declaration.type_members, key=lambda member: member.member_name):
163                     field_name = '::'.join(['Inspector', 'Protocol', domain.domain_name, ucfirst(type_declaration.type_name), ucfirst(type_member.member_name)])
164                     lines.append('const char* %s = "%s";' % (field_name, type_member.member_name))
165
166         return '\n'.join(lines)
167
168     def _generate_builders_for_domain(self, domain):
169         sections = []
170         declarations_to_generate = filter(lambda decl: self.type_needs_shape_assertions(decl.type), domain.type_declarations)
171
172         for type_declaration in declarations_to_generate:
173             for type_member in type_declaration.type_members:
174                 if isinstance(type_member.type, EnumType):
175                     sections.append(self._generate_assertion_for_enum(type_member, type_declaration))
176
177             if isinstance(type_declaration.type, ObjectType):
178                 sections.append(self._generate_assertion_for_object_declaration(type_declaration))
179                 if Generator.type_needs_runtime_casts(type_declaration.type):
180                     sections.append(self._generate_runtime_cast_for_object_declaration(type_declaration))
181
182         return '\n\n'.join(sections)
183
184     def _generate_runtime_cast_for_object_declaration(self, object_declaration):
185         args = {
186             'objectType': CppGenerator.cpp_protocol_type_for_type(object_declaration.type)
187         }
188         return Template(CppTemplates.ProtocolObjectRuntimeCast).substitute(None, **args)
189
190     def _generate_assertion_for_object_declaration(self, object_declaration):
191         required_members = filter(lambda member: not member.is_optional, object_declaration.type_members)
192         optional_members = filter(lambda member: member.is_optional, object_declaration.type_members)
193         should_count_properties = not Generator.type_has_open_fields(object_declaration.type)
194         lines = []
195
196         lines.append('#if !ASSERT_DISABLED')
197         lines.append('void BindingTraits<%s>::assertValueHasExpectedType(Inspector::InspectorValue* value)' % (CppGenerator.cpp_protocol_type_for_type(object_declaration.type)))
198         lines.append("""{
199     ASSERT_ARG(value, value);
200     RefPtr<InspectorObject> object;
201     bool castSucceeded = value->asObject(object);
202     ASSERT_UNUSED(castSucceeded, castSucceeded);""")
203         for type_member in required_members:
204             args = {
205                 'memberName': type_member.member_name,
206                 'assertMethod': CppGenerator.cpp_assertion_method_for_type_member(type_member, object_declaration)
207             }
208
209             lines.append("""    {
210         InspectorObject::iterator %(memberName)sPos = object->find(ASCIILiteral("%(memberName)s"));
211         ASSERT(%(memberName)sPos != object->end());
212         %(assertMethod)s(%(memberName)sPos->value.get());
213     }""" % args)
214
215         if should_count_properties:
216             lines.append('')
217             lines.append('    int foundPropertiesCount = %s;' % len(required_members))
218
219         for type_member in optional_members:
220             args = {
221                 'memberName': type_member.member_name,
222                 'assertMethod': CppGenerator.cpp_assertion_method_for_type_member(type_member, object_declaration)
223             }
224
225             lines.append("""    {
226         InspectorObject::iterator %(memberName)sPos = object->find(ASCIILiteral("%(memberName)s"));
227         if (%(memberName)sPos != object->end()) {
228             %(assertMethod)s(%(memberName)sPos->value.get());""" % args)
229
230             if should_count_properties:
231                 lines.append('            ++foundPropertiesCount;')
232             lines.append('        }')
233             lines.append('    }')
234
235         if should_count_properties:
236             lines.append('    if (foundPropertiesCount != object->size())')
237             lines.append('        FATAL("Unexpected properties in object: %s\\n", object->toJSONString().ascii().data());')
238         lines.append('}')
239         lines.append('#endif // !ASSERT_DISABLED')
240         return '\n'.join(lines)
241
242     def _generate_assertion_for_enum(self, enum_member, object_declaration):
243         lines = []
244         lines.append('#if !ASSERT_DISABLED')
245         lines.append('void %s(Inspector::InspectorValue* value)' % CppGenerator.cpp_assertion_method_for_type_member(enum_member, object_declaration))
246         lines.append('{')
247         lines.append('    ASSERT_ARG(value, value);')
248         lines.append('    String result;')
249         lines.append('    bool castSucceeded = value->asString(result);')
250         lines.append('    ASSERT(castSucceeded);')
251
252         assert_condition = ' || '.join(['result == "%s"' % enum_value for enum_value in enum_member.type.enum_values()])
253         lines.append('    ASSERT(%s);' % assert_condition)
254         lines.append('}')
255         lines.append('#endif // !ASSERT_DISABLED')
256
257         return '\n'.join(lines)