|  | #!/usr/bin/python | 
|  | # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file | 
|  | # for details. All rights reserved. Use of this source code is governed by a | 
|  | # BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | import copy | 
|  | import database | 
|  | import idlparser | 
|  | import logging | 
|  | import os | 
|  | import os.path | 
|  |  | 
|  | from idlnode import * | 
|  |  | 
|  | _logger = logging.getLogger('databasebuilder') | 
|  |  | 
|  | # Used in source annotations to specify the parent interface declaring | 
|  | # a displaced declaration. The 'via' attribute specifies the parent interface | 
|  | # which implements a displaced declaration. | 
|  | _VIA_ANNOTATION_ATTR_NAME = 'via' | 
|  |  | 
|  | # Used in source annotations to specify the module that the interface was | 
|  | # imported from. | 
|  | _MODULE_ANNOTATION_ATTR_NAME = 'module' | 
|  |  | 
|  |  | 
|  | class DatabaseBuilderOptions(object): | 
|  | """Used in specifying options when importing new interfaces""" | 
|  |  | 
|  | def __init__(self, | 
|  | idl_syntax=idlparser.WEBIDL_SYNTAX, | 
|  | idl_defines=[], | 
|  | source=None, source_attributes={}, | 
|  | type_rename_map={}, | 
|  | rename_operation_arguments_on_merge=False, | 
|  | add_new_interfaces=True, | 
|  | obsolete_old_declarations=False): | 
|  | """Constructor. | 
|  | Args: | 
|  | idl_syntax -- the syntax of the IDL file that is imported. | 
|  | idl_defines -- list of definitions for the idl gcc pre-processor | 
|  | source -- the origin of the IDL file, used for annotating the | 
|  | database. | 
|  | source_attributes -- this map of attributes is used as | 
|  | annotation attributes. | 
|  | rename_operation_arguments_on_merge -- if True, will rename | 
|  | operation arguments when merging using the new name rather | 
|  | than the old. | 
|  | add_new_interfaces -- when False, if an interface is a new | 
|  | addition, it will be ignored. | 
|  | obsolete_old_declarations -- when True, if a declaration | 
|  | from a certain source is not re-declared, it will be removed. | 
|  | """ | 
|  | self.source = source | 
|  | self.source_attributes = source_attributes | 
|  | self.idl_syntax = idl_syntax | 
|  | self.idl_defines = idl_defines | 
|  | self.type_rename_map = type_rename_map | 
|  | self.rename_operation_arguments_on_merge = \ | 
|  | rename_operation_arguments_on_merge | 
|  | self.add_new_interfaces = add_new_interfaces | 
|  | self.obsolete_old_declarations = obsolete_old_declarations | 
|  |  | 
|  |  | 
|  | class DatabaseBuilder(object): | 
|  | def __init__(self, database): | 
|  | """DatabaseBuilder is used for importing and merging interfaces into | 
|  | the Database""" | 
|  | self._database = database | 
|  | self._imported_interfaces = [] | 
|  | self._impl_stmts = [] | 
|  |  | 
|  | def _load_idl_file(self, file_name, import_options): | 
|  | """Loads an IDL file intor memory""" | 
|  | idl_parser = idlparser.IDLParser(import_options.idl_syntax) | 
|  |  | 
|  | try: | 
|  | f = open(file_name, 'r') | 
|  | content = f.read() | 
|  | f.close() | 
|  |  | 
|  | idl_ast = idl_parser.parse(content, | 
|  | defines=import_options.idl_defines) | 
|  | return IDLFile(idl_ast, file_name) | 
|  | except SyntaxError, e: | 
|  | raise RuntimeError('Failed to load file %s: %s' % (file_name, e)) | 
|  |  | 
|  | def _resolve_type_defs(self, idl_file): | 
|  | type_def_map = {} | 
|  | # build map | 
|  | for type_def in idl_file.all(IDLTypeDef): | 
|  | if type_def.type.id != type_def.id: # sanity check | 
|  | type_def_map[type_def.id] = type_def.type.id | 
|  | # use the map | 
|  | for type_node in idl_file.all(IDLType): | 
|  | while type_node.id in type_def_map: | 
|  | type_node.id = type_def_map[type_node.id] | 
|  |  | 
|  | def _strip_ext_attributes(self, idl_file): | 
|  | """Strips unuseful extended attributes.""" | 
|  | for ext_attrs in idl_file.all(IDLExtAttrs): | 
|  | # TODO: Decide which attributes are uninteresting. | 
|  | pass | 
|  |  | 
|  | def _split_declarations(self, interface, optional_argument_whitelist): | 
|  | """Splits read-write attributes and operations with optional | 
|  | arguments into multiple declarations""" | 
|  |  | 
|  | # split attributes into setters and getters | 
|  | new_attributes = [] | 
|  | for attribute in interface.attributes: | 
|  | if attribute.is_fc_getter or attribute.is_fc_setter: | 
|  | new_attributes.append(attribute) | 
|  | continue | 
|  | getter_attr = copy.deepcopy(attribute) | 
|  | getter_attr.is_fc_getter = True | 
|  | new_attributes.append(getter_attr) | 
|  | if not attribute.is_read_only: | 
|  | setter_attr = copy.deepcopy(attribute) | 
|  | setter_attr.is_fc_setter = True | 
|  | new_attributes.append(setter_attr) | 
|  | interface.attributes = new_attributes | 
|  |  | 
|  | # Remove optional annotations from legacy optional arguments. | 
|  | for op in interface.operations: | 
|  | for i in range(0, len(op.arguments)): | 
|  | argument = op.arguments[i] | 
|  |  | 
|  | in_optional_whitelist = (interface.id, op.id, argument.id) in optional_argument_whitelist | 
|  | if in_optional_whitelist or set(['Optional', 'Callback']).issubset(argument.ext_attrs.keys()): | 
|  | argument.is_optional = True | 
|  | argument.ext_attrs['RequiredCppParameter'] = None | 
|  | continue | 
|  |  | 
|  | if argument.is_optional: | 
|  | if 'Optional' in argument.ext_attrs: | 
|  | optional_value = argument.ext_attrs['Optional'] | 
|  | if optional_value: | 
|  | argument.is_optional = False | 
|  | del argument.ext_attrs['Optional'] | 
|  |  | 
|  | # split operations with optional args into multiple operations | 
|  | new_ops = [] | 
|  | for op in interface.operations: | 
|  | for i in range(0, len(op.arguments)): | 
|  | if op.arguments[i].is_optional: | 
|  | op.arguments[i].is_optional = False | 
|  | new_op = copy.deepcopy(op) | 
|  | new_op.arguments = new_op.arguments[:i] | 
|  | new_ops.append(new_op) | 
|  | new_ops.append(op) | 
|  | interface.operations = new_ops | 
|  |  | 
|  | def _rename_types(self, idl_file, import_options): | 
|  | """Rename interface and type names with names provided in the | 
|  | options. Also clears scopes from scoped names""" | 
|  |  | 
|  | def rename(name): | 
|  | name_parts = name.split('::') | 
|  | name = name_parts[-1] | 
|  | if name in import_options.type_rename_map: | 
|  | name = import_options.type_rename_map[name] | 
|  | return name | 
|  |  | 
|  | def rename_node(idl_node): | 
|  | idl_node.id = rename(idl_node.id) | 
|  |  | 
|  | def rename_ext_attrs(ext_attrs_node): | 
|  | for type_valued_attribute_name in ['Supplemental']: | 
|  | if type_valued_attribute_name in ext_attrs_node: | 
|  | value = ext_attrs_node[type_valued_attribute_name] | 
|  | if isinstance(value, str): | 
|  | ext_attrs_node[type_valued_attribute_name] = rename(value) | 
|  |  | 
|  | map(rename_node, idl_file.all(IDLInterface)) | 
|  | map(rename_node, idl_file.all(IDLType)) | 
|  | map(rename_ext_attrs, idl_file.all(IDLExtAttrs)) | 
|  |  | 
|  | def _annotate(self, interface, module_name, import_options): | 
|  | """Adds @ annotations based on the source and source_attributes | 
|  | members of import_options.""" | 
|  |  | 
|  | source = import_options.source | 
|  | if not source: | 
|  | return | 
|  |  | 
|  | def add_source_annotation(idl_node): | 
|  | annotation = IDLAnnotation( | 
|  | copy.deepcopy(import_options.source_attributes)) | 
|  | idl_node.annotations[source] = annotation | 
|  | if ((isinstance(idl_node, IDLInterface) or | 
|  | isinstance(idl_node, IDLMember)) and | 
|  | idl_node.is_fc_suppressed): | 
|  | annotation['suppressed'] = None | 
|  |  | 
|  | add_source_annotation(interface) | 
|  | interface.annotations[source][_MODULE_ANNOTATION_ATTR_NAME] = module_name | 
|  |  | 
|  | map(add_source_annotation, interface.parents) | 
|  | map(add_source_annotation, interface.constants) | 
|  | map(add_source_annotation, interface.attributes) | 
|  | map(add_source_annotation, interface.operations) | 
|  |  | 
|  | def _sign(self, node): | 
|  | """Computes a unique signature for the node, for merging purposed, by | 
|  | concatenating types and names in the declaration.""" | 
|  | if isinstance(node, IDLType): | 
|  | res = node.id | 
|  | if res.startswith('unsigned '): | 
|  | res = res[len('unsigned '):] | 
|  | return res | 
|  |  | 
|  | res = [] | 
|  | if isinstance(node, IDLInterface): | 
|  | res = ['interface', node.id] | 
|  | elif isinstance(node, IDLParentInterface): | 
|  | res = ['parent', self._sign(node.type)] | 
|  | elif isinstance(node, IDLOperation): | 
|  | res = ['op'] | 
|  | for special in node.specials: | 
|  | res.append(special) | 
|  | if node.id is not None: | 
|  | res.append(node.id) | 
|  | for arg in node.arguments: | 
|  | res.append(self._sign(arg.type)) | 
|  | res.append(self._sign(node.type)) | 
|  | elif isinstance(node, IDLAttribute): | 
|  | res = [] | 
|  | if node.is_fc_getter: | 
|  | res.append('getter') | 
|  | elif node.is_fc_setter: | 
|  | res.append('setter') | 
|  | res.append(node.id) | 
|  | res.append(self._sign(node.type)) | 
|  | elif isinstance(node, IDLConstant): | 
|  | res = [] | 
|  | res.append('const') | 
|  | res.append(node.id) | 
|  | res.append(node.value) | 
|  | res.append(self._sign(node.type)) | 
|  | else: | 
|  | raise TypeError("Can't sign input of type %s" % type(node)) | 
|  | return ':'.join(res) | 
|  |  | 
|  | def _build_signatures_map(self, idl_node_list): | 
|  | """Creates a hash table mapping signatures to idl_nodes for the | 
|  | given list of nodes""" | 
|  | res = {} | 
|  | for idl_node in idl_node_list: | 
|  | sig = self._sign(idl_node) | 
|  | if sig is None: | 
|  | continue | 
|  | if sig in res: | 
|  | raise RuntimeError('Warning: Multiple members have the same ' | 
|  | 'signature: "%s"' % sig) | 
|  | res[sig] = idl_node | 
|  | return res | 
|  |  | 
|  | def _get_parent_interfaces(self, interface): | 
|  | """Return a list of all the parent interfaces of a given interface""" | 
|  | res = [] | 
|  |  | 
|  | def recurse(current_interface): | 
|  | if current_interface in res: | 
|  | return | 
|  | res.append(current_interface) | 
|  | for parent in current_interface.parents: | 
|  | parent_name = parent.type.id | 
|  | if self._database.HasInterface(parent_name): | 
|  | recurse(self._database.GetInterface(parent_name)) | 
|  |  | 
|  | recurse(interface) | 
|  | return res[1:] | 
|  |  | 
|  | def _merge_ext_attrs(self, old_attrs, new_attrs): | 
|  | """Merges two sets of extended attributes. | 
|  |  | 
|  | Returns: True if old_attrs has changed. | 
|  | """ | 
|  | changed = False | 
|  | for (name, value) in new_attrs.items(): | 
|  | if name in old_attrs and old_attrs[name] == value: | 
|  | pass # Identical | 
|  | else: | 
|  | old_attrs[name] = value | 
|  | changed = True | 
|  | return changed | 
|  |  | 
|  | def _merge_nodes(self, old_list, new_list, import_options): | 
|  | """Merges two lists of nodes. Annotates nodes with the source of each | 
|  | node. | 
|  |  | 
|  | Returns: | 
|  | True if the old_list has changed. | 
|  |  | 
|  | Args: | 
|  | old_list -- the list to merge into. | 
|  | new_list -- list containing more nodes. | 
|  | import_options -- controls how merging is done. | 
|  | """ | 
|  | changed = False | 
|  |  | 
|  | source = import_options.source | 
|  |  | 
|  | old_signatures_map = self._build_signatures_map(old_list) | 
|  | new_signatures_map = self._build_signatures_map(new_list) | 
|  |  | 
|  | # Merge new items | 
|  | for (sig, new_node) in new_signatures_map.items(): | 
|  | if sig not in old_signatures_map: | 
|  | # New node: | 
|  | old_list.append(new_node) | 
|  | changed = True | 
|  | else: | 
|  | # Merge old and new nodes: | 
|  | old_node = old_signatures_map[sig] | 
|  | if (source not in old_node.annotations | 
|  | and source in new_node.annotations): | 
|  | old_node.annotations[source] = new_node.annotations[source] | 
|  | changed = True | 
|  | # Maybe rename arguments: | 
|  | if isinstance(old_node, IDLOperation): | 
|  | for i in range(0, len(old_node.arguments)): | 
|  | old_arg_name = old_node.arguments[i].id | 
|  | new_arg_name = new_node.arguments[i].id | 
|  | if (old_arg_name != new_arg_name | 
|  | and (old_arg_name == 'arg' | 
|  | or old_arg_name.endswith('Arg') | 
|  | or import_options.rename_operation_arguments_on_merge)): | 
|  | old_node.arguments[i].id = new_arg_name | 
|  | changed = True | 
|  | # Maybe merge annotations: | 
|  | if (isinstance(old_node, IDLAttribute) or | 
|  | isinstance(old_node, IDLOperation)): | 
|  | if self._merge_ext_attrs(old_node.ext_attrs, new_node.ext_attrs): | 
|  | changed = True | 
|  |  | 
|  | # Remove annotations on obsolete items from the same source | 
|  | if import_options.obsolete_old_declarations: | 
|  | for (sig, old_node) in old_signatures_map.items(): | 
|  | if (source in old_node.annotations | 
|  | and sig not in new_signatures_map): | 
|  | _logger.warn('%s not available in %s anymore' % | 
|  | (sig, source)) | 
|  | del old_node.annotations[source] | 
|  | changed = True | 
|  |  | 
|  | return changed | 
|  |  | 
|  | def _merge_interfaces(self, old_interface, new_interface, import_options): | 
|  | """Merges the new_interface into the old_interface, annotating the | 
|  | interface with the sources of each change.""" | 
|  |  | 
|  | changed = False | 
|  |  | 
|  | source = import_options.source | 
|  | if (source and source not in old_interface.annotations and | 
|  | source in new_interface.annotations and | 
|  | not new_interface.is_supplemental): | 
|  | old_interface.annotations[source] = new_interface.annotations[source] | 
|  | changed = True | 
|  |  | 
|  | def merge_list(what): | 
|  | old_list = old_interface.__dict__[what] | 
|  | new_list = new_interface.__dict__[what] | 
|  |  | 
|  | if what != 'parents' and old_interface.id != new_interface.id: | 
|  | for node in new_list: | 
|  | node.ext_attrs['ImplementedBy'] = new_interface.id | 
|  |  | 
|  | changed = self._merge_nodes(old_list, new_list, import_options) | 
|  |  | 
|  | # Delete list items with zero remaining annotations. | 
|  | if changed and import_options.obsolete_old_declarations: | 
|  |  | 
|  | def has_annotations(idl_node): | 
|  | return len(idl_node.annotations) | 
|  |  | 
|  | old_interface.__dict__[what] = filter(has_annotations, old_list) | 
|  |  | 
|  | return changed | 
|  |  | 
|  | # Smartly merge various declarations: | 
|  | if merge_list('parents'): | 
|  | changed = True | 
|  | if merge_list('constants'): | 
|  | changed = True | 
|  | if merge_list('attributes'): | 
|  | changed = True | 
|  | if merge_list('operations'): | 
|  | changed = True | 
|  |  | 
|  | if self._merge_ext_attrs(old_interface.ext_attrs, new_interface.ext_attrs): | 
|  | changed = True | 
|  |  | 
|  | _logger.info('merged interface %s (changed=%s, supplemental=%s)' % | 
|  | (old_interface.id, changed, new_interface.is_supplemental)) | 
|  |  | 
|  | return changed | 
|  |  | 
|  | def _merge_impl_stmt(self, impl_stmt, import_options): | 
|  | """Applies "X implements Y" statemetns on the proper places in the | 
|  | database""" | 
|  | implementor_name = impl_stmt.implementor.id | 
|  | implemented_name = impl_stmt.implemented.id | 
|  | _logger.info('merging impl stmt %s implements %s' % | 
|  | (implementor_name, implemented_name)) | 
|  |  | 
|  | source = import_options.source | 
|  | if self._database.HasInterface(implementor_name): | 
|  | interface = self._database.GetInterface(implementor_name) | 
|  | if interface.parents is None: | 
|  | interface.parents = [] | 
|  | for parent in interface.parents: | 
|  | if parent.type.id == implemented_name: | 
|  | if source and source not in parent.annotations: | 
|  | parent.annotations[source] = IDLAnnotation( | 
|  | import_options.source_attributes) | 
|  | return | 
|  | # not found, so add new one | 
|  | parent = IDLParentInterface(None) | 
|  | parent.type = IDLType(implemented_name) | 
|  | if source: | 
|  | parent.annotations[source] = IDLAnnotation( | 
|  | import_options.source_attributes) | 
|  | interface.parents.append(parent) | 
|  |  | 
|  | def merge_imported_interfaces(self, optional_argument_whitelist): | 
|  | """Merges all imported interfaces and loads them into the DB.""" | 
|  |  | 
|  | # Step 1: Pre process imported interfaces | 
|  | for interface, module_name, import_options in self._imported_interfaces: | 
|  | self._split_declarations(interface, optional_argument_whitelist) | 
|  | self._annotate(interface, module_name, import_options) | 
|  |  | 
|  | # Step 2: Add all new interfaces and merge overlapping ones | 
|  | for interface, module_name, import_options in self._imported_interfaces: | 
|  | if not interface.is_supplemental: | 
|  | if self._database.HasInterface(interface.id): | 
|  | old_interface = self._database.GetInterface(interface.id) | 
|  | self._merge_interfaces(old_interface, interface, import_options) | 
|  | else: | 
|  | if import_options.add_new_interfaces: | 
|  | self._database.AddInterface(interface) | 
|  |  | 
|  | # Step 3: Merge in supplemental interfaces | 
|  | for interface, module_name, import_options in self._imported_interfaces: | 
|  | if interface.is_supplemental: | 
|  | target_name = interface.ext_attrs['Supplemental'] | 
|  | if target_name: | 
|  | # [Supplemental=DOMWindow] - merge into DOMWindow. | 
|  | target = target_name | 
|  | else: | 
|  | # [Supplemental] - merge into existing inteface with same name. | 
|  | target = interface.id | 
|  | if self._database.HasInterface(target): | 
|  | old_interface = self._database.GetInterface(target) | 
|  | self._merge_interfaces(old_interface, interface, import_options) | 
|  | else: | 
|  | raise Exception("Supplemental target '%s' not found", target) | 
|  |  | 
|  | # Step 4: Resolve 'implements' statements | 
|  | for impl_stmt, import_options in self._impl_stmts: | 
|  | self._merge_impl_stmt(impl_stmt, import_options) | 
|  |  | 
|  | self._impl_stmts = [] | 
|  | self._imported_interfaces = [] | 
|  |  | 
|  | def import_idl_file(self, file_path, | 
|  | import_options=DatabaseBuilderOptions()): | 
|  | """Parses, loads into memory and cleans up and IDL file""" | 
|  | idl_file = self._load_idl_file(file_path, import_options) | 
|  |  | 
|  | self._strip_ext_attributes(idl_file) | 
|  | self._resolve_type_defs(idl_file) | 
|  | self._rename_types(idl_file, import_options) | 
|  |  | 
|  | def enabled(idl_node): | 
|  | return self._is_node_enabled(idl_node, import_options.idl_defines) | 
|  |  | 
|  | for module in idl_file.modules: | 
|  | for interface in module.interfaces: | 
|  | if not self._is_node_enabled(interface, import_options.idl_defines): | 
|  | _logger.info('skipping interface %s/%s (source=%s file=%s)' | 
|  | % (module.id, interface.id, import_options.source, | 
|  | file_path)) | 
|  | continue | 
|  |  | 
|  | _logger.info('importing interface %s/%s (source=%s file=%s)' | 
|  | % (module.id, interface.id, import_options.source, | 
|  | file_path)) | 
|  | interface.attributes = filter(enabled, interface.attributes) | 
|  | interface.operations = filter(enabled, interface.operations) | 
|  | self._imported_interfaces.append((interface, module.id, import_options)) | 
|  |  | 
|  | for implStmt in module.implementsStatements: | 
|  | self._impl_stmts.append((implStmt, import_options)) | 
|  |  | 
|  | def _is_node_enabled(self, node, idl_defines): | 
|  | if not 'Conditional' in node.ext_attrs: | 
|  | return True | 
|  |  | 
|  | def enabled(condition): | 
|  | return 'ENABLE_%s' % condition in idl_defines | 
|  |  | 
|  | conditional = node.ext_attrs['Conditional'] | 
|  | if conditional.find('&') != -1: | 
|  | for condition in conditional.split('&'): | 
|  | if not enabled(condition): | 
|  | return False | 
|  | return True | 
|  |  | 
|  | for condition in conditional.split('|'): | 
|  | if enabled(condition): | 
|  | return True | 
|  | return False | 
|  |  | 
|  | def fix_displacements(self, source): | 
|  | """E.g. In W3C, something is declared on HTMLDocument but in WebKit | 
|  | its on Document, so we need to mark that something in HTMLDocument | 
|  | with @WebKit(via=Document). The 'via' attribute specifies the | 
|  | parent interface that has the declaration.""" | 
|  |  | 
|  | for interface in self._database.GetInterfaces(): | 
|  | changed = False | 
|  |  | 
|  | _logger.info('fixing displacements in %s' % interface.id) | 
|  |  | 
|  | for parent_interface in self._get_parent_interfaces(interface): | 
|  | _logger.info('scanning parent %s of %s' % | 
|  | (parent_interface.id, interface.id)) | 
|  |  | 
|  | def fix_nodes(local_list, parent_list): | 
|  | changed = False | 
|  | parent_signatures_map = self._build_signatures_map( | 
|  | parent_list) | 
|  | for idl_node in local_list: | 
|  | sig = self._sign(idl_node) | 
|  | if sig in parent_signatures_map: | 
|  | parent_member = parent_signatures_map[sig] | 
|  | if (source in parent_member.annotations | 
|  | and source not in idl_node.annotations | 
|  | and _VIA_ANNOTATION_ATTR_NAME | 
|  | not in parent_member.annotations[source]): | 
|  | idl_node.annotations[source] = IDLAnnotation( | 
|  | {_VIA_ANNOTATION_ATTR_NAME: parent_interface.id}) | 
|  | changed = True | 
|  | return changed | 
|  |  | 
|  | changed = fix_nodes(interface.constants, | 
|  | parent_interface.constants) or changed | 
|  | changed = fix_nodes(interface.attributes, | 
|  | parent_interface.attributes) or changed | 
|  | changed = fix_nodes(interface.operations, | 
|  | parent_interface.operations) or changed | 
|  | if changed: | 
|  | _logger.info('fixed displaced declarations in %s' % | 
|  | interface.id) | 
|  |  | 
|  | def normalize_annotations(self, sources): | 
|  | """Makes the IDLs less verbose by removing annotation attributes | 
|  | that are identical to the ones defined at the interface level. | 
|  |  | 
|  | Args: | 
|  | sources -- list of source names to normalize.""" | 
|  | for interface in self._database.GetInterfaces(): | 
|  | _logger.debug('normalizing annotations for %s' % interface.id) | 
|  | for source in sources: | 
|  | if (source not in interface.annotations or | 
|  | not interface.annotations[source]): | 
|  | continue | 
|  | top_level_annotation = interface.annotations[source] | 
|  |  | 
|  | def normalize(idl_node): | 
|  | if (source in idl_node.annotations | 
|  | and idl_node.annotations[source]): | 
|  | annotation = idl_node.annotations[source] | 
|  | for name, value in annotation.items(): | 
|  | if (name in top_level_annotation | 
|  | and value == top_level_annotation[name]): | 
|  | del annotation[name] | 
|  |  | 
|  | map(normalize, interface.parents) | 
|  | map(normalize, interface.constants) | 
|  | map(normalize, interface.attributes) | 
|  | map(normalize, interface.operations) |