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 os |
| 10 from generator import * |
| 11 from systembase import * |
| 12 from systemfrog import * |
| 13 from systeminterface import * |
| 14 |
| 15 # Members from the standard dom that should not be exposed publicly in dart:html |
| 16 # but need to be exposed internally to implement dart:html on top of a standard |
| 17 # browser. |
| 18 _private_html_members = { |
| 19 'Element': set(['clientLeft', 'clientTop', 'clientWidth', 'clientHeight', |
| 20 'offsetLeft', 'offsetTop', 'offsetWidth', 'offsetHeight', |
| 21 'scrollLeft', 'scrollTop', 'scrollWidth', 'scrollHeight', |
| 22 'childElementCount', 'firstElementChild', 'hasAttribute', |
| 23 'getAttribute', 'removeAttribute', 'setAttribute', 'className', |
| 24 'children']), |
| 25 'Node' : set(['appendChild', 'removeChild', 'replaceChild', 'attributes', |
| 26 'childNodes']), |
| 27 # TODO(jacobr): other direct translate methods on node such as |
| 28 # textContext->text |
| 29 'Document': set(['createElement', 'createEvent']), |
| 30 'Window': set(['getComputedStyle']), |
| 31 'EventTarget': set(['removeEventListener', 'addEventListener', |
| 32 'dispatchEvent']), |
| 33 'Event': set(['initEvent', 'target', 'srcElement', 'currentTarget']) |
| 34 } |
| 35 |
| 36 # Members from the standard dom that exist in the dart:html library with |
| 37 # identical functionality but with cleaner names. |
| 38 html_library_renames = { |
| 39 'Document.createTextNode': 'Text.Text', |
| 40 'Document.get:defaultView': 'Document.get:window', |
| 41 'DocumentFragment.querySelector': 'Element.query', |
| 42 'Element.querySelector': 'Element.query', |
| 43 'Document.querySelector': 'Element.query', |
| 44 'DocumentFragment.querySelectorAll': 'Element.queryAll', |
| 45 'DocumentFragment.querySelectorAll': 'Element.queryAll', |
| 46 'Element.querySelectorAll': 'Element.queryAll', |
| 47 'Element.scrollIntoViewIfNeeded': 'Element.scrollIntoView', |
| 48 'Node.cloneNode': 'Node.clone', |
| 49 'Node.get:nextSibling': 'Node.get:nextNode', |
| 50 'Node.get:ownerDocument': 'Node.get:document', |
| 51 'Node.get:parentNode': 'Node.get:parent', |
| 52 'Node.get:previousSibling': 'Node.get:previousNode', |
| 53 } |
| 54 |
| 55 # Members and classes from the dom that should be removed completelly from |
| 56 # dart:html. These could be expressed in the IDL instead but expressing this |
| 57 # as a simple table instead is more concise. |
| 58 # TODO(jacobr): cleanup and augment this list. |
| 59 _html_library_remove = set([ |
| 60 'Window.get:document', # Removed as we have a custom implementation. |
| 61 'NodeList.item', |
| 62 "Attr.*", |
| 63 # "BarProp.*", |
| 64 # "BarInfo.*", |
| 65 # "Blob.webkitSlice", |
| 66 # "CDATASection.*", |
| 67 # "Comment.*", |
| 68 # "DOMImplementation.*", |
| 69 # TODO(jacobr): listing title here is a temporary hack due to a frog bug |
| 70 # involving when an interface inherits from another interface and defines |
| 71 # the same field. BUG(1633) |
| 72 "Document.get:title", |
| 73 "Document.set:title", |
| 74 "Element.get:title", |
| 75 "Element.set:title", |
| 76 "Document.get:documentElement", |
| 77 "Document.get:forms", |
| 78 # "Document.get:selectedStylesheetSet", |
| 79 # "Document.set:selectedStylesheetSet", |
| 80 # "Document.get:preferredStylesheetSet", |
| 81 "Document.get:links", |
| 82 "Document.getElementsByTagName", |
| 83 "Document.set:domain", |
| 84 "Document.get:implementation", |
| 85 "Document.createAttributeNS", |
| 86 "Document.get:inputEncoding", |
| 87 "Document.getElementsByClassName", |
| 88 "Document.get:compatMode", |
| 89 "Document.importNode", |
| 90 "Document.evaluate", |
| 91 "Document.get:images", |
| 92 "Document.querySelector", |
| 93 "Document.createExpression", |
| 94 "Document.getOverrideStyle", |
| 95 "Document.get:xmlStandalone", |
| 96 "Document.set:xmlStandalone", |
| 97 "Document.createComment", |
| 98 "Document.adoptNode", |
| 99 "Document.get:characterSet", |
| 100 "Document.createAttribute", |
| 101 "Document.querySelectorAll", |
| 102 "Document.get:URL", |
| 103 "Document.createElementNS", |
| 104 "Document.createEntityReference", |
| 105 "Document.get:documentURI", |
| 106 "Document.set:documentURI", |
| 107 "Document.createNodeIterator", |
| 108 "Document.createProcessingInstruction", |
| 109 "Document.get:doctype", |
| 110 "Document.getElementsByName", |
| 111 "Document.createTreeWalker", |
| 112 "Document.get:location", |
| 113 "Document.set:location", |
| 114 "Document.createNSResolver", |
| 115 "Document.get:xmlEncoding", |
| 116 "Document.get:defaultCharset", |
| 117 "Document.get:applets", |
| 118 "Document.getSelection", |
| 119 "Document.get:xmlVersion", |
| 120 "Document.set:xmlVersion", |
| 121 "Document.get:anchors", |
| 122 "Document.getElementsByTagNameNS", |
| 123 "DocumentType.*", |
| 124 "Element.hasAttributeNS", |
| 125 "Element.getAttributeNS", |
| 126 "Element.setAttributeNode", |
| 127 "Element.getAttributeNode", |
| 128 "Element.removeAttributeNode", |
| 129 "Element.removeAttributeNS", |
| 130 "Element.setAttributeNodeNS", |
| 131 "Element.getAttributeNodeNS", |
| 132 "Element.setAttributeNS", |
| 133 # "EventSource.get:url", |
| 134 # TODO(jacobr): should these be removed? |
| 135 "Document.close", |
| 136 "Document.hasFocus", |
| 137 |
| 138 "Document.get:vlinkColor", |
| 139 "Document.set:vlinkColor", |
| 140 "Document.captureEvents", |
| 141 "Document.releaseEvents", |
| 142 "Document.get:compatMode", |
| 143 "Document.get:designMode", |
| 144 "Document.set:designMode", |
| 145 "Document.get:dir", |
| 146 "Document.set:dir", |
| 147 "Document.get:all", |
| 148 "Document.set:all", |
| 149 "Document.write", |
| 150 "Document.get:fgColor", |
| 151 "Document.set:fgColor", |
| 152 "Document.get:bgColor", |
| 153 "Document.set:bgColor", |
| 154 "Document.get:plugins", |
| 155 "Document.get:alinkColor", |
| 156 "Document.set:alinkColor", |
| 157 "Document.get:embeds", |
| 158 "Document.open", |
| 159 "Document.clear", |
| 160 "Document.get:scripts", |
| 161 "Document.writeln", |
| 162 "Document.get:linkColor", |
| 163 "Document.set:linkColor", |
| 164 "Element.get:itemRef", |
| 165 "Element.set:className", |
| 166 "Element.get:outerText", |
| 167 "Element.set:outerText", |
| 168 "Element.get:accessKey", |
| 169 "Element.set:accessKey", |
| 170 "Element.get:itemType", |
| 171 "Element.get:innerText", |
| 172 "Element.set:innerText", |
| 173 "Element.set:outerHTML", |
| 174 "Element.get:itemScope", |
| 175 "Element.set:itemScope", |
| 176 "Element.get:itemValue", |
| 177 "Element.set:itemValue", |
| 178 "Element.get:itemId", |
| 179 "Element.set:itemId", |
| 180 "Element.get:itemProp", |
| 181 "EmbedElement.getSVGDocument", |
| 182 "FormElement.get:elements", |
| 183 "HTMLFrameElement.*", |
| 184 "HTMLFrameSetElement.*", |
| 185 "HTMLHtmlElement.get:version", |
| 186 "HTMLHtmlElement.set:version", |
| 187 # "IFrameElement.getSVGDocument", #TODO(jacobr): should this be removed |
| 188 "InputElement.get:dirName", |
| 189 "InputElement.set:dirName", |
| 190 "HTMLIsIndexElement.*", |
| 191 "ObjectElement.getSVGDocument", |
| 192 "HTMLOptionsCollection.*", |
| 193 "HTMLPropertiesCollection.*", |
| 194 "SelectElement.remove", |
| 195 "TextAreaElement.get:dirName", |
| 196 "TextAreaElement.set:dirName", |
| 197 "NamedNodeMap.*", |
| 198 "Node.isEqualNode", |
| 199 "Node.get:TEXT_NODE", |
| 200 "Node.hasAttributes", |
| 201 "Node.get:DOCUMENT_TYPE_NODE", |
| 202 "Node.get:DOCUMENT_POSITION_FOLLOWING", |
| 203 "Node.get:childNodes", |
| 204 "Node.lookupNamespaceURI", |
| 205 "Node.get:ELEMENT_NODE", |
| 206 "Node.get:namespaceURI", |
| 207 "Node.get:DOCUMENT_FRAGMENT_NODE", |
| 208 "Node.get:localName", |
| 209 "Node.dispatchEvent", |
| 210 "Node.isDefaultNamespace", |
| 211 "Node.compareDocumentPosition", |
| 212 "Node.get:baseURI", |
| 213 "Node.isSameNode", |
| 214 "Node.get:DOCUMENT_POSITION_DISCONNECTED", |
| 215 "Node.get:DOCUMENT_NODE", |
| 216 "Node.get:DOCUMENT_POSITION_CONTAINS", |
| 217 "Node.get:COMMENT_NODE", |
| 218 "Node.get:ENTITY_REFERENCE_NODE", |
| 219 "Node.isSupported", |
| 220 "Node.get:firstChild", |
| 221 "Node.get:DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC", |
| 222 "Node.get:lastChild", |
| 223 "Node.get:attributes", |
| 224 "Node.get:NOTATION_NODE", |
| 225 "Node.normalize", |
| 226 "Node.get:parentElement", |
| 227 "Node.get:ATTRIBUTE_NODE", |
| 228 "Node.get:ENTITY_NODE", |
| 229 "Node.get:DOCUMENT_POSITION_CONTAINED_BY", |
| 230 "Node.get:prefix", |
| 231 "Node.set:prefix", |
| 232 "Node.get:DOCUMENT_POSITION_PRECEDING", |
| 233 "Node.get:nodeType", |
| 234 "Node.removeEventListener", |
| 235 "Node.get:nodeValue", |
| 236 "Node.set:nodeValue", |
| 237 "Node.get:CDATA_SECTION_NODE", |
| 238 "Node.get:nodeName", |
| 239 "Node.addEventListener", |
| 240 "Node.lookupPrefix", |
| 241 "Node.get:PROCESSING_INSTRUCTION_NODE", |
| 242 "Notification.dispatchEvent", |
| 243 "Notification.addEventListener", |
| 244 "Notification.removeEventListener"]) |
| 245 |
| 246 # Events without onEventName attributes in the IDL we want to support. |
| 247 # We can automatically extract most event event names by checking for |
| 248 # onEventName methods in the IDL but some events aren't listed so we need |
| 249 # to manually add them here so that they are easy for users to find. |
| 250 _html_manual_events = { |
| 251 'Element': ['touchleave', 'webkitTransitionEnd'], |
| 252 'Window': ['DOMContentLoaded'] |
| 253 } |
| 254 |
| 255 # These event names must be camel case when attaching event listeners |
| 256 # using addEventListener even though the onEventName properties in the DOM for |
| 257 # them are not camel case. |
| 258 _on_attribute_to_event_name_mapping = { |
| 259 'webkitanimationend': 'webkitAnimationEnd', |
| 260 'webkitanimationiteration': 'webkitAnimationIteration', |
| 261 'webkitanimationstart': 'webkitAnimationStart', |
| 262 'webkitfullscreenchange': 'webkitFullScreenChange', |
| 263 'webkitfullscreenerror': 'webkitFullScreenError', |
| 264 'webkitspeechchange': 'webkitSpeechChange', |
| 265 'webkittransitionend': 'webkitTransitionEnd', |
| 266 } |
| 267 |
| 268 # Mapping from raw event names to the pretty camelCase event names exposed as |
| 269 # properties in dart:html. If the DOM exposes a new event name, you will need |
| 270 # to add the lower case to camel case conversion for that event name here. |
| 271 _html_event_names = { |
| 272 'DOMContentLoaded': 'contentLoaded', |
| 273 'touchleave': 'touchLeave', |
| 274 'abort': 'abort', |
| 275 'beforecopy': 'beforeCopy', |
| 276 'beforecut': 'beforeCut', |
| 277 'beforepaste': 'beforePaste', |
| 278 'beforeunload': 'beforeUnload', |
| 279 'blur': 'blur', |
| 280 'cached': 'cached', |
| 281 'canplay': 'canPlay', |
| 282 'canplaythrough': 'canPlayThrough', |
| 283 'change': 'change', |
| 284 'checking': 'checking', |
| 285 'click': 'click', |
| 286 'close': 'close', |
| 287 'contextmenu': 'contextMenu', |
| 288 'copy': 'copy', |
| 289 'cut': 'cut', |
| 290 'dblclick': 'doubleClick', |
| 291 'devicemotion': 'deviceMotion', |
| 292 'deviceorientation': 'deviceOrientation', |
| 293 'display': 'display', |
| 294 'downloading': 'downloading', |
| 295 'drag': 'drag', |
| 296 'dragend': 'dragEnd', |
| 297 'dragenter': 'dragEnter', |
| 298 'dragleave': 'dragLeave', |
| 299 'dragover': 'dragOver', |
| 300 'dragstart': 'dragStart', |
| 301 'drop': 'drop', |
| 302 'durationchange': 'durationChange', |
| 303 'emptied': 'emptied', |
| 304 'ended': 'ended', |
| 305 'error': 'error', |
| 306 'focus': 'focus', |
| 307 'hashchange': 'hashChange', |
| 308 'input': 'input', |
| 309 'invalid': 'invalid', |
| 310 'keydown': 'keyDown', |
| 311 'keypress': 'keyPress', |
| 312 'keyup': 'keyUp', |
| 313 'load': 'load', |
| 314 'loadeddata': 'loadedData', |
| 315 'loadedmetadata': 'loadedMetadata', |
| 316 'loadend': 'loadEnd', |
| 317 'loadstart': 'loadStart', |
| 318 'message': 'message', |
| 319 'mousedown': 'mouseDown', |
| 320 'mousemove': 'mouseMove', |
| 321 'mouseout': 'mouseOut', |
| 322 'mouseover': 'mouseOver', |
| 323 'mouseup': 'mouseUp', |
| 324 'mousewheel': 'mouseWheel', |
| 325 'noupdate': 'noUpdate', |
| 326 'obsolete': 'obsolete', |
| 327 'offline': 'offline', |
| 328 'online': 'online', |
| 329 'open': 'open', |
| 330 'pagehide': 'pageHide', |
| 331 'pageshow': 'pageShow', |
| 332 'paste': 'paste', |
| 333 'pause': 'pause', |
| 334 'play': 'play', |
| 335 'playing': 'playing', |
| 336 'popstate': 'popState', |
| 337 'progress': 'progress', |
| 338 'ratechange': 'rateChange', |
| 339 'readystatechange': 'readyStateChange', |
| 340 'reset': 'reset', |
| 341 'resize': 'resize', |
| 342 'scroll': 'scroll', |
| 343 'search': 'search', |
| 344 'seeked': 'seeked', |
| 345 'seeking': 'seeking', |
| 346 'select': 'select', |
| 347 'selectionchange': 'selectionChange', |
| 348 'selectstart': 'selectStart', |
| 349 'show': 'show', |
| 350 'stalled': 'stalled', |
| 351 'storage': 'storage', |
| 352 'submit': 'submit', |
| 353 'suspend': 'suspend', |
| 354 'timeupdate': 'timeUpdate', |
| 355 'touchcancel': 'touchCancel', |
| 356 'touchend': 'touchEnd', |
| 357 'touchmove': 'touchMove', |
| 358 'touchstart': 'touchStart', |
| 359 'unload': 'unload', |
| 360 'updateready': 'updateReady', |
| 361 'volumechange': 'volumeChange', |
| 362 'waiting': 'waiting', |
| 363 'webkitAnimationEnd': 'animationEnd', |
| 364 'webkitAnimationIteration': 'animationIteration', |
| 365 'webkitAnimationStart': 'animationStart', |
| 366 'webkitFullScreenChange': 'fullScreenChange', |
| 367 'webkitFullScreenError': 'fullScreenError', |
| 368 'webkitSpeechChange': 'speechChange', |
| 369 'webkitTransitionEnd': 'transitionEnd' |
| 370 } |
| 371 |
| 372 def _OnAttributeToEventName(on_method): |
| 373 event_name = on_method.id[2:] |
| 374 if event_name in _on_attribute_to_event_name_mapping: |
| 375 return _on_attribute_to_event_name_mapping[event_name] |
| 376 else: |
| 377 return event_name |
| 378 |
| 379 def _DomToHtmlEvents(interface_id, events): |
| 380 event_names = set(map(_OnAttributeToEventName, events)) |
| 381 if interface_id in _html_manual_events: |
| 382 for manual_event_name in _html_manual_events[interface_id]: |
| 383 event_names.add(manual_event_name) |
| 384 |
| 385 return sorted(event_names, key=lambda name: _html_event_names[name]) |
| 386 |
| 387 # ------------------------------------------------------------------------------ |
| 388 |
| 389 class HtmlSystem(System): |
| 390 |
| 391 def __init__(self, templates, database, emitters, output_dir, generator): |
| 392 super(HtmlSystem, self).__init__( |
| 393 templates, database, emitters, output_dir) |
| 394 self._event_classes = set() |
| 395 self._seen_event_names = {} |
| 396 self._generator = generator |
| 397 |
| 398 def _AllowInHtmlLibrary(self, interface, member): |
| 399 if self._PrivateInHtmlLibrary(interface, member): |
| 400 return False |
| 401 for interface_name in ([interface.id] + |
| 402 self._generator._AllImplementedInterfaces(interface)): |
| 403 if interface.id + '.' + member in _html_library_remove: |
| 404 return False |
| 405 return True |
| 406 |
| 407 def _PrivateInHtmlLibrary(self, interface, member): |
| 408 for interface_name in ([interface.id] + |
| 409 self._generator._AllImplementedInterfaces(interface)): |
| 410 if (interface_name in _private_html_members and |
| 411 member in _private_html_members[interface_name]): |
| 412 return True |
| 413 return False |
| 414 |
| 415 # TODO(jacobr): this already exists |
| 416 def _TraverseParents(self, interface, callback): |
| 417 for parent in interface.parents: |
| 418 parent_id = parent.type.id |
| 419 if self._database.HasInterface(parent_id): |
| 420 parent_interface = self._database.GetInterface(parent_id) |
| 421 callback(parent_interface) |
| 422 self._TraverseParents(parent_interface, callback) |
| 423 |
| 424 # TODO(jacobr): this isn't quite right.... |
| 425 def _GetParentsEventsClasses(self, interface): |
| 426 # Ugly hack as we don't specify that Document inherits from Element |
| 427 # in our IDL. |
| 428 if interface.id == 'Document': |
| 429 return ['ElementEvents'] |
| 430 |
| 431 interfaces_with_events = set() |
| 432 def visit(parent): |
| 433 if parent.id in self._event_classes: |
| 434 interfaces_with_events.add(parent) |
| 435 |
| 436 self._TraverseParents(interface, visit) |
| 437 if len(interfaces_with_events) == 0: |
| 438 return ['Events'] |
| 439 else: |
| 440 names = [] |
| 441 for interface in interfaces_with_events: |
| 442 names.append(interface.id + 'Events') |
| 443 return names |
| 444 |
| 445 class HtmlInterfacesSystem(HtmlSystem): |
| 446 |
| 447 def __init__(self, templates, database, emitters, output_dir, generator): |
| 448 super(HtmlInterfacesSystem, self).__init__( |
| 449 templates, database, emitters, output_dir, generator) |
| 450 self._dart_interface_file_paths = [] |
| 451 |
| 452 def InterfaceGenerator(self, |
| 453 interface, |
| 454 common_prefix, |
| 455 super_interface_name, |
| 456 source_filter): |
| 457 """.""" |
| 458 interface_name = interface.id |
| 459 dart_interface_file_path = self._FilePathForDartInterface(interface_name) |
| 460 |
| 461 self._dart_interface_file_paths.append(dart_interface_file_path) |
| 462 |
| 463 dart_interface_code = self._emitters.FileEmitter(dart_interface_file_path) |
| 464 |
| 465 template_file = 'interface_%s.darttemplate' % interface_name |
| 466 template = self._templates.TryLoad(template_file) |
| 467 if not template: |
| 468 template = self._templates.Load('interface.darttemplate') |
| 469 |
| 470 return HtmlDartInterfaceGenerator( |
| 471 interface, dart_interface_code, |
| 472 template, |
| 473 common_prefix, super_interface_name, |
| 474 source_filter, self) |
| 475 |
| 476 def ProcessCallback(self, interface, info): |
| 477 """Generates a typedef for the callback interface.""" |
| 478 interface_name = interface.id |
| 479 file_path = self._FilePathForDartInterface(interface_name) |
| 480 self._ProcessCallback(interface, info, file_path) |
| 481 |
| 482 def GenerateLibraries(self, lib_dir): |
| 483 pass |
| 484 |
| 485 |
| 486 def _FilePathForDartInterface(self, interface_name): |
| 487 """Returns the file path of the Dart interface definition.""" |
| 488 # TODO(jmesserly): is this the right path |
| 489 return os.path.join(self._output_dir, 'html', 'interface', |
| 490 '%s.dart' % interface_name) |
| 491 |
| 492 # ------------------------------------------------------------------------------ |
| 493 |
| 494 # TODO(jmesserly): inheritance is probably not the right way to factor this long |
| 495 # term, but it makes merging better for now. |
| 496 class HtmlDartInterfaceGenerator(DartInterfaceGenerator): |
| 497 """Generates Dart Interface definition for one DOM IDL interface.""" |
| 498 |
| 499 def __init__(self, interface, emitter, template, |
| 500 common_prefix, super_interface, source_filter, system): |
| 501 super(HtmlDartInterfaceGenerator, self).__init__(interface, |
| 502 emitter, template, common_prefix, super_interface, source_filter) |
| 503 self._system = system |
| 504 |
| 505 def StartInterface(self): |
| 506 typename = self._interface.id |
| 507 |
| 508 extends = [] |
| 509 suppressed_extends = [] |
| 510 |
| 511 for parent in self._interface.parents: |
| 512 # TODO(vsm): Remove source_filter. |
| 513 if MatchSourceFilter(self._source_filter, parent): |
| 514 # Parent is a DOM type. |
| 515 extends.append(parent.type.id) |
| 516 elif '<' in parent.type.id: |
| 517 # Parent is a Dart collection type. |
| 518 # TODO(vsm): Make this check more robust. |
| 519 extends.append(parent.type.id) |
| 520 else: |
| 521 suppressed_extends.append('%s.%s' % |
| 522 (self._common_prefix, parent.type.id)) |
| 523 |
| 524 comment = ' extends' |
| 525 extends_str = '' |
| 526 if extends: |
| 527 extends_str += ' extends ' + ', '.join(extends) |
| 528 comment = ',' |
| 529 if suppressed_extends: |
| 530 extends_str += ' /*%s %s */' % (comment, ', '.join(suppressed_extends)) |
| 531 |
| 532 if typename in interface_factories: |
| 533 extends_str += ' default ' + interface_factories[typename] |
| 534 |
| 535 # TODO(vsm): Add appropriate package / namespace syntax. |
| 536 (self._members_emitter, |
| 537 self._top_level_emitter) = self._emitter.Emit( |
| 538 self._template + '$!TOP_LEVEL', |
| 539 ID=typename, |
| 540 EXTENDS=extends_str) |
| 541 |
| 542 element_type = MaybeTypedArrayElementType(self._interface) |
| 543 if element_type: |
| 544 self._members_emitter.Emit( |
| 545 '\n' |
| 546 ' $CTOR(int length);\n' |
| 547 '\n' |
| 548 ' $CTOR.fromList(List<$TYPE> list);\n' |
| 549 '\n' |
| 550 ' $CTOR.fromBuffer(ArrayBuffer buffer);\n', |
| 551 CTOR=self._interface.id, |
| 552 TYPE=element_type) |
| 553 |
| 554 def AddAttribute(self, getter, setter): |
| 555 if getter and not self._system._AllowInHtmlLibrary(self._interface, |
| 556 'get:' + getter.id): |
| 557 getter = None |
| 558 if setter and not self._system._AllowInHtmlLibrary(self._interface, |
| 559 'set:' + setter.id): |
| 560 setter = None |
| 561 if not getter and not setter: |
| 562 return |
| 563 if getter and setter and getter.type.id == setter.type.id: |
| 564 self._members_emitter.Emit('\n $TYPE $NAME;\n', |
| 565 NAME=getter.id, TYPE=getter.type.id); |
| 566 return |
| 567 if getter and not setter: |
| 568 self._members_emitter.Emit('\n final $TYPE $NAME;\n', |
| 569 NAME=getter.id, TYPE=getter.type.id); |
| 570 return |
| 571 raise Exception('Unexpected getter/setter combination %s %s' % |
| 572 (getter, setter)) |
| 573 |
| 574 def AddOperation(self, info): |
| 575 """ |
| 576 Arguments: |
| 577 operations - contains the overloads, one or more operations with the same |
| 578 name. |
| 579 """ |
| 580 if self._system._AllowInHtmlLibrary(self._interface, info.name): |
| 581 self._members_emitter.Emit('\n' |
| 582 ' $TYPE $NAME($PARAMS);\n', |
| 583 TYPE=info.type_name, |
| 584 NAME=info.name, |
| 585 PARAMS=info.ParametersInterfaceDeclaration()) |
| 586 |
| 587 def FinishInterface(self): |
| 588 pass |
| 589 |
| 590 def AddConstant(self, constant): |
| 591 self._EmitConstant(self._members_emitter, constant) |
| 592 |
| 593 def AddEventAttributes(self, event_attrs): |
| 594 event_attrs = _DomToHtmlEvents(self._interface.id, event_attrs) |
| 595 self._system._event_classes.add(self._interface.id) |
| 596 events_interface = self._interface.id + 'Events' |
| 597 self._members_emitter.Emit('\n $TYPE get on();\n', |
| 598 TYPE=events_interface) |
| 599 events_members = self._emitter.Emit( |
| 600 '\ninterface $INTERFACE extends $PARENTS {\n$!MEMBERS}\n', |
| 601 INTERFACE=events_interface, |
| 602 PARENTS=', '.join( |
| 603 self._system._GetParentsEventsClasses(self._interface))) |
| 604 |
| 605 for event_name in event_attrs: |
| 606 if event_name in _html_event_names: |
| 607 events_members.Emit('\n EventListenerList get $NAME();\n', |
| 608 NAME=_html_event_names[event_name]) |
| 609 else: |
| 610 raise Exception('No known html even name for event: ' + event_name) |
| 611 |
| 612 # ------------------------------------------------------------------------------ |
| 613 |
| 614 # TODO(jmesserly): inheritance is probably not the right way to factor this long |
| 615 # term, but it makes merging better for now. |
| 616 class HtmlFrogClassGenerator(FrogInterfaceGenerator): |
| 617 """Generates a Frog class for the dart:html library from a DOM IDL |
| 618 interface. |
| 619 """ |
| 620 |
| 621 def __init__(self, system, interface, template, super_interface, dart_code): |
| 622 super(HtmlFrogClassGenerator, self).__init__( |
| 623 system, interface, template, super_interface, dart_code) |
| 624 |
| 625 |
| 626 def StartInterface(self): |
| 627 interface = self._interface |
| 628 interface_name = interface.id |
| 629 |
| 630 self._class_name = self._ImplClassName(interface_name) |
| 631 |
| 632 base = None |
| 633 if interface.parents: |
| 634 supertype = interface.parents[0].type.id |
| 635 # FIXME: We're currently injecting List<..> and EventTarget as |
| 636 # supertypes in dart.idl. We should annotate/preserve as |
| 637 # attributes instead. For now, this hack lets the interfaces |
| 638 # inherit, but not the classes. |
| 639 if (not IsDartListType(supertype) and |
| 640 not supertype == 'EventTarget'): |
| 641 base = self._ImplClassName(supertype) |
| 642 if IsDartCollectionType(supertype): |
| 643 # List methods are injected in AddIndexer. |
| 644 pass |
| 645 elif supertype == 'EventTarget': |
| 646 # Most implementors of EventTarget specify the EventListener operations |
| 647 # again. If the operations are not specified, try to inherit from the |
| 648 # EventTarget implementation. |
| 649 # |
| 650 # Applies to MessagePort. |
| 651 if not [op for op in interface.operations if op.id == 'addEventListener'
]: |
| 652 base = self._ImplClassName(supertype) |
| 653 else: |
| 654 base = self._ImplClassName(supertype) |
| 655 |
| 656 native_spec = MakeNativeSpec(interface.javascript_binding_name) |
| 657 |
| 658 extends = ' extends ' + base if base else '' |
| 659 |
| 660 # TODO: Include all implemented interfaces, including other Lists. |
| 661 implements = [interface_name] |
| 662 element_type = MaybeTypedArrayElementType(self._interface) |
| 663 if element_type: |
| 664 implements.append('List<' + element_type + '>') |
| 665 |
| 666 self._members_emitter = self._dart_code.Emit( |
| 667 self._template, |
| 668 #class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { |
| 669 #$!MEMBERS |
| 670 #} |
| 671 CLASSNAME=self._class_name, |
| 672 EXTENDS=extends, |
| 673 IMPLEMENTS=' implements ' + ', '.join(implements), |
| 674 NATIVESPEC=' native "' + native_spec + '"') |
| 675 |
| 676 element_type = MaybeTypedArrayElementType(interface) |
| 677 if element_type: |
| 678 self.AddTypedArrayConstructors(element_type) |
| 679 |
| 680 def AddAttribute(self, getter, setter): |
| 681 |
| 682 if self._system._PrivateInHtmlLibrary(self._interface, getter.id): |
| 683 if getter: |
| 684 self._AddGetter(getter, True) |
| 685 if setter: |
| 686 self._AddSetter(setter, True) |
| 687 return |
| 688 if getter and not self._system._AllowInHtmlLibrary(self._interface, |
| 689 'get:' + getter.id): |
| 690 getter = None |
| 691 if setter and not self._system._AllowInHtmlLibrary(self._interface, |
| 692 'set:' + setter.id): |
| 693 setter = None |
| 694 if not getter and not setter: |
| 695 return |
| 696 # If the (getter, setter) pair is shadowing, we can't generate a shadowing |
| 697 # field (Issue 1633). |
| 698 (super_getter, super_getter_interface) = self._FindShadowedAttribute(getter) |
| 699 (super_setter, super_setter_interface) = self._FindShadowedAttribute(setter) |
| 700 if super_getter or super_setter: |
| 701 if getter and not setter and super_getter and not super_setter: |
| 702 if getter.type.id == super_getter.type.id: |
| 703 # Compatible getter, use the superclass property. This works because |
| 704 # JavaScript will do its own dynamic dispatch. |
| 705 output_type = getter and self._NarrowOutputType(getter.type.id) |
| 706 self._members_emitter.Emit( |
| 707 '\n' |
| 708 ' // Use implementation from $SUPER.\n' |
| 709 ' // final $TYPE $NAME;\n', |
| 710 SUPER=super_getter_interface.id, |
| 711 NAME=getter.id, TYPE=output_type) |
| 712 return |
| 713 |
| 714 self._members_emitter.Emit('\n // Shadowing definition.') |
| 715 if getter: |
| 716 self._AddGetter(getter, False) |
| 717 if setter: |
| 718 self._AddSetter(setter, False) |
| 719 return |
| 720 |
| 721 if self._interface.id != 'Document': |
| 722 output_type = getter and self._NarrowOutputType(getter.type.id) |
| 723 input_type = setter and self._NarrowInputType(setter.type.id) |
| 724 if getter and setter and input_type == output_type: |
| 725 self._members_emitter.Emit( |
| 726 '\n $TYPE $NAME;\n', |
| 727 NAME=getter.id, TYPE=output_type) |
| 728 return |
| 729 if getter and not setter: |
| 730 self._members_emitter.Emit( |
| 731 '\n final $TYPE $NAME;\n', |
| 732 NAME=getter.id, TYPE=output_type) |
| 733 return |
| 734 self._AddAttributeUsingProperties(getter, setter, False) |
| 735 |
| 736 def _AddAttributeUsingProperties(self, getter, setter, private): |
| 737 if getter: |
| 738 self._AddGetter(getter, private) |
| 739 if setter: |
| 740 self._AddSetter(setter, private) |
| 741 |
| 742 def _AddGetter(self, attr, private): |
| 743 # TODO(sra): Remove native body when Issue 829 fixed. |
| 744 self._members_emitter.Emit( |
| 745 '\n $TYPE get $PRIVATE$NAME() native "return $THIS.$NAME;";\n', |
| 746 NAME=attr.id, TYPE=self._NarrowOutputType(attr.type.id), |
| 747 PRIVATE='_' if private else '', |
| 748 THIS='this.parentNode' if self._interface.id == 'Document' else 'this' |
| 749 ) |
| 750 |
| 751 def _AddSetter(self, attr, private): |
| 752 # TODO(sra): Remove native body when Issue 829 fixed. |
| 753 self._members_emitter.Emit( |
| 754 '\n void set $PRIVATE$NAME($TYPE value)' |
| 755 ' native "$THIS.$NAME = value;";\n', |
| 756 NAME=attr.id, TYPE=self._NarrowInputType(attr.type.id), |
| 757 PRIVATE='_' if private else '', |
| 758 THIS='this.parentNode' if self._interface.id == 'Document' else 'this') |
| 759 |
| 760 def AddOperation(self, info): |
| 761 """ |
| 762 Arguments: |
| 763 info: An OperationInfo object. |
| 764 """ |
| 765 private_in_html = self._system._PrivateInHtmlLibrary(self._interface, |
| 766 info.name) |
| 767 if private_in_html or self._interface.id == 'Document': |
| 768 # TODO(vsm): Handle overloads. |
| 769 # TODO(jacobr): handle document more efficiently for cases where any |
| 770 # document is fine. For example: use window.document instead of |
| 771 # this.parentNode. |
| 772 return_type = self._NarrowOutputType(info.type_name) |
| 773 self._members_emitter.Emit( |
| 774 '\n' |
| 775 ' $TYPE $PRIVATE$NAME($PARAMS)' |
| 776 ' native "$(RETURN)$(THIS).$NAME($PARAMNAMES);";\n', |
| 777 TYPE=return_type, |
| 778 RETURN='' if return_type == 'void' else 'return ', |
| 779 NAME=info.name, |
| 780 PRIVATE='_' if private_in_html else '', |
| 781 THIS='this.parentNode' if self._interface.id == 'Document' |
| 782 else 'this', |
| 783 PARAMNAMES=info.ParametersAsArgumentList(), |
| 784 PARAMS=info.ParametersImplementationDeclaration( |
| 785 lambda type_name: self._NarrowInputType(type_name))) |
| 786 elif self._system._AllowInHtmlLibrary(self._interface, info.name): |
| 787 # TODO(jacobr): this is duplicated from the parent class. |
| 788 self._members_emitter.Emit( |
| 789 '\n' |
| 790 ' $TYPE $NAME($PARAMS) native;\n', |
| 791 TYPE=self._NarrowOutputType(info.type_name), |
| 792 NAME=info.name, |
| 793 PARAMS=info.ParametersImplementationDeclaration( |
| 794 lambda type_name: self._NarrowInputType(type_name))) |
| 795 |
| 796 def AddEventAttributes(self, event_attrs): |
| 797 event_attrs = _DomToHtmlEvents(self._interface.id, event_attrs) |
| 798 events_class = '_' + self._interface.id + 'EventsImpl' |
| 799 events_interface = self._interface.id + 'Events' |
| 800 self._members_emitter.Emit( |
| 801 '\n $TYPE get on() =>\n new $TYPE($EVENTTARGET);\n', |
| 802 TYPE=events_class, |
| 803 EVENTTARGET='_jsDocument' if self._interface.id == 'Document' |
| 804 else 'this') |
| 805 |
| 806 self._system._event_classes.add(self._interface.id) |
| 807 |
| 808 parent_event_classes = self._system._GetParentsEventsClasses( |
| 809 self._interface) |
| 810 if len(parent_event_classes) != 1: |
| 811 raise Exception('Only one parent event class allowed ' |
| 812 + self._interface.id) |
| 813 |
| 814 # TODO(jacobr): specify the type of _ptr as EventTarget |
| 815 events_members = self._dart_code.Emit( |
| 816 '\n' |
| 817 'class $CLASSNAME extends $SUPER implements $INTERFACE {\n' |
| 818 ' $CLASSNAME(_ptr) : super(_ptr);\n' |
| 819 '$!MEMBERS}\n', |
| 820 TARGETCLASS=self._NarrowOutputType(self._interface.id), |
| 821 CLASSNAME=events_class, |
| 822 INTERFACE=events_interface, |
| 823 SUPER='_' + parent_event_classes[0] + 'Impl') |
| 824 |
| 825 for event_name in event_attrs: |
| 826 if event_name in _html_event_names: |
| 827 events_members.Emit( |
| 828 "\n" |
| 829 " EventListenerList get $NAME() => _get('$RAWNAME');\n", |
| 830 RAWNAME=event_name, |
| 831 NAME=_html_event_names[event_name]) |
| 832 else: |
| 833 raise Exception('No known html even name for event: ' + event_name) |
| 834 |
| 835 # ------------------------------------------------------------------------------ |
| 836 |
| 837 class HtmlFrogSystem(HtmlSystem): |
| 838 |
| 839 def __init__(self, templates, database, emitters, output_dir, generator): |
| 840 super(HtmlFrogSystem, self).__init__( |
| 841 templates, database, emitters, output_dir, generator) |
| 842 self._dart_frog_file_paths = [] |
| 843 |
| 844 |
| 845 def InterfaceGenerator(self, |
| 846 interface, |
| 847 common_prefix, |
| 848 super_interface_name, |
| 849 source_filter): |
| 850 """.""" |
| 851 dart_frog_file_path = self._FilePathForFrogImpl(interface.id) |
| 852 self._dart_frog_file_paths.append(dart_frog_file_path) |
| 853 |
| 854 template_file = 'impl_%s.darttemplate' % interface.id |
| 855 template = self._templates.TryLoad(template_file) |
| 856 if not template: |
| 857 template = self._templates.Load('frog_impl.darttemplate') |
| 858 |
| 859 dart_code = self._emitters.FileEmitter(dart_frog_file_path) |
| 860 return HtmlFrogClassGenerator(self, interface, template, |
| 861 super_interface_name, dart_code) |
| 862 |
| 863 def GenerateLibraries(self, lib_dir): |
| 864 self._GenerateLibFile( |
| 865 'html_frog.darttemplate', |
| 866 os.path.join(lib_dir, 'html_frog.dart'), |
| 867 (self._interface_system._dart_interface_file_paths + |
| 868 self._interface_system._dart_callback_file_paths + |
| 869 self._dart_frog_file_paths)) |
| 870 |
| 871 def Finish(self): |
| 872 pass |
| 873 |
| 874 def _FilePathForFrogImpl(self, interface_name): |
| 875 """Returns the file path of the Frog implementation.""" |
| 876 # TODO(jmesserly): is this the right path |
| 877 return os.path.join(self._output_dir, 'html', 'frog', |
| 878 '%s.dart' % interface_name) |
| 879 |
| 880 # ------------------------------------------------------------------------------ |
| 881 |
| 882 class WrappingInterfaceGenerator(object): |
| 883 """Generates Dart and JS implementation for one DOM IDL interface.""" |
| 884 |
| 885 def __init__(self, interface, super_interface, dart_code, base_members): |
| 886 """Generates Dart and JS code for the given interface. |
| 887 |
| 888 Args: |
| 889 |
| 890 interface: an IDLInterface instance. It is assumed that all types have |
| 891 been converted to Dart types (e.g. int, String), unless they are in |
| 892 the same package as the interface. |
| 893 super_interface: A string or None, the name of the common interface that |
| 894 this interface implements, if any. |
| 895 dart_code: an Emitter for the file containing the Dart implementation |
| 896 class. |
| 897 base_members: a set of names of members defined in a base class. This is |
| 898 used to avoid static member 'overriding' in the generated Dart code. |
| 899 """ |
| 900 self._interface = interface |
| 901 self._super_interface = super_interface |
| 902 self._dart_code = dart_code |
| 903 self._base_members = base_members |
| 904 self._current_secondary_parent = None |
| 905 |
| 906 |
| 907 def StartInterface(self): |
| 908 interface = self._interface |
| 909 interface_name = interface.id |
| 910 |
| 911 self._class_name = self._ImplClassName(interface_name) |
| 912 |
| 913 base = self._BaseClassName(interface) |
| 914 |
| 915 (self._members_emitter, |
| 916 self._top_level_emitter) = self._dart_code.Emit( |
| 917 '\n' |
| 918 'class $CLASS extends $BASE implements $INTERFACE {\n' |
| 919 ' $CLASS() : super() {}\n' |
| 920 '\n' |
| 921 ' static create_$CLASS() native {\n' |
| 922 ' return new $CLASS();\n' |
| 923 ' }\n' |
| 924 '$!MEMBERS' |
| 925 '\n' |
| 926 ' String get typeName() { return "$INTERFACE"; }\n' |
| 927 '}\n' |
| 928 '$!TOP_LEVEL', |
| 929 CLASS=self._class_name, BASE=base, INTERFACE=interface_name) |
| 930 |
| 931 def _ImplClassName(self, type_name): |
| 932 return '_' + type_name + 'WrappingImplementation' |
| 933 |
| 934 def _BaseClassName(self, interface): |
| 935 if not interface.parents: |
| 936 return 'DOMWrapperBase' |
| 937 |
| 938 supertype = interface.parents[0].type.id |
| 939 |
| 940 # FIXME: We're currently injecting List<..> and EventTarget as |
| 941 # supertypes in dart.idl. We should annotate/preserve as |
| 942 # attributes instead. For now, this hack lets the interfaces |
| 943 # inherit, but not the classes. |
| 944 # List methods are injected in AddIndexer. |
| 945 if IsDartListType(supertype) or IsDartCollectionType(supertype): |
| 946 return 'DOMWrapperBase' |
| 947 |
| 948 if supertype == 'EventTarget': |
| 949 # Most implementors of EventTarget specify the EventListener operations |
| 950 # again. If the operations are not specified, try to inherit from the |
| 951 # EventTarget implementation. |
| 952 # |
| 953 # Applies to MessagePort. |
| 954 if not [op for op in interface.operations if op.id == 'addEventListener']: |
| 955 return self._ImplClassName(supertype) |
| 956 return 'DOMWrapperBase' |
| 957 |
| 958 return self._ImplClassName(supertype) |
| 959 |
| 960 def FinishInterface(self): |
| 961 """.""" |
| 962 pass |
| 963 |
| 964 def AddConstant(self, constant): |
| 965 # Constants are already defined on the interface. |
| 966 pass |
| 967 |
| 968 def _MethodName(self, prefix, name): |
| 969 method_name = prefix + name |
| 970 if name in self._base_members: # Avoid illegal Dart 'static override'. |
| 971 method_name = method_name + '_' + self._interface.id |
| 972 return method_name |
| 973 |
| 974 def AddAttribute(self, getter, setter): |
| 975 if getter: |
| 976 self._AddGetter(getter) |
| 977 if setter: |
| 978 self._AddSetter(setter) |
| 979 |
| 980 def _AddGetter(self, attr): |
| 981 # FIXME: Instead of injecting the interface name into the method when it is |
| 982 # also implemented in the base class, suppress the method altogether if it |
| 983 # has the same signature. I.e., let the JS do the virtual dispatch instead. |
| 984 method_name = self._MethodName('_get_', attr.id) |
| 985 self._members_emitter.Emit( |
| 986 '\n' |
| 987 ' $TYPE get $NAME() { return $METHOD(this); }\n' |
| 988 ' static $TYPE $METHOD(var _this) native;\n', |
| 989 NAME=attr.id, TYPE=attr.type.id, METHOD=method_name) |
| 990 |
| 991 def _AddSetter(self, attr): |
| 992 # FIXME: See comment on getter. |
| 993 method_name = self._MethodName('_set_', attr.id) |
| 994 self._members_emitter.Emit( |
| 995 '\n' |
| 996 ' void set $NAME($TYPE value) { $METHOD(this, value); }\n' |
| 997 ' static void $METHOD(var _this, $TYPE value) native;\n', |
| 998 NAME=attr.id, TYPE=attr.type.id, METHOD=method_name) |
| 999 |
| 1000 def AddSecondaryAttribute(self, interface, getter, setter): |
| 1001 self._SecondaryContext(interface) |
| 1002 self.AddAttribute(getter, setter) |
| 1003 |
| 1004 def AddSecondaryOperation(self, interface, info): |
| 1005 self._SecondaryContext(interface) |
| 1006 self.AddOperation(info) |
| 1007 |
| 1008 def AddEventAttributes(self, event_attrs): |
| 1009 pass |
| 1010 |
| 1011 def _SecondaryContext(self, interface): |
| 1012 if interface is not self._current_secondary_parent: |
| 1013 self._current_secondary_parent = interface |
| 1014 self._members_emitter.Emit('\n // From $WHERE\n', WHERE=interface.id) |
| 1015 |
| 1016 def AddIndexer(self, element_type): |
| 1017 """Adds all the methods required to complete implementation of List.""" |
| 1018 # We would like to simply inherit the implementation of everything except |
| 1019 # get length(), [], and maybe []=. It is possible to extend from a base |
| 1020 # array implementation class only when there is no other implementation |
| 1021 # inheritance. There might be no implementation inheritance other than |
| 1022 # DOMBaseWrapper for many classes, but there might be some where the |
| 1023 # array-ness is introduced by a non-root interface: |
| 1024 # |
| 1025 # interface Y extends X, List<T> ... |
| 1026 # |
| 1027 # In the non-root case we have to choose between: |
| 1028 # |
| 1029 # class YImpl extends XImpl { add List<T> methods; } |
| 1030 # |
| 1031 # and |
| 1032 # |
| 1033 # class YImpl extends ListBase<T> { copies of transitive XImpl methods; } |
| 1034 # |
| 1035 if self._HasNativeIndexGetter(self._interface): |
| 1036 self._EmitNativeIndexGetter(self._interface, element_type) |
| 1037 else: |
| 1038 self._members_emitter.Emit( |
| 1039 '\n' |
| 1040 ' $TYPE operator[](int index) {\n' |
| 1041 ' return item(index);\n' |
| 1042 ' }\n', |
| 1043 TYPE=element_type) |
| 1044 |
| 1045 if self._HasNativeIndexSetter(self._interface): |
| 1046 self._EmitNativeIndexSetter(self._interface, element_type) |
| 1047 else: |
| 1048 self._members_emitter.Emit( |
| 1049 '\n' |
| 1050 ' void operator[]=(int index, $TYPE value) {\n' |
| 1051 ' throw new UnsupportedOperationException("Cannot assign element of
immutable List.");\n' |
| 1052 ' }\n', |
| 1053 TYPE=element_type) |
| 1054 |
| 1055 self._members_emitter.Emit( |
| 1056 '\n' |
| 1057 ' void add($TYPE value) {\n' |
| 1058 ' throw new UnsupportedOperationException("Cannot add to immutable Li
st.");\n' |
| 1059 ' }\n' |
| 1060 '\n' |
| 1061 ' void addLast($TYPE value) {\n' |
| 1062 ' throw new UnsupportedOperationException("Cannot add to immutable Li
st.");\n' |
| 1063 ' }\n' |
| 1064 '\n' |
| 1065 ' void addAll(Collection<$TYPE> collection) {\n' |
| 1066 ' throw new UnsupportedOperationException("Cannot add to immutable Li
st.");\n' |
| 1067 ' }\n' |
| 1068 '\n' |
| 1069 ' void sort(int compare($TYPE a, $TYPE b)) {\n' |
| 1070 ' throw new UnsupportedOperationException("Cannot sort immutable List
.");\n' |
| 1071 ' }\n' |
| 1072 '\n' |
| 1073 ' void copyFrom(List<Object> src, int srcStart, ' |
| 1074 'int dstStart, int count) {\n' |
| 1075 ' throw new UnsupportedOperationException("This object is immutable."
);\n' |
| 1076 ' }\n' |
| 1077 '\n' |
| 1078 ' int indexOf($TYPE element, [int start = 0]) {\n' |
| 1079 ' return _Lists.indexOf(this, element, start, this.length);\n' |
| 1080 ' }\n' |
| 1081 '\n' |
| 1082 ' int lastIndexOf($TYPE element, [int start = null]) {\n' |
| 1083 ' if (start === null) start = length - 1;\n' |
| 1084 ' return _Lists.lastIndexOf(this, element, start);\n' |
| 1085 ' }\n' |
| 1086 '\n' |
| 1087 ' int clear() {\n' |
| 1088 ' throw new UnsupportedOperationException("Cannot clear immutable Lis
t.");\n' |
| 1089 ' }\n' |
| 1090 '\n' |
| 1091 ' $TYPE removeLast() {\n' |
| 1092 ' throw new UnsupportedOperationException("Cannot removeLast on immut
able List.");\n' |
| 1093 ' }\n' |
| 1094 '\n' |
| 1095 ' $TYPE last() {\n' |
| 1096 ' return this[length - 1];\n' |
| 1097 ' }\n' |
| 1098 '\n' |
| 1099 ' void forEach(void f($TYPE element)) {\n' |
| 1100 ' _Collections.forEach(this, f);\n' |
| 1101 ' }\n' |
| 1102 '\n' |
| 1103 ' Collection map(f($TYPE element)) {\n' |
| 1104 ' return _Collections.map(this, [], f);\n' |
| 1105 ' }\n' |
| 1106 '\n' |
| 1107 ' Collection<$TYPE> filter(bool f($TYPE element)) {\n' |
| 1108 ' return _Collections.filter(this, new List<$TYPE>(), f);\n' |
| 1109 ' }\n' |
| 1110 '\n' |
| 1111 ' bool every(bool f($TYPE element)) {\n' |
| 1112 ' return _Collections.every(this, f);\n' |
| 1113 ' }\n' |
| 1114 '\n' |
| 1115 ' bool some(bool f($TYPE element)) {\n' |
| 1116 ' return _Collections.some(this, f);\n' |
| 1117 ' }\n' |
| 1118 '\n' |
| 1119 ' void setRange(int start, int length, List<$TYPE> from, [int startFrom
]) {\n' |
| 1120 ' throw new UnsupportedOperationException("Cannot setRange on immutab
le List.");\n' |
| 1121 ' }\n' |
| 1122 '\n' |
| 1123 ' void removeRange(int start, int length) {\n' |
| 1124 ' throw new UnsupportedOperationException("Cannot removeRange on immu
table List.");\n' |
| 1125 ' }\n' |
| 1126 '\n' |
| 1127 ' void insertRange(int start, int length, [$TYPE initialValue]) {\n' |
| 1128 ' throw new UnsupportedOperationException("Cannot insertRange on immu
table List.");\n' |
| 1129 ' }\n' |
| 1130 '\n' |
| 1131 ' List<$TYPE> getRange(int start, int length) {\n' |
| 1132 ' throw new NotImplementedException();\n' |
| 1133 ' }\n' |
| 1134 '\n' |
| 1135 ' bool isEmpty() {\n' |
| 1136 ' return length == 0;\n' |
| 1137 ' }\n' |
| 1138 '\n' |
| 1139 ' Iterator<$TYPE> iterator() {\n' |
| 1140 ' return new _FixedSizeListIterator<$TYPE>(this);\n' |
| 1141 ' }\n', |
| 1142 TYPE=element_type) |
| 1143 |
| 1144 def _HasNativeIndexGetter(self, interface): |
| 1145 return ('HasIndexGetter' in interface.ext_attrs or |
| 1146 'HasNumericIndexGetter' in interface.ext_attrs) |
| 1147 |
| 1148 def _EmitNativeIndexGetter(self, interface, element_type): |
| 1149 method_name = '_index' |
| 1150 self._members_emitter.Emit( |
| 1151 '\n' |
| 1152 ' $TYPE operator[](int index) { return $METHOD(this, index); }\n' |
| 1153 ' static $TYPE $METHOD(var _this, int index) native;\n', |
| 1154 TYPE=element_type, METHOD=method_name) |
| 1155 |
| 1156 def _HasNativeIndexSetter(self, interface): |
| 1157 return 'HasCustomIndexSetter' in interface.ext_attrs |
| 1158 |
| 1159 def _EmitNativeIndexSetter(self, interface, element_type): |
| 1160 method_name = '_set_index' |
| 1161 self._members_emitter.Emit( |
| 1162 '\n' |
| 1163 ' void operator[]=(int index, $TYPE value) {\n' |
| 1164 ' return $METHOD(this, index, value);\n' |
| 1165 ' }\n' |
| 1166 ' static $METHOD(_this, index, value) native;\n', |
| 1167 TYPE=element_type, METHOD=method_name) |
| 1168 |
| 1169 def AddOperation(self, info): |
| 1170 """ |
| 1171 Arguments: |
| 1172 info: An OperationInfo object. |
| 1173 """ |
| 1174 body = self._members_emitter.Emit( |
| 1175 '\n' |
| 1176 ' $TYPE $NAME($PARAMS) {\n' |
| 1177 '$!BODY' |
| 1178 ' }\n', |
| 1179 TYPE=info.type_name, |
| 1180 NAME=info.name, |
| 1181 PARAMS=info.ParametersImplementationDeclaration()) |
| 1182 |
| 1183 # Process in order of ascending number of arguments to ensure missing |
| 1184 # optional arguments are processed early. |
| 1185 overloads = sorted(info.overloads, |
| 1186 key=lambda overload: len(overload.arguments)) |
| 1187 self._native_version = 0 |
| 1188 fallthrough = self.GenerateDispatch(body, info, ' ', 0, overloads) |
| 1189 if fallthrough: |
| 1190 body.Emit(' throw "Incorrect number or type of arguments";\n'); |
| 1191 |
| 1192 def GenerateSingleOperation(self, emitter, info, indent, operation): |
| 1193 """Generates a call to a single operation. |
| 1194 |
| 1195 Arguments: |
| 1196 emitter: an Emitter for the body of a block of code. |
| 1197 info: the compound information about the operation and its overloads. |
| 1198 indent: an indentation string for generated code. |
| 1199 operation: the IDLOperation to call. |
| 1200 """ |
| 1201 # TODO(sra): Do we need to distinguish calling with missing optional |
| 1202 # arguments from passing 'null' which is represented as 'undefined'? |
| 1203 def UnwrapArgExpression(name, type): |
| 1204 # TODO: Type specific unwrapping. |
| 1205 return '__dom_unwrap(%s)' % (name) |
| 1206 |
| 1207 def ArgNameAndUnwrapper(arg_info, overload_arg): |
| 1208 (name, type, value) = arg_info |
| 1209 return (name, UnwrapArgExpression(name, type)) |
| 1210 |
| 1211 names_and_unwrappers = [ArgNameAndUnwrapper(info.arg_infos[i], arg) |
| 1212 for (i, arg) in enumerate(operation.arguments)] |
| 1213 unwrap_args = [unwrap_arg for (_, unwrap_arg) in names_and_unwrappers] |
| 1214 arg_names = [name for (name, _) in names_and_unwrappers] |
| 1215 |
| 1216 self._native_version += 1 |
| 1217 native_name = self._MethodName('_', info.name) |
| 1218 if self._native_version > 1: |
| 1219 native_name = '%s_%s' % (native_name, self._native_version) |
| 1220 |
| 1221 argument_expressions = ', '.join(['this'] + arg_names) |
| 1222 if info.type_name != 'void': |
| 1223 emitter.Emit('$(INDENT)return $NATIVENAME($ARGS);\n', |
| 1224 INDENT=indent, |
| 1225 NATIVENAME=native_name, |
| 1226 ARGS=argument_expressions) |
| 1227 else: |
| 1228 emitter.Emit('$(INDENT)$NATIVENAME($ARGS);\n' |
| 1229 '$(INDENT)return;\n', |
| 1230 INDENT=indent, |
| 1231 NATIVENAME=native_name, |
| 1232 ARGS=argument_expressions) |
| 1233 |
| 1234 self._members_emitter.Emit(' static $TYPE $NAME($PARAMS) native;\n', |
| 1235 NAME=native_name, |
| 1236 TYPE=info.type_name, |
| 1237 PARAMS=', '.join(['receiver'] + arg_names) ) |
| 1238 |
| 1239 |
| 1240 def GenerateDispatch(self, emitter, info, indent, position, overloads): |
| 1241 """Generates a dispatch to one of the overloads. |
| 1242 |
| 1243 Arguments: |
| 1244 emitter: an Emitter for the body of a block of code. |
| 1245 info: the compound information about the operation and its overloads. |
| 1246 indent: an indentation string for generated code. |
| 1247 position: the index of the parameter to dispatch on. |
| 1248 overloads: a list of the remaining IDLOperations to dispatch. |
| 1249 |
| 1250 Returns True if the dispatch can fall through on failure, False if the code |
| 1251 always dispatches. |
| 1252 """ |
| 1253 |
| 1254 def NullCheck(name): |
| 1255 return '%s === null' % name |
| 1256 |
| 1257 def TypeCheck(name, type): |
| 1258 return '%s is %s' % (name, type) |
| 1259 |
| 1260 if position == len(info.arg_infos): |
| 1261 if len(overloads) > 1: |
| 1262 raise Exception('Duplicate operations ' + str(overloads)) |
| 1263 operation = overloads[0] |
| 1264 self.GenerateSingleOperation(emitter, info, indent, operation) |
| 1265 return False |
| 1266 |
| 1267 # FIXME: Consider a simpler dispatch that iterates over the |
| 1268 # overloads and generates an overload specific check. Revisit |
| 1269 # when we move to named optional arguments. |
| 1270 |
| 1271 # Partition the overloads to divide and conquer on the dispatch. |
| 1272 positive = [] |
| 1273 negative = [] |
| 1274 first_overload = overloads[0] |
| 1275 (param_name, param_type, param_default) = info.arg_infos[position] |
| 1276 |
| 1277 if position < len(first_overload.arguments): |
| 1278 # FIXME: This will not work if the second overload has a more |
| 1279 # precise type than the first. E.g., |
| 1280 # void foo(Node x); |
| 1281 # void foo(Element x); |
| 1282 type = first_overload.arguments[position].type.id |
| 1283 test = TypeCheck(param_name, type) |
| 1284 pred = lambda op: len(op.arguments) > position and op.arguments[position].
type.id == type |
| 1285 else: |
| 1286 type = None |
| 1287 test = NullCheck(param_name) |
| 1288 pred = lambda op: position >= len(op.arguments) |
| 1289 |
| 1290 for overload in overloads: |
| 1291 if pred(overload): |
| 1292 positive.append(overload) |
| 1293 else: |
| 1294 negative.append(overload) |
| 1295 |
| 1296 if positive and negative: |
| 1297 (true_code, false_code) = emitter.Emit( |
| 1298 '$(INDENT)if ($COND) {\n' |
| 1299 '$!TRUE' |
| 1300 '$(INDENT)} else {\n' |
| 1301 '$!FALSE' |
| 1302 '$(INDENT)}\n', |
| 1303 COND=test, INDENT=indent) |
| 1304 fallthrough1 = self.GenerateDispatch( |
| 1305 true_code, info, indent + ' ', position + 1, positive) |
| 1306 fallthrough2 = self.GenerateDispatch( |
| 1307 false_code, info, indent + ' ', position, negative) |
| 1308 return fallthrough1 or fallthrough2 |
| 1309 |
| 1310 if negative: |
| 1311 raise Exception('Internal error, must be all positive') |
| 1312 |
| 1313 # All overloads require the same test. Do we bother? |
| 1314 |
| 1315 # If the test is the same as the method's formal parameter then checked mode |
| 1316 # will have done the test already. (It could be null too but we ignore that |
| 1317 # case since all the overload behave the same and we don't know which types |
| 1318 # in the IDL are not nullable.) |
| 1319 if type == param_type: |
| 1320 return self.GenerateDispatch( |
| 1321 emitter, info, indent, position + 1, positive) |
| 1322 |
| 1323 # Otherwise the overloads have the same type but the type is a substype of |
| 1324 # the method's synthesized formal parameter. e.g we have overloads f(X) and |
| 1325 # f(Y), implemented by the synthesized method f(Z) where X<Z and Y<Z. The |
| 1326 # dispatch has removed f(X), leaving only f(Y), but there is no guarantee |
| 1327 # that Y = Z-X, so we need to check for Y. |
| 1328 true_code = emitter.Emit( |
| 1329 '$(INDENT)if ($COND) {\n' |
| 1330 '$!TRUE' |
| 1331 '$(INDENT)}\n', |
| 1332 COND=test, INDENT=indent) |
| 1333 self.GenerateDispatch( |
| 1334 true_code, info, indent + ' ', position + 1, positive) |
| 1335 return True |
OLD | NEW |