| 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 import emitter | |
| 10 import os | |
| 11 from generator import * | |
| 12 from htmldartgenerator import * | |
| 13 | |
| 14 _js_custom_members = set([ | |
| 15 'AudioBufferSourceNode.start', | |
| 16 'AudioBufferSourceNode.stop', | |
| 17 'AudioContext.createGain', | |
| 18 'AudioContext.createScriptProcessor', | |
| 19 'Console.memory', | |
| 20 'Console.profiles', | |
| 21 'Console.assertCondition', | |
| 22 'Console.count', | |
| 23 'Console.debug', | |
| 24 'Console.dir', | |
| 25 'Console.dirxml', | |
| 26 'Console.error', | |
| 27 'Console.group', | |
| 28 'Console.groupCollapsed', | |
| 29 'Console.groupEnd', | |
| 30 'Console.info', | |
| 31 'Console.log', | |
| 32 'Console.markTimeline', | |
| 33 'Console.profile', | |
| 34 'Console.profileEnd', | |
| 35 'Console.time', | |
| 36 'Console.timeEnd', | |
| 37 'Console.timeStamp', | |
| 38 'Console.trace', | |
| 39 'Console.warn', | |
| 40 'CSSStyleDeclaration.setProperty', | |
| 41 'Element.insertAdjacentElement', | |
| 42 'Element.insertAdjacentHTML', | |
| 43 'Element.insertAdjacentText', | |
| 44 'Element.remove', | |
| 45 'ElementEvents.mouseWheel', | |
| 46 'HTMLCanvasElement.getContext', | |
| 47 'HTMLTableElement.createTBody', | |
| 48 'IDBDatabase.transaction', | |
| 49 'KeyboardEvent.initKeyboardEvent', | |
| 50 'MouseEvent.offsetX', | |
| 51 'MouseEvent.offsetY', | |
| 52 'Navigator.language', | |
| 53 'URL.createObjectURL', | |
| 54 'URL.revokeObjectURL', | |
| 55 'WheelEvent.wheelDeltaX', | |
| 56 'WheelEvent.wheelDeltaY', | |
| 57 'Window.cancelAnimationFrame', | |
| 58 'Window.console', | |
| 59 'Window.document', | |
| 60 'Window.indexedDB', | |
| 61 'Window.location', | |
| 62 'Window.open', | |
| 63 'Window.requestAnimationFrame', | |
| 64 'Window.webkitCancelAnimationFrame', | |
| 65 'Window.webkitRequestAnimationFrame', | |
| 66 'WorkerContext.indexedDB', | |
| 67 ]) | |
| 68 | |
| 69 | |
| 70 # Classes that offer only static methods, and therefore we should suppress | |
| 71 # constructor creation. | |
| 72 _static_classes = set(['Url']) | |
| 73 | |
| 74 # Information for generating element constructors. | |
| 75 # | |
| 76 # TODO(sra): maybe remove all the argument complexity and use cascades. | |
| 77 # | |
| 78 # var c = new CanvasElement(width: 100, height: 70); | |
| 79 # var c = new CanvasElement()..width = 100..height = 70; | |
| 80 # | |
| 81 class ElementConstructorInfo(object): | |
| 82 def __init__(self, name=None, tag=None, | |
| 83 params=[], opt_params=[], | |
| 84 factory_provider_name='document'): | |
| 85 self.name = name # The constructor name 'h1' in 'HeadingElement.h1' | |
| 86 self.tag = tag or name # The HTML or SVG tag | |
| 87 self.params = params | |
| 88 self.opt_params = opt_params | |
| 89 self.factory_provider_name = factory_provider_name | |
| 90 | |
| 91 def ConstructorInfo(self, interface_name): | |
| 92 info = OperationInfo() | |
| 93 info.overloads = None | |
| 94 info.declared_name = interface_name | |
| 95 info.name = interface_name | |
| 96 info.constructor_name = self.name | |
| 97 info.js_name = None | |
| 98 info.type_name = interface_name | |
| 99 info.param_infos = map(lambda tXn: ParamInfo(tXn[1], tXn[0], True), | |
| 100 self.opt_params) | |
| 101 info.requires_named_arguments = True | |
| 102 info.factory_parameters = ['"%s"' % self.tag] | |
| 103 info.pure_dart_constructor = True | |
| 104 return info | |
| 105 | |
| 106 _html_element_constructors = { | |
| 107 'AnchorElement' : | |
| 108 ElementConstructorInfo(tag='a', opt_params=[('DOMString', 'href')]), | |
| 109 'AreaElement': 'area', | |
| 110 'ButtonElement': 'button', | |
| 111 'BRElement': 'br', | |
| 112 'BaseElement': 'base', | |
| 113 'BodyElement': 'body', | |
| 114 'ButtonElement': 'button', | |
| 115 'CanvasElement': | |
| 116 ElementConstructorInfo(tag='canvas', | |
| 117 opt_params=[('int', 'width'), ('int', 'height')]), | |
| 118 'ContentElement': 'content', | |
| 119 'DataListElement': 'datalist', | |
| 120 'DListElement': 'dl', | |
| 121 'DetailsElement': 'details', | |
| 122 'DivElement': 'div', | |
| 123 'EmbedElement': 'embed', | |
| 124 'FieldSetElement': 'fieldset', | |
| 125 'FormElement': 'form', | |
| 126 'HRElement': 'hr', | |
| 127 'HeadElement': 'head', | |
| 128 'HeadingElement': [ElementConstructorInfo('h1'), | |
| 129 ElementConstructorInfo('h2'), | |
| 130 ElementConstructorInfo('h3'), | |
| 131 ElementConstructorInfo('h4'), | |
| 132 ElementConstructorInfo('h5'), | |
| 133 ElementConstructorInfo('h6')], | |
| 134 'HtmlElement': 'html', | |
| 135 'IFrameElement': 'iframe', | |
| 136 'ImageElement': | |
| 137 ElementConstructorInfo(tag='img', | |
| 138 opt_params=[('DOMString', 'src'), | |
| 139 ('int', 'width'), ('int', 'height')]), | |
| 140 'KeygenElement': 'keygen', | |
| 141 'LIElement': 'li', | |
| 142 'LabelElement': 'label', | |
| 143 'LegendElement': 'legend', | |
| 144 'LinkElement': 'link', | |
| 145 'MapElement': 'map', | |
| 146 'MenuElement': 'menu', | |
| 147 'MeterElement': 'meter', | |
| 148 'OListElement': 'ol', | |
| 149 'ObjectElement': 'object', | |
| 150 'OptGroupElement': 'optgroup', | |
| 151 'OutputElement': 'output', | |
| 152 'ParagraphElement': 'p', | |
| 153 'ParamElement': 'param', | |
| 154 'PreElement': 'pre', | |
| 155 'ProgressElement': 'progress', | |
| 156 'ScriptElement': 'script', | |
| 157 'SelectElement': 'select', | |
| 158 'SourceElement': 'source', | |
| 159 'SpanElement': 'span', | |
| 160 'StyleElement': 'style', | |
| 161 'TableCaptionElement': 'caption', | |
| 162 'TableCellElement': 'td', | |
| 163 'TableColElement': 'col', | |
| 164 'TableElement': 'table', | |
| 165 'TableRowElement': 'tr', | |
| 166 #'TableSectionElement' <thead> <tbody> <tfoot> | |
| 167 'TextAreaElement': 'textarea', | |
| 168 'TitleElement': 'title', | |
| 169 'TrackElement': 'track', | |
| 170 'UListElement': 'ul', | |
| 171 'VideoElement': 'video' | |
| 172 } | |
| 173 | |
| 174 _svg_element_constructors = { | |
| 175 'AElement': 'a', | |
| 176 'AnimateColorElement': 'animateColor', | |
| 177 'AnimateElement': 'animate', | |
| 178 'AnimateMotionElement': 'animateMotion', | |
| 179 'AnimateTransformElement': 'animateTransform', | |
| 180 'AnimationElement': 'animation', | |
| 181 'CircleElement': 'circle', | |
| 182 'ClipPathElement': 'clipPath', | |
| 183 'CursorElement': 'cursor', | |
| 184 'DefsElement': 'defs', | |
| 185 'DescElement': 'desc', | |
| 186 'EllipseElement': 'ellipse', | |
| 187 'FilterElement': 'filter', | |
| 188 'FontElement': 'font', | |
| 189 'FontFaceElement': 'font-face', | |
| 190 'FontFaceFormatElement': 'font-face-format', | |
| 191 'FontFaceNameElement': 'font-face-name', | |
| 192 'FontFaceSrcElement': 'font-face-src', | |
| 193 'FontFaceUriElement': 'font-face-uri', | |
| 194 'ForeignObjectElement': 'foreignObject', | |
| 195 'GlyphElement': 'glyph', | |
| 196 'GElement': 'g', | |
| 197 'HKernElement': 'hkern', | |
| 198 'ImageElement': 'image', | |
| 199 'LinearGradientElement': 'linearGradient', | |
| 200 'LineElement': 'line', | |
| 201 'MarkerElement': 'marker', | |
| 202 'MaskElement': 'mask', | |
| 203 'MPathElement': 'mpath', | |
| 204 'PathElement': 'path', | |
| 205 'PatternElement': 'pattern', | |
| 206 'PolygonElement': 'polygon', | |
| 207 'PolylineElement': 'polyline', | |
| 208 'RadialGradientElement': 'radialGradient', | |
| 209 'RectElement': 'rect', | |
| 210 'ScriptElement': 'script', | |
| 211 'SetElement': 'set', | |
| 212 'StopElement': 'stop', | |
| 213 'StyleElement': 'style', | |
| 214 'SwitchElement': 'switch', | |
| 215 'SymbolElement': 'symbol', | |
| 216 'TextElement': 'text', | |
| 217 'TitleElement': 'title', | |
| 218 'TRefElement': 'tref', | |
| 219 'TSpanElement': 'tspan', | |
| 220 'UseElement': 'use', | |
| 221 'ViewElement': 'view', | |
| 222 'VKernElement': 'vkern', | |
| 223 } | |
| 224 | |
| 225 _element_constructors = { | |
| 226 'html': _html_element_constructors, | |
| 227 'indexed_db': {}, | |
| 228 'svg': _svg_element_constructors, | |
| 229 'web_audio': {}, | |
| 230 } | |
| 231 | |
| 232 _factory_ctr_strings = { | |
| 233 'html': { | |
| 234 'provider_name': 'document', | |
| 235 'constructor_name': '$dom_createElement' | |
| 236 }, | |
| 237 'indexed_db': { | |
| 238 'provider_name': 'document', | |
| 239 'constructor_name': '$dom_createElement' | |
| 240 }, | |
| 241 'svg': { | |
| 242 'provider_name': '_SvgElementFactoryProvider', | |
| 243 'constructor_name': 'createSvgElement_tag', | |
| 244 }, | |
| 245 'web_audio': { | |
| 246 'provider_name': 'document', | |
| 247 'constructor_name': '$dom_createElement' | |
| 248 }, | |
| 249 } | |
| 250 | |
| 251 def ElementConstructorInfos(typename, element_constructors, | |
| 252 factory_provider_name='_Elements'): | |
| 253 """Returns list of ElementConstructorInfos about the convenience constructors | |
| 254 for an Element or SvgElement.""" | |
| 255 # TODO(sra): Handle multiple and named constructors. | |
| 256 if typename not in element_constructors: | |
| 257 return [] | |
| 258 infos = element_constructors[typename] | |
| 259 if isinstance(infos, str): | |
| 260 infos = ElementConstructorInfo(tag=infos, | |
| 261 factory_provider_name=factory_provider_name) | |
| 262 if not isinstance(infos, list): | |
| 263 infos = [infos] | |
| 264 return infos | |
| 265 | |
| 266 # ------------------------------------------------------------------------------ | |
| 267 | |
| 268 class HtmlDartInterfaceGenerator(object): | |
| 269 """Generates dart interface and implementation for the DOM IDL interface.""" | |
| 270 | |
| 271 def __init__(self, options, library_emitter, event_generator, interface, | |
| 272 backend): | |
| 273 self._renamer = options.renamer | |
| 274 self._database = options.database | |
| 275 self._template_loader = options.templates | |
| 276 self._type_registry = options.type_registry | |
| 277 self._options = options | |
| 278 self._library_emitter = library_emitter | |
| 279 self._event_generator = event_generator | |
| 280 self._interface = interface | |
| 281 self._backend = backend | |
| 282 self._interface_type_info = self._type_registry.TypeInfo(self._interface.id) | |
| 283 self._library_name = self._renamer.GetLibraryName(self._interface) | |
| 284 | |
| 285 def Generate(self): | |
| 286 if 'Callback' in self._interface.ext_attrs: | |
| 287 self.GenerateCallback() | |
| 288 else: | |
| 289 self.GenerateInterface() | |
| 290 | |
| 291 def GenerateCallback(self): | |
| 292 """Generates a typedef for the callback interface.""" | |
| 293 handlers = [operation for operation in self._interface.operations | |
| 294 if operation.id == 'handleEvent'] | |
| 295 info = AnalyzeOperation(self._interface, handlers) | |
| 296 code = self._library_emitter.FileEmitter(self._interface.id, | |
| 297 self._library_name) | |
| 298 code.Emit(self._template_loader.Load('callback.darttemplate')) | |
| 299 | |
| 300 typedef_name = self._renamer.RenameInterface(self._interface) | |
| 301 code.Emit('typedef void $NAME($PARAMS);\n', | |
| 302 NAME=typedef_name, | |
| 303 PARAMS=info.ParametersDeclaration(self._DartType)) | |
| 304 self._backend.GenerateCallback(info) | |
| 305 | |
| 306 def GenerateInterface(self): | |
| 307 interface_name = self._interface_type_info.interface_name() | |
| 308 | |
| 309 factory_provider = None | |
| 310 if interface_name in interface_factories: | |
| 311 factory_provider = interface_factories[interface_name] | |
| 312 factory_constructor_name = None | |
| 313 | |
| 314 constructors = [] | |
| 315 if interface_name in _static_classes: | |
| 316 constructor_info = None | |
| 317 else: | |
| 318 constructor_info = AnalyzeConstructor(self._interface) | |
| 319 if constructor_info: | |
| 320 constructors.append(constructor_info) | |
| 321 # TODO(antonm): consider removing it later. | |
| 322 factory_provider = interface_name | |
| 323 | |
| 324 # HTML Elements and SVG Elements have convenience constructors. | |
| 325 infos = ElementConstructorInfos(interface_name, | |
| 326 _element_constructors[self._library_name], factory_provider_name= | |
| 327 _factory_ctr_strings[self._library_name]['provider_name']) | |
| 328 | |
| 329 if infos: | |
| 330 factory_constructor_name = _factory_ctr_strings[ | |
| 331 self._library_name]['constructor_name'] | |
| 332 | |
| 333 for info in infos: | |
| 334 constructors.append(info.ConstructorInfo(self._interface.id)) | |
| 335 if factory_provider: | |
| 336 assert factory_provider == info.factory_provider_name | |
| 337 else: | |
| 338 factory_provider = info.factory_provider_name | |
| 339 | |
| 340 implementation_emitter = self._ImplementationEmitter() | |
| 341 | |
| 342 base_type_info = None | |
| 343 if self._interface.parents: | |
| 344 supertype = self._interface.parents[0].type.id | |
| 345 if not IsDartCollectionType(supertype) and not IsPureInterface(supertype): | |
| 346 base_type_info = self._type_registry.TypeInfo(supertype) | |
| 347 if base_type_info.merged_into() \ | |
| 348 and self._backend.ImplementsMergedMembers(): | |
| 349 base_type_info = self._type_registry.TypeInfo( | |
| 350 base_type_info.merged_into()) | |
| 351 | |
| 352 if base_type_info: | |
| 353 base_class = base_type_info.implementation_name() | |
| 354 else: | |
| 355 base_class = self._backend.RootClassName() | |
| 356 | |
| 357 implements = self._backend.AdditionalImplementedInterfaces() | |
| 358 for parent in self._interface.parents: | |
| 359 parent_type_info = self._type_registry.TypeInfo(parent.type.id) | |
| 360 if parent_type_info.interface_name() != base_class and \ | |
| 361 parent_type_info != base_type_info: | |
| 362 implements.append(parent_type_info.interface_name()) | |
| 363 | |
| 364 secure_base_name = self._backend.SecureBaseName(interface_name) | |
| 365 if secure_base_name: | |
| 366 implements.append(secure_base_name) | |
| 367 | |
| 368 implements_str = '' | |
| 369 if implements: | |
| 370 implements_str = ' implements ' + ', '.join(set(implements)) | |
| 371 | |
| 372 annotations = FindCommonAnnotations(self._interface.doc_js_name) | |
| 373 annotations_str = '' | |
| 374 if annotations: | |
| 375 annotations_str = '\n' + '\n'.join(annotations) | |
| 376 | |
| 377 self._implementation_members_emitter = implementation_emitter.Emit( | |
| 378 self._backend.ImplementationTemplate(), | |
| 379 LIBRARYNAME=self._library_name, | |
| 380 ANNOTATIONS=annotations_str, | |
| 381 CLASSNAME=self._interface_type_info.implementation_name(), | |
| 382 EXTENDS=' extends %s' % base_class if base_class else '', | |
| 383 IMPLEMENTS=implements_str, | |
| 384 DOMNAME=self._interface.doc_js_name, | |
| 385 NATIVESPEC=self._backend.NativeSpec()) | |
| 386 self._backend.StartInterface(self._implementation_members_emitter) | |
| 387 self._backend.EmitHelpers(base_class) | |
| 388 self._backend.AddConstructors( | |
| 389 constructors, factory_provider, factory_constructor_name) | |
| 390 | |
| 391 events_class_name = self._event_generator.ProcessInterface( | |
| 392 self._interface, interface_name, | |
| 393 self._backend.CustomJSMembers(), | |
| 394 implementation_emitter) | |
| 395 if events_class_name: | |
| 396 self._backend.EmitEventGetter(events_class_name) | |
| 397 | |
| 398 merged_interface = self._interface_type_info.merged_interface() | |
| 399 if merged_interface: | |
| 400 self._backend.AddMembers(self._database.GetInterface(merged_interface), | |
| 401 not self._backend.ImplementsMergedMembers()) | |
| 402 | |
| 403 self._backend.AddMembers(self._interface) | |
| 404 self._backend.AddSecondaryMembers(self._interface) | |
| 405 self._backend.FinishInterface() | |
| 406 | |
| 407 def _ImplementationEmitter(self): | |
| 408 basename = self._interface_type_info.implementation_name() | |
| 409 if (self._interface_type_info.merged_into() and | |
| 410 self._backend.ImplementsMergedMembers()): | |
| 411 # Merged members are implemented in target interface implementation. | |
| 412 return emitter.Emitter() | |
| 413 return self._library_emitter.FileEmitter(basename, self._library_name) | |
| 414 | |
| 415 def _DartType(self, type_name): | |
| 416 return self._type_registry.DartType(type_name) | |
| 417 | |
| 418 | |
| 419 # ------------------------------------------------------------------------------ | |
| 420 | |
| 421 class Dart2JSBackend(HtmlDartGenerator): | |
| 422 """Generates a dart2js class for the dart:html library from a DOM IDL | |
| 423 interface. | |
| 424 """ | |
| 425 | |
| 426 def __init__(self, interface, options): | |
| 427 super(Dart2JSBackend, self).__init__(interface, options) | |
| 428 | |
| 429 self._database = options.database | |
| 430 self._template_loader = options.templates | |
| 431 self._type_registry = options.type_registry | |
| 432 self._renamer = options.renamer | |
| 433 self._interface_type_info = self._type_registry.TypeInfo(self._interface.id) | |
| 434 self._current_secondary_parent = None | |
| 435 | |
| 436 def ImplementsMergedMembers(self): | |
| 437 return True | |
| 438 | |
| 439 def GenerateCallback(self, info): | |
| 440 pass | |
| 441 | |
| 442 def RootClassName(self): | |
| 443 return None | |
| 444 | |
| 445 def AdditionalImplementedInterfaces(self): | |
| 446 implements = super(Dart2JSBackend, self).AdditionalImplementedInterfaces() | |
| 447 if self._interface_type_info.list_item_type(): | |
| 448 implements.append('JavaScriptIndexingBehavior') | |
| 449 return implements | |
| 450 | |
| 451 def NativeSpec(self): | |
| 452 native_spec = MakeNativeSpec(self._interface.javascript_binding_name) | |
| 453 return ' native "%s"' % native_spec | |
| 454 | |
| 455 def ImplementationTemplate(self): | |
| 456 if IsPureInterface(self._interface.id): | |
| 457 return self._template_loader.Load('pure_interface.darttemplate') | |
| 458 | |
| 459 template_file = ('impl_%s.darttemplate' % | |
| 460 self._interface.doc_js_name) | |
| 461 return (self._template_loader.TryLoad(template_file) or | |
| 462 self._template_loader.Load('dart2js_impl.darttemplate')) | |
| 463 | |
| 464 def StartInterface(self, members_emitter): | |
| 465 self._members_emitter = members_emitter | |
| 466 | |
| 467 def FinishInterface(self): | |
| 468 pass | |
| 469 | |
| 470 def EmitStaticFactory(self, constructor_info): | |
| 471 arguments = constructor_info.ParametersAsArgumentList() | |
| 472 if arguments: | |
| 473 arguments = ', ' + arguments | |
| 474 self._members_emitter.Emit( | |
| 475 " static $INTERFACE_NAME _create($PARAMETERS_DECLARATION) => JS(" | |
| 476 "'$INTERFACE_NAME', " | |
| 477 "'new $CONSTRUCTOR_NAME($ARGUMENTS_PATTERN)'$ARGUMENTS);\n", | |
| 478 INTERFACE_NAME=self._interface_type_info.interface_name(), | |
| 479 PARAMETERS_DECLARATION=constructor_info.ParametersDeclaration( | |
| 480 self._DartType), | |
| 481 CONSTRUCTOR_NAME=constructor_info.name or self._interface.doc_js_name, | |
| 482 ARGUMENTS_PATTERN=','.join(['#'] * len(constructor_info.param_infos)), | |
| 483 ARGUMENTS=arguments) | |
| 484 | |
| 485 def SecondaryContext(self, interface): | |
| 486 if interface is not self._current_secondary_parent: | |
| 487 self._current_secondary_parent = interface | |
| 488 self._members_emitter.Emit('\n // From $WHERE\n', WHERE=interface.id) | |
| 489 | |
| 490 def AddIndexer(self, element_type): | |
| 491 """Adds all the methods required to complete implementation of List.""" | |
| 492 # We would like to simply inherit the implementation of everything except | |
| 493 # length, [], and maybe []=. It is possible to extend from a base | |
| 494 # array implementation class only when there is no other implementation | |
| 495 # inheritance. There might be no implementation inheritance other than | |
| 496 # DOMBaseWrapper for many classes, but there might be some where the | |
| 497 # array-ness is introduced by a non-root interface: | |
| 498 # | |
| 499 # interface Y extends X, List<T> ... | |
| 500 # | |
| 501 # In the non-root case we have to choose between: | |
| 502 # | |
| 503 # class YImpl extends XImpl { add List<T> methods; } | |
| 504 # | |
| 505 # and | |
| 506 # | |
| 507 # class YImpl extends ListBase<T> { copies of transitive XImpl methods; } | |
| 508 # | |
| 509 self._members_emitter.Emit( | |
| 510 '\n' | |
| 511 ' $TYPE operator[](int index) => JS("$TYPE", "#[#]", this, index);\n', | |
| 512 TYPE=self.SecureOutputType(element_type)) | |
| 513 | |
| 514 if 'CustomIndexedSetter' in self._interface.ext_attrs: | |
| 515 self._members_emitter.Emit( | |
| 516 '\n' | |
| 517 ' void operator[]=(int index, $TYPE value) {' | |
| 518 ' JS("void", "#[#] = #", this, index, value); }', | |
| 519 TYPE=self._NarrowInputType(element_type)) | |
| 520 else: | |
| 521 self._members_emitter.Emit( | |
| 522 '\n' | |
| 523 ' void operator[]=(int index, $TYPE value) {\n' | |
| 524 ' throw new UnsupportedError("Cannot assign element of immutable Li
st.");\n' | |
| 525 ' }\n', | |
| 526 TYPE=self._NarrowInputType(element_type)) | |
| 527 | |
| 528 self.EmitListMixin(self._DartType(element_type)) | |
| 529 | |
| 530 def EmitAttribute(self, attribute, html_name, read_only): | |
| 531 if self._HasCustomImplementation(attribute.id): | |
| 532 return | |
| 533 | |
| 534 if IsPureInterface(self._interface.id): | |
| 535 self._AddInterfaceAttribute(attribute, html_name) | |
| 536 return | |
| 537 | |
| 538 # If the attribute is shadowing, we can't generate a shadowing | |
| 539 # field (Issue 1633). | |
| 540 # TODO(sra): _FindShadowedAttribute does not take into account the html | |
| 541 # renaming. we should be looking for another attribute that has the same | |
| 542 # html_name. Two attributes with the same IDL name might not match if one | |
| 543 # is renamed. | |
| 544 (super_attribute, super_attribute_interface) = self._FindShadowedAttribute( | |
| 545 attribute) | |
| 546 if super_attribute: | |
| 547 if read_only: | |
| 548 if attribute.type.id == super_attribute.type.id: | |
| 549 # Compatible attribute, use the superclass property. This works | |
| 550 # because JavaScript will do its own dynamic dispatch. | |
| 551 self._members_emitter.Emit( | |
| 552 '\n' | |
| 553 ' // Use implementation from $SUPER.\n' | |
| 554 ' // final $TYPE $NAME;\n', | |
| 555 SUPER=super_attribute_interface, | |
| 556 NAME=html_name, | |
| 557 TYPE=self.SecureOutputType(attribute.type.id)) | |
| 558 return | |
| 559 self._members_emitter.Emit('\n // Shadowing definition.') | |
| 560 self._AddAttributeUsingProperties(attribute, html_name, read_only) | |
| 561 return | |
| 562 | |
| 563 # If the type has a conversion we need a getter or setter to contain the | |
| 564 # conversion code. | |
| 565 if (self._OutputConversion(attribute.type.id, attribute.id) or | |
| 566 self._InputConversion(attribute.type.id, attribute.id)): | |
| 567 self._AddAttributeUsingProperties(attribute, html_name, read_only) | |
| 568 return | |
| 569 | |
| 570 output_type = self.SecureOutputType(attribute.type.id) | |
| 571 input_type = self._NarrowInputType(attribute.type.id) | |
| 572 annotations = self._Annotations(attribute.type.id, attribute.id) | |
| 573 rename = self._RenamingAnnotation(attribute.id, html_name) | |
| 574 self.EmitAttributeDocumentation(attribute) | |
| 575 if not read_only: | |
| 576 self._members_emitter.Emit( | |
| 577 '\n $RENAME$ANNOTATIONS$TYPE $NAME;' | |
| 578 '\n', | |
| 579 RENAME=rename, | |
| 580 ANNOTATIONS=annotations, | |
| 581 NAME=html_name, | |
| 582 TYPE=output_type) | |
| 583 else: | |
| 584 template = '\n $RENAME$(ANNOTATIONS)final $TYPE $NAME;\n' | |
| 585 # Need to use a getter for list.length properties so we can add a | |
| 586 # setter which throws an exception, satisfying List API. | |
| 587 if self._interface_type_info.list_item_type() and html_name == 'length': | |
| 588 template = ('\n $RENAME$(ANNOTATIONS)$TYPE get $NAME => ' + | |
| 589 'JS("$TYPE", "#.$NAME", this);\n') | |
| 590 self._members_emitter.Emit( | |
| 591 template, | |
| 592 RENAME=rename, | |
| 593 ANNOTATIONS=annotations, | |
| 594 NAME=html_name, | |
| 595 TYPE=output_type) | |
| 596 | |
| 597 def _AddAttributeUsingProperties(self, attribute, html_name, read_only): | |
| 598 self._AddRenamingGetter(attribute, html_name) | |
| 599 if not read_only: | |
| 600 self._AddRenamingSetter(attribute, html_name) | |
| 601 | |
| 602 def _AddInterfaceAttribute(self, attribute, html_name): | |
| 603 self._members_emitter.Emit( | |
| 604 '\n $TYPE $NAME;' | |
| 605 '\n', | |
| 606 NAME=html_name, | |
| 607 TYPE=self.SecureOutputType(attribute.type.id)) | |
| 608 | |
| 609 def _AddRenamingGetter(self, attr, html_name): | |
| 610 self.EmitAttributeDocumentation(attr) | |
| 611 | |
| 612 conversion = self._OutputConversion(attr.type.id, attr.id) | |
| 613 if conversion: | |
| 614 return self._AddConvertingGetter(attr, html_name, conversion) | |
| 615 return_type = self.SecureOutputType(attr.type.id) | |
| 616 native_type = self._NarrowToImplementationType(attr.type.id) | |
| 617 self._members_emitter.Emit( | |
| 618 # TODO(sra): Use metadata to provide native name. | |
| 619 '\n $TYPE get $HTML_NAME => JS("$NATIVE_TYPE", "#.$NAME", this);' | |
| 620 '\n', | |
| 621 HTML_NAME=html_name, | |
| 622 NAME=attr.id, | |
| 623 TYPE=return_type, | |
| 624 NATIVE_TYPE=native_type) | |
| 625 | |
| 626 def _AddRenamingSetter(self, attr, html_name): | |
| 627 self.EmitAttributeDocumentation(attr) | |
| 628 | |
| 629 conversion = self._InputConversion(attr.type.id, attr.id) | |
| 630 if conversion: | |
| 631 return self._AddConvertingSetter(attr, html_name, conversion) | |
| 632 self._members_emitter.Emit( | |
| 633 # TODO(sra): Use metadata to provide native name. | |
| 634 '\n void set $HTML_NAME($TYPE value) {' | |
| 635 '\n JS("void", "#.$NAME = #", this, value);' | |
| 636 '\n }' | |
| 637 '\n', | |
| 638 HTML_NAME=html_name, | |
| 639 NAME=attr.id, | |
| 640 TYPE=self._NarrowInputType(attr.type.id)) | |
| 641 | |
| 642 def _AddConvertingGetter(self, attr, html_name, conversion): | |
| 643 self._members_emitter.Emit( | |
| 644 '\n $RETURN_TYPE get $HTML_NAME => $CONVERT(this._$(HTML_NAME));' | |
| 645 "\n @JSName('$NAME')" | |
| 646 '\n $(ANNOTATIONS)final $NATIVE_TYPE _$HTML_NAME;' | |
| 647 '\n', | |
| 648 ANNOTATIONS=self._Annotations(attr.type.id, html_name), | |
| 649 CONVERT=conversion.function_name, | |
| 650 HTML_NAME=html_name, | |
| 651 NAME=attr.id, | |
| 652 RETURN_TYPE=conversion.output_type, | |
| 653 NATIVE_TYPE=conversion.input_type) | |
| 654 | |
| 655 def _AddConvertingSetter(self, attr, html_name, conversion): | |
| 656 self._members_emitter.Emit( | |
| 657 # TODO(sra): Use metadata to provide native name. | |
| 658 '\n void set $HTML_NAME($INPUT_TYPE value) {' | |
| 659 '\n this._$HTML_NAME = $CONVERT(value);' | |
| 660 '\n }' | |
| 661 '\n void set _$HTML_NAME(/*$NATIVE_TYPE*/ value) {' | |
| 662 '\n JS("void", "#.$NAME = #", this, value);' | |
| 663 '\n }' | |
| 664 '\n', | |
| 665 CONVERT=conversion.function_name, | |
| 666 HTML_NAME=html_name, | |
| 667 NAME=attr.id, | |
| 668 INPUT_TYPE=conversion.input_type, | |
| 669 NATIVE_TYPE=conversion.output_type) | |
| 670 | |
| 671 def AmendIndexer(self, element_type): | |
| 672 pass | |
| 673 | |
| 674 def EmitOperation(self, info, html_name): | |
| 675 """ | |
| 676 Arguments: | |
| 677 info: An OperationInfo object. | |
| 678 """ | |
| 679 if self._HasCustomImplementation(info.name): | |
| 680 return | |
| 681 | |
| 682 self.EmitOperationDocumentation(info) | |
| 683 | |
| 684 if IsPureInterface(self._interface.id): | |
| 685 self._AddInterfaceOperation(info, html_name) | |
| 686 elif any(self._OperationRequiresConversions(op) for op in info.overloads): | |
| 687 # Any conversions needed? | |
| 688 self._AddOperationWithConversions(info, html_name) | |
| 689 else: | |
| 690 self._AddDirectNativeOperation(info, html_name) | |
| 691 | |
| 692 def _AddDirectNativeOperation(self, info, html_name): | |
| 693 self._members_emitter.Emit( | |
| 694 '\n' | |
| 695 ' $RENAME$ANNOTATIONS$MODIFIERS$TYPE $NAME($PARAMS) native;\n', | |
| 696 RENAME=self._RenamingAnnotation(info.declared_name, html_name), | |
| 697 ANNOTATIONS=self._Annotations(info.type_name, info.declared_name), | |
| 698 MODIFIERS='static ' if info.IsStatic() else '', | |
| 699 TYPE=self.SecureOutputType(info.type_name), | |
| 700 NAME=html_name, | |
| 701 PARAMS=info.ParametersDeclaration(self._NarrowInputType)) | |
| 702 | |
| 703 def _AddOperationWithConversions(self, info, html_name): | |
| 704 # Assert all operations have same return type. | |
| 705 assert len(set([op.type.id for op in info.operations])) == 1 | |
| 706 output_conversion = self._OutputConversion(info.type_name, | |
| 707 info.declared_name) | |
| 708 if output_conversion: | |
| 709 return_type = output_conversion.output_type | |
| 710 native_return_type = output_conversion.input_type | |
| 711 else: | |
| 712 return_type = self._NarrowInputType(info.type_name) | |
| 713 native_return_type = return_type | |
| 714 | |
| 715 def InputType(type_name): | |
| 716 conversion = self._InputConversion(type_name, info.declared_name) | |
| 717 if conversion: | |
| 718 return conversion.input_type | |
| 719 else: | |
| 720 return self._NarrowInputType(type_name) if type_name else 'dynamic' | |
| 721 | |
| 722 body = self._members_emitter.Emit( | |
| 723 '\n' | |
| 724 ' $MODIFIERS$TYPE $(HTML_NAME)($PARAMS) {\n' | |
| 725 '$!BODY' | |
| 726 ' }\n', | |
| 727 MODIFIERS='static ' if info.IsStatic() else '', | |
| 728 TYPE=return_type, | |
| 729 HTML_NAME=html_name, | |
| 730 PARAMS=info.ParametersDeclaration(InputType)) | |
| 731 | |
| 732 parameter_names = [param_info.name for param_info in info.param_infos] | |
| 733 parameter_types = [InputType(param_info.type_id) | |
| 734 for param_info in info.param_infos] | |
| 735 operations = info.operations | |
| 736 | |
| 737 method_version = [0] | |
| 738 temp_version = [0] | |
| 739 | |
| 740 def GenerateCall(operation, argument_count, checks): | |
| 741 if checks: | |
| 742 (stmts_emitter, call_emitter) = body.Emit( | |
| 743 ' if ($CHECKS) {\n$!STMTS$!CALL }\n', | |
| 744 INDENT=' ', | |
| 745 CHECKS=' &&\n '.join(checks)) | |
| 746 else: | |
| 747 (stmts_emitter, call_emitter) = body.Emit('$!A$!B', INDENT=' '); | |
| 748 | |
| 749 method_version[0] += 1 | |
| 750 target = '_%s_%d' % (html_name, method_version[0]); | |
| 751 arguments = [] | |
| 752 target_parameters = [] | |
| 753 for position, arg in enumerate(operation.arguments[:argument_count]): | |
| 754 conversion = self._InputConversion(arg.type.id, operation.id) | |
| 755 param_name = operation.arguments[position].id | |
| 756 if conversion: | |
| 757 temp_version[0] += 1 | |
| 758 temp_name = '%s_%s' % (param_name, temp_version[0]) | |
| 759 temp_type = conversion.output_type | |
| 760 stmts_emitter.Emit( | |
| 761 '$(INDENT)$TYPE $NAME = $CONVERT($ARG);\n', | |
| 762 TYPE=TypeOrVar(temp_type), | |
| 763 NAME=temp_name, | |
| 764 CONVERT=conversion.function_name, | |
| 765 ARG=parameter_names[position]) | |
| 766 arguments.append(temp_name) | |
| 767 param_type = temp_type | |
| 768 verified_type = temp_type # verified by assignment in checked mode. | |
| 769 else: | |
| 770 arguments.append(parameter_names[position]) | |
| 771 param_type = self._NarrowInputType(arg.type.id) | |
| 772 # Verified by argument checking on entry to the dispatcher. | |
| 773 | |
| 774 verified_type = InputType(info.param_infos[position].type_id) | |
| 775 # The native method does not need an argument type if we know the type
. | |
| 776 # But we do need the native methods to have correct function types, so | |
| 777 # be conservative. | |
| 778 if param_type == verified_type: | |
| 779 if param_type in ['String', 'num', 'int', 'double', 'bool', 'Object'
]: | |
| 780 param_type = 'dynamic' | |
| 781 | |
| 782 target_parameters.append( | |
| 783 '%s%s' % (TypeOrNothing(param_type), param_name)) | |
| 784 | |
| 785 argument_list = ', '.join(arguments) | |
| 786 # TODO(sra): If the native method has zero type checks, we can 'inline' is | |
| 787 # and call it directly with a JS-expression. | |
| 788 call = '%s(%s)' % (target, argument_list) | |
| 789 | |
| 790 if output_conversion: | |
| 791 call = '%s(%s)' % (output_conversion.function_name, call) | |
| 792 | |
| 793 if operation.type.id == 'void': | |
| 794 call_emitter.Emit('$(INDENT)$CALL;\n$(INDENT)return;\n', | |
| 795 CALL=call) | |
| 796 else: | |
| 797 call_emitter.Emit('$(INDENT)return $CALL;\n', CALL=call) | |
| 798 | |
| 799 self._members_emitter.Emit( | |
| 800 ' $RENAME$ANNOTATIONS$MODIFIERS$TYPE$TARGET($PARAMS) native;\n', | |
| 801 RENAME=self._RenamingAnnotation(info.declared_name, target), | |
| 802 ANNOTATIONS=self._Annotations(info.type_name, info.declared_name), | |
| 803 MODIFIERS='static ' if info.IsStatic() else '', | |
| 804 TYPE=TypeOrNothing(native_return_type), | |
| 805 TARGET=target, | |
| 806 PARAMS=', '.join(target_parameters)) | |
| 807 | |
| 808 def GenerateChecksAndCall(operation, argument_count): | |
| 809 checks = [] | |
| 810 for i in range(0, argument_count): | |
| 811 argument = operation.arguments[i] | |
| 812 parameter_name = parameter_names[i] | |
| 813 test_type = self._DartType(argument.type.id) | |
| 814 if test_type in ['dynamic', 'Object']: | |
| 815 checks.append('?%s' % parameter_name) | |
| 816 elif test_type != parameter_types[i]: | |
| 817 checks.append('(?%s && (%s is %s || %s == null))' % ( | |
| 818 parameter_name, parameter_name, test_type, parameter_name)) | |
| 819 | |
| 820 checks.extend(['!?%s' % name for name in parameter_names[argument_count:]]
) | |
| 821 # There can be multiple presence checks. We need them all since a later | |
| 822 # optional argument could have been passed by name, leaving 'holes'. | |
| 823 GenerateCall(operation, argument_count, checks) | |
| 824 | |
| 825 # TODO: Optimize the dispatch to avoid repeated checks. | |
| 826 if len(operations) > 1: | |
| 827 for operation in operations: | |
| 828 for position, argument in enumerate(operation.arguments): | |
| 829 if self._IsOptional(operation, argument): | |
| 830 GenerateChecksAndCall(operation, position) | |
| 831 GenerateChecksAndCall(operation, len(operation.arguments)) | |
| 832 body.Emit( | |
| 833 ' throw new ArgumentError("Incorrect number or type of arguments");
' | |
| 834 '\n'); | |
| 835 else: | |
| 836 operation = operations[0] | |
| 837 argument_count = len(operation.arguments) | |
| 838 for position, argument in list(enumerate(operation.arguments))[::-1]: | |
| 839 if self._IsOptional(operation, argument): | |
| 840 check = '?%s' % parameter_names[position] | |
| 841 GenerateCall(operation, position + 1, [check]) | |
| 842 argument_count = position | |
| 843 GenerateCall(operation, argument_count, []) | |
| 844 | |
| 845 def _AddInterfaceOperation(self, info, html_name): | |
| 846 self._members_emitter.Emit( | |
| 847 '\n' | |
| 848 ' $TYPE $NAME($PARAMS);\n', | |
| 849 TYPE=self.SecureOutputType(info.type_name), | |
| 850 NAME=info.name, | |
| 851 PARAMS=info.ParametersDeclaration(self._NarrowInputType)) | |
| 852 | |
| 853 def _IsOptional(self, operation, argument): | |
| 854 return IsOptional(argument) | |
| 855 | |
| 856 | |
| 857 def _OperationRequiresConversions(self, operation): | |
| 858 return (self._OperationRequiresOutputConversion(operation) or | |
| 859 self._OperationRequiresInputConversions(operation)) | |
| 860 | |
| 861 def _OperationRequiresOutputConversion(self, operation): | |
| 862 return self._OutputConversion(operation.type.id, operation.id) | |
| 863 | |
| 864 def _OperationRequiresInputConversions(self, operation): | |
| 865 return any(self._InputConversion(arg.type.id, operation.id) | |
| 866 for arg in operation.arguments) | |
| 867 | |
| 868 def _OutputConversion(self, idl_type, member): | |
| 869 return FindConversion(idl_type, 'get', self._interface.id, member) | |
| 870 | |
| 871 def _InputConversion(self, idl_type, member): | |
| 872 return FindConversion(idl_type, 'set', self._interface.id, member) | |
| 873 | |
| 874 def _HasCustomImplementation(self, member_name): | |
| 875 member_name = '%s.%s' % (self._interface.doc_js_name, member_name) | |
| 876 return member_name in _js_custom_members | |
| 877 | |
| 878 def _RenamingAnnotation(self, idl_name, member_name): | |
| 879 if member_name != idl_name: | |
| 880 return "@JSName('%s')\n " % idl_name | |
| 881 return '' | |
| 882 | |
| 883 def _Annotations(self, idl_type, idl_member_name): | |
| 884 annotations = FindDart2JSAnnotations(idl_type, self._interface.id, | |
| 885 idl_member_name) | |
| 886 if annotations: | |
| 887 return '%s\n ' % annotations | |
| 888 return_type = self.SecureOutputType(idl_type) | |
| 889 native_type = self._NarrowToImplementationType(idl_type) | |
| 890 if native_type != return_type: | |
| 891 return "@Returns('%s') @Creates('%s')\n " % (native_type, native_type) | |
| 892 else: | |
| 893 return '' | |
| 894 | |
| 895 def CustomJSMembers(self): | |
| 896 return _js_custom_members | |
| 897 | |
| 898 def _NarrowToImplementationType(self, type_name): | |
| 899 return self._type_registry.TypeInfo(type_name).narrow_dart_type() | |
| 900 | |
| 901 def _NarrowInputType(self, type_name): | |
| 902 return self._NarrowToImplementationType(type_name) | |
| 903 | |
| 904 def _FindShadowedAttribute(self, attr): | |
| 905 """Returns (attribute, superinterface) or (None, None).""" | |
| 906 def FindInParent(interface): | |
| 907 """Returns matching attribute in parent, or None.""" | |
| 908 if interface.parents: | |
| 909 parent = interface.parents[0] | |
| 910 if IsDartCollectionType(parent.type.id): | |
| 911 return (None, None) | |
| 912 if IsPureInterface(parent.type.id): | |
| 913 return (None, None) | |
| 914 if self._database.HasInterface(parent.type.id): | |
| 915 interfaces_to_search_in = [] | |
| 916 parent_interface_name = parent.type.id | |
| 917 interfaces_to_search_in.append(parent_interface_name) | |
| 918 parent_type_info = self._type_registry.TypeInfo(parent_interface_name) | |
| 919 if parent_type_info.merged_into(): | |
| 920 # IDL parent was merged into another interface, which became a | |
| 921 # parent interface in Dart. | |
| 922 parent_interface_name = parent_type_info.merged_into() | |
| 923 interfaces_to_search_in.append(parent_interface_name) | |
| 924 elif parent_type_info.merged_interface(): | |
| 925 # IDL parent has another interface that was merged into it. | |
| 926 interfaces_to_search_in.append(parent_type_info.merged_interface()) | |
| 927 | |
| 928 for interface_name in interfaces_to_search_in: | |
| 929 interface = self._database.GetInterface(interface_name) | |
| 930 attr2 = FindMatchingAttribute(interface, attr) | |
| 931 if attr2: | |
| 932 return (attr2, parent_interface_name) | |
| 933 | |
| 934 return FindInParent( | |
| 935 self._database.GetInterface(parent_interface_name)) | |
| 936 return (None, None) | |
| 937 | |
| 938 return FindInParent(self._interface) if attr else (None, None) | |
| 939 | |
| 940 def _DartType(self, type_name): | |
| 941 return self._type_registry.DartType(type_name) | |
| 942 | |
| 943 # ------------------------------------------------------------------------------ | |
| 944 | |
| 945 class DartLibraryEmitter(): | |
| 946 def __init__(self, multiemitter, dart_sources_dir, dart_libraries): | |
| 947 self._multiemitter = multiemitter | |
| 948 self._dart_sources_dir = dart_sources_dir | |
| 949 self._path_to_emitter = {} | |
| 950 self._dart_libraries = dart_libraries | |
| 951 | |
| 952 def FileEmitter(self, basename, library_name, template=None): | |
| 953 aux_dir = os.path.join(self._dart_sources_dir, library_name) | |
| 954 path = os.path.join(aux_dir, '%s.dart' % basename) | |
| 955 if not path in self._path_to_emitter: | |
| 956 emitter = self._multiemitter.FileEmitter(path) | |
| 957 if not template is None: | |
| 958 emitter = emitter.Emit(template) | |
| 959 self._path_to_emitter[path] = emitter | |
| 960 | |
| 961 self._dart_libraries.AddFile(basename, library_name, path) | |
| 962 return self._path_to_emitter[path] | |
| 963 | |
| 964 def EmitLibraries(self, auxiliary_dir): | |
| 965 self._dart_libraries.Emit(self._multiemitter, auxiliary_dir) | |
| 966 | |
| 967 # ------------------------------------------------------------------------------ | |
| 968 class DartLibrary(): | |
| 969 def __init__(self, name, template_loader, library_type, output_dir): | |
| 970 self._template = template_loader.Load( | |
| 971 '%s_%s.darttemplate' % (name, library_type)) | |
| 972 self._dart_path = os.path.join( | |
| 973 output_dir, '%s_%s.dart' % (name, library_type)) | |
| 974 self._paths = [] | |
| 975 | |
| 976 def AddFile(self, path): | |
| 977 self._paths.append(path) | |
| 978 | |
| 979 def Emit(self, emitter, auxiliary_dir): | |
| 980 def massage_path(path): | |
| 981 # The most robust way to emit path separators is to use / always. | |
| 982 return path.replace('\\', '/') | |
| 983 | |
| 984 library_emitter = emitter.FileEmitter(self._dart_path) | |
| 985 library_file_dir = os.path.dirname(self._dart_path) | |
| 986 auxiliary_dir = os.path.relpath(auxiliary_dir, library_file_dir) | |
| 987 imports_emitter = library_emitter.Emit( | |
| 988 self._template, AUXILIARY_DIR=massage_path(auxiliary_dir)) | |
| 989 | |
| 990 for path in sorted(self._paths): | |
| 991 relpath = os.path.relpath(path, library_file_dir) | |
| 992 imports_emitter.Emit( | |
| 993 "part '$PATH';\n", PATH=massage_path(relpath)) | |
| 994 | |
| 995 # ------------------------------------------------------------------------------ | |
| 996 | |
| 997 class DartLibraries(): | |
| 998 def __init__(self, libraries, template_loader, library_type, output_dir): | |
| 999 self._libraries = {} | |
| 1000 for library_name in libraries: | |
| 1001 self._libraries[library_name] = DartLibrary( | |
| 1002 library_name, template_loader, library_type, output_dir) | |
| 1003 | |
| 1004 def AddFile(self, basename, library_name, path): | |
| 1005 self._libraries[library_name].AddFile(path) | |
| 1006 | |
| 1007 def Emit(self, emitter, auxiliary_dir): | |
| 1008 for lib in self._libraries.values(): | |
| 1009 lib.Emit(emitter, auxiliary_dir) | |
| OLD | NEW |