]> git.saurik.com Git - apple/javascriptcore.git/blob - inspector/scripts/codegen/generator.py
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / inspector / scripts / codegen / generator.py
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2014 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 import logging
28 import os.path
29 import re
30 from string import Template
31
32 from generator_templates import GeneratorTemplates as Templates
33 from models import PrimitiveType, ObjectType, ArrayType, EnumType, AliasedType, Frameworks
34
35 log = logging.getLogger('global')
36
37
38 def ucfirst(str):
39 return str[:1].upper() + str[1:]
40
41 _ALWAYS_UPPERCASED_ENUM_VALUE_SUBSTRINGS = set(['API', 'CSS', 'DOM', 'HTML', 'XHR', 'XML'])
42
43 # These objects are built manually by creating and setting InspectorValues.
44 # Before sending these over the protocol, their shapes are checked against the specification.
45 # So, any types referenced by these types require debug-only assertions that check values.
46 # Calculating necessary assertions is annoying, and adds a lot of complexity to the generator.
47
48 # FIXME: This should be converted into a property in JSON.
49 _TYPES_NEEDING_RUNTIME_CASTS = set([
50 "Runtime.RemoteObject",
51 "Runtime.PropertyDescriptor",
52 "Runtime.InternalPropertyDescriptor",
53 "Runtime.CollectionEntry",
54 "Debugger.FunctionDetails",
55 "Debugger.CallFrame",
56 "Canvas.TraceLog",
57 "Canvas.ResourceInfo",
58 "Canvas.ResourceState",
59 # This should be a temporary hack. TimelineEvent should be created via generated C++ API.
60 "Timeline.TimelineEvent",
61 # For testing purposes only.
62 "Test.TypeNeedingCast"
63 ])
64
65 # FIXME: This should be converted into a property in JSON.
66 _TYPES_WITH_OPEN_FIELDS = set([
67 "Timeline.TimelineEvent",
68 # InspectorStyleSheet not only creates this property but wants to read it and modify it.
69 "CSS.CSSProperty",
70 # InspectorResourceAgent needs to update mime-type.
71 "Network.Response",
72 # For testing purposes only.
73 "Test.OpenParameterBundle"
74 ])
75
76
77 class Generator:
78 def __init__(self, model, input_filepath):
79 self._model = model
80 self._input_filepath = input_filepath
81
82 def model(self):
83 return self._model
84
85 def generate_license(self):
86 return Template(Templates.CopyrightBlock).substitute(None, inputFilename=os.path.basename(self._input_filepath))
87
88 # These methods are overridden by subclasses.
89 def non_supplemental_domains(self):
90 return filter(lambda domain: not domain.is_supplemental, self.model().domains)
91
92 def domains_to_generate(self):
93 return self.non_supplemental_domains()
94
95 def generate_output(self):
96 pass
97
98 def output_filename(self):
99 pass
100
101 def encoding_for_enum_value(self, enum_value):
102 if not hasattr(self, "_assigned_enum_values"):
103 self._traverse_and_assign_enum_values()
104
105 return self._enum_value_encodings[enum_value]
106
107 def assigned_enum_values(self):
108 if not hasattr(self, "_assigned_enum_values"):
109 self._traverse_and_assign_enum_values()
110
111 return self._assigned_enum_values[:] # Slice.
112
113 @staticmethod
114 def type_needs_runtime_casts(_type):
115 return _type.qualified_name() in _TYPES_NEEDING_RUNTIME_CASTS
116
117 @staticmethod
118 def type_has_open_fields(_type):
119 return _type.qualified_name() in _TYPES_WITH_OPEN_FIELDS
120
121 def type_needs_shape_assertions(self, _type):
122 if not hasattr(self, "_types_needing_shape_assertions"):
123 self.calculate_types_requiring_shape_assertions(self.model().domains)
124
125 return _type in self._types_needing_shape_assertions
126
127 # To restrict the domains over which we compute types needing assertions, call this method
128 # before generating any output with the desired domains parameter. The computed
129 # set of types will not be automatically regenerated on subsequent calls to
130 # Generator.types_needing_shape_assertions().
131 def calculate_types_requiring_shape_assertions(self, domains):
132 domain_names = map(lambda domain: domain.domain_name, domains)
133 log.debug("> Calculating types that need shape assertions (eligible domains: %s)" % ", ".join(domain_names))
134
135 # Mutates the passed-in set; this simplifies checks to prevent infinite recursion.
136 def gather_transitively_referenced_types(_type, gathered_types):
137 if _type in gathered_types:
138 return
139
140 if isinstance(_type, ObjectType):
141 log.debug("> Adding type %s to list of types needing shape assertions." % _type.qualified_name())
142 gathered_types.add(_type)
143 for type_member in _type.members:
144 gather_transitively_referenced_types(type_member.type, gathered_types)
145 elif isinstance(_type, EnumType):
146 log.debug("> Adding type %s to list of types needing shape assertions." % _type.qualified_name())
147 gathered_types.add(_type)
148 elif isinstance(_type, AliasedType):
149 gather_transitively_referenced_types(_type.aliased_type, gathered_types)
150 elif isinstance(_type, ArrayType):
151 gather_transitively_referenced_types(_type.element_type, gathered_types)
152
153 found_types = set()
154 for domain in domains:
155 for declaration in domain.type_declarations:
156 if declaration.type.qualified_name() in _TYPES_NEEDING_RUNTIME_CASTS:
157 log.debug("> Gathering types referenced by %s to generate shape assertions." % declaration.type.qualified_name())
158 gather_transitively_referenced_types(declaration.type, found_types)
159
160 self._types_needing_shape_assertions = found_types
161
162 # Private helper instance methods.
163 def _traverse_and_assign_enum_values(self):
164 self._enum_value_encodings = {}
165 self._assigned_enum_values = []
166 all_types = []
167
168 domains = self.non_supplemental_domains()
169
170 for domain in domains:
171 for type_declaration in domain.type_declarations:
172 all_types.append(type_declaration.type)
173 for type_member in type_declaration.type_members:
174 all_types.append(type_member.type)
175
176 for domain in domains:
177 for event in domain.events:
178 all_types.extend([parameter.type for parameter in event.event_parameters])
179
180 for domain in domains:
181 for command in domain.commands:
182 all_types.extend([parameter.type for parameter in command.call_parameters])
183 all_types.extend([parameter.type for parameter in command.return_parameters])
184
185 for _type in all_types:
186 if not isinstance(_type, EnumType):
187 continue
188 map(self._assign_encoding_for_enum_value, _type.enum_values())
189
190 def _assign_encoding_for_enum_value(self, enum_value):
191 if enum_value in self._enum_value_encodings:
192 return
193
194 self._enum_value_encodings[enum_value] = len(self._assigned_enum_values)
195 self._assigned_enum_values.append(enum_value)
196
197 # Miscellaneous text manipulation routines.
198 def wrap_with_guard_for_domain(self, domain, text):
199 if self.model().framework is Frameworks.WebInspector:
200 return text
201 guard = domain.feature_guard
202 if guard:
203 return Generator.wrap_with_guard(guard, text)
204 return text
205
206 @staticmethod
207 def wrap_with_guard(guard, text):
208 return '\n'.join([
209 '#if %s' % guard,
210 text,
211 '#endif // %s' % guard,
212 ])
213
214 @staticmethod
215 def stylized_name_for_enum_value(enum_value):
216 regex = '(%s)' % "|".join(_ALWAYS_UPPERCASED_ENUM_VALUE_SUBSTRINGS)
217
218 def replaceCallback(match):
219 return match.group(1).upper()
220
221 # Split on hyphen, introduce camelcase, and force uppercasing of acronyms.
222 subwords = map(ucfirst, enum_value.split('-'))
223 return re.sub(re.compile(regex, re.IGNORECASE), replaceCallback, "".join(subwords))
224
225 @staticmethod
226 def js_name_for_parameter_type(_type):
227 _type = _type
228 if isinstance(_type, AliasedType):
229 _type = _type.aliased_type # Fall through.
230 if isinstance(_type, EnumType):
231 _type = _type.primitive_type # Fall through.
232
233 if isinstance(_type, (ArrayType, ObjectType)):
234 return 'object'
235 if isinstance(_type, PrimitiveType):
236 if _type.qualified_name() in ['object', 'any']:
237 return 'object'
238 elif _type.qualified_name() in ['integer', 'number']:
239 return 'number'
240 else:
241 return _type.qualified_name()