| 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 import systembase | |
| 15 from generator import * | |
| 16 | |
| 17 _logger = logging.getLogger('dartgenerator') | |
| 18 | |
| 19 def MergeNodes(node, other): | |
| 20 node.operations.extend(other.operations) | |
| 21 for attribute in other.attributes: | |
| 22 if not node.has_attribute(attribute): | |
| 23 node.attributes.append(attribute) | |
| 24 | |
| 25 node.constants.extend(other.constants) | |
| 26 | |
| 27 class DartGenerator(object): | |
| 28 """Utilities to generate Dart APIs and corresponding JavaScript.""" | |
| 29 | |
| 30 def __init__(self): | |
| 31 self._auxiliary_files = {} | |
| 32 self._dart_templates_re = re.compile(r'[\w.:]+<([\w\.<>:]+)>') | |
| 33 | |
| 34 def _StripModules(self, type_name): | |
| 35 return type_name.split('::')[-1] | |
| 36 | |
| 37 def _IsCompoundType(self, database, type_name): | |
| 38 if IsRegisteredType(type_name): | |
| 39 return True | |
| 40 | |
| 41 if type_name.endswith('?'): | |
| 42 return self._IsCompoundType(database, type_name[:-len('?')]) | |
| 43 | |
| 44 if type_name.endswith('[]'): | |
| 45 return self._IsCompoundType(database, type_name[:-len('[]')]) | |
| 46 | |
| 47 stripped_type_name = self._StripModules(type_name) | |
| 48 if database.HasInterface(stripped_type_name): | |
| 49 return True | |
| 50 | |
| 51 dart_template_match = self._dart_templates_re.match(type_name) | |
| 52 if dart_template_match: | |
| 53 # Dart templates | |
| 54 parent_type_name = type_name[0 : dart_template_match.start(1) - 1] | |
| 55 sub_type_name = dart_template_match.group(1) | |
| 56 return (self._IsCompoundType(database, parent_type_name) and | |
| 57 self._IsCompoundType(database, sub_type_name)) | |
| 58 return False | |
| 59 | |
| 60 def _IsDartType(self, type_name): | |
| 61 return '.' in type_name | |
| 62 | |
| 63 def LoadAuxiliary(self, auxiliary_dir): | |
| 64 def Visitor(_, dirname, names): | |
| 65 for name in names: | |
| 66 if name.endswith('.dart'): | |
| 67 name = name[0:-5] # strip off ".dart" | |
| 68 self._auxiliary_files[name] = os.path.join(dirname, name) | |
| 69 os.path.walk(auxiliary_dir, Visitor, None) | |
| 70 | |
| 71 def RenameTypes(self, database, conversion_table, rename_javascript_binding_na
mes): | |
| 72 """Renames interfaces using the given conversion table. | |
| 73 | |
| 74 References through all interfaces will be renamed as well. | |
| 75 | |
| 76 Args: | |
| 77 database: the database to apply the renames to. | |
| 78 conversion_table: maps old names to new names. | |
| 79 """ | |
| 80 | |
| 81 if conversion_table is None: | |
| 82 conversion_table = {} | |
| 83 | |
| 84 # Rename interfaces: | |
| 85 for old_name, new_name in conversion_table.items(): | |
| 86 if database.HasInterface(old_name): | |
| 87 _logger.info('renaming interface %s to %s' % (old_name, new_name)) | |
| 88 interface = database.GetInterface(old_name) | |
| 89 if not database.HasInterface(new_name): | |
| 90 interface.id = new_name | |
| 91 database.DeleteInterface(old_name) | |
| 92 database.AddInterface(interface) | |
| 93 | |
| 94 if rename_javascript_binding_names: | |
| 95 interface.javascript_binding_name = new_name | |
| 96 interface.doc_js_name = new_name | |
| 97 for member in (interface.operations + interface.constants | |
| 98 + interface.attributes): | |
| 99 member.doc_js_interface_name = new_name | |
| 100 | |
| 101 | |
| 102 # Fix references: | |
| 103 for interface in database.GetInterfaces(): | |
| 104 for idl_type in interface.all(idlnode.IDLType): | |
| 105 type_name = self._StripModules(idl_type.id) | |
| 106 if type_name in conversion_table: | |
| 107 idl_type.id = conversion_table[type_name] | |
| 108 | |
| 109 def FilterMembersWithUnidentifiedTypes(self, database): | |
| 110 """Removes unidentified types. | |
| 111 | |
| 112 Removes constants, attributes, operations and parents with unidentified | |
| 113 types. | |
| 114 """ | |
| 115 | |
| 116 for interface in database.GetInterfaces(): | |
| 117 def IsIdentified(idl_node): | |
| 118 node_name = idl_node.id if idl_node.id else 'parent' | |
| 119 for idl_type in idl_node.all(idlnode.IDLType): | |
| 120 type_name = idl_type.id | |
| 121 if (type_name is not None and | |
| 122 self._IsCompoundType(database, type_name)): | |
| 123 continue | |
| 124 _logger.warn('removing %s in %s which has unidentified type %s' % | |
| 125 (node_name, interface.id, type_name)) | |
| 126 return False | |
| 127 return True | |
| 128 | |
| 129 interface.constants = filter(IsIdentified, interface.constants) | |
| 130 interface.attributes = filter(IsIdentified, interface.attributes) | |
| 131 interface.operations = filter(IsIdentified, interface.operations) | |
| 132 interface.parents = filter(IsIdentified, interface.parents) | |
| 133 | |
| 134 def FilterInterfaces(self, database, | |
| 135 and_annotations=[], | |
| 136 or_annotations=[], | |
| 137 exclude_displaced=[], | |
| 138 exclude_suppressed=[]): | |
| 139 """Filters a database to remove interfaces and members that are missing | |
| 140 annotations. | |
| 141 | |
| 142 The FremontCut IDLs use annotations to specify implementation | |
| 143 status in various platforms. For example, if a member is annotated | |
| 144 with @WebKit, this means that the member is supported by WebKit. | |
| 145 | |
| 146 Args: | |
| 147 database -- the database to filter | |
| 148 all_annotations -- a list of annotation names a member has to | |
| 149 have or it will be filtered. | |
| 150 or_annotations -- if a member has one of these annotations, it | |
| 151 won't be filtered even if it is missing some of the | |
| 152 all_annotations. | |
| 153 exclude_displaced -- if a member has this annotation and it | |
| 154 is marked as displaced it will always be filtered. | |
| 155 exclude_suppressed -- if a member has this annotation and it | |
| 156 is marked as suppressed it will always be filtered. | |
| 157 """ | |
| 158 | |
| 159 # Filter interfaces and members whose annotations don't match. | |
| 160 for interface in database.GetInterfaces(): | |
| 161 def HasAnnotations(idl_node): | |
| 162 """Utility for determining if an IDLNode has all | |
| 163 the required annotations""" | |
| 164 for a in exclude_displaced: | |
| 165 if (a in idl_node.annotations | |
| 166 and 'via' in idl_node.annotations[a]): | |
| 167 return False | |
| 168 for a in exclude_suppressed: | |
| 169 if (a in idl_node.annotations | |
| 170 and 'suppressed' in idl_node.annotations[a]): | |
| 171 return False | |
| 172 for a in or_annotations: | |
| 173 if a in idl_node.annotations: | |
| 174 return True | |
| 175 if and_annotations == []: | |
| 176 return False | |
| 177 for a in and_annotations: | |
| 178 if a not in idl_node.annotations: | |
| 179 return False | |
| 180 return True | |
| 181 | |
| 182 if HasAnnotations(interface): | |
| 183 interface.constants = filter(HasAnnotations, interface.constants) | |
| 184 interface.attributes = filter(HasAnnotations, interface.attributes) | |
| 185 interface.operations = filter(HasAnnotations, interface.operations) | |
| 186 interface.parents = filter(HasAnnotations, interface.parents) | |
| 187 else: | |
| 188 database.DeleteInterface(interface.id) | |
| 189 | |
| 190 self.FilterMembersWithUnidentifiedTypes(database) | |
| 191 | |
| 192 def Generate(self, database, system, super_database=None, webkit_renames={}): | |
| 193 self._database = database | |
| 194 | |
| 195 # Collect interfaces | |
| 196 interfaces = [] | |
| 197 for interface in database.GetInterfaces(): | |
| 198 if not MatchSourceFilter(interface): | |
| 199 # Skip this interface since it's not present in the required source | |
| 200 _logger.info('Omitting interface - %s' % interface.id) | |
| 201 continue | |
| 202 interfaces.append(interface) | |
| 203 | |
| 204 # TODO(sra): Use this list of exception names to generate information to | |
| 205 # tell dart2js which exceptions can be passed from JS to Dart code. | |
| 206 exceptions = self._CollectExceptions(interfaces) | |
| 207 | |
| 208 super_map = dict((v, k) for k, v in webkit_renames.iteritems()) | |
| 209 | |
| 210 # Render all interfaces into Dart and save them in files. | |
| 211 for interface in self._PreOrderInterfaces(interfaces): | |
| 212 | |
| 213 super_name = interface.id | |
| 214 | |
| 215 if super_name in super_map: | |
| 216 super_name = super_map[super_name] | |
| 217 | |
| 218 if (super_database is not None and | |
| 219 super_database.HasInterface(super_name)): | |
| 220 interface.ext_attrs['synthesizedSuperInterfaceName'] = super_name | |
| 221 | |
| 222 interface_name = interface.id | |
| 223 auxiliary_file = self._auxiliary_files.get(interface_name) | |
| 224 if auxiliary_file is not None: | |
| 225 _logger.info('Skipping %s because %s exists' % ( | |
| 226 interface_name, auxiliary_file)) | |
| 227 continue | |
| 228 | |
| 229 if 'Callback' in interface.ext_attrs: | |
| 230 handlers = [op for op in interface.operations if op.id == 'handleEvent'] | |
| 231 info = AnalyzeOperation(interface, handlers) | |
| 232 system.ProcessCallback(interface, info) | |
| 233 else: | |
| 234 _logger.info('Generating %s' % interface.id) | |
| 235 system.ProcessInterface(interface) | |
| 236 | |
| 237 system.GenerateLibraries() | |
| 238 system.Finish() | |
| 239 | |
| 240 def _PreOrderInterfaces(self, interfaces): | |
| 241 """Returns the interfaces in pre-order, i.e. parents first.""" | |
| 242 seen = set() | |
| 243 ordered = [] | |
| 244 def visit(interface): | |
| 245 if interface.id in seen: | |
| 246 return | |
| 247 seen.add(interface.id) | |
| 248 for parent in interface.parents: | |
| 249 if IsDartCollectionType(parent.type.id): | |
| 250 continue | |
| 251 if self._database.HasInterface(parent.type.id): | |
| 252 parent_interface = self._database.GetInterface(parent.type.id) | |
| 253 visit(parent_interface) | |
| 254 ordered.append(interface) | |
| 255 | |
| 256 for interface in interfaces: | |
| 257 visit(interface) | |
| 258 return ordered | |
| 259 | |
| 260 def _CollectExceptions(self, interfaces): | |
| 261 """Returns the names of all exception classes raised.""" | |
| 262 exceptions = set() | |
| 263 for interface in interfaces: | |
| 264 for attribute in interface.attributes: | |
| 265 if attribute.get_raises: | |
| 266 exceptions.add(attribute.get_raises.id) | |
| 267 if attribute.set_raises: | |
| 268 exceptions.add(attribute.set_raises.id) | |
| 269 for operation in interface.operations: | |
| 270 if operation.raises: | |
| 271 exceptions.add(operation.raises.id) | |
| 272 return exceptions | |
| 273 | |
| 274 | |
| 275 def FixEventTargets(self, database): | |
| 276 for interface in database.GetInterfaces(): | |
| 277 # Create fake EventTarget parent interface for interfaces that have | |
| 278 # 'EventTarget' extended attribute. | |
| 279 if 'EventTarget' in interface.ext_attrs: | |
| 280 ast = [('Annotation', [('Id', 'WebKit')]), | |
| 281 ('InterfaceType', ('ScopedName', 'EventTarget'))] | |
| 282 interface.parents.append(idlnode.IDLParentInterface(ast)) | |
| 283 | |
| 284 def AddMissingArguments(self, database): | |
| 285 ARG = idlnode.IDLArgument([('Type', ('ScopedName', 'Object')), ('Id', 'arg')
]) | |
| 286 for interface in database.GetInterfaces(): | |
| 287 for operation in interface.operations: | |
| 288 if operation.ext_attrs.get('CallWith') == 'ScriptArguments|CallStack': | |
| 289 operation.arguments.append(ARG) | |
| OLD | NEW |