OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 2 # |
| 3 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. |
| 6 |
| 7 # This script reads the global interface data collected by |
| 8 # compute_interfaces_info_overall.py, and writes out the code which adds |
| 9 # bindings for origin-trial-enabled features at runtime. |
| 10 |
| 11 # pylint: disable=W0403 |
| 12 |
| 13 import optparse |
| 14 import os |
| 15 import posixpath |
| 16 import sys |
| 17 from collections import defaultdict, namedtuple |
| 18 |
| 19 from code_generator import (initialize_jinja_env, normalize_and_sort_includes, |
| 20 render_template) |
| 21 from idl_reader import IdlReader |
| 22 from utilities import (create_component_info_provider, write_file, |
| 23 idl_filename_to_component) |
| 24 from v8_utilities import v8_class_name, v8_class_name_or_partial, uncapitalize |
| 25 |
| 26 # Make sure extension is .py, not .pyc or .pyo, so doesn't depend on caching |
| 27 MODULE_PYNAME = os.path.splitext(os.path.basename(__file__))[0] + '.py' |
| 28 |
| 29 |
| 30 ConditionalInterfaceInfo = namedtuple('ConditionalInterfaceInfo', [ |
| 31 'name', 'v8_class', 'v8_class_or_partial', 'is_global']) |
| 32 |
| 33 |
| 34 def get_install_functions(interfaces, feature_names): |
| 35 """Construct a list of V8 bindings installation functions for each feature |
| 36 on each interface. |
| 37 |
| 38 interfaces is a list of ConditionalInterfaceInfo tuples |
| 39 feature_names is a list of strings, containing names of features which can |
| 40 be installed on those interfaces. |
| 41 """ |
| 42 return [ |
| 43 {"condition": 'OriginTrials::%sEnabled' % uncapitalize(feature_name), |
| 44 "name": feature_name, |
| 45 "install_method": "install%s" % feature_name, |
| 46 "v8_class": interface_info.v8_class, |
| 47 "v8_class_or_partial": interface_info.v8_class_or_partial} |
| 48 for feature_name in feature_names |
| 49 for interface_info in interfaces] |
| 50 |
| 51 |
| 52 def get_conditional_feature_names_from_interface(interface): |
| 53 feature_names = set() |
| 54 if ('OriginTrialEnabled' in interface.extended_attributes and |
| 55 interface.is_partial): |
| 56 feature_names.add(interface.extended_attributes['OriginTrialEnabled']) |
| 57 for operation in interface.operations: |
| 58 if 'OriginTrialEnabled' in operation.extended_attributes: |
| 59 feature_names.add( |
| 60 operation.extended_attributes['OriginTrialEnabled']) |
| 61 for attribute in interface.attributes: |
| 62 if 'OriginTrialEnabled' in attribute.extended_attributes: |
| 63 feature_names.add( |
| 64 attribute.extended_attributes['OriginTrialEnabled']) |
| 65 return list(feature_names) |
| 66 |
| 67 |
| 68 def read_idl_file(reader, idl_filename): |
| 69 interfaces = reader.read_idl_file(idl_filename).interfaces |
| 70 # There should only be a single interface defined in an IDL file. Return it. |
| 71 assert len(interfaces) == 1 |
| 72 return interfaces.values()[0] |
| 73 |
| 74 |
| 75 def interface_is_global(interface): |
| 76 return ('Global' in interface.extended_attributes or |
| 77 'PrimaryGlobal' in interface.extended_attributes) |
| 78 |
| 79 |
| 80 def conditional_features_info(info_provider, reader, idl_filenames, target_compo
nent): |
| 81 """Read a set of IDL files and compile the mapping between interfaces and |
| 82 the conditional features defined on them. |
| 83 |
| 84 Returns a tuple (features_for_type, types_for_feature, includes): |
| 85 - features_for_type is a mapping of interface->feature |
| 86 - types_for_feature is the reverse mapping: feature->interface |
| 87 - includes is a set of header files which need to be included in the |
| 88 generated implementation code. |
| 89 """ |
| 90 features_for_type = defaultdict(set) |
| 91 types_for_feature = defaultdict(set) |
| 92 includes = set() |
| 93 |
| 94 for idl_filename in idl_filenames: |
| 95 interface = read_idl_file(reader, idl_filename) |
| 96 feature_names = get_conditional_feature_names_from_interface(interface) |
| 97 if feature_names: |
| 98 is_global = interface_is_global(interface) |
| 99 if interface.is_partial: |
| 100 # For partial interfaces, we need to generate different |
| 101 # includes if the parent interface is in a different |
| 102 # component. |
| 103 parent_interface_info = info_provider.interfaces_info[interface.
name] |
| 104 parent_interface = read_idl_file( |
| 105 reader, parent_interface_info.get('full_path')) |
| 106 is_global = is_global or interface_is_global(parent_interface) |
| 107 parent_component = idl_filename_to_component( |
| 108 parent_interface_info.get('full_path')) |
| 109 if interface.is_partial and target_component != parent_component: |
| 110 includes.add('bindings/%s/v8/V8%s.h' % |
| 111 (parent_component, interface.name)) |
| 112 includes.add('bindings/%s/v8/V8%sPartial.h' % |
| 113 (target_component, interface.name)) |
| 114 else: |
| 115 includes.add('bindings/%s/v8/V8%s.h' % |
| 116 (target_component, interface.name)) |
| 117 # If this is a partial interface in the same component as |
| 118 # its parent, then treat it as a non-partial interface. |
| 119 interface.is_partial = False |
| 120 interface_info = ConditionalInterfaceInfo(interface.name, |
| 121 v8_class_name(interface), |
| 122 v8_class_name_or_partial( |
| 123 interface), |
| 124 is_global) |
| 125 for feature_name in feature_names: |
| 126 features_for_type[interface_info].add(feature_name) |
| 127 types_for_feature[feature_name].add(interface_info) |
| 128 |
| 129 return features_for_type, types_for_feature, includes |
| 130 |
| 131 |
| 132 def conditional_features_context(generator_name, feature_info): |
| 133 context = {'code_generator': generator_name} |
| 134 |
| 135 # Unpack the feature info tuple. |
| 136 features_for_type, types_for_feature, includes = feature_info |
| 137 |
| 138 # Add includes needed for cpp code and normalize. |
| 139 includes.update([ |
| 140 "core/context_features/ContextFeatureSettings.h", |
| 141 "core/dom/ExecutionContext.h", |
| 142 "core/frame/Frame.h", |
| 143 "core/origin_trials/OriginTrials.h", |
| 144 "platform/bindings/ConditionalFeatures.h", |
| 145 "platform/bindings/ScriptState.h", |
| 146 # TODO(iclelland): Remove the need to explicitly include this; it is |
| 147 # here because the ContextFeatureSettings code needs it. |
| 148 "bindings/core/v8/V8Window.h", |
| 149 ]) |
| 150 context['includes'] = normalize_and_sort_includes(includes) |
| 151 |
| 152 # For each interface, collect a list of bindings installation functions to |
| 153 # call, organized by conditional feature. |
| 154 context['installers_by_interface'] = [ |
| 155 {"name": interface_info.name, |
| 156 "is_global": interface_info.is_global, |
| 157 "v8_class": interface_info.v8_class, |
| 158 "installers": get_install_functions([interface_info], feature_names)} |
| 159 for interface_info, feature_names in features_for_type.items()] |
| 160 context['installers_by_interface'].sort(key=lambda x: x['name']) |
| 161 |
| 162 # For each conditional feature, collect a list of bindings installation |
| 163 # functions to call, organized by interface. |
| 164 context['installers_by_feature'] = [ |
| 165 {"name": feature_name, |
| 166 "installers": get_install_functions(interfaces, [feature_name])} |
| 167 for feature_name, interfaces in types_for_feature.items()] |
| 168 context['installers_by_feature'].sort(key=lambda x: x['name']) |
| 169 |
| 170 return context |
| 171 |
| 172 |
| 173 def parse_options(): |
| 174 parser = optparse.OptionParser() |
| 175 parser.add_option('--cache-directory', |
| 176 help='cache directory, defaults to output directory') |
| 177 parser.add_option('--output-directory') |
| 178 parser.add_option('--info-dir') |
| 179 parser.add_option('--target-component', |
| 180 type='choice', |
| 181 choices=['Core', 'Modules'], |
| 182 help='target component to generate code') |
| 183 parser.add_option('--idl-files-list') |
| 184 |
| 185 options, _ = parser.parse_args() |
| 186 if options.output_directory is None: |
| 187 parser.error('Must specify output directory using --output-directory.') |
| 188 return options |
| 189 |
| 190 |
| 191 def generate_conditional_features(info_provider, options, idl_filenames): |
| 192 reader = IdlReader(info_provider.interfaces_info, options.cache_directory) |
| 193 jinja_env = initialize_jinja_env(options.cache_directory) |
| 194 |
| 195 # Extract the bidirectional mapping of conditional features <-> interfaces |
| 196 # from the global info provider and the supplied list of IDL files. |
| 197 feature_info = conditional_features_info(info_provider, |
| 198 reader, idl_filenames, |
| 199 options.target_component.lower()) |
| 200 |
| 201 # Convert that mapping into the context required for the Jinja2 templates. |
| 202 template_context = conditional_features_context( |
| 203 MODULE_PYNAME, feature_info) |
| 204 |
| 205 # Generate and write out the header file |
| 206 header_text = render_template(jinja_env.get_template( |
| 207 "ConditionalFeaturesFor%s.h.tmpl" % options.target_component.title()), t
emplate_context) |
| 208 header_path = posixpath.join(options.output_directory, |
| 209 "ConditionalFeaturesFor%s.h" % options.target_c
omponent.title()) |
| 210 write_file(header_text, header_path) |
| 211 |
| 212 # Generate and write out the implementation file |
| 213 cpp_text = render_template(jinja_env.get_template( |
| 214 "ConditionalFeaturesFor%s.cpp.tmpl" % options.target_component.title()),
template_context) |
| 215 cpp_path = posixpath.join(options.output_directory, |
| 216 "ConditionalFeaturesFor%s.cpp" % options.target_co
mponent.title()) |
| 217 write_file(cpp_text, cpp_path) |
| 218 |
| 219 |
| 220 def main(): |
| 221 options = parse_options() |
| 222 |
| 223 info_provider = create_component_info_provider( |
| 224 os.path.normpath(options.info_dir), options.target_component.lower()) |
| 225 idl_filenames = map(str.strip, open(options.idl_files_list)) |
| 226 |
| 227 generate_conditional_features(info_provider, options, idl_filenames) |
| 228 return 0 |
| 229 |
| 230 |
| 231 if __name__ == '__main__': |
| 232 sys.exit(main()) |
OLD | NEW |