OLD | NEW |
1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 # pylint: disable=import-error,print-statement,relative-import | 5 # pylint: disable=import-error,print-statement,relative-import |
6 | 6 |
7 """Generates Web Agent API bindings. | 7 """Generates Web Agent API bindings. |
8 | 8 |
9 The Web Agent API bindings provide a stable, IDL-generated interface for the | 9 The Web Agent API bindings provide a stable, IDL-generated interface for the |
10 Web Agents. | 10 Web Agents. |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
76 | 76 |
77 def type_from_definition(self, idl_definition): | 77 def type_from_definition(self, idl_definition): |
78 # TODO(dglazkov): The output of this method must be a reasonable C++ | 78 # TODO(dglazkov): The output of this method must be a reasonable C++ |
79 # type that can be used directly in the jinja2 template. | 79 # type that can be used directly in the jinja2 template. |
80 return idl_definition.idl_type.base_type | 80 return idl_definition.idl_type.base_type |
81 | 81 |
82 def base_class_includes(self): | 82 def base_class_includes(self): |
83 return set(['platform/heap/Handle.h']) | 83 return set(['platform/heap/Handle.h']) |
84 | 84 |
85 | 85 |
| 86 class MethodOverloadSplitter(object): |
| 87 """Because of union and optional types being used as arguments, some |
| 88 operations may result in more than one generated method. This class |
| 89 contains the logic for spliting an operation into multiple C++ overloads. |
| 90 """ |
| 91 |
| 92 def __init__(self, idl_operation): |
| 93 self.idl_operation = idl_operation |
| 94 |
| 95 def _update_argument_lists(self, argument_lists, idl_types): |
| 96 """Given a list of IdlTypes and an existing list of argument lists (yes, |
| 97 this is a list of lists), produces a next generation of the list of |
| 98 lists. This is where the actual splitting into overloads happens. |
| 99 """ |
| 100 result = [] |
| 101 for argument_list in argument_lists: |
| 102 for idl_type in idl_types: |
| 103 new_argument_list = list(argument_list) |
| 104 if idl_type is not None: |
| 105 new_argument_list.append(idl_type) |
| 106 result.append(new_argument_list) |
| 107 return result |
| 108 |
| 109 def _enumerate_argument_types(self, idl_argument): |
| 110 """Given an IdlArgument, returns a list of types that are included |
| 111 in this argument. If optional, the list will include a 'None'.""" |
| 112 argument_type = idl_argument.idl_type |
| 113 # TODO(dglazkov): What should we do with primitive nullable args? |
| 114 if (argument_type.is_nullable and |
| 115 argument_type.inner_type.is_primitive_type): |
| 116 raise ValueError('Primitive nullable types are not supported.') |
| 117 |
| 118 idl_types = [] |
| 119 if idl_argument.is_optional: |
| 120 idl_types.append(None) # None is used to convey optionality. |
| 121 if argument_type.is_union_type: |
| 122 idl_types = idl_types + argument_type.member_types |
| 123 else: |
| 124 idl_types.append(argument_type) |
| 125 return idl_types |
| 126 |
| 127 def split_into_overloads(self): |
| 128 """Splits an operation into one or more overloads that correctly reflect |
| 129 the WebIDL semantics of the operation arguments. For example, |
| 130 running this method on an IdlOperation that represents this WebIDL |
| 131 definition: |
| 132 |
| 133 void addEventListener( |
| 134 DOMString type, |
| 135 EventListener? listener, |
| 136 optional (AddEventListenerOptions or boolean) options) |
| 137 |
| 138 will produce a list of 3 argument lists: |
| 139 |
| 140 1) [DOMString, EventListener], since the third argument is optional, |
| 141 2) [DOMString, EventListener, AddEventListenerOptions], since the |
| 142 third argument is a union type with AddEventListenerOptions as |
| 143 one of its member types, and |
| 144 3) [DOMString, EventListener, boolean], since the other union member |
| 145 type of the third argument is boolean. |
| 146 |
| 147 This example is also captured as test in |
| 148 MethodOverloadSplitterTest.test_split_add_event_listener. |
| 149 """ |
| 150 |
| 151 argument_lists = [[]] |
| 152 for idl_argument in self.idl_operation.arguments: |
| 153 idl_types = self._enumerate_argument_types(idl_argument) |
| 154 argument_lists = self._update_argument_lists(argument_lists, |
| 155 idl_types) |
| 156 return argument_lists |
| 157 |
| 158 |
86 class InterfaceContextBuilder(object): | 159 class InterfaceContextBuilder(object): |
87 def __init__(self, code_generator, type_resolver): | 160 def __init__(self, code_generator, type_resolver): |
88 self.result = {'code_generator': code_generator} | 161 self.result = {'code_generator': code_generator} |
89 self.type_resolver = type_resolver | 162 self.type_resolver = type_resolver |
90 | 163 |
91 def set_class_name(self, class_name): | 164 def set_class_name(self, class_name): |
92 converter = NameStyleConverter(class_name) | 165 converter = NameStyleConverter(class_name) |
93 self.result['class_name'] = converter.to_all_cases() | 166 self.result['class_name'] = converter.to_all_cases() |
94 self._ensure_set('cpp_includes').update( | 167 self._ensure_set('cpp_includes').update( |
95 self.type_resolver.includes_from_interface(class_name)) | 168 self.type_resolver.includes_from_interface(class_name)) |
(...skipping 15 matching lines...) Expand all Loading... |
111 | 184 |
112 def add_attribute(self, idl_attribute): | 185 def add_attribute(self, idl_attribute): |
113 self._ensure_list('attributes').append( | 186 self._ensure_list('attributes').append( |
114 self.create_attribute(idl_attribute)) | 187 self.create_attribute(idl_attribute)) |
115 self._ensure_set('cpp_includes').update( | 188 self._ensure_set('cpp_includes').update( |
116 self.type_resolver.includes_from_definition(idl_attribute)) | 189 self.type_resolver.includes_from_definition(idl_attribute)) |
117 | 190 |
118 def add_operation(self, idl_operation): | 191 def add_operation(self, idl_operation): |
119 if not idl_operation.name: | 192 if not idl_operation.name: |
120 return | 193 return |
121 self._ensure_list('methods').append( | 194 overload_splitter = MethodOverloadSplitter(idl_operation) |
122 self.create_method(idl_operation)) | 195 overloads = overload_splitter.split_into_overloads() |
123 self._ensure_set('cpp_includes').update( | 196 argument_names = [argument.name for argument |
124 self.type_resolver.includes_from_definition(idl_operation)) | 197 in idl_operation.arguments] |
| 198 for argument_types in overloads: |
| 199 arguments = [] |
| 200 for position, argument_type in enumerate(argument_types): |
| 201 arguments.append( |
| 202 self.create_argument(argument_names[position], |
| 203 argument_type)) |
| 204 self._ensure_list('methods').append( |
| 205 self.create_method(idl_operation, arguments)) |
| 206 self._ensure_set('cpp_includes').update( |
| 207 self.type_resolver.includes_from_definition(idl_operation)) |
125 | 208 |
126 def create_method(self, idl_operation): | 209 def create_argument(self, argument_name, argument_type): |
127 name = idl_operation.name | 210 name_converter = NameStyleConverter(argument_name) |
| 211 return { |
| 212 'name': name_converter.to_snake_case(), |
| 213 'type': argument_type.base_type, |
| 214 } |
| 215 |
| 216 def create_method(self, idl_operation, arguments): |
| 217 name_converter = NameStyleConverter(idl_operation.name) |
128 return_type = self.type_resolver.type_from_definition(idl_operation) | 218 return_type = self.type_resolver.type_from_definition(idl_operation) |
129 return { | 219 return { |
130 'name': name, | 220 'name': name_converter.to_upper_camel_case(), |
131 'return_type': return_type | 221 'type': return_type, |
| 222 'arguments': arguments |
132 } | 223 } |
133 | 224 |
134 def create_attribute(self, idl_attribute): | 225 def create_attribute(self, idl_attribute): |
135 name = idl_attribute.name | 226 name = idl_attribute.name |
136 return_type = self.type_resolver.type_from_definition(idl_attribute) | 227 return_type = self.type_resolver.type_from_definition(idl_attribute) |
137 return { | 228 return { |
138 'name': name, | 229 'name': name, |
139 'return_type': return_type | 230 'type': return_type |
140 } | 231 } |
141 | 232 |
142 def build(self): | 233 def build(self): |
143 return self.result | 234 return self.result |
144 | 235 |
145 | 236 |
146 class CodeGeneratorWebAgentAPI(CodeGeneratorBase): | 237 class CodeGeneratorWebAgentAPI(CodeGeneratorBase): |
147 def __init__(self, info_provider, cache_dir, output_dir): | 238 def __init__(self, info_provider, cache_dir, output_dir): |
148 CodeGeneratorBase.__init__(self, MODULE_PYNAME, info_provider, | 239 CodeGeneratorBase.__init__(self, MODULE_PYNAME, info_provider, |
149 cache_dir, output_dir) | 240 cache_dir, output_dir) |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
181 | 272 |
182 # TODO(dglazkov): Implement dictionaries | 273 # TODO(dglazkov): Implement dictionaries |
183 if definition_name not in definitions.interfaces: | 274 if definition_name not in definitions.interfaces: |
184 return None | 275 return None |
185 | 276 |
186 interface = definitions.interfaces[definition_name] | 277 interface = definitions.interfaces[definition_name] |
187 if WEB_AGENT_API_IDL_ATTRIBUTE not in interface.extended_attributes: | 278 if WEB_AGENT_API_IDL_ATTRIBUTE not in interface.extended_attributes: |
188 return None | 279 return None |
189 | 280 |
190 return self.generate_interface_code(interface) | 281 return self.generate_interface_code(interface) |
OLD | NEW |