Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(565)

Side by Side Diff: third_party/WebKit/Source/bindings/scripts/generate_conditional_features.py

Issue 2970003002: Add code generation for ConditionalFeatures bindings code (Closed)
Patch Set: Style fixes Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698