| OLD | NEW |
| (Empty) | |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 """Generate Blink WebModules bindings (.h and .cpp files). |
| 6 |
| 7 These are used to expose C++ bindings to the rest of Blink for building |
| 8 features on top of web APIs instead of baking them into the core code. |
| 9 The way these APIs work is to wrap a pointer to the underlying type and |
| 10 then pass the wrapped thing by value. Compilers will optimize the |
| 11 wrapper out entirely, so there is no performance overhead, but lets |
| 12 us enforce that a strictly controlled API surface is exposed to the calling |
| 13 code. |
| 14 |
| 15 We don't want to expose the bad parts of the platform, so we only expose |
| 16 APIs that have the WebModulesExposed annotation. |
| 17 |
| 18 Input: An object of class IdlDefinitions, containing an IDL interface X |
| 19 Output: WebModuleX.h and WebModuleX.cpp |
| 20 |
| 21 Design doc: http://www.chromium.org/developers/design-documents/idl-compiler |
| 22 """ |
| 23 |
| 24 import os |
| 25 import posixpath |
| 26 import sys |
| 27 |
| 28 # TODO(ojan): Share this path handling code with code_generator_v8.py.. |
| 29 |
| 30 # Path handling for libraries and templates |
| 31 # Paths have to be normalized because Jinja uses the exact template path to |
| 32 # determine the hash used in the cache filename, and we need a pre-caching step |
| 33 # to be concurrency-safe. Use absolute path because __file__ is absolute if |
| 34 # module is imported, and relative if executed directly. |
| 35 # If paths differ between pre-caching and individual file compilation, the cache |
| 36 # is regenerated, which causes a race condition and breaks concurrent build, |
| 37 # since some compile processes will try to read the partially written cache. |
| 38 module_path, module_filename = os.path.split(os.path.realpath(__file__)) |
| 39 third_party_dir = os.path.normpath(os.path.join( |
| 40 module_path, os.pardir, os.pardir, os.pardir, os.pardir)) |
| 41 templates_dir = os.path.normpath(os.path.join( |
| 42 module_path, os.pardir, 'templates')) |
| 43 # Make sure extension is .py, not .pyc or .pyo, so doesn't depend on caching |
| 44 module_pyname = os.path.splitext(module_filename)[0] + '.py' |
| 45 |
| 46 # jinja2 is in chromium's third_party directory. |
| 47 # Insert at 1 so at front to override system libraries, and |
| 48 # after path[0] == invoking script dir |
| 49 sys.path.insert(1, third_party_dir) |
| 50 import jinja2 |
| 51 |
| 52 from v8_utilities import cpp_name |
| 53 from utilities import idl_filename_to_component |
| 54 |
| 55 |
| 56 def initialize_jinja_env(cache_dir): |
| 57 return jinja2.Environment( |
| 58 loader=jinja2.FileSystemLoader(templates_dir), |
| 59 # Bytecode cache is not concurrency-safe unless pre-cached: |
| 60 # if pre-cached this is read-only, but writing creates a race condition. |
| 61 bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir), |
| 62 keep_trailing_newline=True, # newline-terminate generated files |
| 63 lstrip_blocks=True, # so can indent control flow tags |
| 64 trim_blocks=True) |
| 65 |
| 66 |
| 67 def web_modules_class_name(interface_name): |
| 68 return "WebModule" + interface_name |
| 69 |
| 70 |
| 71 def includes_for_type(component, idl_type): |
| 72 base_idl_type = idl_type.preprocessed_type.base_type |
| 73 |
| 74 # TODO(ojan): Make this work for things that have more dependencies (e.g. No
deLists). |
| 75 # e.g. see v8_types.includes_for_type. |
| 76 return set(['bindings/%s/webmodules/WebModule%s.h' % (component, base_idl_ty
pe)]) |
| 77 |
| 78 |
| 79 class Attribute(object): |
| 80 def __init__(self, name, idl_type): |
| 81 self.name = name |
| 82 self.idl_type = idl_type |
| 83 |
| 84 |
| 85 class CodeGeneratorWebModules(object): |
| 86 def __init__(self, info_provider, cache_dir, output_dir): |
| 87 self.info_provider = info_provider |
| 88 self.jinja_env = initialize_jinja_env(cache_dir) |
| 89 self.output_dir = output_dir |
| 90 |
| 91 def generate_code(self, definitions, definition_name): |
| 92 """Returns .h/.cpp code as ((path, content)...).""" |
| 93 # TODO(ojan): Make this work for dictionaries as well. |
| 94 assert(definition_name in definitions.interfaces) |
| 95 return self.generate_interface_code( |
| 96 definitions, definition_name, |
| 97 definitions.interfaces[definition_name]) |
| 98 |
| 99 def generate_interface_code(self, definitions, interface_name, interface): |
| 100 interface_info = self.info_provider.interfaces_info[interface_name] |
| 101 full_path = interface_info.get('full_path') |
| 102 component = idl_filename_to_component(full_path) |
| 103 |
| 104 attributes = [] |
| 105 type_includes = set() |
| 106 for attribute in interface.attributes: |
| 107 if 'WebModulesExposed' in attribute.extended_attributes: |
| 108 type_includes = type_includes.union( |
| 109 includes_for_type(component, attribute.idl_type)) |
| 110 |
| 111 # TODO(ojan): handle void return types |
| 112 return_type = 'WebModule%s' % attribute.idl_type.preprocessed_ty
pe.base_type |
| 113 attribute = Attribute(cpp_name(attribute), return_type) |
| 114 attributes.append(attribute) |
| 115 |
| 116 template_context = { |
| 117 'class_name': web_modules_class_name(interface_name), |
| 118 'header_includes': sorted([ |
| 119 interface_info['include_path'], |
| 120 ] + list(type_includes)), |
| 121 'attributes': attributes, |
| 122 'interface_name': interface_name |
| 123 } |
| 124 |
| 125 header_path = posixpath.join(self.output_dir, 'WebModule%s.h' % interfac
e_name) |
| 126 cpp_path = posixpath.join(self.output_dir, 'WebModule%s.cpp' % interface
_name) |
| 127 |
| 128 header_template = self.jinja_env.get_template('web_modules_interface.h') |
| 129 cpp_template = self.jinja_env.get_template('web_modules_interface.cpp') |
| 130 |
| 131 return ( |
| 132 (header_path, header_template.render(template_context)), |
| 133 (cpp_path, cpp_template.render(template_context)), |
| 134 ) |
| OLD | NEW |