Added --examine to produce analysis of IDL files

Reviewers=vsm@google.com,alan@google.com

R=vsm@google.com

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

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@44467 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
terry@google.com 2015-03-13 15:53:55 +00:00
parent d322d386db
commit 5c84e1b473
5 changed files with 238 additions and 7 deletions

View file

@ -233,6 +233,9 @@ def main():
parser.add_option('--verbose', dest='logging_level',
action='store_false', default=logging.WARNING,
help='Output all informational messages')
parser.add_option('--examine', dest='examine_idls',
action='store_true', default=None,
help='Analyze IDL files')
parser.add_option('--logging', dest='logging', type='int',
action='store', default=logging.NOTSET,
help='Level of logging 20 is Info, 30 is Warnings, 40 is Errors')
@ -263,7 +266,7 @@ def main():
UpdateCssProperties()
# Parse the IDL and create the database.
database = fremontcutbuilder.main(options.parallel, logging_level=logging_level)
database = fremontcutbuilder.main(options.parallel, logging_level=logging_level, examine_idls=options.examine_idls)
GenerateFromDatabase(database, dart2js_output_dir, dartium_output_dir,
options.update_dom_metadata, logging_level)

View file

@ -275,6 +275,13 @@ class Database(object):
raise RuntimeError('Dictionary %s already exists' % dictionary_name)
self._all_dictionaries[dictionary_name] = dictionary
def GetDictionaries(self):
"""Returns a list of all loaded dictionaries."""
res = []
for _, dictionary in sorted(self._all_dictionaries.items()):
res.append(dictionary)
return res
def TransitiveSecondaryParents(self, interface, propagate_event_target):
"""Returns a list of all non-primary parents.

View file

@ -753,6 +753,7 @@ class DatabaseBuilder(object):
for interface in self._database.GetInterfaces():
map(all_types, interface.all(IDLExtAttrFunctionValue))
map(all_types, interface.attributes)
map(all_types, interface.operations)
def fetch_constructor_data(self, options):
@ -772,3 +773,220 @@ class DatabaseBuilder(object):
if 'V8EnabledAtRuntime' in attr.ext_attrs:
interface.ext_attrs['synthesizedV8EnabledAtRuntime'] = \
attr.ext_attrs['V8EnabledAtRuntime'] or attr.id
# Iterate of the database looking for relationships between dictionaries and
# interfaces marked with NoInterfaceObject. This mechanism can be used for
# other IDL analysis.
def examine_database(self):
# Contains list of dictionary structure: {'dictionary': dictionary, 'usages': []}
self._diag_dictionaries = [];
self._dictionaries_used_types = [];
# Record any dictionary.
for dictionary in self._database.GetDictionaries():
self._diag_dictionaries.append({'dictionary': dictionary, 'usages': []});
# Contains list of NoInterfaceObject structures: {'no_interface_object': dictionary, 'usages': []}
self._diag_no_interfaces = [];
self._no_interfaces_used_types = [];
# Record any interface with Blink IDL Extended Attribute 'NoInterfaceObject'.
for interface in self._database.GetInterfaces():
if interface.is_no_interface_object:
self._diag_no_interfaces.append({'no_interface_object': interface, 'usages': []});
for interface in self._database.GetInterfaces():
self._constructors(interface)
self._constructors(interface, check_dictionaries=False)
for attribute in interface.attributes:
self._attribute_operation(interface, attribute)
self._attribute_operation(interface, attribute, check_dictionaries=False)
for operation in interface.operations:
self._attribute_operation(interface, operation)
self._attribute_operation(interface, operation, check_dictionaries=False)
# Report all dictionaries and their usage.
self._output_examination()
# Report all interface marked with NoInterfaceObject and their usage.
self._output_examination(check_dictionaries=False)
print '\nKey:'
print ' (READ-ONLY) - read-only attribute has relationship'
print ' (GET/SET) - attribute has relationship'
print ' RETURN - operation\'s returned value has relationship'
print ' (ARGUMENT) - operation\'s argument(s) has relationship'
print '\n\nExamination Complete\n'
def _output_examination(self, check_dictionaries=True):
# Output diagnostics. First columns is Dictionary or NoInterfaceObject e.g.,
# | Dictionary | Used In Interface | Usage Operation/Attribute |
print '\n\n'
title_bar = ['Dictionary', 'Used In Interface', 'Usage Operation/Attribute'] if check_dictionaries \
else ['NoInterfaceObject', 'Used In Interface', 'Usage Operation/Attribute']
self._tabulate_title(title_bar)
diags = self._diag_dictionaries if check_dictionaries else self._diag_no_interfaces
for diag in diags:
self._tabluate([diag['dictionary' if check_dictionaries else 'no_interface_object'].id, '', ''])
for usage in diag['usages']:
detail = ''
if 'attribute' in usage:
attribute_type = 'READ-ONLY' if not usage['argument'] else 'GET/SET'
detail = '(%s) %s' % (attribute_type, usage['attribute'])
elif 'operation' in usage:
detail = '%s %s%s' % ('RETURN' if usage['result'] else '',
usage['operation'],
'(ARGUMENT)' if usage['argument'] else '')
self._tabluate([None, usage['interface'], detail])
self._tabulate_break()
# operation_or_attribute either IDLOperation or IDLAttribute if None then
# its a constructor (IDLExtAttrFunctionValue).
def _mark_usage(self, interface, operation_or_attribute = None, check_dictionaries=True):
for diag in self._diag_dictionaries if check_dictionaries else self._diag_no_interfaces:
for usage in diag['usages']:
if not usage['interface']:
usage['interface'] = interface.id
if isinstance(operation_or_attribute, IDLOperation):
usage['operation'] = operation_or_attribute.id
if check_dictionaries:
usage['result'] = hasattr(operation_or_attribute.type, 'dictionary') and \
operation_or_attribute.type.dictionary == diag['dictionary'].id
else:
usage['result'] = operation_or_attribute.type.id == diag['no_interface_object'].id
usage['argument'] = False
for argument in operation_or_attribute.arguments:
if check_dictionaries:
arg = hasattr(argument.type, 'dictionary') and argument.type.dictionary == diag['dictionary'].id
else:
arg = argument.type.id == diag['no_interface_object'].id
if arg:
usage['argument'] = arg
elif isinstance(operation_or_attribute, IDLAttribute):
usage['attribute'] = operation_or_attribute.id
usage['result'] = True
usage['argument'] = not operation_or_attribute.is_read_only
elif not operation_or_attribute:
# Its a constructor only argument is dictionary or interface with NoInterfaceObject.
usage['operation'] = 'constructor'
usage['result'] = False
usage['argument'] = True
def _remember_usage(self, node, check_dictionaries=True):
if check_dictionaries:
used_types = self._dictionaries_used_types
diag_list = self._diag_dictionaries
diag_name = 'dictionary'
else:
used_types = self._no_interfaces_used_types
diag_list = self._diag_no_interfaces
diag_name = 'no_interface_object'
if len(used_types) > 0:
normalized_used = list(set(used_types))
for recorded_id in normalized_used:
for diag in diag_list:
if diag[diag_name].id == recorded_id:
diag['usages'].append({'interface': None, 'node': node})
# Iterator function to look for any IDLType that is a dictionary then remember
# that dictionary.
def _dictionary_used(self, type_node):
if hasattr(type_node, 'dictionary'):
dictionary_id = type_node.dictionary
if self._database.HasDictionary(dictionary_id):
for diag_dictionary in self._diag_dictionaries:
if diag_dictionary['dictionary'].id == dictionary_id:
# Record the dictionary that was referenced.
self._dictionaries_used_types.append(dictionary_id)
return
# If we get to this point, the IDL dictionary was never defined ... oops.
print 'DIAGNOSE_ERROR: IDL Dictionary %s doesn\'t exist.' % dictionary_id
# Iterator function to look for any IDLType that is an interface marked with
# NoInterfaceObject then remember that interface.
def _no_interface_used(self, type_node):
if hasattr(type_node, 'id'):
no_interface_id = type_node.id
if self._database.HasInterface(no_interface_id):
no_interface = self._database.GetInterface(no_interface_id)
if no_interface.is_no_interface_object:
for diag_no_interface in self._diag_no_interfaces:
if diag_no_interface['no_interface_object'].id == no_interface_id:
# Record the interface marked with NoInterfaceObject.
self._no_interfaces_used_types.append(no_interface_id)
return
def _constructors(self, interface, check_dictionaries=True):
if check_dictionaries:
self._dictionaries_used_types = []
constructor_function = self._dictionary_constructor_types
else:
self._no_interfaces_used_types = [];
constructor_function = self._no_interface_constructor_types
map(constructor_function, interface.all(IDLExtAttrFunctionValue))
self._mark_usage(interface, check_dictionaries=check_dictionaries)
# Scan an attribute or operation for a dictionary or interface with NoInterfaceObject
# reference.
def _attribute_operation(self, interface, operation_attribute, check_dictionaries=True):
if check_dictionaries:
self._dictionaries_used_types = []
used = self._dictionary_used
else:
self._no_interfaces_used_types = [];
used = self._no_interface_used
map(used, operation_attribute.all(IDLType))
self._remember_usage(operation_attribute, check_dictionaries=check_dictionaries)
self._mark_usage(interface, operation_attribute, check_dictionaries=check_dictionaries)
# Iterator function for map to iterate over all constructor types
# (IDLExtAttrFunctionValue) that have a dictionary reference.
def _dictionary_constructor_types(self, node):
self._dictionaries_used_types = []
map(self._dictionary_used, node.all(IDLType))
self._remember_usage(node)
# Iterator function for map to iterate over all constructor types
# (IDLExtAttrFunctionValue) that reference an interface with NoInterfaceObject.
def _no_interface_constructor_types(self, node):
self._no_interfaces_used_types = [];
map(self._no_interface_used, node.all(IDLType))
self._remember_usage(node, check_dictionaries=False)
# Maximum width of each column.
def _TABULATE_WIDTH(self):
return 45
def _tabulate_title(self, row_title):
title_separator = "=" * self._TABULATE_WIDTH()
self._tabluate([title_separator, title_separator, title_separator])
self._tabluate(row_title)
self._tabluate([title_separator, title_separator, title_separator])
def _tabulate_break(self):
break_separator = "-" * self._TABULATE_WIDTH()
self._tabluate([break_separator, break_separator, break_separator])
def _tabluate(self, columns):
"""Tabulate a list of columns for a row. Each item in columns is a column
value each column will be padded up to _TABULATE_WIDTH. Each
column starts/ends with a vertical bar '|' the format a row:
| columns[0] | columns[1] | columns[2] | ... |
"""
if len(columns) > 0:
for column in columns:
value = '' if not column else column
sys.stdout.write('|{0:^{1}}'.format(value, self._TABULATE_WIDTH()))
else:
sys.stdout.write('|{0:^{1}}'.format('', self._TABULATE_WIDTH()))
sys.stdout.write('|\n')

View file

@ -36,7 +36,7 @@ FEATURE_DEFINES = [
]
def build_database(idl_files, database_dir, feature_defines=None,
logging_level=logging.WARNING):
logging_level=logging.WARNING, examine_idls=False):
"""This code reconstructs the FremontCut IDL database from W3C,
WebKit and Dart IDL files."""
current_dir = os.path.dirname(__file__)
@ -98,6 +98,11 @@ def build_database(idl_files, database_dir, feature_defines=None,
# Map any IDL defined dictionaries to Dictionary.
builder.map_dictionaries()
# Examine all IDL and produce a diagnoses of areas (e.g., list dictionaries
# declared and usage, etc.)
if examine_idls:
builder.examine_database()
conditionals_met = set(
'ENABLE_' + conditional for conditional in builder.conditionals_met)
known_conditionals = set(FEATURE_DEFINES + FEATURE_DISABLED)
@ -116,12 +121,9 @@ def build_database(idl_files, database_dir, feature_defines=None,
print 'Merging interfaces %s seconds' % round(time.time() - start_time, 2)
# TODO(terry): Don't generate the database cache.
# db.Save()
return db
def main(parallel=False, logging_level=logging.WARNING):
def main(parallel=False, logging_level=logging.WARNING, examine_idls=False):
current_dir = os.path.dirname(__file__)
idl_files = []
@ -167,7 +169,7 @@ def main(parallel=False, logging_level=logging.WARNING):
database_dir = os.path.join(current_dir, '..', 'database')
return build_database(idl_files, database_dir, logging_level=logging_level)
return build_database(idl_files, database_dir, logging_level=logging_level, examine_idls=examine_idls)
if __name__ == '__main__':
sys.exit(main())

View file

@ -508,6 +508,7 @@ class IDLExtAttrs(IDLDictNode):
return self.values()
# IDLExtAttrFunctionValue is used for constructors defined in the IDL.
class IDLExtAttrFunctionValue(IDLNode):
"""IDLExtAttrFunctionValue."""
def __init__(self, func_value_ast, arg_list_ast, is_blink=False):