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 |