| 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 |