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