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

Unified Diff: third_party/WebKit/Source/bindings/scripts/generate_conditional_features.py

Issue 2970003002: Add code generation for ConditionalFeatures bindings code (Closed)
Patch Set: Readability 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 side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/bindings/scripts/generate_conditional_features.py
diff --git a/third_party/WebKit/Source/bindings/scripts/generate_conditional_features.py b/third_party/WebKit/Source/bindings/scripts/generate_conditional_features.py
new file mode 100755
index 0000000000000000000000000000000000000000..861e2c25f1598026c8ef806f2c93168167767380
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/scripts/generate_conditional_features.py
@@ -0,0 +1,232 @@
+#!/usr/bin/python
+#
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script reads the global interface data collected by
+# compute_interfaces_info_overall.py, and writes out the code which adds
+# bindings for origin-trial-enabled features at runtime.
+
+# pylint: disable=W0403
+
+import optparse
+import os
+import posixpath
+import sys
+from collections import defaultdict, namedtuple
+
+from code_generator import (initialize_jinja_env, normalize_and_sort_includes,
+ render_template)
+from idl_reader import IdlReader
+from utilities import (create_component_info_provider, write_file,
+ idl_filename_to_component)
+from v8_utilities import v8_class_name, v8_class_name_or_partial, uncapitalize
+
+# Make sure extension is .py, not .pyc or .pyo, so doesn't depend on caching
+MODULE_PYNAME = os.path.splitext(os.path.basename(__file__))[0] + '.py'
+
+
+ConditionalInterfaceInfo = namedtuple('ConditionalInterfaceInfo', [
+ 'name', 'v8_class', 'v8_class_or_partial', 'is_global'])
+
+
+def get_install_functions(interfaces, feature_names):
+ """Construct a list of V8 bindings installation functions for each feature
+ on each interface.
+
+ interfaces is a list of ConditionalInterfaceInfo tuples
+ feature_names is a list of strings, containing names of features which can
+ be installed on those interfaces.
+ """
+ return [
+ {"condition": 'OriginTrials::%sEnabled' % uncapitalize(feature_name),
+ "name": feature_name,
+ "install_method": "install%s" % feature_name,
+ "v8_class": interface_info.v8_class,
+ "v8_class_or_partial": interface_info.v8_class_or_partial}
+ for feature_name in feature_names
+ for interface_info in interfaces]
+
+
+def get_conditional_feature_names_from_interface(interface):
+ feature_names = set()
+ if ('OriginTrialEnabled' in interface.extended_attributes and
+ interface.is_partial):
+ feature_names.add(interface.extended_attributes['OriginTrialEnabled'])
+ for operation in interface.operations:
+ if 'OriginTrialEnabled' in operation.extended_attributes:
+ feature_names.add(
+ operation.extended_attributes['OriginTrialEnabled'])
+ for attribute in interface.attributes:
+ if 'OriginTrialEnabled' in attribute.extended_attributes:
+ feature_names.add(
+ attribute.extended_attributes['OriginTrialEnabled'])
+ return list(feature_names)
+
+
+def read_idl_file(reader, idl_filename):
+ interfaces = reader.read_idl_file(idl_filename).interfaces
+ # There should only be a single interface defined in an IDL file. Return it.
+ assert len(interfaces) == 1
+ return interfaces.values()[0]
+
+
+def interface_is_global(interface):
+ return ('Global' in interface.extended_attributes or
+ 'PrimaryGlobal' in interface.extended_attributes)
+
+
+def conditional_features_info(info_provider, reader, idl_filenames, target_component):
+ """Read a set of IDL files and compile the mapping between interfaces and
+ the conditional features defined on them.
+
+ Returns a tuple (features_for_type, types_for_feature, includes):
+ - features_for_type is a mapping of interface->feature
+ - types_for_feature is the reverse mapping: feature->interface
+ - includes is a set of header files which need to be included in the
+ generated implementation code.
+ """
+ features_for_type = defaultdict(set)
+ types_for_feature = defaultdict(set)
+ includes = set()
+
+ for idl_filename in idl_filenames:
+ interface = read_idl_file(reader, idl_filename)
+ feature_names = get_conditional_feature_names_from_interface(interface)
+ if feature_names:
+ is_global = interface_is_global(interface)
+ if interface.is_partial:
+ # For partial interfaces, we need to generate different
+ # includes if the parent interface is in a different
+ # component.
+ parent_interface_info = info_provider.interfaces_info[interface.name]
+ parent_interface = read_idl_file(
+ reader, parent_interface_info.get('full_path'))
+ is_global = is_global or interface_is_global(parent_interface)
+ parent_component = idl_filename_to_component(
+ parent_interface_info.get('full_path'))
+ if interface.is_partial and target_component != parent_component:
+ includes.add('bindings/%s/v8/V8%s.h' %
+ (parent_component, interface.name))
+ includes.add('bindings/%s/v8/V8%sPartial.h' %
+ (target_component, interface.name))
+ else:
+ includes.add('bindings/%s/v8/V8%s.h' %
+ (target_component, interface.name))
+ # If this is a partial interface in the same component as
+ # its parent, then treat it as a non-partial interface.
+ interface.is_partial = False
+ interface_info = ConditionalInterfaceInfo(interface.name,
+ v8_class_name(interface),
+ v8_class_name_or_partial(
+ interface),
+ is_global)
+ for feature_name in feature_names:
+ features_for_type[interface_info].add(feature_name)
+ types_for_feature[feature_name].add(interface_info)
+
+ return features_for_type, types_for_feature, includes
+
+
+def conditional_features_context(generator_name, feature_info):
+ context = {'code_generator': generator_name}
+
+ # Unpack the feature info tuple.
+ features_for_type, types_for_feature, includes = feature_info
+
+ # Add includes needed for cpp code and normalize.
+ includes.update([
+ "core/context_features/ContextFeatureSettings.h",
+ "core/dom/ExecutionContext.h",
+ "core/frame/Frame.h",
+ "core/origin_trials/OriginTrials.h",
+ "platform/bindings/ConditionalFeatures.h",
+ "platform/bindings/ScriptState.h",
+ # TODO(iclelland): Remove the need to explicitly include this; it is
+ # here because the ContextFeatureSettings code needs it.
+ "bindings/core/v8/V8Window.h",
+ ])
+ context['includes'] = normalize_and_sort_includes(includes)
+
+ # For each interface, collect a list of bindings installation functions to
+ # call, organized by conditional feature.
+ context['installers_by_interface'] = [
+ {"name": interface_info.name,
+ "is_global": interface_info.is_global,
+ "v8_class": interface_info.v8_class,
+ "installers": get_install_functions([interface_info], feature_names)}
+ for interface_info, feature_names in features_for_type.items()]
+ context['installers_by_interface'].sort(key=lambda x: x['name'])
+
+ # For each conditional feature, collect a list of bindings installation
+ # functions to call, organized by interface.
+ context['installers_by_feature'] = [
+ {"name": feature_name,
+ "installers": get_install_functions(interfaces, [feature_name])}
+ for feature_name, interfaces in types_for_feature.items()]
+ context['installers_by_feature'].sort(key=lambda x: x['name'])
+
+ return context
+
+
+def parse_options():
+ parser = optparse.OptionParser()
+ parser.add_option('--cache-directory',
+ help='cache directory, defaults to output directory')
+ parser.add_option('--output-directory')
+ parser.add_option('--info-dir')
+ parser.add_option('--target-component',
+ type='choice',
+ choices=['Core', 'Modules'],
+ help='target component to generate code')
+ parser.add_option('--idl-files-list')
+
+ options, _ = parser.parse_args()
+ if options.output_directory is None:
+ parser.error('Must specify output directory using --output-directory.')
+ return options
+
+
+def generate_conditional_features(info_provider, options, idl_filenames):
+ reader = IdlReader(info_provider.interfaces_info, options.cache_directory)
+ jinja_env = initialize_jinja_env(options.cache_directory)
+
+ # Extract the bidirectional mapping of conditional features <-> interfaces
+ # from the global info provider and the supplied list of IDL files.
+ feature_info = conditional_features_info(info_provider,
+ reader, idl_filenames,
+ options.target_component.lower())
+
+ # Convert that mapping into the context required for the Jinja2 templates.
+ template_context = conditional_features_context(
+ MODULE_PYNAME, feature_info)
+
+ # Generate and write out the header file
+ header_text = render_template(jinja_env.get_template(
+ "ConditionalFeaturesFor%s.h.tmpl" % options.target_component.title()), template_context)
+ header_path = posixpath.join(options.output_directory,
+ "ConditionalFeaturesFor%s.h" % options.target_component.title())
+ write_file(header_text, header_path)
+
+ # Generate and write out the implementation file
+ cpp_text = render_template(jinja_env.get_template(
+ "ConditionalFeaturesFor%s.cpp.tmpl" % options.target_component.title()), template_context)
+ cpp_path = posixpath.join(options.output_directory,
+ "ConditionalFeaturesFor%s.cpp" % options.target_component.title())
+ write_file(cpp_text, cpp_path)
+
+
+def main():
+ options = parse_options()
+
+ info_provider = create_component_info_provider(
+ os.path.normpath(options.info_dir), options.target_component.lower())
+ idl_filenames = map(str.strip, open(options.idl_files_list))
+
+ generate_conditional_features(info_provider, options, idl_filenames)
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
« no previous file with comments | « third_party/WebKit/Source/bindings/modules/v8/v8.gni ('k') | third_party/WebKit/Source/bindings/scripts/scripts.gni » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698