Chromium Code Reviews| 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 | |
| 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 def get_install_functions(interfaces, feature_names): | |
| 31 return [ | |
| 32 {"condition": 'OriginTrials::%sEnabled' % uncapitalize(feature_name), | |
| 33 "name": feature_name, | |
| 34 "install_method": "install%s" % feature_name, | |
| 35 "v8_class": interface_info[1], | |
| 36 "v8_class_or_partial": interface_info[2]} | |
|
haraken
2017/07/17 07:28:17
What is [1] and [2]? Is there any more readable wa
iclelland
2017/07/17 14:24:21
I created a namedtuple subclass to allow these ite
| |
| 37 for feature_name in feature_names | |
| 38 for interface_info in interfaces] | |
| 39 | |
| 40 | |
| 41 def get_conditional_feature_names_from_interface(interface): | |
| 42 feature_names = set() | |
| 43 if ('OriginTrialEnabled' in interface.extended_attributes and | |
| 44 interface.is_partial): | |
| 45 feature_names.add(interface.extended_attributes['OriginTrialEnabled']) | |
| 46 for operation in interface.operations: | |
| 47 if 'OriginTrialEnabled' in operation.extended_attributes: | |
| 48 feature_names.add( | |
| 49 operation.extended_attributes['OriginTrialEnabled']) | |
| 50 for attribute in interface.attributes: | |
| 51 if 'OriginTrialEnabled' in attribute.extended_attributes: | |
| 52 feature_names.add( | |
| 53 attribute.extended_attributes['OriginTrialEnabled']) | |
| 54 return list(feature_names) | |
| 55 | |
| 56 | |
| 57 def read_idl_file(reader, idl_filename): | |
| 58 interfaces = reader.read_idl_file(idl_filename).interfaces | |
| 59 assert len(interfaces) == 1 | |
| 60 return interfaces.items()[0][1] | |
|
haraken
2017/07/17 07:28:17
Ditto.
iclelland
2017/07/17 14:24:21
Changed this to values(), since we don't actually
| |
| 61 | |
| 62 | |
| 63 def interface_is_global(interface): | |
| 64 return ('Global' in interface.extended_attributes or | |
| 65 'PrimaryGlobal' in interface.extended_attributes) | |
| 66 | |
| 67 | |
| 68 def conditional_features_info(info_provider, reader, idl_filenames, target_compo nent): | |
| 69 """Read a set of IDL files and compile the mapping between interfaces and | |
| 70 the conditional features defined on them. | |
| 71 | |
| 72 Returns a tuple (features_for_type, types_for_feature, includes): | |
| 73 - features_for_type is a mapping of interface->feature | |
| 74 - types_for_feature is the reverse mapping: feature->interface | |
| 75 - includes is a set of header files which need to be included in the | |
| 76 generated implementation code. | |
| 77 """ | |
| 78 features_for_type = defaultdict(set) | |
| 79 types_for_feature = defaultdict(set) | |
| 80 includes = set() | |
| 81 | |
| 82 for idl_filename in idl_filenames: | |
| 83 interface = read_idl_file(reader, idl_filename) | |
| 84 feature_names = get_conditional_feature_names_from_interface(interface) | |
| 85 if feature_names: | |
| 86 is_global = interface_is_global(interface) | |
| 87 if interface.is_partial: | |
| 88 # For partial interfaces, we need to generate different | |
| 89 # includes if the parent interface is in a different | |
| 90 # component. | |
| 91 parent_interface_info = info_provider.interfaces_info[interface. name] | |
| 92 parent_interface = read_idl_file( | |
| 93 reader, parent_interface_info.get('full_path')) | |
| 94 is_global = is_global or interface_is_global(parent_interface) | |
| 95 parent_component = idl_filename_to_component( | |
| 96 parent_interface_info.get('full_path')) | |
| 97 if interface.is_partial and target_component != parent_component: | |
| 98 includes.add('bindings/%s/v8/V8%s.h' % | |
| 99 (parent_component, interface.name)) | |
| 100 includes.add('bindings/%s/v8/V8%sPartial.h' % | |
| 101 (target_component, interface.name)) | |
| 102 else: | |
| 103 includes.add('bindings/%s/v8/V8%s.h' % | |
| 104 (target_component, interface.name)) | |
| 105 # If this is a partial interface in the same component as | |
| 106 # its parent, then treat it as a non-partial interface. | |
| 107 interface.is_partial = False | |
| 108 interface_info = (interface.name, | |
| 109 v8_class_name(interface), | |
| 110 v8_class_name_or_partial(interface), | |
| 111 is_global) | |
| 112 for feature_name in feature_names: | |
| 113 features_for_type[interface_info].add(feature_name) | |
| 114 types_for_feature[feature_name].add(interface_info) | |
| 115 | |
| 116 return features_for_type, types_for_feature, includes | |
| 117 | |
| 118 | |
| 119 def conditional_features_context(generator_name, feature_info): | |
| 120 context = {'code_generator': generator_name} | |
| 121 | |
| 122 # Unpack the feature info tuple. | |
| 123 features_for_type, types_for_feature, includes = feature_info | |
| 124 | |
| 125 # Add includes needed for cpp code and normalize. | |
| 126 includes.update([ | |
| 127 "core/context_features/ContextFeatureSettings.h", | |
| 128 "core/dom/ExecutionContext.h", | |
| 129 "core/frame/Frame.h", | |
| 130 "core/origin_trials/OriginTrials.h", | |
| 131 "platform/bindings/ConditionalFeatures.h", | |
| 132 "platform/bindings/ScriptState.h", | |
| 133 # TODO(iclelland): Remove the need to explicitly include this; it is | |
| 134 # here because the ContextFeatureSettings code needs it. | |
| 135 "bindings/core/v8/V8Window.h", | |
| 136 ]) | |
| 137 context['includes'] = normalize_and_sort_includes(includes) | |
| 138 | |
| 139 # For each interface, collect a list of bindings installation functions to | |
| 140 # call, organized by conditional feature. | |
| 141 context['installers_by_interface'] = [ | |
| 142 {"name": interface_info[0], | |
| 143 "is_global": interface_info[3], | |
| 144 "v8_class": interface_info[1], | |
|
haraken
2017/07/17 07:28:17
Ditto.
iclelland
2017/07/17 14:24:21
Namedtuple makes this more readable now.
| |
| 145 "installers": get_install_functions([interface_info], feature_names)} | |
| 146 for interface_info, feature_names in features_for_type.items()] | |
| 147 context['installers_by_interface'].sort(key=lambda x: x['name']) | |
| 148 | |
| 149 # For each conditional feature, collect a list of bindings installation | |
| 150 # functions to call, organized by interface. | |
| 151 context['installers_by_feature'] = [ | |
| 152 {"name": feature_name, | |
| 153 "installers": get_install_functions(interfaces, [feature_name])} | |
| 154 for feature_name, interfaces in types_for_feature.items()] | |
| 155 context['installers_by_feature'].sort(key=lambda x: x['name']) | |
| 156 | |
| 157 return context | |
| 158 | |
| 159 | |
| 160 def parse_options(): | |
| 161 parser = optparse.OptionParser() | |
| 162 parser.add_option('--cache-directory', | |
| 163 help='cache directory, defaults to output directory') | |
| 164 parser.add_option('--output-directory') | |
| 165 parser.add_option('--info-dir') | |
| 166 parser.add_option('--target-component', | |
| 167 type='choice', | |
| 168 choices=['Core', 'Modules'], | |
| 169 help='target component to generate code') | |
| 170 parser.add_option('--idl-files-list') | |
| 171 | |
| 172 options, _ = parser.parse_args() | |
| 173 if options.output_directory is None: | |
| 174 parser.error('Must specify output directory using --output-directory.') | |
| 175 return options | |
| 176 | |
| 177 | |
| 178 def generate_conditional_features(info_provider, options, idl_filenames): | |
| 179 reader = IdlReader(info_provider.interfaces_info, options.cache_directory) | |
| 180 jinja_env = initialize_jinja_env(options.cache_directory) | |
| 181 | |
| 182 # Extract the bidirectional mapping of conditional features <-> interfaces | |
| 183 # from the global info provider and the supplied list of IDL files. | |
| 184 feature_info = conditional_features_info(info_provider, | |
| 185 reader, idl_filenames, | |
| 186 options.target_component.lower()) | |
| 187 | |
| 188 # Convert that mapping into the context required for the Jinja2 templates. | |
| 189 template_context = conditional_features_context( | |
| 190 MODULE_PYNAME, feature_info) | |
| 191 | |
| 192 # Generate and write out the header file | |
| 193 header_text = render_template(jinja_env.get_template( | |
| 194 "ConditionalFeaturesFor%s.h.tmpl" % options.target_component.title()), t emplate_context) | |
| 195 header_path = posixpath.join(options.output_directory, | |
| 196 "ConditionalFeaturesFor%s.h" % options.target_c omponent.title()) | |
| 197 write_file(header_text, header_path) | |
| 198 | |
| 199 # Generate and write out the implementation file | |
| 200 cpp_text = render_template(jinja_env.get_template( | |
| 201 "ConditionalFeaturesFor%s.cpp.tmpl" % options.target_component.title()), template_context) | |
| 202 cpp_path = posixpath.join(options.output_directory, | |
| 203 "ConditionalFeaturesFor%s.cpp" % options.target_co mponent.title()) | |
| 204 write_file(cpp_text, cpp_path) | |
| 205 | |
| 206 | |
| 207 def main(): | |
| 208 options = parse_options() | |
| 209 | |
| 210 info_provider = create_component_info_provider( | |
| 211 os.path.normpath(options.info_dir), options.target_component.lower()) | |
| 212 idl_filenames = map(str.strip, open(options.idl_files_list)) | |
| 213 | |
| 214 generate_conditional_features(info_provider, options, idl_filenames) | |
| 215 return 0 | |
| 216 | |
| 217 | |
| 218 if __name__ == '__main__': | |
| 219 sys.exit(main()) | |
| OLD | NEW |