| 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 provides shared functionality for the system to generate | |
| 7 dart:html APIs from the IDL database.""" | |
| 8 | |
| 9 from generator import AnalyzeOperation, ConstantOutputOrder, \ | |
| 10 DartDomNameOfAttribute, FindMatchingAttribute, IsDartCollectionType, \ | |
| 11 IsPureInterface, TypeOrNothing | |
| 12 | |
| 13 # Types that are accessible cross-frame in a limited fashion. | |
| 14 # In these cases, the base type (e.g., WindowBase) provides restricted access | |
| 15 # while the subtype (e.g., Window) provides full access to the | |
| 16 # corresponding objects if there are from the same frame. | |
| 17 _secure_base_types = { | |
| 18 'Window': 'WindowBase', | |
| 19 'Location': 'LocationBase', | |
| 20 'History': 'HistoryBase', | |
| 21 } | |
| 22 | |
| 23 class HtmlDartGenerator(object): | |
| 24 def __init__(self, interface, options): | |
| 25 self._database = options.database | |
| 26 self._interface = interface | |
| 27 self._type_registry = options.type_registry | |
| 28 self._interface_type_info = self._type_registry.TypeInfo(self._interface.id) | |
| 29 self._renamer = options.renamer | |
| 30 | |
| 31 def EmitAttributeDocumentation(self, attribute): | |
| 32 """ Emits the MDN dartdoc comment for an attribute. | |
| 33 """ | |
| 34 dom_name = DartDomNameOfAttribute(attribute) | |
| 35 self._members_emitter.Emit('\n /// @domName $DOMINTERFACE.$DOMNAME;' | |
| 36 ' @docsEditable true', | |
| 37 DOMINTERFACE=attribute.doc_js_interface_name, | |
| 38 DOMNAME=dom_name) | |
| 39 | |
| 40 def EmitOperationDocumentation(self, operation): | |
| 41 """ Emits the MDN dartdoc comment for an operation. | |
| 42 """ | |
| 43 self._members_emitter.Emit('\n /// @domName $DOMINTERFACE.$DOMNAME;' | |
| 44 ' @docsEditable true', | |
| 45 DOMINTERFACE=operation.overloads[0].doc_js_interface_name, | |
| 46 DOMNAME=operation.name) | |
| 47 | |
| 48 def EmitEventGetter(self, events_class_name): | |
| 49 self._members_emitter.Emit( | |
| 50 '\n /// @domName EventTarget.addEventListener, ' | |
| 51 'EventTarget.removeEventListener, EventTarget.dispatchEvent;' | |
| 52 ' @docsEditable true' | |
| 53 '\n $TYPE get on =>\n new $TYPE(this);\n', | |
| 54 TYPE=events_class_name) | |
| 55 | |
| 56 def AddMembers(self, interface, declare_only=False): | |
| 57 for const in sorted(interface.constants, ConstantOutputOrder): | |
| 58 self.AddConstant(const) | |
| 59 | |
| 60 for attr in sorted(interface.attributes, ConstantOutputOrder): | |
| 61 if attr.type.id != 'EventListener': | |
| 62 self.AddAttribute(attr, declare_only) | |
| 63 | |
| 64 # The implementation should define an indexer if the interface directly | |
| 65 # extends List. | |
| 66 element_type = None | |
| 67 requires_indexer = False | |
| 68 if self._interface_type_info.list_item_type(): | |
| 69 self.AddIndexer(self._interface_type_info.list_item_type()) | |
| 70 else: | |
| 71 for parent in self._database.Hierarchy(self._interface): | |
| 72 if parent == self._interface: | |
| 73 continue | |
| 74 parent_type_info = self._type_registry.TypeInfo(parent.id) | |
| 75 if parent_type_info.list_item_type(): | |
| 76 self.AmendIndexer(parent_type_info.list_item_type()) | |
| 77 break | |
| 78 | |
| 79 # Group overloaded operations by id. | |
| 80 operationsById = {} | |
| 81 for operation in interface.operations: | |
| 82 if operation.id not in operationsById: | |
| 83 operationsById[operation.id] = [] | |
| 84 operationsById[operation.id].append(operation) | |
| 85 | |
| 86 # Generate operations. | |
| 87 for id in sorted(operationsById.keys()): | |
| 88 operations = operationsById[id] | |
| 89 info = AnalyzeOperation(interface, operations) | |
| 90 self.AddOperation(info, declare_only) | |
| 91 | |
| 92 def AddSecondaryMembers(self, interface): | |
| 93 # With multiple inheritance, attributes and operations of non-first | |
| 94 # interfaces need to be added. Sometimes the attribute or operation is | |
| 95 # defined in the current interface as well as a parent. In that case we | |
| 96 # avoid making a duplicate definition and pray that the signatures match. | |
| 97 secondary_parents = self._TransitiveSecondaryParents(interface) | |
| 98 for parent_interface in sorted(secondary_parents): | |
| 99 if isinstance(parent_interface, str): | |
| 100 continue | |
| 101 for attr in sorted(parent_interface.attributes, ConstantOutputOrder): | |
| 102 if not FindMatchingAttribute(interface, attr): | |
| 103 self.SecondaryContext(parent_interface) | |
| 104 self.AddAttribute(attr) | |
| 105 | |
| 106 # Group overloaded operations by id. | |
| 107 operationsById = {} | |
| 108 for operation in parent_interface.operations: | |
| 109 if operation.id not in operationsById: | |
| 110 operationsById[operation.id] = [] | |
| 111 operationsById[operation.id].append(operation) | |
| 112 | |
| 113 # Generate operations. | |
| 114 for id in sorted(operationsById.keys()): | |
| 115 if not any(op.id == id for op in interface.operations): | |
| 116 operations = operationsById[id] | |
| 117 info = AnalyzeOperation(interface, operations) | |
| 118 self.SecondaryContext(parent_interface) | |
| 119 self.AddOperation(info) | |
| 120 | |
| 121 def AddConstant(self, constant): | |
| 122 const_name = self._renamer.RenameMember( | |
| 123 self._interface.id, constant, constant.id, dartify_name=False) | |
| 124 if not const_name: | |
| 125 return | |
| 126 type = TypeOrNothing(self._DartType(constant.type.id), constant.type.id) | |
| 127 self._members_emitter.Emit('\n static const $TYPE$NAME = $VALUE;\n', | |
| 128 NAME=const_name, | |
| 129 TYPE=type, | |
| 130 VALUE=constant.value) | |
| 131 | |
| 132 def AddAttribute(self, attribute, declare_only=False): | |
| 133 """ Adds an attribute to the generated class. | |
| 134 Arguments: | |
| 135 attribute - The attribute which is to be added. | |
| 136 declare_only- True if the attribute should be declared as an abstract | |
| 137 member and not include invocation code. | |
| 138 """ | |
| 139 dom_name = DartDomNameOfAttribute(attribute) | |
| 140 attr_name = self._renamer.RenameMember( | |
| 141 self._interface.id, attribute, dom_name, 'get:') | |
| 142 if not attr_name or self._IsPrivate(attr_name): | |
| 143 return | |
| 144 | |
| 145 html_setter_name = self._renamer.RenameMember( | |
| 146 self._interface.id, attribute, dom_name, 'set:') | |
| 147 read_only = (attribute.is_read_only or 'Replaceable' in attribute.ext_attrs | |
| 148 or not html_setter_name) | |
| 149 | |
| 150 # We don't yet handle inconsistent renames of the getter and setter yet. | |
| 151 assert(not html_setter_name or attr_name == html_setter_name) | |
| 152 | |
| 153 if declare_only: | |
| 154 self.DeclareAttribute(attribute, | |
| 155 self.SecureOutputType(attribute.type.id), attr_name, read_only) | |
| 156 else: | |
| 157 self.EmitAttribute(attribute, attr_name, read_only) | |
| 158 | |
| 159 def AddOperation(self, info, declare_only=False): | |
| 160 """ Adds an operation to the generated class. | |
| 161 Arguments: | |
| 162 info - The operation info of the operation to be added. | |
| 163 declare_only- True if the operation should be declared as an abstract | |
| 164 member and not include invocation code. | |
| 165 """ | |
| 166 # FIXME: When we pass in operations[0] below, we're assuming all | |
| 167 # overloaded operations have the same security attributes. This | |
| 168 # is currently true, but we should consider filtering earlier or | |
| 169 # merging the relevant data into info itself. | |
| 170 method_name = self._renamer.RenameMember(self._interface.id, | |
| 171 info.operations[0], | |
| 172 info.name) | |
| 173 if not method_name: | |
| 174 if info.name == 'item': | |
| 175 # FIXME: item should be renamed to operator[], not removed. | |
| 176 self.EmitOperation(info, '_item') | |
| 177 return | |
| 178 | |
| 179 if declare_only: | |
| 180 self.DeclareOperation(info, | |
| 181 self.SecureOutputType(info.type_name), method_name) | |
| 182 else: | |
| 183 self.EmitOperation(info, method_name) | |
| 184 | |
| 185 def AdditionalImplementedInterfaces(self): | |
| 186 # TODO: Include all implemented interfaces, including other Lists. | |
| 187 implements = [] | |
| 188 if self._interface_type_info.is_typed_array(): | |
| 189 element_type = self._interface_type_info.list_item_type() | |
| 190 implements.append('List<%s>' % self._DartType(element_type)) | |
| 191 if self._interface_type_info.list_item_type(): | |
| 192 item_type_info = self._type_registry.TypeInfo( | |
| 193 self._interface_type_info.list_item_type()) | |
| 194 implements.append('List<%s>' % item_type_info.dart_type()) | |
| 195 return implements | |
| 196 | |
| 197 def AddConstructors(self, | |
| 198 constructors, factory_name, factory_constructor_name): | |
| 199 """ Adds all of the constructors. | |
| 200 Arguments: | |
| 201 constructors - List of the constructors to be added. | |
| 202 factory_name - Name of the factory for this class. | |
| 203 factory_constructor_name - The name of the constructor on the | |
| 204 factory_name to call (calls an autogenerated FactoryProvider | |
| 205 if unspecified) | |
| 206 """ | |
| 207 for constructor_info in constructors: | |
| 208 self._AddConstructor( | |
| 209 constructor_info, factory_name, factory_constructor_name) | |
| 210 | |
| 211 typed_array_type = None | |
| 212 for interface in self._database.Hierarchy(self._interface): | |
| 213 type_info = self._type_registry.TypeInfo(interface.id) | |
| 214 if type_info.is_typed_array(): | |
| 215 typed_array_type = type_info.list_item_type() | |
| 216 break | |
| 217 if typed_array_type: | |
| 218 self._members_emitter.Emit( | |
| 219 '\n' | |
| 220 ' factory $CTOR(int length) =>\n' | |
| 221 ' $FACTORY.create$(CTOR)(length);\n' | |
| 222 '\n' | |
| 223 ' factory $CTOR.fromList(List<$TYPE> list) =>\n' | |
| 224 ' $FACTORY.create$(CTOR)_fromList(list);\n' | |
| 225 '\n' | |
| 226 ' factory $CTOR.fromBuffer(ArrayBuffer buffer, ' | |
| 227 '[int byteOffset, int length]) => \n' | |
| 228 ' $FACTORY.create$(CTOR)_fromBuffer(buffer, byteOffset, length);\n'
, | |
| 229 CTOR=self._interface.id, | |
| 230 TYPE=self._DartType(typed_array_type), | |
| 231 FACTORY=factory_name) | |
| 232 | |
| 233 def _AddConstructor(self, | |
| 234 constructor_info, factory_name, factory_constructor_name): | |
| 235 self._members_emitter.Emit('\n ///@docsEditable true'); | |
| 236 | |
| 237 if not factory_constructor_name: | |
| 238 factory_constructor_name = '_create' | |
| 239 factory_parameters = constructor_info.ParametersAsArgumentList() | |
| 240 has_factory_provider = True | |
| 241 else: | |
| 242 factory_parameters = ', '.join(constructor_info.factory_parameters) | |
| 243 has_factory_provider = False | |
| 244 | |
| 245 has_optional = any(param_info.is_optional | |
| 246 for param_info in constructor_info.param_infos) | |
| 247 | |
| 248 if not has_optional: | |
| 249 self._members_emitter.Emit( | |
| 250 '\n' | |
| 251 ' factory $CTOR($PARAMS) => ' | |
| 252 '$FACTORY.$CTOR_FACTORY_NAME($FACTORY_PARAMS);\n', | |
| 253 CTOR=constructor_info._ConstructorFullName(self._DartType), | |
| 254 PARAMS=constructor_info.ParametersDeclaration(self._DartType), | |
| 255 FACTORY=factory_name, | |
| 256 CTOR_FACTORY_NAME=factory_constructor_name, | |
| 257 FACTORY_PARAMS=factory_parameters) | |
| 258 else: | |
| 259 if has_factory_provider: | |
| 260 dispatcher_emitter = self._members_emitter.Emit( | |
| 261 '\n' | |
| 262 ' factory $CTOR($PARAMS) {\n' | |
| 263 '$!DISPATCHER' | |
| 264 ' return $FACTORY._create($FACTORY_PARAMS);\n' | |
| 265 ' }\n', | |
| 266 CTOR=constructor_info._ConstructorFullName(self._DartType), | |
| 267 PARAMS=constructor_info.ParametersDeclaration(self._DartType), | |
| 268 FACTORY=factory_name, | |
| 269 FACTORY_PARAMS=constructor_info.ParametersAsArgumentList()) | |
| 270 | |
| 271 for index, param_info in enumerate(constructor_info.param_infos): | |
| 272 if param_info.is_optional: | |
| 273 dispatcher_emitter.Emit( | |
| 274 ' if (!?$OPT_PARAM_NAME) {\n' | |
| 275 ' return $FACTORY._create($FACTORY_PARAMS);\n' | |
| 276 ' }\n', | |
| 277 OPT_PARAM_NAME=constructor_info.param_infos[index].name, | |
| 278 FACTORY=factory_name, | |
| 279 FACTORY_PARAMS=constructor_info.ParametersAsArgumentList(index)) | |
| 280 else: | |
| 281 inits = self._members_emitter.Emit( | |
| 282 '\n' | |
| 283 ' factory $CONSTRUCTOR($PARAMS) {\n' | |
| 284 ' var e = $FACTORY.$CTOR_FACTORY_NAME($FACTORY_PARAMS);\n' | |
| 285 '$!INITS' | |
| 286 ' return e;\n' | |
| 287 ' }\n', | |
| 288 CONSTRUCTOR=constructor_info._ConstructorFullName(self._DartType), | |
| 289 FACTORY=factory_name, | |
| 290 CTOR_FACTORY_NAME=factory_constructor_name, | |
| 291 PARAMS=constructor_info.ParametersDeclaration(self._DartType), | |
| 292 FACTORY_PARAMS=factory_parameters) | |
| 293 | |
| 294 for index, param_info in enumerate(constructor_info.param_infos): | |
| 295 if param_info.is_optional: | |
| 296 inits.Emit(' if ($E != null) e.$E = $E;\n', | |
| 297 E=constructor_info.param_infos[index].name) | |
| 298 | |
| 299 if not constructor_info.pure_dart_constructor: | |
| 300 template_file = ('factoryprovider_%s.darttemplate' % self._interface.doc_j
s_name) | |
| 301 template = self._template_loader.TryLoad(template_file) | |
| 302 if template: | |
| 303 # There is a class specific factory. | |
| 304 # TODO(antonm): should move into the class template. | |
| 305 self._members_emitter.Emit(template) | |
| 306 else: | |
| 307 self.EmitStaticFactory(constructor_info) | |
| 308 | |
| 309 def EmitHelpers(self, base_class): | |
| 310 pass | |
| 311 | |
| 312 def DeclareAttribute(self, attribute, type_name, attr_name, read_only): | |
| 313 """ Declares an attribute but does not include the code to invoke it. | |
| 314 """ | |
| 315 self.EmitAttributeDocumentation(attribute) | |
| 316 if read_only: | |
| 317 template = '\n $TYPE get $NAME;\n' | |
| 318 else: | |
| 319 template = '\n $TYPE $NAME;\n' | |
| 320 | |
| 321 self._members_emitter.Emit(template, | |
| 322 NAME=attr_name, | |
| 323 TYPE=type_name) | |
| 324 | |
| 325 def DeclareOperation(self, operation, return_type_name, method_name): | |
| 326 """ Declares an operation but does not include the code to invoke it. | |
| 327 Arguments: | |
| 328 operation - The operation to be declared. | |
| 329 return_type_name - The name of the return type. | |
| 330 method_name - The name of the method. | |
| 331 """ | |
| 332 self.EmitOperationDocumentation(operation) | |
| 333 self._members_emitter.Emit( | |
| 334 '\n' | |
| 335 ' $TYPE $NAME($PARAMS);\n', | |
| 336 TYPE=return_type_name, | |
| 337 NAME=method_name, | |
| 338 PARAMS=operation.ParametersDeclaration(self._DartType)) | |
| 339 | |
| 340 def EmitListMixin(self, element_name): | |
| 341 # TODO(sra): Use separate mixins for mutable implementations of List<T>. | |
| 342 # TODO(sra): Use separate mixins for typed array implementations of List<T>. | |
| 343 template_file = 'immutable_list_mixin.darttemplate' | |
| 344 has_contains = any(op.id == 'contains' for op in self._interface.operations) | |
| 345 has_clear = any(op.id == 'clear' for op in self._interface.operations) | |
| 346 has_length = False | |
| 347 has_length_setter = False | |
| 348 for attr in self._interface.attributes: | |
| 349 if attr.id == 'length': | |
| 350 has_length = True | |
| 351 has_length_setter = not attr.is_read_only | |
| 352 | |
| 353 has_num_items = any(attr.id == 'numberOfItems' | |
| 354 for attr in self._interface.attributes) | |
| 355 | |
| 356 template = self._template_loader.Load( | |
| 357 template_file, | |
| 358 { | |
| 359 'DEFINE_CONTAINS': not has_contains, | |
| 360 'DEFINE_CLEAR': not has_clear, | |
| 361 'DEFINE_LENGTH_AS_NUM_ITEMS': not has_length and has_num_items, | |
| 362 'DEFINE_LENGTH_SETTER': not has_length_setter, | |
| 363 }) | |
| 364 self._members_emitter.Emit(template, E=element_name) | |
| 365 | |
| 366 def SecureOutputType(self, type_name, is_dart_type=False): | |
| 367 """ Converts the type name to the secure type name for return types. | |
| 368 """ | |
| 369 if is_dart_type: | |
| 370 dart_name = type_name | |
| 371 else: | |
| 372 dart_name = self._DartType(type_name) | |
| 373 # We only need to secure Window. Only local History and Location are | |
| 374 # returned in generated code. | |
| 375 assert(dart_name != 'HistoryBase' and dart_name != 'LocationBase') | |
| 376 if dart_name == 'Window': | |
| 377 return _secure_base_types[dart_name] | |
| 378 return dart_name | |
| 379 | |
| 380 def SecureBaseName(self, type_name): | |
| 381 if type_name in _secure_base_types: | |
| 382 return _secure_base_types[type_name] | |
| 383 | |
| 384 def _TransitiveSecondaryParents(self, interface): | |
| 385 """Returns a list of all non-primary parents. | |
| 386 | |
| 387 The list contains the interface objects for interfaces defined in the | |
| 388 database, and the name for undefined interfaces. | |
| 389 """ | |
| 390 def walk(parents): | |
| 391 for parent in parents: | |
| 392 parent_name = parent.type.id | |
| 393 if parent_name == 'EventTarget': | |
| 394 # Currently EventTarget is implemented as a mixin, not a proper | |
| 395 # super interface---ignore its members. | |
| 396 continue | |
| 397 if IsDartCollectionType(parent_name): | |
| 398 result.append(parent_name) | |
| 399 continue | |
| 400 if self._database.HasInterface(parent_name): | |
| 401 parent_interface = self._database.GetInterface(parent_name) | |
| 402 result.append(parent_interface) | |
| 403 walk(parent_interface.parents) | |
| 404 | |
| 405 result = [] | |
| 406 if interface.parents: | |
| 407 parent = interface.parents[0] | |
| 408 if IsPureInterface(parent.type.id): | |
| 409 walk(interface.parents) | |
| 410 else: | |
| 411 walk(interface.parents[1:]) | |
| 412 return result | |
| 413 | |
| 414 def _DartType(self, type_name): | |
| 415 return self._type_registry.DartType(type_name) | |
| 416 | |
| 417 def _IsPrivate(self, name): | |
| 418 return name.startswith('_') | |
| OLD | NEW |