| #!/usr/bin/env python3 | |
| # Copyright (c) 2012, 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. | |
| """This module generates Dart APIs from the IDL database.""" | |
| import emitter | |
| import idlnode | |
| import logging | |
| import os | |
| import re | |
| import shutil | |
| from generator import * | |
| from idlnode import IDLType, IDLInterface, resolveTypedef | |
| _logger = logging.getLogger('dartgenerator') | |
| def MergeNodes(node, other): | |
| node.operations.extend(other.operations) | |
| for attribute in other.attributes: | |
| if not node.has_attribute(attribute): | |
| node.attributes.append(attribute) | |
| node.constants.extend(other.constants) | |
| class DartGenerator(object): | |
| """Utilities to generate Dart APIs and corresponding JavaScript.""" | |
| def __init__(self, logging_level=logging.WARNING): | |
| self._auxiliary_files = {} | |
| self._dart_templates_re = re.compile(r'[\w.:]+<([\w \.<>:]+)>') | |
| _logger.setLevel(logging_level) | |
| def _StripModules(self, type_name): | |
| return type_name.split('::')[-1] | |
| def _IsCompoundType(self, database, type_name): | |
| if IsRegisteredType(type_name): | |
| return True | |
| # References a typedef - normally a union type. | |
| if database.HasTypeDef(type_name): | |
| return True | |
| if type_name.endswith('?'): | |
| return self._IsCompoundType(database, type_name[:-len('?')]) | |
| if type_name.endswith('[]'): | |
| return self._IsCompoundType(database, type_name[:-len('[]')]) | |
| stripped_type_name = self._StripModules(type_name) | |
| if (database.HasInterface(stripped_type_name) or | |
| database.HasDictionary(stripped_type_name)): | |
| return True | |
| if database.HasEnum(stripped_type_name): | |
| return True | |
| dart_template_match = self._dart_templates_re.match(type_name) | |
| if dart_template_match: | |
| # Dart templates | |
| parent_type_name = type_name[0:dart_template_match.start(1) - 1] | |
| sub_type_name = dart_template_match.group(1) | |
| return (self._IsCompoundType(database, parent_type_name) and | |
| self._IsCompoundType(database, sub_type_name)) | |
| return False | |
| def _IsDartType(self, type_name): | |
| return '.' in type_name | |
| def LoadAuxiliary(self, auxiliary_dir): | |
| for (dirname, _, names) in os.walk(auxiliary_dir): | |
| for name in names: | |
| if name.endswith('.dart'): | |
| name = name[0:-5] # strip off ".dart" | |
| self._auxiliary_files[name] = os.path.join(dirname, name) | |
| def FilterMembersWithUnidentifiedTypes(self, database): | |
| """Removes unidentified types. | |
| Removes constants, attributes, operations and parents with unidentified | |
| types. | |
| """ | |
| for interface in database.GetInterfaces(): | |
| def IsIdentified(idl_node): | |
| node_name = idl_node.id if idl_node.id else 'parent' | |
| for idl_type in idl_node.all(idlnode.IDLType): | |
| type_name = idl_type.id | |
| if (type_name is not None and | |
| self._IsCompoundType(database, type_name)): | |
| continue | |
| # Ignore constructor warnings. | |
| if not (interface.id in [ | |
| 'Window', 'WorkerContext', 'WorkerGlobalScope' | |
| ] and type_name.endswith('Constructor')): | |
| _logger.warn( | |
| 'removing %s in %s which has unidentified type %s' % | |
| (node_name, interface.id, type_name)) | |
| return False | |
| return True | |
| interface.constants = list(filter(IsIdentified, | |
| interface.constants)) | |
| interface.attributes = list( | |
| filter(IsIdentified, interface.attributes)) | |
| interface.operations = list( | |
| filter(IsIdentified, interface.operations)) | |
| interface.parents = list(filter(IsIdentified, interface.parents)) | |
| def FilterInterfaces(self, | |
| database, | |
| and_annotations=[], | |
| or_annotations=[], | |
| exclude_displaced=[], | |
| exclude_suppressed=[]): | |
| """Filters a database to remove interfaces and members that are missing | |
| annotations. | |
| The FremontCut IDLs use annotations to specify implementation | |
| status in various platforms. For example, if a member is annotated | |
| with @WebKit, this means that the member is supported by WebKit. | |
| Args: | |
| database -- the database to filter | |
| all_annotations -- a list of annotation names a member has to | |
| have or it will be filtered. | |
| or_annotations -- if a member has one of these annotations, it | |
| won't be filtered even if it is missing some of the | |
| all_annotations. | |
| exclude_displaced -- if a member has this annotation and it | |
| is marked as displaced it will always be filtered. | |
| exclude_suppressed -- if a member has this annotation and it | |
| is marked as suppressed it will always be filtered. | |
| """ | |
| # Filter interfaces and members whose annotations don't match. | |
| for interface in database.GetInterfaces(): | |
| def HasAnnotations(idl_node): | |
| """Utility for determining if an IDLNode has all | |
| the required annotations""" | |
| for a in exclude_displaced: | |
| if (a in idl_node.annotations and | |
| 'via' in idl_node.annotations[a]): | |
| return False | |
| for a in exclude_suppressed: | |
| if (a in idl_node.annotations and | |
| 'suppressed' in idl_node.annotations[a]): | |
| return False | |
| for a in or_annotations: | |
| if a in idl_node.annotations: | |
| return True | |
| if and_annotations == []: | |
| return False | |
| for a in and_annotations: | |
| if a not in idl_node.annotations: | |
| return False | |
| return True | |
| if HasAnnotations(interface): | |
| interface.constants = filter(HasAnnotations, | |
| interface.constants) | |
| interface.attributes = filter(HasAnnotations, | |
| interface.attributes) | |
| interface.operations = filter(HasAnnotations, | |
| interface.operations) | |
| interface.parents = filter(HasAnnotations, interface.parents) | |
| else: | |
| database.DeleteInterface(interface.id) | |
| self.FilterMembersWithUnidentifiedTypes(database) | |
| def Generate(self, database, super_database, generate_interface): | |
| self._database = database | |
| # Collect interfaces | |
| interfaces = [] | |
| for interface in database.GetInterfaces(): | |
| if not MatchSourceFilter(interface): | |
| # Skip this interface since it's not present in the required source | |
| _logger.info('Omitting interface - %s' % interface.id) | |
| continue | |
| interfaces.append(interface) | |
| # All web_gl constants from WebGLRenderingContextBase, WebGL2RenderingContextBase, WebGLDrawBuffers are generated | |
| # in a synthesized class WebGL. Those IDLConstants are in web_gl_constants. | |
| web_gl_constants = [] | |
| # Render all interfaces into Dart and save them in files. | |
| for interface in self._PreOrderInterfaces(interfaces): | |
| interface_name = interface.id | |
| auxiliary_file = self._auxiliary_files.get(interface_name) | |
| if auxiliary_file is not None: | |
| _logger.info('Skipping %s because %s exists' % (interface_name, | |
| auxiliary_file)) | |
| continue | |
| _logger.info('Generating %s' % interface.id) | |
| generate_interface(interface, gl_constants=web_gl_constants) | |
| # Generate the WEB_GL constants | |
| web_gl_constants_interface = IDLInterface(None, "WebGL") | |
| web_gl_constants_interface.constants = web_gl_constants | |
| self._database._all_interfaces['WebGL'] = web_gl_constants_interface | |
| generate_interface(web_gl_constants_interface) | |
| def _PreOrderInterfaces(self, interfaces): | |
| """Returns the interfaces in pre-order, i.e. parents first.""" | |
| seen = set() | |
| ordered = [] | |
| def visit(interface): | |
| if interface.id in seen: | |
| return | |
| seen.add(interface.id) | |
| for parent in interface.parents: | |
| if IsDartCollectionType(parent.type.id): | |
| continue | |
| if self._database.HasInterface(parent.type.id): | |
| parent_interface = self._database.GetInterface( | |
| parent.type.id) | |
| visit(parent_interface) | |
| ordered.append(interface) | |
| for interface in interfaces: | |
| visit(interface) | |
| return ordered | |
| def IsEventTarget(self, database, interface): | |
| if interface.id == 'EventTarget': | |
| return True | |
| for parent in interface.parents: | |
| parent_name = parent.type.id | |
| if database.HasInterface(parent_name): | |
| parent_interface = database.GetInterface(parent.type.id) | |
| if self.IsEventTarget(database, parent_interface): | |
| return True | |
| return False | |
| def FixEventTargets(self, database): | |
| for interface in database.GetInterfaces(): | |
| if self.IsEventTarget(database, interface): | |
| # Add as an attribute for easy querying in generation code. | |
| interface.ext_attrs['EventTarget'] = None | |
| elif 'EventTarget' in interface.ext_attrs: | |
| # Create fake EventTarget parent interface for interfaces that have | |
| # 'EventTarget' extended attribute. | |
| ast = [('Annotation', [('Id', 'WebKit')]), | |
| ('InterfaceType', ('ScopedName', 'EventTarget'))] | |
| interface.parents.append(idlnode.IDLParentInterface(ast)) | |
| def AddMissingArguments(self, database): | |
| ARG = idlnode.IDLArgument([('Type', ('ScopedName', 'object')), | |
| ('Id', 'arg')]) | |
| for interface in database.GetInterfaces(): | |
| for operation in interface.operations: | |
| call_with = operation.ext_attrs.get('CallWith', []) | |
| if not (isinstance(call_with, list)): | |
| call_with = [call_with] | |
| constructor_with = operation.ext_attrs.get( | |
| 'ConstructorCallWith', []) | |
| if not (isinstance(constructor_with, list)): | |
| constructor_with = [constructor_with] | |
| call_with = call_with + constructor_with | |
| if 'ScriptArguments' in call_with: | |
| operation.arguments.append(ARG) | |
| def CleanupOperationArguments(self, database): | |
| for interface in database.GetInterfaces(): | |
| for operation in interface.operations: | |
| # TODO(terry): Hack to remove 3rd arguments in setInterval/setTimeout. | |
| if ((operation.id == 'setInterval' or operation.id == 'setTimeout') and \ | |
| operation.arguments[0].type.id == 'any'): | |
| operation.arguments.pop(2) | |
| # Massage any operation argument type that is IDLEnum to String. | |
| for index, argument in enumerate(operation.arguments): | |
| type_name = argument.type.id | |
| if database.HasEnum(type_name): | |
| operation.arguments[index].type = IDLType('DOMString') |