| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/python | |
| 2 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 3 # for details. All rights reserved. Use of this source code is governed by a | |
| 4 # BSD-style license that can be found in the LICENSE file. | |
| 5 | |
| 6 """This module generates Dart APIs from the IDL database.""" | |
| 7 | |
| 8 import emitter | |
| 9 import idlnode | |
| 10 import logging | |
| 11 import os | |
| 12 import re | |
| 13 import shutil | |
| 14 from generator import * | |
| 15 | |
| 16 _logger = logging.getLogger('dartgenerator') | |
| 17 | |
| 18 def MergeNodes(node, other): | |
| 19 node.operations.extend(other.operations) | |
| 20 for attribute in other.attributes: | |
| 21 if not node.has_attribute(attribute): | |
| 22 node.attributes.append(attribute) | |
| 23 | |
| 24 node.constants.extend(other.constants) | |
| 25 | |
| 26 class DartGenerator(object): | |
| 27 """Utilities to generate Dart APIs and corresponding JavaScript.""" | |
| 28 | |
| 29 def __init__(self): | |
| 30 self._auxiliary_files = {} | |
| 31 self._dart_templates_re = re.compile(r'[\w.:]+<([\w\.<>:]+)>') | |
| 32 | |
| 33 def _StripModules(self, type_name): | |
| 34 return type_name.split('::')[-1] | |
| 35 | |
| 36 def _IsCompoundType(self, database, type_name): | |
| 37 if IsRegisteredType(type_name): | |
| 38 return True | |
| 39 | |
| 40 if type_name.endswith('?'): | |
| 41 return self._IsCompoundType(database, type_name[:-len('?')]) | |
| 42 | |
| 43 if type_name.endswith('[]'): | |
| 44 return self._IsCompoundType(database, type_name[:-len('[]')]) | |
| 45 | |
| 46 stripped_type_name = self._StripModules(type_name) | |
| 47 if database.HasInterface(stripped_type_name): | |
| 48 return True | |
| 49 | |
| 50 dart_template_match = self._dart_templates_re.match(type_name) | |
| 51 if dart_template_match: | |
| 52 # Dart templates | |
| 53 parent_type_name = type_name[0 : dart_template_match.start(1) - 1] | |
| 54 sub_type_name = dart_template_match.group(1) | |
| 55 return (self._IsCompoundType(database, parent_type_name) and | |
| 56 self._IsCompoundType(database, sub_type_name)) | |
| 57 return False | |
| 58 | |
| 59 def _IsDartType(self, type_name): | |
| 60 return '.' in type_name | |
| 61 | |
| 62 def LoadAuxiliary(self, auxiliary_dir): | |
| 63 def Visitor(_, dirname, names): | |
| 64 for name in names: | |
| 65 if name.endswith('.dart'): | |
| 66 name = name[0:-5] # strip off ".dart" | |
| 67 self._auxiliary_files[name] = os.path.join(dirname, name) | |
| 68 os.path.walk(auxiliary_dir, Visitor, None) | |
| 69 | |
| 70 def FilterMembersWithUnidentifiedTypes(self, database): | |
| 71 """Removes unidentified types. | |
| 72 | |
| 73 Removes constants, attributes, operations and parents with unidentified | |
| 74 types. | |
| 75 """ | |
| 76 | |
| 77 for interface in database.GetInterfaces(): | |
| 78 def IsIdentified(idl_node): | |
| 79 node_name = idl_node.id if idl_node.id else 'parent' | |
| 80 for idl_type in idl_node.all(idlnode.IDLType): | |
| 81 type_name = idl_type.id | |
| 82 if (type_name is not None and | |
| 83 self._IsCompoundType(database, type_name)): | |
| 84 continue | |
| 85 _logger.warn('removing %s in %s which has unidentified type %s' % | |
| 86 (node_name, interface.id, type_name)) | |
| 87 return False | |
| 88 return True | |
| 89 | |
| 90 interface.constants = filter(IsIdentified, interface.constants) | |
| 91 interface.attributes = filter(IsIdentified, interface.attributes) | |
| 92 interface.operations = filter(IsIdentified, interface.operations) | |
| 93 interface.parents = filter(IsIdentified, interface.parents) | |
| 94 | |
| 95 def FilterInterfaces(self, database, | |
| 96 and_annotations=[], | |
| 97 or_annotations=[], | |
| 98 exclude_displaced=[], | |
| 99 exclude_suppressed=[]): | |
| 100 """Filters a database to remove interfaces and members that are missing | |
| 101 annotations. | |
| 102 | |
| 103 The FremontCut IDLs use annotations to specify implementation | |
| 104 status in various platforms. For example, if a member is annotated | |
| 105 with @WebKit, this means that the member is supported by WebKit. | |
| 106 | |
| 107 Args: | |
| 108 database -- the database to filter | |
| 109 all_annotations -- a list of annotation names a member has to | |
| 110 have or it will be filtered. | |
| 111 or_annotations -- if a member has one of these annotations, it | |
| 112 won't be filtered even if it is missing some of the | |
| 113 all_annotations. | |
| 114 exclude_displaced -- if a member has this annotation and it | |
| 115 is marked as displaced it will always be filtered. | |
| 116 exclude_suppressed -- if a member has this annotation and it | |
| 117 is marked as suppressed it will always be filtered. | |
| 118 """ | |
| 119 | |
| 120 # Filter interfaces and members whose annotations don't match. | |
| 121 for interface in database.GetInterfaces(): | |
| 122 def HasAnnotations(idl_node): | |
| 123 """Utility for determining if an IDLNode has all | |
| 124 the required annotations""" | |
| 125 for a in exclude_displaced: | |
| 126 if (a in idl_node.annotations | |
| 127 and 'via' in idl_node.annotations[a]): | |
| 128 return False | |
| 129 for a in exclude_suppressed: | |
| 130 if (a in idl_node.annotations | |
| 131 and 'suppressed' in idl_node.annotations[a]): | |
| 132 return False | |
| 133 for a in or_annotations: | |
| 134 if a in idl_node.annotations: | |
| 135 return True | |
| 136 if and_annotations == []: | |
| 137 return False | |
| 138 for a in and_annotations: | |
| 139 if a not in idl_node.annotations: | |
| 140 return False | |
| 141 return True | |
| 142 | |
| 143 if HasAnnotations(interface): | |
| 144 interface.constants = filter(HasAnnotations, interface.constants) | |
| 145 interface.attributes = filter(HasAnnotations, interface.attributes) | |
| 146 interface.operations = filter(HasAnnotations, interface.operations) | |
| 147 interface.parents = filter(HasAnnotations, interface.parents) | |
| 148 else: | |
| 149 database.DeleteInterface(interface.id) | |
| 150 | |
| 151 # Ugly temporary hack | |
| 152 websocket_interface = database.GetInterface('WebSocket') | |
| 153 def make_object(**fields): | |
| 154 o = type('Anon', (object,), {})() | |
| 155 for k, v in fields.items(): setattr(o, k, v) | |
| 156 o.ext_attrs = {} | |
| 157 return o | |
| 158 arg = make_object(id = 'url', type = make_object(id = 'DOMString')) | |
| 159 websocket_interface.ext_attrs['Constructor'] = make_object(arguments = [ar
g]) | |
| 160 websocket_interface.ext_attrs['CustomConstructor'] = True | |
| 161 | |
| 162 self.FilterMembersWithUnidentifiedTypes(database) | |
| 163 | |
| 164 def Generate(self, database, super_database, generate_interface): | |
| 165 self._database = database | |
| 166 | |
| 167 # Collect interfaces | |
| 168 interfaces = [] | |
| 169 for interface in database.GetInterfaces(): | |
| 170 if not MatchSourceFilter(interface): | |
| 171 # Skip this interface since it's not present in the required source | |
| 172 _logger.info('Omitting interface - %s' % interface.id) | |
| 173 continue | |
| 174 interfaces.append(interface) | |
| 175 | |
| 176 # TODO(sra): Use this list of exception names to generate information to | |
| 177 # tell dart2js which exceptions can be passed from JS to Dart code. | |
| 178 exceptions = self._CollectExceptions(interfaces) | |
| 179 | |
| 180 # Render all interfaces into Dart and save them in files. | |
| 181 for interface in self._PreOrderInterfaces(interfaces): | |
| 182 interface_name = interface.id | |
| 183 auxiliary_file = self._auxiliary_files.get(interface_name) | |
| 184 if auxiliary_file is not None: | |
| 185 _logger.info('Skipping %s because %s exists' % ( | |
| 186 interface_name, auxiliary_file)) | |
| 187 continue | |
| 188 | |
| 189 _logger.info('Generating %s' % interface.id) | |
| 190 generate_interface(interface) | |
| 191 | |
| 192 def _PreOrderInterfaces(self, interfaces): | |
| 193 """Returns the interfaces in pre-order, i.e. parents first.""" | |
| 194 seen = set() | |
| 195 ordered = [] | |
| 196 def visit(interface): | |
| 197 if interface.id in seen: | |
| 198 return | |
| 199 seen.add(interface.id) | |
| 200 for parent in interface.parents: | |
| 201 if IsDartCollectionType(parent.type.id): | |
| 202 continue | |
| 203 if self._database.HasInterface(parent.type.id): | |
| 204 parent_interface = self._database.GetInterface(parent.type.id) | |
| 205 visit(parent_interface) | |
| 206 ordered.append(interface) | |
| 207 | |
| 208 for interface in interfaces: | |
| 209 visit(interface) | |
| 210 return ordered | |
| 211 | |
| 212 def _CollectExceptions(self, interfaces): | |
| 213 """Returns the names of all exception classes raised.""" | |
| 214 exceptions = set() | |
| 215 for interface in interfaces: | |
| 216 for attribute in interface.attributes: | |
| 217 if attribute.get_raises: | |
| 218 exceptions.add(attribute.get_raises.id) | |
| 219 if attribute.set_raises: | |
| 220 exceptions.add(attribute.set_raises.id) | |
| 221 for operation in interface.operations: | |
| 222 if operation.raises: | |
| 223 exceptions.add(operation.raises.id) | |
| 224 return exceptions | |
| 225 | |
| 226 | |
| 227 def FixEventTargets(self, database): | |
| 228 for interface in database.GetInterfaces(): | |
| 229 # Create fake EventTarget parent interface for interfaces that have | |
| 230 # 'EventTarget' extended attribute. | |
| 231 if 'EventTarget' in interface.ext_attrs: | |
| 232 ast = [('Annotation', [('Id', 'WebKit')]), | |
| 233 ('InterfaceType', ('ScopedName', 'EventTarget'))] | |
| 234 interface.parents.append(idlnode.IDLParentInterface(ast)) | |
| 235 | |
| 236 def AddMissingArguments(self, database): | |
| 237 ARG = idlnode.IDLArgument([('Type', ('ScopedName', 'object')), ('Id', 'arg')
]) | |
| 238 for interface in database.GetInterfaces(): | |
| 239 for operation in interface.operations: | |
| 240 if operation.ext_attrs.get('CallWith') == 'ScriptArguments|ScriptState': | |
| 241 operation.arguments.append(ARG) | |
| OLD | NEW |