Commit 79c6adce authored by bashi@chromium.org's avatar bashi@chromium.org

IDL: Avoid global variables in compute_interfaces_info_individual.py

compute_interfaces_info_individual has global variables. These variables
are updated each time we call compute_info_individual(). This could be
a problem when we want to call compute_info_individual() for separate
components in the same process. Such cases could happen in
run-bindings-test when we add union types collection in
compute_info_individual(). We would want to collect all union types
which are used by IDL files under Source/bindings/test/idls, but we
don't want to collect them for IDL files under Source/{core,modules}.
This CL introduces a class and moves global variables into the class
as instance variables.

This CL doesn't change generated bindings code at this moment.

BUG=425916

Review URL: https://codereview.chromium.org/671863002

git-svn-id: svn://svn.chromium.org/blink/trunk@184314 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 5ac77eb5
......@@ -53,13 +53,6 @@ from utilities import get_file_contents, read_file_to_list, idl_filename_to_inte
module_path = os.path.dirname(__file__)
source_path = os.path.normpath(os.path.join(module_path, os.pardir, os.pardir))
# Global variables (filled in and exported)
interfaces_info = {}
partial_interface_files = defaultdict(lambda: {
'full_paths': [],
'include_paths': [],
})
class IdlBadFilenameError(Exception):
"""Raised if an IDL filename disagrees with the interface name in the file."""
......@@ -111,13 +104,6 @@ def include_path(idl_filename, implemented_as=None):
return posixpath.join(relative_dir, cpp_class_name + '.h')
def add_paths_to_partials_dict(partial_interface_name, full_path, this_include_path=None):
paths_dict = partial_interface_files[partial_interface_name]
paths_dict['full_paths'].append(full_path)
if this_include_path:
paths_dict['include_paths'].append(this_include_path)
def get_implements_from_definitions(definitions, definition_name):
left_interfaces = []
right_interfaces = []
......@@ -142,70 +128,90 @@ def get_put_forward_interfaces_from_definition(definition):
if 'PutForwards' in attribute.extended_attributes))
def compute_info_individual(idl_filename, reader):
definitions = reader.read_idl_file(idl_filename)
if len(definitions.interfaces) > 0:
definition = next(definitions.interfaces.itervalues())
interface_info = {
'is_callback_interface': definition.is_callback,
'is_dictionary': False,
# Interfaces that are referenced (used as types) and that we introspect
# during code generation (beyond interface-level data ([ImplementedAs],
# is_callback_interface, ancestors, and inherited extended attributes):
# deep dependencies.
# These cause rebuilds of referrers, due to the dependency, so these
# should be minimized; currently only targets of [PutForwards].
'referenced_interfaces': get_put_forward_interfaces_from_definition(definition),
}
elif len(definitions.dictionaries) > 0:
definition = next(definitions.dictionaries.itervalues())
interface_info = {
'is_callback_interface': False,
'is_dictionary': True,
'referenced_interfaces': None,
class InterfaceInfoCollector(object):
"""A class that collects interface information from idl files."""
def __init__(self, cache_directory=None):
self.reader = IdlReader(interfaces_info=None, outputdir=cache_directory)
self.interfaces_info = {}
self.partial_interface_files = defaultdict(lambda: {
'full_paths': [],
'include_paths': [],
})
def add_paths_to_partials_dict(self, partial_interface_name, full_path,
this_include_path=None):
paths_dict = self.partial_interface_files[partial_interface_name]
paths_dict['full_paths'].append(full_path)
if this_include_path:
paths_dict['include_paths'].append(this_include_path)
def collect_info(self, idl_filename):
"""Reads an idl file and collects information which is required by the
binding code generation."""
definitions = self.reader.read_idl_file(idl_filename)
if len(definitions.interfaces) > 0:
definition = next(definitions.interfaces.itervalues())
interface_info = {
'is_callback_interface': definition.is_callback,
'is_dictionary': False,
# Interfaces that are referenced (used as types) and that we
# introspect during code generation (beyond interface-level
# data ([ImplementedAs], is_callback_interface, ancestors, and
# inherited extended attributes): deep dependencies.
# These cause rebuilds of referrers, due to the dependency,
# so these should be minimized; currently only targets of
# [PutForwards].
'referenced_interfaces': get_put_forward_interfaces_from_definition(definition),
}
elif len(definitions.dictionaries) > 0:
definition = next(definitions.dictionaries.itervalues())
interface_info = {
'is_callback_interface': False,
'is_dictionary': True,
'referenced_interfaces': None,
}
else:
raise Exception('IDL file must contain one interface or dictionary')
extended_attributes = definition.extended_attributes
implemented_as = extended_attributes.get('ImplementedAs')
full_path = os.path.realpath(idl_filename)
this_include_path = None if 'NoImplHeader' in extended_attributes else include_path(idl_filename, implemented_as)
if definition.is_partial:
# We don't create interface_info for partial interfaces, but
# adds paths to another dict.
self.add_paths_to_partials_dict(definition.name, full_path, this_include_path)
return
# 'implements' statements can be included in either the file for the
# implement*ing* interface (lhs of 'implements') or implement*ed* interface
# (rhs of 'implements'). Store both for now, then merge to implement*ing*
# interface later.
left_interfaces, right_interfaces = get_implements_from_definitions(
definitions, definition.name)
interface_info.update({
'extended_attributes': extended_attributes,
'full_path': full_path,
'implemented_as': implemented_as,
'implemented_by_interfaces': left_interfaces,
'implements_interfaces': right_interfaces,
'include_path': this_include_path,
# FIXME: temporary private field, while removing old treatement of
# 'implements': http://crbug.com/360435
'is_legacy_treat_as_partial_interface': 'LegacyTreatAsPartialInterface' in extended_attributes,
'parent': definition.parent,
'relative_dir': relative_dir_posix(idl_filename),
})
self.interfaces_info[definition.name] = interface_info
def get_info_as_dict(self):
"""Returns info packaged as a dict."""
return {
'interfaces_info': self.interfaces_info,
# Can't pickle defaultdict, convert to dict
'partial_interface_files': dict(self.partial_interface_files),
}
else:
raise Exception('IDL file must contain one interface or dictionary')
extended_attributes = definition.extended_attributes
implemented_as = extended_attributes.get('ImplementedAs')
full_path = os.path.realpath(idl_filename)
this_include_path = None if 'NoImplHeader' in extended_attributes else include_path(idl_filename, implemented_as)
if definition.is_partial:
# We don't create interface_info for partial interfaces, but
# adds paths to another dict.
add_paths_to_partials_dict(definition.name, full_path, this_include_path)
return
# 'implements' statements can be included in either the file for the
# implement*ing* interface (lhs of 'implements') or implement*ed* interface
# (rhs of 'implements'). Store both for now, then merge to implement*ing*
# interface later.
left_interfaces, right_interfaces = get_implements_from_definitions(definitions, definition.name)
interface_info.update({
'extended_attributes': extended_attributes,
'full_path': full_path,
'implemented_as': implemented_as,
'implemented_by_interfaces': left_interfaces,
'implements_interfaces': right_interfaces,
'include_path': this_include_path,
# FIXME: temporary private field, while removing old treatement of
# 'implements': http://crbug.com/360435
'is_legacy_treat_as_partial_interface': 'LegacyTreatAsPartialInterface' in extended_attributes,
'parent': definition.parent,
'relative_dir': relative_dir_posix(idl_filename),
})
interfaces_info[definition.name] = interface_info
def info_individual():
"""Returns info packaged as a dict."""
return {
'interfaces_info': interfaces_info,
# Can't pickle defaultdict, convert to dict
'partial_interface_files': dict(partial_interface_files),
}
################################################################################
......@@ -224,12 +230,12 @@ def main():
# Compute information for individual files
# Information is stored in global variables interfaces_info and
# partial_interface_files.
reader = IdlReader(interfaces_info=None, outputdir=options.cache_directory)
info_collector = InterfaceInfoCollector(options.cache_directory)
for idl_filename in idl_files:
compute_info_individual(idl_filename, reader)
info_collector.collect_info(idl_filename)
write_pickle_file(options.interfaces_info_file,
info_individual(),
info_collector.get_info_as_dict(),
options.write_file_only_if_changed)
......
......@@ -41,7 +41,7 @@ source_path = os.path.normpath(os.path.join(module_path, os.pardir, os.pardir,
sys.path.append(source_path) # for Source/bindings imports
import bindings.scripts.compute_interfaces_info_individual
from bindings.scripts.compute_interfaces_info_individual import compute_info_individual, info_individual
from bindings.scripts.compute_interfaces_info_individual import InterfaceInfoCollector
import bindings.scripts.compute_interfaces_info_overall
from bindings.scripts.compute_interfaces_info_overall import compute_interfaces_info_overall, interfaces_info
from bindings.scripts.idl_compiler import IdlCompilerDictionaryImpl, IdlCompilerV8
......@@ -106,6 +106,14 @@ def generate_interface_dependencies(output_directory):
for filename in fnmatch.filter(files, '*.idl'))
return idl_paths
def collect_blink_idl_paths():
"""Returns IDL file paths which blink actually uses."""
idl_paths = []
for component in COMPONENT_DIRECTORY:
directory = os.path.join(source_path, component)
idl_paths.extend(idl_paths_recursive(directory))
return idl_paths
# We compute interfaces info for *all* IDL files, not just test IDL
# files, as code generator output depends on inheritance (both ancestor
# chain and inherited extended attributes), and some real interfaces
......@@ -116,14 +124,8 @@ def generate_interface_dependencies(output_directory):
# since this is also special-cased and Node inherits from EventTarget,
# but this inheritance information requires computing dependencies for
# the real Node.idl file.
non_test_idl_paths = []
test_idl_paths = []
test_idl_dir = test_input_directory + os.sep
for idl_path in idl_paths_recursive(source_path):
if idl_path.startswith(test_idl_dir):
test_idl_paths.append(idl_path)
else:
non_test_idl_paths.append(idl_path)
non_test_idl_paths = collect_blink_idl_paths()
test_idl_paths = idl_paths_recursive(test_input_directory)
# 2-stage computation: individual, then overall
#
# Properly should compute separately by component (currently test
......@@ -134,13 +136,14 @@ def generate_interface_dependencies(output_directory):
# In order to allow test IDL files to override the production IDL files if
# they have the same interface name, process the test IDL files after the
# non-test IDL files.
reader = IdlReader()
info_individuals = []
info_collector = InterfaceInfoCollector()
for idl_path_list in (non_test_idl_paths, test_idl_paths):
for idl_path in idl_path_list:
if os.path.basename(idl_path) in NON_BLINK_IDL_FILES:
continue
compute_info_individual(idl_path, reader)
info_individuals = [info_individual()]
info_collector.collect_info(idl_path)
info_individuals.append(info_collector.get_info_as_dict())
# TestDictionary.{h,cpp} are placed under Source/bindings/tests/idls/core.
# However, IdlCompiler generates TestDictionary.{h,cpp} by using relative_dir.
# So the files will be generated under output_dir/core/bindings/tests/idls/core.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment