#!/usr/bin/python # 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): def Visitor(_, dirname, names): for name in names: if name.endswith('.dart'): name = name[0:-5] # strip off ".dart" self._auxiliary_files[name] = os.path.join(dirname, name) os.path.walk(auxiliary_dir, Visitor, None) 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 = filter(IsIdentified, interface.constants) interface.attributes = filter(IsIdentified, interface.attributes) interface.operations = filter(IsIdentified, interface.operations) interface.parents = 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')