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