OLD | NEW |
1 # Copyright (C) 2013 Google Inc. All rights reserved. | 1 # Copyright (C) 2013 Google Inc. All rights reserved. |
2 # | 2 # |
3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
5 # met: | 5 # met: |
6 # | 6 # |
7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
122 IdlType.set_enums(info_provider.enumerations) | 122 IdlType.set_enums(info_provider.enumerations) |
123 IdlType.set_implemented_as_interfaces(interfaces_info['implemented_as_interf
aces']) | 123 IdlType.set_implemented_as_interfaces(interfaces_info['implemented_as_interf
aces']) |
124 IdlType.set_garbage_collected_types(interfaces_info['garbage_collected_inter
faces']) | 124 IdlType.set_garbage_collected_types(interfaces_info['garbage_collected_inter
faces']) |
125 v8_types.set_component_dirs(interfaces_info['component_dirs']) | 125 v8_types.set_component_dirs(interfaces_info['component_dirs']) |
126 | 126 |
127 | 127 |
128 def should_generate_code(definitions): | 128 def should_generate_code(definitions): |
129 return definitions.interfaces or definitions.dictionaries | 129 return definitions.interfaces or definitions.dictionaries |
130 | 130 |
131 | 131 |
132 def depending_union_type_name(idl_type): | 132 def depends_on_union_types(idl_type): |
133 """Returns the union type name if the given idl_type depends on a | 133 """Returns true when a given idl_type depends on union containers |
134 union type. | 134 directly. |
135 """ | 135 """ |
136 def find_base_type(current_type): | 136 if idl_type.is_union_type: |
137 if current_type.is_array_or_sequence_type: | 137 return True |
138 return find_base_type(current_type.element_type) | 138 if idl_type.is_array_or_sequence_type: |
139 if current_type.is_nullable: | 139 return idl_type.element_type.is_union_type |
140 return find_base_type(current_type.inner_type) | 140 return False |
141 return current_type | |
142 base_type = find_base_type(idl_type) | |
143 if base_type.is_union_type: | |
144 return base_type.name | |
145 return None | |
146 | 141 |
147 | 142 |
148 class TypedefResolver(Visitor): | 143 class TypedefResolver(Visitor): |
149 def __init__(self, info_provider): | 144 def __init__(self, info_provider): |
150 self.info_provider = info_provider | 145 self.info_provider = info_provider |
151 | 146 |
152 def resolve(self, definitions, definition_name): | 147 def resolve(self, definitions, definition_name): |
153 """Traverse definitions and resolves typedefs with the actual types.""" | 148 """Traverse definitions and resolves typedefs with the actual types.""" |
154 self.typedefs = {} | 149 self.typedefs = {} |
155 for name, typedef in self.info_provider.typedefs.iteritems(): | 150 for name, typedef in self.info_provider.typedefs.iteritems(): |
156 self.typedefs[name] = typedef.idl_type | 151 self.typedefs[name] = typedef.idl_type |
157 self.additional_header_includes = set() | 152 self.additional_includes = set() |
158 definitions.accept(self) | 153 definitions.accept(self) |
159 self._update_dependencies_include_paths(definition_name) | 154 self._update_dependencies_include_paths(definition_name) |
160 | 155 |
161 def _update_dependencies_include_paths(self, definition_name): | 156 def _update_dependencies_include_paths(self, definition_name): |
162 interface_info = self.info_provider.interfaces_info[definition_name] | 157 interface_info = self.info_provider.interfaces_info[definition_name] |
163 interface_info['additional_header_includes'] = set( | 158 dependencies_include_paths = interface_info['dependencies_include_paths'
] |
164 self.additional_header_includes) | 159 for include_path in self.additional_includes: |
| 160 if include_path not in dependencies_include_paths: |
| 161 dependencies_include_paths.append(include_path) |
165 | 162 |
166 def _resolve_typedefs(self, typed_object): | 163 def _resolve_typedefs(self, typed_object): |
167 """Resolve typedefs to actual types in the object.""" | 164 """Resolve typedefs to actual types in the object.""" |
168 for attribute_name in typed_object.idl_type_attributes: | 165 for attribute_name in typed_object.idl_type_attributes: |
169 try: | 166 try: |
170 idl_type = getattr(typed_object, attribute_name) | 167 idl_type = getattr(typed_object, attribute_name) |
171 except AttributeError: | 168 except AttributeError: |
172 continue | 169 continue |
173 if not idl_type: | 170 if not idl_type: |
174 continue | 171 continue |
175 resolved_idl_type = idl_type.resolve_typedefs(self.typedefs) | 172 resolved_idl_type = idl_type.resolve_typedefs(self.typedefs) |
176 # TODO(bashi): Dependency resolution shouldn't happen here. | 173 if depends_on_union_types(resolved_idl_type): |
177 # Move this into includes_for_type() families. | 174 self.additional_includes.add( |
178 union_type_name = depending_union_type_name(resolved_idl_type) | 175 self.info_provider.include_path_for_union_types) |
179 if union_type_name: | |
180 self.additional_header_includes.add( | |
181 self.info_provider.include_path_for_union_types( | |
182 union_type_name)) | |
183 # Need to re-assign the attribute, not just mutate idl_type, since | 176 # Need to re-assign the attribute, not just mutate idl_type, since |
184 # type(idl_type) may change. | 177 # type(idl_type) may change. |
185 setattr(typed_object, attribute_name, resolved_idl_type) | 178 setattr(typed_object, attribute_name, resolved_idl_type) |
186 | 179 |
187 def visit_typed_object(self, typed_object): | 180 def visit_typed_object(self, typed_object): |
188 self._resolve_typedefs(typed_object) | 181 self._resolve_typedefs(typed_object) |
189 | 182 |
190 | 183 |
191 class CodeGeneratorBase(object): | 184 class CodeGeneratorBase(object): |
192 """Base class for v8 bindings generator and IDL dictionary impl generator""" | 185 """Base class for v8 bindings generator and IDL dictionary impl generator""" |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
265 template_context = interface_context(interface) | 258 template_context = interface_context(interface) |
266 includes.update(interface_info.get('cpp_includes', {}).get(component, se
t())) | 259 includes.update(interface_info.get('cpp_includes', {}).get(component, se
t())) |
267 if not interface.is_partial and not is_testing_target(full_path): | 260 if not interface.is_partial and not is_testing_target(full_path): |
268 template_context['header_includes'].add(self.info_provider.include_p
ath_for_export) | 261 template_context['header_includes'].add(self.info_provider.include_p
ath_for_export) |
269 template_context['exported'] = self.info_provider.specifier_for_expo
rt | 262 template_context['exported'] = self.info_provider.specifier_for_expo
rt |
270 # Add the include for interface itself | 263 # Add the include for interface itself |
271 if IdlType(interface_name).is_typed_array: | 264 if IdlType(interface_name).is_typed_array: |
272 template_context['header_includes'].add('core/dom/DOMTypedArray.h') | 265 template_context['header_includes'].add('core/dom/DOMTypedArray.h') |
273 elif interface_info['include_path']: | 266 elif interface_info['include_path']: |
274 template_context['header_includes'].add(interface_info['include_path
']) | 267 template_context['header_includes'].add(interface_info['include_path
']) |
275 template_context['header_includes'].update( | 268 |
276 interface_info.get('additional_header_includes', [])) | |
277 header_template = self.jinja_env.get_template(header_template_filename) | 269 header_template = self.jinja_env.get_template(header_template_filename) |
278 cpp_template = self.jinja_env.get_template(cpp_template_filename) | 270 cpp_template = self.jinja_env.get_template(cpp_template_filename) |
279 header_text, cpp_text = render_template( | 271 header_text, cpp_text = render_template( |
280 include_paths, header_template, cpp_template, template_context, | 272 include_paths, header_template, cpp_template, template_context, |
281 component) | 273 component) |
282 header_path, cpp_path = self.output_paths(interface_name) | 274 header_path, cpp_path = self.output_paths(interface_name) |
283 return ( | 275 return ( |
284 (header_path, header_text), | 276 (header_path, header_text), |
285 (cpp_path, cpp_text), | 277 (cpp_path, cpp_text), |
286 ) | 278 ) |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
324 if not definition_name in definitions.dictionaries: | 316 if not definition_name in definitions.dictionaries: |
325 raise ValueError('%s is not an IDL dictionary') | 317 raise ValueError('%s is not an IDL dictionary') |
326 interfaces_info = self.info_provider.interfaces_info | 318 interfaces_info = self.info_provider.interfaces_info |
327 dictionary = definitions.dictionaries[definition_name] | 319 dictionary = definitions.dictionaries[definition_name] |
328 interface_info = interfaces_info[definition_name] | 320 interface_info = interfaces_info[definition_name] |
329 header_template = self.jinja_env.get_template('dictionary_impl.h') | 321 header_template = self.jinja_env.get_template('dictionary_impl.h') |
330 cpp_template = self.jinja_env.get_template('dictionary_impl.cpp') | 322 cpp_template = self.jinja_env.get_template('dictionary_impl.cpp') |
331 template_context = v8_dictionary.dictionary_impl_context( | 323 template_context = v8_dictionary.dictionary_impl_context( |
332 dictionary, interfaces_info) | 324 dictionary, interfaces_info) |
333 include_paths = interface_info.get('dependencies_include_paths') | 325 include_paths = interface_info.get('dependencies_include_paths') |
| 326 # Add union containers header file to header_includes rather than |
| 327 # cpp file so that union containers can be used in dictionary headers. |
| 328 union_container_headers = [header for header in include_paths |
| 329 if header.find('UnionTypes') > 0] |
| 330 include_paths = [header for header in include_paths |
| 331 if header not in union_container_headers] |
| 332 template_context['header_includes'].update(union_container_headers) |
334 if not is_testing_target(interface_info.get('full_path')): | 333 if not is_testing_target(interface_info.get('full_path')): |
335 template_context['exported'] = self.info_provider.specifier_for_expo
rt | 334 template_context['exported'] = self.info_provider.specifier_for_expo
rt |
336 template_context['header_includes'].add(self.info_provider.include_p
ath_for_export) | 335 template_context['header_includes'].add(self.info_provider.include_p
ath_for_export) |
337 template_context['header_includes'].update( | |
338 interface_info.get('additional_header_includes', [])) | |
339 header_text, cpp_text = render_template( | 336 header_text, cpp_text = render_template( |
340 include_paths, header_template, cpp_template, template_context) | 337 include_paths, header_template, cpp_template, template_context) |
341 header_path, cpp_path = self.output_paths( | 338 header_path, cpp_path = self.output_paths( |
342 cpp_name(dictionary), interface_info) | 339 cpp_name(dictionary), interface_info) |
343 return ( | 340 return ( |
344 (header_path, header_text), | 341 (header_path, header_text), |
345 (cpp_path, cpp_text), | 342 (cpp_path, cpp_text), |
346 ) | 343 ) |
347 | 344 |
348 | 345 |
349 class CodeGeneratorUnionType(object): | 346 class CodeGeneratorUnionType(object): |
350 """Generates union type container classes. | 347 """Generates union type container classes. |
351 This generator is different from CodeGeneratorV8 and | 348 This generator is different from CodeGeneratorV8 and |
352 CodeGeneratorDictionaryImpl. It assumes that all union types are already | 349 CodeGeneratorDictionaryImpl. It assumes that all union types are already |
353 collected. It doesn't process idl files directly. | 350 collected. It doesn't process idl files directly. |
354 """ | 351 """ |
355 def __init__(self, info_provider, cache_dir, output_dir, target_component): | 352 def __init__(self, info_provider, cache_dir, output_dir, target_component): |
356 self.info_provider = info_provider | 353 self.info_provider = info_provider |
357 self.jinja_env = initialize_jinja_env(cache_dir) | 354 self.jinja_env = initialize_jinja_env(cache_dir) |
358 self.output_dir = output_dir | 355 self.output_dir = output_dir |
359 self.target_component = target_component | 356 self.target_component = target_component |
360 set_global_type_info(info_provider) | 357 set_global_type_info(info_provider) |
361 | 358 |
362 def _generate_container_code(self, union_type): | 359 def generate_code(self): |
363 header_template = self.jinja_env.get_template('union_container.h') | 360 union_types = self.info_provider.union_types |
364 cpp_template = self.jinja_env.get_template('union_container.cpp') | 361 if not union_types: |
365 template_context = v8_union.container_context( | 362 return () |
366 union_type, self.info_provider.interfaces_info) | 363 header_template = self.jinja_env.get_template('union.h') |
367 template_context['header_includes'].append( | 364 cpp_template = self.jinja_env.get_template('union.cpp') |
368 self.info_provider.include_path_for_export) | 365 template_context = v8_union.union_context( |
| 366 union_types, self.info_provider.interfaces_info) |
| 367 template_context['code_generator'] = module_pyname |
| 368 capitalized_component = self.target_component.capitalize() |
| 369 template_context['exported'] = self.info_provider.specifier_for_export |
| 370 template_context['header_filename'] = 'bindings/%s/v8/UnionTypes%s.h' %
( |
| 371 self.target_component, capitalized_component) |
| 372 template_context['macro_guard'] = 'UnionType%s_h' % capitalized_componen
t |
| 373 additional_header_includes = [self.info_provider.include_path_for_export
] |
| 374 |
| 375 # Add UnionTypesCore.h as a dependency when we generate modules union ty
pes |
| 376 # because we only generate union type containers which are used by both |
| 377 # core and modules in UnionTypesCore.h. |
| 378 # FIXME: This is an ad hoc workaround and we need a general way to |
| 379 # handle core <-> modules dependency. |
| 380 if self.target_component == 'modules': |
| 381 additional_header_includes.append( |
| 382 'bindings/core/v8/UnionTypesCore.h') |
| 383 |
369 template_context['header_includes'] = normalize_and_sort_includes( | 384 template_context['header_includes'] = normalize_and_sort_includes( |
370 template_context['header_includes']) | 385 template_context['header_includes'] + additional_header_includes) |
371 template_context['code_generator'] = module_pyname | 386 |
372 template_context['exported'] = self.info_provider.specifier_for_export | |
373 header_text = header_template.render(template_context) | 387 header_text = header_template.render(template_context) |
374 cpp_text = cpp_template.render(template_context) | 388 cpp_text = cpp_template.render(template_context) |
375 name = union_type.cpp_type | 389 header_path = posixpath.join(self.output_dir, |
376 header_path = posixpath.join(self.output_dir, '%s.h' % name) | 390 'UnionTypes%s.h' % capitalized_component) |
377 cpp_path = posixpath.join(self.output_dir, '%s.cpp' % name) | 391 cpp_path = posixpath.join(self.output_dir, |
| 392 'UnionTypes%s.cpp' % capitalized_component) |
378 return ( | 393 return ( |
379 (header_path, header_text), | 394 (header_path, header_text), |
380 (cpp_path, cpp_text), | 395 (cpp_path, cpp_text), |
381 ) | 396 ) |
382 | 397 |
383 def _get_union_types_for_containers(self): | |
384 union_types = self.info_provider.union_types | |
385 if not union_types: | |
386 return None | |
387 # For container classes we strip nullable wrappers. For example, | |
388 # both (A or B)? and (A? or B) will become AOrB. This should be OK | |
389 # because container classes can handle null and it seems that | |
390 # distinguishing (A or B)? and (A? or B) doesn't make sense. | |
391 container_cpp_types = set() | |
392 union_types_for_containers = set() | |
393 for union_type in union_types: | |
394 cpp_type = union_type.cpp_type | |
395 if cpp_type not in container_cpp_types: | |
396 union_types_for_containers.add(union_type) | |
397 container_cpp_types.add(cpp_type) | |
398 return union_types_for_containers | |
399 | |
400 def generate_code(self): | |
401 union_types = self._get_union_types_for_containers() | |
402 if not union_types: | |
403 return () | |
404 outputs = set() | |
405 for union_type in union_types: | |
406 outputs.update(self._generate_container_code(union_type)) | |
407 return outputs | |
408 | |
409 | 398 |
410 def initialize_jinja_env(cache_dir): | 399 def initialize_jinja_env(cache_dir): |
411 jinja_env = jinja2.Environment( | 400 jinja_env = jinja2.Environment( |
412 loader=jinja2.FileSystemLoader(templates_dir), | 401 loader=jinja2.FileSystemLoader(templates_dir), |
413 # Bytecode cache is not concurrency-safe unless pre-cached: | 402 # Bytecode cache is not concurrency-safe unless pre-cached: |
414 # if pre-cached this is read-only, but writing creates a race condition. | 403 # if pre-cached this is read-only, but writing creates a race condition. |
415 bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir), | 404 bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir), |
416 keep_trailing_newline=True, # newline-terminate generated files | 405 keep_trailing_newline=True, # newline-terminate generated files |
417 lstrip_blocks=True, # so can indent control flow tags | 406 lstrip_blocks=True, # so can indent control flow tags |
418 trim_blocks=True) | 407 trim_blocks=True) |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
467 | 456 |
468 # Create a dummy file as output for the build system, | 457 # Create a dummy file as output for the build system, |
469 # since filenames of individual cache files are unpredictable and opaque | 458 # since filenames of individual cache files are unpredictable and opaque |
470 # (they are hashes of the template path, which varies based on environment) | 459 # (they are hashes of the template path, which varies based on environment) |
471 with open(dummy_filename, 'w') as dummy_file: | 460 with open(dummy_filename, 'w') as dummy_file: |
472 pass # |open| creates or touches the file | 461 pass # |open| creates or touches the file |
473 | 462 |
474 | 463 |
475 if __name__ == '__main__': | 464 if __name__ == '__main__': |
476 sys.exit(main(sys.argv)) | 465 sys.exit(main(sys.argv)) |
OLD | NEW |