3 # Copyright (c) 2014 Apple Inc. All rights reserved.
4 # Copyright (c) 2014 University of Washington. All rights reserved.
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
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.
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.
30 from string
import Template
32 from generator_templates
import GeneratorTemplates
as Templates
33 from models
import PrimitiveType
, ObjectType
, ArrayType
, EnumType
, AliasedType
, Frameworks
35 log
= logging
.getLogger('global')
39 return str[:1].upper() + str[1:]
41 _ALWAYS_UPPERCASED_ENUM_VALUE_SUBSTRINGS
= set(['API', 'CSS', 'DOM', 'HTML', 'XHR', 'XML'])
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.
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",
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"
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.
70 # InspectorResourceAgent needs to update mime-type.
72 # For testing purposes only.
73 "Test.OpenParameterBundle"
78 def __init__(self
, model
, input_filepath
):
80 self
._input
_filepath
= input_filepath
85 def generate_license(self
):
86 return Template(Templates
.CopyrightBlock
).substitute(None, inputFilename
=os
.path
.basename(self
._input
_filepath
))
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
)
92 def domains_to_generate(self
):
93 return self
.non_supplemental_domains()
95 def generate_output(self
):
98 def output_filename(self
):
101 def encoding_for_enum_value(self
, enum_value
):
102 if not hasattr(self
, "_assigned_enum_values"):
103 self
._traverse
_and
_assign
_enum
_values
()
105 return self
._enum
_value
_encodings
[enum_value
]
107 def assigned_enum_values(self
):
108 if not hasattr(self
, "_assigned_enum_values"):
109 self
._traverse
_and
_assign
_enum
_values
()
111 return self
._assigned
_enum
_values
[:] # Slice.
114 def type_needs_runtime_casts(_type
):
115 return _type
.qualified_name() in _TYPES_NEEDING_RUNTIME_CASTS
118 def type_has_open_fields(_type
):
119 return _type
.qualified_name() in _TYPES_WITH_OPEN_FIELDS
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
)
125 return _type
in self
._types
_needing
_shape
_assertions
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
))
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
:
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
)
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
)
160 self
._types
_needing
_shape
_assertions
= found_types
162 # Private helper instance methods.
163 def _traverse_and_assign_enum_values(self
):
164 self
._enum
_value
_encodings
= {}
165 self
._assigned
_enum
_values
= []
168 domains
= self
.non_supplemental_domains()
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)
176 for domain
in domains
:
177 for event
in domain
.events
:
178 all_types
.extend([parameter
.type for parameter
in event
.event_parameters
])
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
])
185 for _type
in all_types
:
186 if not isinstance(_type
, EnumType
):
188 map(self
._assign
_encoding
_for
_enum
_value
, _type
.enum_values())
190 def _assign_encoding_for_enum_value(self
, enum_value
):
191 if enum_value
in self
._enum
_value
_encodings
:
194 self
._enum
_value
_encodings
[enum_value
] = len(self
._assigned
_enum
_values
)
195 self
._assigned
_enum
_values
.append(enum_value
)
197 # Miscellaneous text manipulation routines.
198 def wrap_with_guard_for_domain(self
, domain
, text
):
199 if self
.model().framework
is Frameworks
.WebInspector
:
201 guard
= domain
.feature_guard
203 return Generator
.wrap_with_guard(guard
, text
)
207 def wrap_with_guard(guard
, text
):
211 '#endif // %s' % guard
,
215 def stylized_name_for_enum_value(enum_value
):
216 regex
= '(%s)' % "|".join(_ALWAYS_UPPERCASED_ENUM_VALUE_SUBSTRINGS
)
218 def replaceCallback(match
):
219 return match
.group(1).upper()
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
))
226 def js_name_for_parameter_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.
233 if isinstance(_type
, (ArrayType
, ObjectType
)):
235 if isinstance(_type
, PrimitiveType
):
236 if _type
.qualified_name() in ['object', 'any']:
238 elif _type
.qualified_name() in ['integer', 'number']:
241 return _type
.qualified_name()