OLD | NEW |
| (Empty) |
1 #!/usr/bin/python | |
2 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
3 # for details. All rights reserved. Use of this source code is governed by a | |
4 # BSD-style license that can be found in the LICENSE file. | |
5 | |
6 """This module provides shared functionality for the system to generate | |
7 Dart:html APIs from the IDL database.""" | |
8 | |
9 from systemfrog import * | |
10 from systeminterface import * | |
11 | |
12 # Members from the standard dom that should not be exposed publicly in dart:html | |
13 # but need to be exposed internally to implement dart:html on top of a standard | |
14 # browser. | |
15 _private_html_members = set([ | |
16 'Element.clientLeft', | |
17 'Element.clientTop', | |
18 'Element.clientWidth', | |
19 'Element.clientHeight', | |
20 'Element.offsetLeft', | |
21 'Element.offsetTop', | |
22 'Element.offsetWidth', | |
23 'Element.offsetHeight', | |
24 'Element.scrollLeft', | |
25 'Element.scrollTop', | |
26 'Element.scrollWidth', | |
27 'Element.scrollHeight', | |
28 'Element.childElementCount', | |
29 'Element.firstElementChild', | |
30 'Element.hasAttribute', | |
31 'Element.getAttribute', | |
32 'Element.removeAttribute', | |
33 'Element.setAttribute', | |
34 'Element.className', | |
35 'Element.children', | |
36 'Element.querySelectorAll', | |
37 'NodeSelector.querySelectorAll', | |
38 'Document.querySelectorAll', | |
39 'DocumentFragment.querySelectorAll', | |
40 'Element.getBoundingClientRect', | |
41 'Element.getClientRects', | |
42 'Node.appendChild', | |
43 'Node.removeChild', | |
44 'Node.replaceChild', | |
45 'Node.attributes', | |
46 'Node.childNodes', | |
47 'Document.createElement', | |
48 'Document.createElementNS', | |
49 'Document.createEvent', | |
50 'Document.createTextNode', | |
51 'Document.createTouchList', | |
52 'Window.getComputedStyle', | |
53 'EventTarget.removeEventListener', | |
54 'EventTarget.addEventListener', | |
55 'EventTarget.dispatchEvent', | |
56 'Event.initEvent', | |
57 'MouseEvent.initMouseEvent', | |
58 ]) | |
59 | |
60 # Members from the standard dom that exist in the dart:html library with | |
61 # identical functionality but with cleaner names. | |
62 _html_library_renames = { | |
63 'Document.defaultView': 'window', | |
64 'DocumentFragment.querySelector': 'query', | |
65 'NodeSelector.querySelector': 'query', | |
66 'Element.querySelector': 'query', | |
67 'Element.webkitMatchesSelector' : 'matchesSelector', | |
68 'Element.scrollIntoViewIfNeeded': 'scrollIntoView', | |
69 'Document.querySelector': 'query', | |
70 'Node.cloneNode': 'clone', | |
71 'Node.nextSibling': 'nextNode', | |
72 'Node.ownerDocument': 'document', | |
73 'Node.parentNode': 'parent', | |
74 'Node.previousSibling': 'previousNode', | |
75 'Node.textContent': 'text', | |
76 'SVGElement.className': '_svgClassName', | |
77 'SVGAnimatedString.className': '_svgClassName', | |
78 'SVGStylable.className': '_svgClassName', | |
79 } | |
80 | |
81 #TODO(jacobr): inject annotations into the interfaces based on this table and | |
82 # on _html_library_renames. | |
83 _injected_doc_fragments = { | |
84 'Element.query': ' /** @domName querySelector, Document.getElementById */', | |
85 } | |
86 # Members and classes from the dom that should be removed completelly from | |
87 # dart:html. These could be expressed in the IDL instead but expressing this | |
88 # as a simple table instead is more concise. | |
89 # Syntax is: ClassName.(get\.|set\.)?MemberName | |
90 # Using get: and set: is optional and should only be used when a getter needs | |
91 # to be suppressed but not the setter, etc. | |
92 # TODO(jacobr): cleanup and augment this list. | |
93 _html_library_remove = set([ | |
94 'Window.get:document', # Removed as we have a custom implementation. | |
95 'NodeList.item', | |
96 "Attr.*", | |
97 # "BarProp.*", | |
98 # "BarInfo.*", | |
99 # "Blob.webkitSlice", | |
100 # "CDATASection.*", | |
101 # "Comment.*", | |
102 # "DOMImplementation.*", | |
103 "Document.get:documentElement", | |
104 "Document.get:forms", | |
105 # "Document.get:selectedStylesheetSet", | |
106 # "Document.set:selectedStylesheetSet", | |
107 # "Document.get:preferredStylesheetSet", | |
108 "Document.get:links", | |
109 "Document.getElementsByTagName", | |
110 "Document.set:domain", | |
111 "Document.get:implementation", | |
112 "Document.createAttributeNS", | |
113 "Document.get:inputEncoding", | |
114 "Document.getElementById", | |
115 "Document.getElementsByClassName", | |
116 "Document.get:height", | |
117 "Document.get:width", | |
118 "Element.getElementsByClassName", | |
119 "Element.getElementsByTagNameNS", | |
120 "Element.getElementsByTagName", | |
121 "Document.get:compatMode", | |
122 "Document.importNode", | |
123 "Document.evaluate", | |
124 "Document.get:images", | |
125 "Document.querySelector", | |
126 "Document.createExpression", | |
127 "Document.getOverrideStyle", | |
128 "Document.xmlStandalone", | |
129 "Document.createComment", | |
130 "Document.adoptNode", | |
131 "Document.get:characterSet", | |
132 "Document.createAttribute", | |
133 "Document.querySelectorAll", | |
134 "Document.get:URL", | |
135 "Document.createEntityReference", | |
136 "Document.get:documentURI", | |
137 "Document.set:documentURI", | |
138 "Document.createNodeIterator", | |
139 "Document.createProcessingInstruction", | |
140 "Document.get:doctype", | |
141 "Document.getElementsByName", | |
142 "Document.createTreeWalker", | |
143 "Document.location", | |
144 "Document.createNSResolver", | |
145 "Document.get:xmlEncoding", | |
146 "Document.get:defaultCharset", | |
147 "Document.get:applets", | |
148 "Document.getSelection", | |
149 "Document.xmlVersion", | |
150 "Document.get:anchors", | |
151 "Document.getElementsByTagNameNS", | |
152 "DocumentType.*", | |
153 "Element.hasAttributeNS", | |
154 "Element.getAttributeNS", | |
155 "Element.setAttributeNode", | |
156 "Element.getAttributeNode", | |
157 "Element.removeAttributeNode", | |
158 "Element.removeAttributeNS", | |
159 "Element.setAttributeNodeNS", | |
160 "Element.getAttributeNodeNS", | |
161 "Element.setAttributeNS", | |
162 "BodyElement.text", | |
163 "AnchorElement.text", | |
164 "OptionElement.text", | |
165 "ScriptElement.text", | |
166 "TitleElement.text", | |
167 # "EventSource.get:url", | |
168 # TODO(jacobr): should these be removed? | |
169 "Document.close", | |
170 "Document.hasFocus", | |
171 | |
172 "Document.vlinkColor", | |
173 "Document.captureEvents", | |
174 "Document.releaseEvents", | |
175 "Document.get:compatMode", | |
176 "Document.designMode", | |
177 "Document.dir", | |
178 "Document.all", | |
179 "Document.write", | |
180 "Document.fgColor", | |
181 "Document.bgColor", | |
182 "Document.get:plugins", | |
183 "Document.alinkColor", | |
184 "Document.get:embeds", | |
185 "Document.open", | |
186 "Document.clear", | |
187 "Document.get:scripts", | |
188 "Document.writeln", | |
189 "Document.linkColor", | |
190 "Element.get:itemRef", | |
191 "Element.outerText", | |
192 "Element.accessKey", | |
193 "Element.get:itemType", | |
194 "Element.innerText", | |
195 "Element.set:outerHTML", | |
196 "Element.itemScope", | |
197 "Element.itemValue", | |
198 "Element.itemId", | |
199 "Element.get:itemProp", | |
200 'Element.scrollIntoView', | |
201 'Element.get:classList', | |
202 "EmbedElement.getSVGDocument", | |
203 "FormElement.get:elements", | |
204 "HTMLFrameElement.*", | |
205 "HTMLFrameSetElement.*", | |
206 "HtmlElement.version", | |
207 "HtmlElement.manifest", | |
208 "Document.version", | |
209 "Document.manifest", | |
210 # "IFrameElement.getSVGDocument", #TODO(jacobr): should this be removed | |
211 "InputElement.dirName", | |
212 "HTMLIsIndexElement.*", | |
213 "ObjectElement.getSVGDocument", | |
214 "HTMLOptionsCollection.*", | |
215 "HTMLPropertiesCollection.*", | |
216 "SelectElement.remove", | |
217 "TextAreaElement.dirName", | |
218 "NamedNodeMap.*", | |
219 "Node.isEqualNode", | |
220 "Node.get:TEXT_NODE", | |
221 "Node.hasAttributes", | |
222 "Node.get:DOCUMENT_TYPE_NODE", | |
223 "Node.get:DOCUMENT_POSITION_FOLLOWING", | |
224 "Node.lookupNamespaceURI", | |
225 "Node.get:ELEMENT_NODE", | |
226 "Node.get:namespaceURI", | |
227 "Node.get:DOCUMENT_FRAGMENT_NODE", | |
228 "Node.get:localName", | |
229 "Node.dispatchEvent", | |
230 "Node.isDefaultNamespace", | |
231 "Node.compareDocumentPosition", | |
232 "Node.get:baseURI", | |
233 "Node.isSameNode", | |
234 "Node.get:DOCUMENT_POSITION_DISCONNECTED", | |
235 "Node.get:DOCUMENT_NODE", | |
236 "Node.get:DOCUMENT_POSITION_CONTAINS", | |
237 "Node.get:COMMENT_NODE", | |
238 "Node.get:ENTITY_REFERENCE_NODE", | |
239 "Node.isSupported", | |
240 "Node.get:firstChild", | |
241 "Node.get:DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC", | |
242 "Node.get:lastChild", | |
243 "Node.get:NOTATION_NODE", | |
244 "Node.normalize", | |
245 "Node.get:parentElement", | |
246 "Node.get:ATTRIBUTE_NODE", | |
247 "Node.get:ENTITY_NODE", | |
248 "Node.get:DOCUMENT_POSITION_CONTAINED_BY", | |
249 "Node.get:prefix", | |
250 "Node.set:prefix", | |
251 "Node.get:DOCUMENT_POSITION_PRECEDING", | |
252 "Node.get:nodeType", | |
253 "Node.removeEventListener", | |
254 "Node.get:nodeValue", | |
255 "Node.set:nodeValue", | |
256 "Node.get:CDATA_SECTION_NODE", | |
257 "Node.get:nodeName", | |
258 "Node.addEventListener", | |
259 "Node.lookupPrefix", | |
260 "Node.get:PROCESSING_INSTRUCTION_NODE", | |
261 "Notification.dispatchEvent", | |
262 "Notification.addEventListener", | |
263 "Notification.removeEventListener"]) | |
264 | |
265 # Events without onEventName attributes in the IDL we want to support. | |
266 # We can automatically extract most event event names by checking for | |
267 # onEventName methods in the IDL but some events aren't listed so we need | |
268 # to manually add them here so that they are easy for users to find. | |
269 _html_manual_events = { | |
270 'Element': ['touchleave', 'webkitTransitionEnd'], | |
271 'Window': ['DOMContentLoaded'] | |
272 } | |
273 | |
274 # These event names must be camel case when attaching event listeners | |
275 # using addEventListener even though the onEventName properties in the DOM for | |
276 # them are not camel case. | |
277 _on_attribute_to_event_name_mapping = { | |
278 'webkitanimationend': 'webkitAnimationEnd', | |
279 'webkitanimationiteration': 'webkitAnimationIteration', | |
280 'webkitanimationstart': 'webkitAnimationStart', | |
281 'webkitspeechchange': 'webkitSpeechChange', | |
282 'webkittransitionend': 'webkitTransitionEnd', | |
283 } | |
284 | |
285 # Mapping from raw event names to the pretty camelCase event names exposed as | |
286 # properties in dart:html. If the DOM exposes a new event name, you will need | |
287 # to add the lower case to camel case conversion for that event name here. | |
288 _html_event_names = { | |
289 'DOMContentLoaded': 'contentLoaded', | |
290 'touchleave': 'touchLeave', | |
291 'abort': 'abort', | |
292 'beforecopy': 'beforeCopy', | |
293 'beforecut': 'beforeCut', | |
294 'beforepaste': 'beforePaste', | |
295 'beforeunload': 'beforeUnload', | |
296 'blur': 'blur', | |
297 'cached': 'cached', | |
298 'canplay': 'canPlay', | |
299 'canplaythrough': 'canPlayThrough', | |
300 'change': 'change', | |
301 'checking': 'checking', | |
302 'click': 'click', | |
303 'close': 'close', | |
304 'contextmenu': 'contextMenu', | |
305 'copy': 'copy', | |
306 'cut': 'cut', | |
307 'dblclick': 'doubleClick', | |
308 'devicemotion': 'deviceMotion', | |
309 'deviceorientation': 'deviceOrientation', | |
310 'display': 'display', | |
311 'downloading': 'downloading', | |
312 'drag': 'drag', | |
313 'dragend': 'dragEnd', | |
314 'dragenter': 'dragEnter', | |
315 'dragleave': 'dragLeave', | |
316 'dragover': 'dragOver', | |
317 'dragstart': 'dragStart', | |
318 'drop': 'drop', | |
319 'durationchange': 'durationChange', | |
320 'emptied': 'emptied', | |
321 'ended': 'ended', | |
322 'error': 'error', | |
323 'focus': 'focus', | |
324 'hashchange': 'hashChange', | |
325 'input': 'input', | |
326 'invalid': 'invalid', | |
327 'keydown': 'keyDown', | |
328 'keypress': 'keyPress', | |
329 'keyup': 'keyUp', | |
330 'load': 'load', | |
331 'loadeddata': 'loadedData', | |
332 'loadedmetadata': 'loadedMetadata', | |
333 'loadend': 'loadEnd', | |
334 'loadstart': 'loadStart', | |
335 'message': 'message', | |
336 'mousedown': 'mouseDown', | |
337 'mousemove': 'mouseMove', | |
338 'mouseout': 'mouseOut', | |
339 'mouseover': 'mouseOver', | |
340 'mouseup': 'mouseUp', | |
341 'mousewheel': 'mouseWheel', | |
342 'noupdate': 'noUpdate', | |
343 'obsolete': 'obsolete', | |
344 'offline': 'offline', | |
345 'online': 'online', | |
346 'open': 'open', | |
347 'pagehide': 'pageHide', | |
348 'pageshow': 'pageShow', | |
349 'paste': 'paste', | |
350 'pause': 'pause', | |
351 'play': 'play', | |
352 'playing': 'playing', | |
353 'popstate': 'popState', | |
354 'progress': 'progress', | |
355 'ratechange': 'rateChange', | |
356 'readystatechange': 'readyStateChange', | |
357 'reset': 'reset', | |
358 'resize': 'resize', | |
359 'scroll': 'scroll', | |
360 'search': 'search', | |
361 'seeked': 'seeked', | |
362 'seeking': 'seeking', | |
363 'select': 'select', | |
364 'selectionchange': 'selectionChange', | |
365 'selectstart': 'selectStart', | |
366 'show': 'show', | |
367 'stalled': 'stalled', | |
368 'storage': 'storage', | |
369 'submit': 'submit', | |
370 'suspend': 'suspend', | |
371 'timeupdate': 'timeUpdate', | |
372 'touchcancel': 'touchCancel', | |
373 'touchend': 'touchEnd', | |
374 'touchmove': 'touchMove', | |
375 'touchstart': 'touchStart', | |
376 'unload': 'unload', | |
377 'updateready': 'updateReady', | |
378 'volumechange': 'volumeChange', | |
379 'waiting': 'waiting', | |
380 'webkitAnimationEnd': 'animationEnd', | |
381 'webkitAnimationIteration': 'animationIteration', | |
382 'webkitAnimationStart': 'animationStart', | |
383 'webkitfullscreenchange': 'fullscreenChange', | |
384 'webkitfullscreenerror': 'fullscreenError', | |
385 'webkitSpeechChange': 'speechChange', | |
386 'webkitTransitionEnd': 'transitionEnd' | |
387 } | |
388 | |
389 # These classes require an explicit declaration for the "on" method even though | |
390 # they don't declare any unique events, because the concrete class hierarchy | |
391 # doesn't match the interface hierarchy. | |
392 _html_explicit_event_classes = set(['DocumentFragment']) | |
393 | |
394 def _OnAttributeToEventName(on_method): | |
395 event_name = on_method.id[2:] | |
396 if event_name in _on_attribute_to_event_name_mapping: | |
397 return _on_attribute_to_event_name_mapping[event_name] | |
398 else: | |
399 return event_name | |
400 | |
401 def _DomToHtmlEvents(interface_id, events): | |
402 event_names = set(map(_OnAttributeToEventName, events)) | |
403 if interface_id in _html_manual_events: | |
404 for manual_event_name in _html_manual_events[interface_id]: | |
405 event_names.add(manual_event_name) | |
406 | |
407 return sorted(event_names, key=lambda name: _html_event_names[name]) | |
408 | |
409 # ------------------------------------------------------------------------------ | |
410 class HtmlSystemShared(object): | |
411 | |
412 def __init__(self, database, generator): | |
413 self._event_classes = set() | |
414 self._seen_event_names = {} | |
415 self._database = database | |
416 self._generator = generator | |
417 | |
418 def _AllowInHtmlLibrary(self, interface, member, member_prefix): | |
419 return not self._Matches(interface, member, member_prefix, | |
420 _html_library_remove) | |
421 | |
422 def _Matches(self, interface, member, member_prefix, candidates): | |
423 for interface_name in self._AllAncestorInterfaces(interface): | |
424 if (DartType(interface_name) + '.' + member in candidates or | |
425 DartType(interface_name) + '.' + member_prefix + member in candidates)
: | |
426 return True | |
427 return False | |
428 | |
429 def MaybeReturnDocument(self, return_type): | |
430 """ | |
431 To make it appear that there are not a distinct Document and | |
432 HTMLHtmlElement (document.documentElement) objects we always use | |
433 documentElement instead of the regular document object so must not | |
434 allow a regular document to leak out. | |
435 """ | |
436 # TODO(jacobr): any method that returns a Node could also theoretically | |
437 # really return a Document but there are alot of methods that return nodes | |
438 # and they all appear to be safe. Consider the alternate strategy of | |
439 # whitelisting just the known safe methods that return Nodes. | |
440 return (DartType(return_type) == 'EventTarget' or | |
441 DartType(return_type) == 'Document') | |
442 | |
443 def _AllAncestorInterfaces(self, interface): | |
444 interfaces = ([interface.id] + | |
445 self._generator._AllImplementedInterfaces(interface)) | |
446 return interfaces | |
447 | |
448 def RenameInHtmlLibrary(self, interface, member, member_prefix=''): | |
449 """ | |
450 Returns the name of the member in the HTML library or None if the member is | |
451 suppressed in the HTML library | |
452 """ | |
453 if not self._AllowInHtmlLibrary(interface, member, member_prefix): | |
454 return None | |
455 | |
456 for interface_name in self._AllAncestorInterfaces(interface): | |
457 name = interface_name + '.' + member | |
458 if name in _html_library_renames: | |
459 return _html_library_renames[name] | |
460 name = interface.id + '.' + member_prefix + member | |
461 if name in _html_library_renames: | |
462 return _html_library_renames[name] | |
463 | |
464 if self._PrivateInHtmlLibrary(interface, member, member_prefix): | |
465 return '_' + member | |
466 | |
467 # No rename required | |
468 return member | |
469 | |
470 def _PrivateInHtmlLibrary(self, interface, member, member_prefix): | |
471 return self._Matches(interface, member, member_prefix, | |
472 _private_html_members) | |
473 | |
474 # TODO(jacobr): this already exists | |
475 def _TraverseParents(self, interface, callback): | |
476 for parent in interface.parents: | |
477 parent_id = parent.type.id | |
478 if self._database.HasInterface(parent_id): | |
479 parent_interface = self._database.GetInterface(parent_id) | |
480 callback(parent_interface) | |
481 self._TraverseParents(parent_interface, callback) | |
482 | |
483 # TODO(jacobr): this isn't quite right.... | |
484 def GetParentsEventsClasses(self, interface): | |
485 # Ugly hack as we don't specify that Document and DocumentFragment inherit | |
486 # from Element in our IDL. | |
487 if interface.id == 'Document' or interface.id == 'DocumentFragment': | |
488 return ['ElementEvents'] | |
489 | |
490 interfaces_with_events = set() | |
491 def visit(parent): | |
492 if parent.id in self._event_classes: | |
493 interfaces_with_events.add(parent) | |
494 | |
495 self._TraverseParents(interface, visit) | |
496 if len(interfaces_with_events) == 0: | |
497 return ['Events'] | |
498 else: | |
499 names = [] | |
500 for interface in interfaces_with_events: | |
501 names.append(interface.id + 'Events') | |
502 return names | |
503 | |
504 def GetParentEventsClass(self, interface): | |
505 parent_event_classes = self.GetParentsEventsClasses(interface) | |
506 if len(parent_event_classes) != 1: | |
507 raise Exception('Only one parent event class allowed ' + interface.id) | |
508 return parent_event_classes[0] | |
509 | |
510 def _ImplClassName(self, type_name): | |
511 return '_' + type_name + 'Impl' | |
512 | |
513 # This returns two values: the first is whether or not an "on" property should | |
514 # be generated for the interface, and the second is the event attributes to | |
515 # generate if it should. | |
516 def GetEventAttributes(self, interface): | |
517 events = set([attr for attr in interface.attributes | |
518 if self._generator._IsEventAttribute(interface, attr)]) | |
519 | |
520 if events or interface.id in _html_explicit_event_classes: | |
521 return True, events | |
522 else: | |
523 return False, None | |
524 | |
525 def IsPrivate(self, name): | |
526 return name.startswith('_') | |
527 | |
528 class HtmlSystem(System): | |
529 | |
530 def __init__(self, templates, database, emitters, output_dir, generator): | |
531 super(HtmlSystem, self).__init__( | |
532 templates, database, emitters, output_dir) | |
533 self._shared = HtmlSystemShared(database, generator) | |
534 | |
535 class HtmlInterfacesSystem(HtmlSystem): | |
536 | |
537 def __init__(self, templates, database, emitters, output_dir, generator): | |
538 super(HtmlInterfacesSystem, self).__init__( | |
539 templates, database, emitters, output_dir, generator) | |
540 self._dart_interface_file_paths = [] | |
541 | |
542 def InterfaceGenerator(self, | |
543 interface, | |
544 common_prefix, | |
545 super_interface_name, | |
546 source_filter): | |
547 """.""" | |
548 interface_name = interface.id | |
549 dart_interface_file_path = self._FilePathForDartInterface(interface_name) | |
550 | |
551 self._dart_interface_file_paths.append(dart_interface_file_path) | |
552 | |
553 dart_interface_code = self._emitters.FileEmitter(dart_interface_file_path) | |
554 | |
555 template_file = 'interface_%s.darttemplate' % interface_name | |
556 template = self._templates.TryLoad(template_file) | |
557 if not template: | |
558 template = self._templates.Load('interface.darttemplate') | |
559 | |
560 return HtmlDartInterfaceGenerator( | |
561 interface, dart_interface_code, | |
562 template, | |
563 common_prefix, super_interface_name, | |
564 source_filter, self, self._shared) | |
565 | |
566 def ProcessCallback(self, interface, info): | |
567 """Generates a typedef for the callback interface.""" | |
568 interface_name = interface.id | |
569 file_path = self._FilePathForDartInterface(interface_name) | |
570 self._ProcessCallback(interface, info, file_path) | |
571 | |
572 def GenerateLibraries(self, lib_dir): | |
573 pass | |
574 | |
575 | |
576 def _FilePathForDartInterface(self, interface_name): | |
577 """Returns the file path of the Dart interface definition.""" | |
578 # TODO(jmesserly): is this the right path | |
579 return os.path.join(self._output_dir, 'html', 'interface', | |
580 '%s.dart' % interface_name) | |
581 | |
582 # ------------------------------------------------------------------------------ | |
583 | |
584 # TODO(jmesserly): inheritance is probably not the right way to factor this long | |
585 # term, but it makes merging better for now. | |
586 class HtmlDartInterfaceGenerator(DartInterfaceGenerator): | |
587 """Generates Dart Interface definition for one DOM IDL interface.""" | |
588 | |
589 def __init__(self, interface, emitter, template, | |
590 common_prefix, super_interface, source_filter, system, shared): | |
591 super(HtmlDartInterfaceGenerator, self).__init__(interface, | |
592 emitter, template, common_prefix, super_interface, source_filter) | |
593 self._system = system | |
594 self._shared = shared | |
595 | |
596 def StartInterface(self): | |
597 typename = self._interface.id | |
598 | |
599 extends = [] | |
600 suppressed_extends = [] | |
601 | |
602 for parent in self._interface.parents: | |
603 # TODO(vsm): Remove source_filter. | |
604 if MatchSourceFilter(self._source_filter, parent): | |
605 # Parent is a DOM type. | |
606 extends.append(DartType(parent.type.id)) | |
607 elif '<' in parent.type.id: | |
608 # Parent is a Dart collection type. | |
609 # TODO(vsm): Make this check more robust. | |
610 extends.append(DartType(parent.type.id)) | |
611 else: | |
612 suppressed_extends.append('%s.%s' % | |
613 (self._common_prefix, DartType(parent.type.id))) | |
614 | |
615 comment = ' extends' | |
616 extends_str = '' | |
617 if extends: | |
618 extends_str += ' extends ' + ', '.join(extends) | |
619 comment = ',' | |
620 if suppressed_extends: | |
621 extends_str += ' /*%s %s */' % (comment, ', '.join(suppressed_extends)) | |
622 | |
623 factory_provider = None | |
624 constructor_info = AnalyzeConstructor(self._interface) | |
625 if constructor_info: | |
626 factory_provider = '_' + typename + 'FactoryProvider'; | |
627 | |
628 if typename in interface_factories: | |
629 factory_provider = interface_factories[typename] | |
630 | |
631 if factory_provider: | |
632 extends_str += ' default ' + factory_provider | |
633 | |
634 # TODO(vsm): Add appropriate package / namespace syntax. | |
635 (self._members_emitter, | |
636 self._top_level_emitter) = self._emitter.Emit( | |
637 self._template + '$!TOP_LEVEL', | |
638 ID=typename, | |
639 EXTENDS=extends_str) | |
640 | |
641 if constructor_info: | |
642 self._members_emitter.Emit( | |
643 '\n' | |
644 ' $CTOR($PARAMS);\n', | |
645 CTOR=typename, | |
646 PARAMS=constructor_info.ParametersInterfaceDeclaration()); | |
647 | |
648 element_type = MaybeTypedArrayElementType(self._interface) | |
649 if element_type: | |
650 self._members_emitter.Emit( | |
651 '\n' | |
652 ' $CTOR(int length);\n' | |
653 '\n' | |
654 ' $CTOR.fromList(List<$TYPE> list);\n' | |
655 '\n' | |
656 ' $CTOR.fromBuffer(ArrayBuffer buffer);\n', | |
657 CTOR=self._interface.id, | |
658 TYPE=DartType(element_type)) | |
659 | |
660 emit_events, events = self._shared.GetEventAttributes(self._interface) | |
661 if not emit_events: | |
662 return | |
663 elif events: | |
664 self.AddEventAttributes(events) | |
665 else: | |
666 self._EmitEventGetter(self._shared.GetParentEventsClass(self._interface)) | |
667 | |
668 def AddAttribute(self, getter, setter): | |
669 html_getter_name = self._shared.RenameInHtmlLibrary( | |
670 self._interface, DartDomNameOfAttribute(getter), 'get:') | |
671 html_setter_name = self._shared.RenameInHtmlLibrary( | |
672 self._interface, DartDomNameOfAttribute(getter), 'set:') | |
673 | |
674 if not html_getter_name or self._shared.IsPrivate(html_getter_name): | |
675 getter = None | |
676 if not html_setter_name or self._shared.IsPrivate(html_setter_name): | |
677 setter = None | |
678 if not getter and not setter: | |
679 return | |
680 | |
681 # We don't yet handle inconsistent renames of the getter and setter yet. | |
682 if html_getter_name and html_setter_name: | |
683 assert html_getter_name == html_setter_name | |
684 if (getter and setter and | |
685 DartType(getter.type.id) == DartType(setter.type.id)): | |
686 self._members_emitter.Emit('\n $TYPE $NAME;\n', | |
687 NAME=html_getter_name, | |
688 TYPE=DartType(getter.type.id)); | |
689 return | |
690 if getter and not setter: | |
691 self._members_emitter.Emit('\n final $TYPE $NAME;\n', | |
692 NAME=html_getter_name, | |
693 TYPE=DartType(getter.type.id)); | |
694 return | |
695 raise Exception('Unexpected getter/setter combination %s %s' % | |
696 (getter, setter)) | |
697 | |
698 def AddOperation(self, info): | |
699 """ | |
700 Arguments: | |
701 operations - contains the overloads, one or more operations with the same | |
702 name. | |
703 """ | |
704 html_name = self._shared.RenameInHtmlLibrary(self._interface, info.name) | |
705 if html_name and not self._shared.IsPrivate(html_name): | |
706 self._members_emitter.Emit('\n' | |
707 ' $TYPE $NAME($PARAMS);\n', | |
708 TYPE=info.type_name, | |
709 NAME=html_name, | |
710 PARAMS=info.ParametersInterfaceDeclaration()) | |
711 | |
712 def FinishInterface(self): | |
713 pass | |
714 | |
715 def AddConstant(self, constant): | |
716 self._EmitConstant(self._members_emitter, constant) | |
717 | |
718 def AddEventAttributes(self, event_attrs): | |
719 event_attrs = _DomToHtmlEvents(self._interface.id, event_attrs) | |
720 self._shared._event_classes.add(self._interface.id) | |
721 events_interface = self._interface.id + 'Events' | |
722 self._EmitEventGetter(events_interface) | |
723 | |
724 events_members = self._emitter.Emit( | |
725 '\ninterface $INTERFACE extends $PARENTS {\n$!MEMBERS}\n', | |
726 INTERFACE=events_interface, | |
727 PARENTS=', '.join( | |
728 self._shared.GetParentsEventsClasses(self._interface))) | |
729 | |
730 for event_name in event_attrs: | |
731 if event_name in _html_event_names: | |
732 events_members.Emit('\n EventListenerList get $NAME();\n', | |
733 NAME=_html_event_names[event_name]) | |
734 else: | |
735 raise Exception('No known html even name for event: ' + event_name) | |
736 | |
737 def _EmitEventGetter(self, events_interface): | |
738 self._members_emitter.Emit('\n $TYPE get on();\n', | |
739 TYPE=events_interface) | |
740 | |
741 # ------------------------------------------------------------------------------ | |
742 | |
743 # TODO(jmesserly): inheritance is probably not the right way to factor this long | |
744 # term, but it makes merging better for now. | |
745 class HtmlFrogClassGenerator(FrogInterfaceGenerator): | |
746 """Generates a Frog class for the dart:html library from a DOM IDL | |
747 interface. | |
748 """ | |
749 | |
750 def __init__(self, system, interface, template, super_interface, dart_code, | |
751 shared): | |
752 super(HtmlFrogClassGenerator, self).__init__( | |
753 system, interface, template, super_interface, dart_code) | |
754 self._shared = shared | |
755 | |
756 def _ImplClassName(self, type_name): | |
757 return self._shared._ImplClassName(type_name) | |
758 | |
759 def StartInterface(self): | |
760 interface = self._interface | |
761 interface_name = interface.id | |
762 | |
763 self._class_name = self._ImplClassName(interface_name) | |
764 | |
765 base = None | |
766 if interface.parents: | |
767 supertype = interface.parents[0].type.id | |
768 if IsDartCollectionType(supertype): | |
769 # List methods are injected in AddIndexer. | |
770 pass | |
771 else: | |
772 base = self._ImplClassName(supertype) | |
773 | |
774 native_spec = MakeNativeSpec(interface.javascript_binding_name) | |
775 | |
776 extends = ' extends ' + base if base else '' | |
777 | |
778 # TODO: Include all implemented interfaces, including other Lists. | |
779 implements = [interface_name] | |
780 element_type = MaybeTypedArrayElementType(self._interface) | |
781 if element_type: | |
782 implements.append('List<%s>' % DartType(element_type)) | |
783 | |
784 self._members_emitter = self._dart_code.Emit( | |
785 self._template, | |
786 #class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { | |
787 #$!MEMBERS | |
788 #} | |
789 CLASSNAME=self._class_name, | |
790 EXTENDS=extends, | |
791 IMPLEMENTS=' implements ' + ', '.join(implements), | |
792 NATIVESPEC=' native "' + native_spec + '"') | |
793 | |
794 if element_type: | |
795 self.AddTypedArrayConstructors(element_type) | |
796 | |
797 # Emit a factory provider class for the constructor. | |
798 constructor_info = AnalyzeConstructor(interface) | |
799 if constructor_info: | |
800 self._EmitFactoryProvider(interface_name, constructor_info) | |
801 | |
802 emit_events, events = self._shared.GetEventAttributes(self._interface) | |
803 if not emit_events: | |
804 return | |
805 elif events: | |
806 self.AddEventAttributes(events) | |
807 else: | |
808 parent_events_class = self._shared.GetParentEventsClass(self._interface) | |
809 self._EmitEventGetter('_' + parent_events_class + 'Impl') | |
810 | |
811 def _EmitFactoryProvider(self, interface_name, constructor_info): | |
812 template_file = 'factoryprovider_%s.darttemplate' % interface_name | |
813 template = self._system._templates.TryLoad(template_file) | |
814 if not template: | |
815 template = self._system._templates.Load('factoryprovider.darttemplate') | |
816 | |
817 factory_provider = '_' + interface_name + 'FactoryProvider' | |
818 emitter = self._system._ImplFileEmitter(factory_provider) | |
819 emitter.Emit( | |
820 template, | |
821 FACTORYPROVIDER=factory_provider, | |
822 CONSTRUCTOR=interface_name, | |
823 PARAMETERS=constructor_info.ParametersImplementationDeclaration(), | |
824 NAMED_CONSTRUCTOR=constructor_info.name or interface_name, | |
825 ARGUMENTS=constructor_info.ParametersAsArgumentList()) | |
826 | |
827 def AddIndexer(self, element_type): | |
828 """Adds all the methods required to complete implementation of List.""" | |
829 # We would like to simply inherit the implementation of everything except | |
830 # get length(), [], and maybe []=. It is possible to extend from a base | |
831 # array implementation class only when there is no other implementation | |
832 # inheritance. There might be no implementation inheritance other than | |
833 # DOMBaseWrapper for many classes, but there might be some where the | |
834 # array-ness is introduced by a non-root interface: | |
835 # | |
836 # interface Y extends X, List<T> ... | |
837 # | |
838 # In the non-root case we have to choose between: | |
839 # | |
840 # class YImpl extends XImpl { add List<T> methods; } | |
841 # | |
842 # and | |
843 # | |
844 # class YImpl extends ListBase<T> { copies of transitive XImpl methods; } | |
845 # | |
846 self._members_emitter.Emit( | |
847 '\n' | |
848 ' $TYPE operator[](int index) native "return this[index];";\n', | |
849 TYPE=self._NarrowOutputType(element_type)) | |
850 | |
851 if 'CustomIndexedSetter' in self._interface.ext_attrs: | |
852 self._members_emitter.Emit( | |
853 '\n' | |
854 ' void operator[]=(int index, $TYPE value) native "this[index] = valu
e";\n', | |
855 TYPE=self._NarrowInputType(element_type)) | |
856 else: | |
857 # The HTML library implementation of NodeList has a custom indexed setter | |
858 # implementation that uses the parent node the NodeList is associated | |
859 # with if one is available. | |
860 if self._interface.id != 'NodeList': | |
861 self._members_emitter.Emit( | |
862 '\n' | |
863 ' void operator[]=(int index, $TYPE value) {\n' | |
864 ' throw new UnsupportedOperationException("Cannot assign element
of immutable List.");\n' | |
865 ' }\n', | |
866 TYPE=self._NarrowInputType(element_type)) | |
867 | |
868 # TODO(sra): Use separate mixins for mutable implementations of List<T>. | |
869 # TODO(sra): Use separate mixins for typed array implementations of List<T>. | |
870 if self._interface.id != 'NodeList': | |
871 template_file = 'immutable_list_mixin.darttemplate' | |
872 template = self._system._templates.Load(template_file) | |
873 self._members_emitter.Emit(template, E=DartType(element_type)) | |
874 | |
875 def AddAttribute(self, getter, setter): | |
876 | |
877 html_getter_name = self._shared.RenameInHtmlLibrary( | |
878 self._interface, DartDomNameOfAttribute(getter), 'get:') | |
879 html_setter_name = self._shared.RenameInHtmlLibrary( | |
880 self._interface, DartDomNameOfAttribute(getter), 'set:') | |
881 | |
882 if not html_getter_name: | |
883 getter = None | |
884 if not html_setter_name: | |
885 setter = None | |
886 | |
887 if not getter and not setter: | |
888 return | |
889 | |
890 if ((getter and (html_getter_name != getter.id or | |
891 self._shared.MaybeReturnDocument(getter.type.id))) or | |
892 (setter and (html_setter_name != setter.id or | |
893 self._shared.MaybeReturnDocument(setter.type.id))) or | |
894 self._interface.id == 'Document'): | |
895 if getter: | |
896 self._AddRenamingGetter(getter, html_getter_name) | |
897 if setter: | |
898 self._AddRenamingSetter(setter, html_setter_name) | |
899 return | |
900 | |
901 # If the (getter, setter) pair is shadowing, we can't generate a shadowing | |
902 # field (Issue 1633). | |
903 (super_getter, super_getter_interface) = self._FindShadowedAttribute(getter) | |
904 (super_setter, super_setter_interface) = self._FindShadowedAttribute(setter) | |
905 if super_getter or super_setter: | |
906 if getter and not setter and super_getter and not super_setter: | |
907 if DartType(getter.type.id) == DartType(super_getter.type.id): | |
908 # Compatible getter, use the superclass property. This works because | |
909 # JavaScript will do its own dynamic dispatch. | |
910 output_type = getter and self._NarrowOutputType(getter.type.id) | |
911 self._members_emitter.Emit( | |
912 '\n' | |
913 ' // Use implementation from $SUPER.\n' | |
914 ' // final $TYPE $NAME;\n', | |
915 SUPER=super_getter_interface.id, | |
916 NAME=DartDomNameOfAttribute(getter), | |
917 TYPE=output_type) | |
918 return | |
919 | |
920 self._members_emitter.Emit('\n // Shadowing definition.') | |
921 self._AddAttributeUsingProperties(getter, setter) | |
922 return | |
923 | |
924 output_type = getter and self._NarrowOutputType(getter.type.id) | |
925 input_type = setter and self._NarrowInputType(setter.type.id) | |
926 if getter and setter and input_type == output_type: | |
927 self._members_emitter.Emit( | |
928 '\n $TYPE $NAME;\n', | |
929 NAME=DartDomNameOfAttribute(getter), | |
930 TYPE=output_type) | |
931 return | |
932 if getter and not setter: | |
933 self._members_emitter.Emit( | |
934 '\n final $TYPE $NAME;\n', | |
935 NAME=DartDomNameOfAttribute(getter), | |
936 TYPE=output_type) | |
937 return | |
938 self._AddAttributeUsingProperties(getter, setter) | |
939 | |
940 def _AddAttributeUsingProperties(self, getter, setter): | |
941 if getter: | |
942 self._AddGetter(getter) | |
943 if setter: | |
944 self._AddSetter(setter) | |
945 | |
946 def _AddGetter(self, attr): | |
947 self._AddRenamingGetter(attr, DartDomNameOfAttribute(attr)) | |
948 | |
949 def _AddSetter(self, attr): | |
950 self._AddRenamingSetter(attr, DartDomNameOfAttribute(attr)) | |
951 | |
952 def _AddRenamingGetter(self, attr, html_name): | |
953 return_type = self._NarrowOutputType(attr.type.id) | |
954 if self._shared.MaybeReturnDocument(attr.type.id): | |
955 self._members_emitter.Emit( | |
956 '\n $TYPE get $(HTML_NAME)() => ' | |
957 '_FixHtmlDocumentReference(_$(HTML_NAME));\n', | |
958 HTML_NAME=html_name, | |
959 TYPE=return_type) | |
960 html_name = '_' + html_name | |
961 # For correctness this needs to be the return type of the native helper | |
962 # method due to the fact that the real HTMLDocument object is not typed | |
963 # as a document. TODO(jacobr): we could simplify this. | |
964 return_type = '_EventTargetImpl' | |
965 | |
966 self._members_emitter.Emit( | |
967 '\n $TYPE get $(HTML_NAME)() native "return $(THIS).$NAME;";\n', | |
968 HTML_NAME=html_name, | |
969 NAME=attr.id, | |
970 TYPE=return_type, | |
971 THIS='this.parentNode' if self._interface.id == 'Document' else 'this') | |
972 | |
973 def _AddRenamingSetter(self, attr, html_name): | |
974 self._members_emitter.Emit( | |
975 '\n void set $HTML_NAME($TYPE value)' | |
976 ' native "$(THIS).$NAME = value;";\n', | |
977 HTML_NAME=html_name, | |
978 NAME=attr.id, | |
979 TYPE=self._NarrowInputType(attr.type.id), | |
980 THIS='this.parentNode' if self._interface.id == 'Document' else 'this') | |
981 | |
982 def AddOperation(self, info): | |
983 """ | |
984 Arguments: | |
985 info: An OperationInfo object. | |
986 """ | |
987 html_name = self._shared.RenameInHtmlLibrary(self._interface, info.name) | |
988 if not html_name: | |
989 return | |
990 | |
991 maybe_return_document = self._shared.MaybeReturnDocument(info.type_name) | |
992 | |
993 # Do we need a native body? | |
994 if (self._interface.id == 'Document' or # Need alternate 'this' | |
995 html_name != info.name or # renamed operation | |
996 maybe_return_document): # need to wrap value | |
997 # For example: use window.document instead of his.parentNode. | |
998 return_type = self._NarrowOutputType(info.type_name) | |
999 | |
1000 operation_emitter = self._members_emitter.Emit('$!SCOPE', | |
1001 THIS=('this.parentNode' if self._interface.id == 'Document' | |
1002 else 'this'), | |
1003 TYPE=return_type, | |
1004 HTML_NAME=html_name, | |
1005 NAME=info.name, | |
1006 RETURN='' if return_type == 'void' else 'return ', | |
1007 PARAMNAMES=info.ParametersAsArgumentList(), | |
1008 PARAMS=info.ParametersImplementationDeclaration( | |
1009 lambda type_name: self._NarrowInputType(type_name))) | |
1010 | |
1011 if maybe_return_document: | |
1012 assert len(info.overloads) == 1 | |
1013 operation_emitter.Emit( | |
1014 '\n' | |
1015 ' $TYPE $(HTML_NAME)($PARAMS) => ' | |
1016 '_FixHtmlDocumentReference(_$(HTML_NAME)($PARAMNAMES));\n' | |
1017 '\n' | |
1018 ' _EventTargetImpl _$(HTML_NAME)($PARAMS)' | |
1019 ' native "return $(THIS).$NAME($PARAMNAMES);";\n') | |
1020 else: | |
1021 operation_emitter.Emit( | |
1022 '\n' | |
1023 ' $TYPE $(HTML_NAME)($PARAMS)' | |
1024 ' native "$(RETURN)$(THIS).$NAME($PARAMNAMES);";\n') | |
1025 else: | |
1026 self._members_emitter.Emit( | |
1027 '\n' | |
1028 ' $TYPE $NAME($PARAMS) native;\n', | |
1029 TYPE=self._NarrowOutputType(info.type_name), | |
1030 NAME=info.name, | |
1031 PARAMS=info.ParametersImplementationDeclaration( | |
1032 lambda type_name: self._NarrowInputType(type_name))) | |
1033 | |
1034 def AddEventAttributes(self, event_attrs): | |
1035 event_attrs = _DomToHtmlEvents(self._interface.id, event_attrs) | |
1036 events_class = '_' + self._interface.id + 'EventsImpl' | |
1037 events_interface = self._interface.id + 'Events' | |
1038 self._EmitEventGetter(events_class) | |
1039 | |
1040 self._shared._event_classes.add(self._interface.id) | |
1041 | |
1042 parent_event_class = self._shared.GetParentEventsClass(self._interface) | |
1043 | |
1044 # TODO(jacobr): specify the type of _ptr as EventTarget | |
1045 events_members = self._dart_code.Emit( | |
1046 '\n' | |
1047 'class $CLASSNAME extends $SUPER implements $INTERFACE {\n' | |
1048 ' $CLASSNAME(_ptr) : super(_ptr);\n' | |
1049 '$!MEMBERS}\n', | |
1050 CLASSNAME=events_class, | |
1051 INTERFACE=events_interface, | |
1052 SUPER='_' + parent_event_class + 'Impl') | |
1053 | |
1054 for event_name in event_attrs: | |
1055 if event_name in _html_event_names: | |
1056 events_members.Emit( | |
1057 "\n" | |
1058 " EventListenerList get $NAME() => _get('$RAWNAME');\n", | |
1059 RAWNAME=event_name, | |
1060 NAME=_html_event_names[event_name]) | |
1061 else: | |
1062 raise Exception('No known html even name for event: ' + event_name) | |
1063 | |
1064 def _EmitEventGetter(self, events_class): | |
1065 self._members_emitter.Emit( | |
1066 '\n $TYPE get on() =>\n new $TYPE($EVENTTARGET);\n', | |
1067 TYPE=events_class, | |
1068 EVENTTARGET='_jsDocument' if self._interface.id == 'Document' | |
1069 else 'this') | |
1070 | |
1071 # ------------------------------------------------------------------------------ | |
1072 | |
1073 class HtmlFrogSystem(HtmlSystem): | |
1074 | |
1075 def __init__(self, templates, database, emitters, output_dir, generator): | |
1076 super(HtmlFrogSystem, self).__init__( | |
1077 templates, database, emitters, output_dir, generator) | |
1078 self._dart_frog_file_paths = [] | |
1079 | |
1080 | |
1081 def InterfaceGenerator(self, | |
1082 interface, | |
1083 common_prefix, | |
1084 super_interface_name, | |
1085 source_filter): | |
1086 """.""" | |
1087 template_file = 'impl_%s.darttemplate' % interface.id | |
1088 template = self._templates.TryLoad(template_file) | |
1089 if not template: | |
1090 template = self._templates.Load('frog_impl.darttemplate') | |
1091 | |
1092 dart_code = self._ImplFileEmitter(interface.id) | |
1093 return HtmlFrogClassGenerator(self, interface, template, | |
1094 super_interface_name, dart_code, self._shared) | |
1095 | |
1096 def GenerateLibraries(self, lib_dir): | |
1097 self._GenerateLibFile( | |
1098 'html_frog.darttemplate', | |
1099 os.path.join(lib_dir, 'html_frog.dart'), | |
1100 (self._interface_system._dart_interface_file_paths + | |
1101 self._interface_system._dart_callback_file_paths + | |
1102 self._dart_frog_file_paths)) | |
1103 | |
1104 def Finish(self): | |
1105 pass | |
1106 | |
1107 def _ImplFileEmitter(self, name): | |
1108 """Returns the file emitter of the Frog implementation file.""" | |
1109 # TODO(jmesserly): is this the right path | |
1110 path = os.path.join(self._output_dir, 'html', 'frog', '%s.dart' % name) | |
1111 self._dart_frog_file_paths.append(path) | |
1112 return self._emitters.FileEmitter(path) | |
1113 | |
1114 # ----------------------------------------------------------------------------- | |
1115 | |
1116 class HtmlDartiumSystem(HtmlSystem): | |
1117 | |
1118 def __init__(self, templates, database, emitters, output_dir, generator): | |
1119 """Prepared for generating wrapping implementation. | |
1120 | |
1121 - Creates emitter for Dart code. | |
1122 """ | |
1123 super(HtmlDartiumSystem, self).__init__( | |
1124 templates, database, emitters, output_dir, generator) | |
1125 self._shared = HtmlSystemShared(database, generator) | |
1126 self._dart_dartium_file_paths = [] | |
1127 self._wrap_cases = [] | |
1128 | |
1129 def InterfaceGenerator(self, | |
1130 interface, | |
1131 common_prefix, | |
1132 super_interface_name, | |
1133 source_filter): | |
1134 """.""" | |
1135 template_file = 'impl_%s.darttemplate' % interface.id | |
1136 template = self._templates.TryLoad(template_file) | |
1137 # TODO(jacobr): change this name as it is confusing. | |
1138 if not template: | |
1139 template = self._templates.Load('frog_impl.darttemplate') | |
1140 | |
1141 dart_code = self._ImplFileEmitter(interface.id) | |
1142 return HtmlDartiumInterfaceGenerator(self, interface, template, | |
1143 super_interface_name, dart_code, self._BaseDefines(interface), | |
1144 self._shared) | |
1145 | |
1146 def _ImplFileEmitter(self, name): | |
1147 """Returns the file emitter of the Dartium implementation file.""" | |
1148 path = os.path.join(self._output_dir, 'html', 'dartium', '%s.dart' % name) | |
1149 self._dart_dartium_file_paths.append(path) | |
1150 return self._emitters.FileEmitter(path); | |
1151 | |
1152 def ProcessCallback(self, interface, info): | |
1153 pass | |
1154 | |
1155 def GenerateLibraries(self, lib_dir): | |
1156 # Library generated for implementation. | |
1157 self._GenerateLibFile( | |
1158 'html_dartium.darttemplate', | |
1159 os.path.join(lib_dir, 'html_dartium.dart'), | |
1160 (self._interface_system._dart_interface_file_paths + | |
1161 self._interface_system._dart_callback_file_paths + | |
1162 self._dart_dartium_file_paths | |
1163 ), | |
1164 WRAPCASES='\n'.join(self._wrap_cases)) | |
1165 | |
1166 def Finish(self): | |
1167 pass | |
1168 | |
1169 # ------------------------------------------------------------------------------ | |
1170 | |
1171 # TODO(jacobr): there is far too much duplicated code between these bindings | |
1172 # and the Frog bindings. A larger scale refactoring needs to be performed to | |
1173 # reduce the duplicated logic. | |
1174 class HtmlDartiumInterfaceGenerator(object): | |
1175 """Generates a wrapper based implementation fo the HTML library that works | |
1176 on Dartium. This is not intended to be the final solution for implementing | |
1177 dart:html on Dartium. Eventually we should generate direct wrapperless | |
1178 dart:html bindings that work on dartium.""" | |
1179 | |
1180 def __init__(self, system, interface, template, super_interface, dart_code, | |
1181 base_members, shared): | |
1182 """Generates Dart wrapper code for the given interface. | |
1183 | |
1184 Args: | |
1185 system: system that is executing this generator. | |
1186 template: template that output is generated into. | |
1187 interface: an IDLInterface instance. It is assumed that all types have | |
1188 been converted to Dart types (e.g. int, String), unless they are in | |
1189 the same package as the interface. | |
1190 super_interface: A string or None, the name of the common interface that | |
1191 this interface implements, if any. | |
1192 dart_code: an Emitter for the file containing the Dart implementation | |
1193 class. | |
1194 base_members: a set of names of members defined in a base class. This is | |
1195 used to avoid static member 'overriding' in the generated Dart code. | |
1196 shared: functionaly shared across all Html generators. | |
1197 """ | |
1198 self._system = system | |
1199 self._interface = interface | |
1200 self._super_interface = super_interface | |
1201 self._dart_code = dart_code | |
1202 self._base_members = base_members | |
1203 self._current_secondary_parent = None | |
1204 self._shared = shared | |
1205 self._template = template | |
1206 | |
1207 def DomObjectName(self): | |
1208 return '_documentPtr' if self._interface.id == 'Document' else '_ptr' | |
1209 | |
1210 # TODO(jacobr): these 3 methods are duplicated. | |
1211 def _NarrowToImplementationType(self, type_name): | |
1212 # TODO(sra): Move into the 'system' and cache the result. | |
1213 if type_name == 'EventListener': | |
1214 # Callbacks are typedef functions so don't have a class. | |
1215 return type_name | |
1216 if self._system._database.HasInterface(type_name): | |
1217 interface = self._system._database.GetInterface(type_name) | |
1218 if RecognizeCallback(interface): | |
1219 # Callbacks are typedef functions so don't have a class. | |
1220 return type_name | |
1221 else: | |
1222 return self._ImplClassName(type_name) | |
1223 return type_name | |
1224 | |
1225 def _NarrowInputType(self, type_name): | |
1226 return self._NarrowToImplementationType(type_name) | |
1227 | |
1228 def _NarrowOutputType(self, type_name): | |
1229 return self._NarrowToImplementationType(type_name) | |
1230 | |
1231 def StartInterface(self): | |
1232 | |
1233 interface = self._interface | |
1234 interface_name = interface.id | |
1235 self._class_name = self._ImplClassName(interface_name) | |
1236 | |
1237 base = None | |
1238 if interface.parents: | |
1239 supertype = interface.parents[0].type.id | |
1240 if not IsDartListType(supertype): | |
1241 base = self._ImplClassName(supertype) | |
1242 if IsDartCollectionType(supertype): | |
1243 # List methods are injected in AddIndexer. | |
1244 pass | |
1245 else: | |
1246 base = self._ImplClassName(supertype) | |
1247 | |
1248 # TODO(jacobr): this is fragile. There isn't a guarantee that dart:dom | |
1249 # will continue to exactly match the IDL names. | |
1250 dom_name = interface.javascript_binding_name | |
1251 # We hard code the cases for these classes | |
1252 if dom_name != 'HTMLHtmlElement' and dom_name != 'Document': | |
1253 self._system._wrap_cases.append( | |
1254 ' case "%s": return new %s._wrap(domObject);' % | |
1255 (dom_name, self._class_name)) | |
1256 | |
1257 extends = ' extends ' + base if base else ' extends _DOMTypeBase' | |
1258 | |
1259 # TODO: Include all implemented interfaces, including other Lists. | |
1260 implements = [interface_name] | |
1261 element_type = MaybeTypedArrayElementType(self._interface) | |
1262 if element_type: | |
1263 implements.append('List<' + DartType(element_type) + '>') | |
1264 implements_str = ', '.join(implements) | |
1265 | |
1266 (self._members_emitter, | |
1267 self._top_level_emitter) = self._dart_code.Emit( | |
1268 self._template + '$!TOP_LEVEL', | |
1269 #class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { | |
1270 #$!MEMBERS | |
1271 #} | |
1272 NATIVESPEC='', # hack to make reusing the same templates work. | |
1273 CLASSNAME=self._class_name, | |
1274 EXTENDS=extends, | |
1275 IMPLEMENTS=' implements ' + implements_str) | |
1276 | |
1277 # Document requires a custom wrapper. | |
1278 if dom_name != 'Document': | |
1279 self._members_emitter.Emit( | |
1280 ' $(CLASSNAME)._wrap(ptr) : super._wrap(ptr);\n', | |
1281 CLASSNAME=self._class_name) | |
1282 | |
1283 # Emit a factory provider class for the constructor. | |
1284 constructor_info = AnalyzeConstructor(interface) | |
1285 if constructor_info: | |
1286 self._EmitFactoryProvider(interface_name, constructor_info) | |
1287 | |
1288 emit_events, events = self._shared.GetEventAttributes(self._interface) | |
1289 if not emit_events: | |
1290 return | |
1291 elif events: | |
1292 self.AddEventAttributes(events) | |
1293 else: | |
1294 parent_events_class = self._shared.GetParentEventsClass(self._interface) | |
1295 self._EmitEventGetter('_' + parent_events_class + 'Impl') | |
1296 | |
1297 def _EmitFactoryProvider(self, interface_name, constructor_info): | |
1298 template_file = 'factoryprovider_%s.darttemplate' % interface_name | |
1299 template = self._system._templates.TryLoad(template_file) | |
1300 if not template: | |
1301 template = self._system._templates.Load('factoryprovider.darttemplate') | |
1302 | |
1303 factory_provider = '_' + interface_name + 'FactoryProvider' | |
1304 emitter = self._system._ImplFileEmitter(factory_provider) | |
1305 emitter.Emit( | |
1306 template, | |
1307 FACTORYPROVIDER=factory_provider, | |
1308 CONSTRUCTOR=interface_name, | |
1309 PARAMETERS=constructor_info.ParametersImplementationDeclaration(), | |
1310 NAMED_CONSTRUCTOR=constructor_info.name or interface_name, | |
1311 ARGUMENTS=self._UnwrappedParameters(constructor_info, | |
1312 len(constructor_info.arg_infos))) | |
1313 | |
1314 def _UnwrappedParameters(self, operation_info, length): | |
1315 """Returns string for an argument list that unwraps first |length| | |
1316 parameters.""" | |
1317 def UnwrapArgInfo(arg_info): | |
1318 (name, type, value) = arg_info | |
1319 # TODO(sra): Type dependent unwrapping. | |
1320 return '_unwrap(%s)' % name | |
1321 | |
1322 return ', '.join(map(UnwrapArgInfo, operation_info.arg_infos[:length])) | |
1323 | |
1324 def _BaseClassName(self, interface): | |
1325 if not interface.parents: | |
1326 return '_DOMTypeBase' | |
1327 | |
1328 supertype = DartType(interface.parents[0].type.id) | |
1329 | |
1330 if IsDartListType(supertype) or IsDartCollectionType(supertype): | |
1331 return 'DOMWrapperBase' | |
1332 | |
1333 if supertype == 'EventTarget': | |
1334 # Most implementors of EventTarget specify the EventListener operations | |
1335 # again. If the operations are not specified, try to inherit from the | |
1336 # EventTarget implementation. | |
1337 # | |
1338 # Applies to MessagePort. | |
1339 if not [op for op in interface.operations if op.id == 'addEventListener']: | |
1340 return self._ImplClassName(supertype) | |
1341 return 'DOMWrapperBase' | |
1342 | |
1343 return self._ImplClassName(supertype) | |
1344 | |
1345 def _ImplClassName(self, type_name): | |
1346 return self._shared._ImplClassName(type_name) | |
1347 | |
1348 def FinishInterface(self): | |
1349 """.""" | |
1350 pass | |
1351 | |
1352 def AddConstant(self, constant): | |
1353 # Constants are already defined on the interface. | |
1354 pass | |
1355 | |
1356 def _MethodName(self, prefix, name): | |
1357 method_name = prefix + name | |
1358 if name in self._base_members: # Avoid illegal Dart 'static override'. | |
1359 method_name = method_name + '_' + self._interface.id | |
1360 return method_name | |
1361 | |
1362 def AddAttribute(self, getter, setter): | |
1363 dom_name = DartDomNameOfAttribute(getter or setter) | |
1364 html_getter_name = self._shared.RenameInHtmlLibrary( | |
1365 self._interface, dom_name, 'get:') | |
1366 html_setter_name = self._shared.RenameInHtmlLibrary( | |
1367 self._interface, dom_name, 'set:') | |
1368 | |
1369 if getter and html_getter_name: | |
1370 self._AddGetter(getter, html_getter_name) | |
1371 if setter and html_setter_name: | |
1372 self._AddSetter(setter, html_setter_name) | |
1373 | |
1374 def _AddGetter(self, attr, html_name): | |
1375 if self._shared.MaybeReturnDocument(attr.type.id): | |
1376 self._members_emitter.Emit( | |
1377 '\n' | |
1378 ' $TYPE get $(HTML_NAME)() => ' | |
1379 '_FixHtmlDocumentReference(_wrap($(THIS).$DOM_NAME));\n', | |
1380 HTML_NAME=html_name, | |
1381 DOM_NAME=DartDomNameOfAttribute(attr), | |
1382 TYPE=DartType(attr.type.id), | |
1383 THIS=self.DomObjectName()) | |
1384 else: | |
1385 self._members_emitter.Emit( | |
1386 '\n' | |
1387 ' $TYPE get $(HTML_NAME)() => _wrap($(THIS).$DOM_NAME);\n', | |
1388 HTML_NAME=html_name, | |
1389 DOM_NAME=DartDomNameOfAttribute(attr), | |
1390 TYPE=DartType(attr.type.id), | |
1391 THIS=self.DomObjectName()) | |
1392 | |
1393 def _AddSetter(self, attr, html_name): | |
1394 self._members_emitter.Emit( | |
1395 '\n' | |
1396 ' void set $(HTML_NAME)($TYPE value) { ' | |
1397 '$(THIS).$DOM_NAME = _unwrap(value); }\n', | |
1398 HTML_NAME=html_name, | |
1399 DOM_NAME=DartDomNameOfAttribute(attr), | |
1400 TYPE=DartType(attr.type.id), | |
1401 THIS=self.DomObjectName()) | |
1402 | |
1403 def AddSecondaryAttribute(self, interface, getter, setter): | |
1404 self._SecondaryContext(interface) | |
1405 self.AddAttribute(getter, setter) | |
1406 | |
1407 def AddSecondaryOperation(self, interface, info): | |
1408 self._SecondaryContext(interface) | |
1409 self.AddOperation(info) | |
1410 | |
1411 def AddEventAttributes(self, event_attrs): | |
1412 event_attrs = _DomToHtmlEvents(self._interface.id, event_attrs) | |
1413 events_class = '_' + self._interface.id + 'EventsImpl' | |
1414 events_interface = self._interface.id + 'Events' | |
1415 self._EmitEventGetter(events_class) | |
1416 | |
1417 self._shared._event_classes.add(self._interface.id) | |
1418 | |
1419 parent_event_class = self._shared.GetParentEventsClass(self._interface) | |
1420 | |
1421 # TODO(jacobr): specify the type of _ptr as EventTarget | |
1422 events_members = self._dart_code.Emit( | |
1423 '\n' | |
1424 'class $CLASSNAME extends $SUPER implements $INTERFACE {\n' | |
1425 ' $CLASSNAME(_ptr) : super(_ptr);\n' | |
1426 '$!MEMBERS}\n', | |
1427 CLASSNAME=events_class, | |
1428 INTERFACE=events_interface, | |
1429 SUPER='_' + parent_event_class + 'Impl') | |
1430 | |
1431 for event_name in event_attrs: | |
1432 if event_name in _html_event_names: | |
1433 events_members.Emit( | |
1434 "\n" | |
1435 " EventListenerList get $NAME() => _get('$RAWNAME');\n", | |
1436 RAWNAME=event_name, | |
1437 NAME=_html_event_names[event_name]) | |
1438 else: | |
1439 raise Exception('No known html even name for event: ' + event_name) | |
1440 | |
1441 def _EmitEventGetter(self, events_class): | |
1442 self._members_emitter.Emit( | |
1443 '\n' | |
1444 ' $TYPE get on() {\n' | |
1445 ' if (_on == null) _on = new $TYPE($EVENTTARGET);\n' | |
1446 ' return _on;\n' | |
1447 ' }\n', | |
1448 TYPE=events_class, | |
1449 EVENTTARGET='_wrappedDocumentPtr' if self._interface.id == 'Document' | |
1450 else 'this') | |
1451 | |
1452 def _SecondaryContext(self, interface): | |
1453 if interface is not self._current_secondary_parent: | |
1454 self._current_secondary_parent = interface | |
1455 self._members_emitter.Emit('\n // From $WHERE\n', WHERE=interface.id) | |
1456 | |
1457 # TODO(jacobr): change this to more directly match the frog version. | |
1458 def AddIndexer(self, element_type): | |
1459 """Adds all the methods required to complete implementation of List.""" | |
1460 # We would like to simply inherit the implementation of everything except | |
1461 # get length(), [], and maybe []=. It is possible to extend from a base | |
1462 # array implementation class only when there is no other implementation | |
1463 # inheritance. There might be no implementation inheritance other than | |
1464 # DOMBaseWrapper for many classes, but there might be some where the | |
1465 # array-ness is introduced by a non-root interface: | |
1466 # | |
1467 # interface Y extends X, List<T> ... | |
1468 # | |
1469 # In the non-root case we have to choose between: | |
1470 # | |
1471 # class YImpl extends XImpl { add List<T> methods; } | |
1472 # | |
1473 # and | |
1474 # | |
1475 # class YImpl extends ListBase<T> { copies of transitive XImpl methods; } | |
1476 # | |
1477 if self._HasNativeIndexGetter(self._interface): | |
1478 self._EmitNativeIndexGetter(self._interface, element_type) | |
1479 else: | |
1480 self._members_emitter.Emit( | |
1481 '\n' | |
1482 ' $TYPE operator[](int index) => _wrap($(THIS)[index]);\n' | |
1483 '\n', | |
1484 THIS=self.DomObjectName(), | |
1485 TYPE=DartType(element_type)) | |
1486 | |
1487 if self._HasNativeIndexSetter(self._interface): | |
1488 self._EmitNativeIndexSetter(self._interface, element_type) | |
1489 else: | |
1490 # The HTML library implementation of NodeList has a custom indexed setter | |
1491 # implementation that uses the parent node the NodeList is associated | |
1492 # with if one is available. | |
1493 if self._interface.id != 'NodeList': | |
1494 self._members_emitter.Emit( | |
1495 '\n' | |
1496 ' void operator[]=(int index, $TYPE value) {\n' | |
1497 ' throw new UnsupportedOperationException("Cannot assign element
of immutable List.");\n' | |
1498 ' }\n', | |
1499 TYPE=DartType(element_type)) | |
1500 | |
1501 # The list interface for this class is manually generated. | |
1502 if self._interface.id == 'NodeList': | |
1503 return | |
1504 | |
1505 self._members_emitter.Emit( | |
1506 '\n' | |
1507 ' void add($TYPE value) {\n' | |
1508 ' throw new UnsupportedOperationException("Cannot add to immutable Li
st.");\n' | |
1509 ' }\n' | |
1510 '\n' | |
1511 ' void addLast($TYPE value) {\n' | |
1512 ' throw new UnsupportedOperationException("Cannot add to immutable Li
st.");\n' | |
1513 ' }\n' | |
1514 '\n' | |
1515 ' void addAll(Collection<$TYPE> collection) {\n' | |
1516 ' throw new UnsupportedOperationException("Cannot add to immutable Li
st.");\n' | |
1517 ' }\n' | |
1518 '\n' | |
1519 ' void sort(int compare($TYPE a, $TYPE b)) {\n' | |
1520 ' throw new UnsupportedOperationException("Cannot sort immutable List
.");\n' | |
1521 ' }\n' | |
1522 '\n' | |
1523 ' void copyFrom(List<Object> src, int srcStart, ' | |
1524 'int dstStart, int count) {\n' | |
1525 ' throw new UnsupportedOperationException("This object is immutable."
);\n' | |
1526 ' }\n' | |
1527 '\n' | |
1528 ' int indexOf($TYPE element, [int start = 0]) {\n' | |
1529 ' return _Lists.indexOf(this, element, start, this.length);\n' | |
1530 ' }\n' | |
1531 '\n' | |
1532 ' int lastIndexOf($TYPE element, [int start = null]) {\n' | |
1533 ' if (start === null) start = length - 1;\n' | |
1534 ' return _Lists.lastIndexOf(this, element, start);\n' | |
1535 ' }\n' | |
1536 '\n' | |
1537 ' int clear() {\n' | |
1538 ' throw new UnsupportedOperationException("Cannot clear immutable Lis
t.");\n' | |
1539 ' }\n' | |
1540 '\n' | |
1541 ' $TYPE removeLast() {\n' | |
1542 ' throw new UnsupportedOperationException("Cannot removeLast on immut
able List.");\n' | |
1543 ' }\n' | |
1544 '\n' | |
1545 ' $TYPE last() {\n' | |
1546 ' return this[length - 1];\n' | |
1547 ' }\n' | |
1548 '\n' | |
1549 ' void forEach(void f($TYPE element)) {\n' | |
1550 ' _Collections.forEach(this, f);\n' | |
1551 ' }\n' | |
1552 '\n' | |
1553 ' Collection map(f($TYPE element)) {\n' | |
1554 ' return _Collections.map(this, [], f);\n' | |
1555 ' }\n' | |
1556 '\n' | |
1557 ' Collection<$TYPE> filter(bool f($TYPE element)) {\n' | |
1558 ' return _Collections.filter(this, new List<$TYPE>(), f);\n' | |
1559 ' }\n' | |
1560 '\n' | |
1561 ' bool every(bool f($TYPE element)) {\n' | |
1562 ' return _Collections.every(this, f);\n' | |
1563 ' }\n' | |
1564 '\n' | |
1565 ' bool some(bool f($TYPE element)) {\n' | |
1566 ' return _Collections.some(this, f);\n' | |
1567 ' }\n' | |
1568 '\n' | |
1569 ' void setRange(int start, int length, List<$TYPE> from, [int startFrom
]) {\n' | |
1570 ' throw new UnsupportedOperationException("Cannot setRange on immutab
le List.");\n' | |
1571 ' }\n' | |
1572 '\n' | |
1573 ' void removeRange(int start, int length) {\n' | |
1574 ' throw new UnsupportedOperationException("Cannot removeRange on immu
table List.");\n' | |
1575 ' }\n' | |
1576 '\n' | |
1577 ' void insertRange(int start, int length, [$TYPE initialValue]) {\n' | |
1578 ' throw new UnsupportedOperationException("Cannot insertRange on immu
table List.");\n' | |
1579 ' }\n' | |
1580 '\n' | |
1581 ' List<$TYPE> getRange(int start, int length) {\n' | |
1582 ' throw new NotImplementedException();\n' | |
1583 ' }\n' | |
1584 '\n' | |
1585 ' bool isEmpty() {\n' | |
1586 ' return length == 0;\n' | |
1587 ' }\n' | |
1588 '\n' | |
1589 ' Iterator<$TYPE> iterator() {\n' | |
1590 ' return new _FixedSizeListIterator<$TYPE>(this);\n' | |
1591 ' }\n', | |
1592 TYPE=DartType(element_type)) | |
1593 | |
1594 def _HasNativeIndexGetter(self, interface): | |
1595 return ('HasIndexGetter' in interface.ext_attrs or | |
1596 'HasNumericIndexGetter' in interface.ext_attrs) | |
1597 | |
1598 def _EmitNativeIndexGetter(self, interface, element_type): | |
1599 method_name = '_index' | |
1600 self._members_emitter.Emit( | |
1601 '\n $TYPE operator[](int index) => _wrap($(THIS)[index]);\n', | |
1602 TYPE=DartType(element_type), | |
1603 THIS=self.DomObjectName(), | |
1604 METHOD=method_name) | |
1605 | |
1606 def _HasNativeIndexSetter(self, interface): | |
1607 return 'HasCustomIndexSetter' in interface.ext_attrs | |
1608 | |
1609 def _EmitNativeIndexSetter(self, interface, element_type): | |
1610 method_name = '_set_index' | |
1611 self._members_emitter.Emit( | |
1612 '\n' | |
1613 ' void operator[]=(int index, $TYPE value) {\n' | |
1614 ' return $(THIS)[index] = _unwrap(value);\n' | |
1615 ' }\n', | |
1616 THIS=self.DomObjectName(), | |
1617 TYPE=DartType(element_type), | |
1618 METHOD=method_name) | |
1619 | |
1620 def AddOperation(self, info): | |
1621 """ | |
1622 Arguments: | |
1623 info: An OperationInfo object. | |
1624 """ | |
1625 html_name = self._shared.RenameInHtmlLibrary(self._interface, info.name) | |
1626 | |
1627 if not html_name: | |
1628 return | |
1629 | |
1630 body = self._members_emitter.Emit( | |
1631 '\n' | |
1632 ' $TYPE $HTML_NAME($PARAMS) {\n' | |
1633 '$!BODY' | |
1634 ' }\n', | |
1635 TYPE=info.type_name, | |
1636 HTML_NAME=html_name, | |
1637 PARAMS=info.ParametersImplementationDeclaration()) | |
1638 | |
1639 # Process in order of ascending number of arguments to ensure missing | |
1640 # optional arguments are processed early. | |
1641 overloads = sorted(info.overloads, | |
1642 key=lambda overload: len(overload.arguments)) | |
1643 self._native_version = 0 | |
1644 fallthrough = self.GenerateDispatch(body, info, ' ', 0, overloads) | |
1645 if fallthrough: | |
1646 body.Emit(' throw "Incorrect number or type of arguments";\n'); | |
1647 | |
1648 def GenerateSingleOperation(self, emitter, info, indent, operation): | |
1649 """Generates a call to a single operation. | |
1650 | |
1651 Arguments: | |
1652 emitter: an Emitter for the body of a block of code. | |
1653 info: the compound information about the operation and its overloads. | |
1654 indent: an indentation string for generated code. | |
1655 operation: the IDLOperation to call. | |
1656 """ | |
1657 argument_expressions = self._UnwrappedParameters( | |
1658 info, | |
1659 len(operation.arguments)) # Just the parameters this far. | |
1660 | |
1661 if info.type_name != 'void': | |
1662 # We could place the logic for handling Document directly in _wrap | |
1663 # but we chose to place it here so that bugs in the wrapper and | |
1664 # wrapperless implementations are more consistent. | |
1665 if self._shared.MaybeReturnDocument(info.type_name): | |
1666 emitter.Emit('$(INDENT)return _FixHtmlDocumentReference(' | |
1667 '_wrap($(THIS).$NAME($ARGS)));\n', | |
1668 INDENT=indent, | |
1669 THIS=self.DomObjectName(), | |
1670 NAME=info.name, | |
1671 ARGS=argument_expressions) | |
1672 else: | |
1673 emitter.Emit('$(INDENT)return _wrap($(THIS).$NAME($ARGS));\n', | |
1674 INDENT=indent, | |
1675 THIS=self.DomObjectName(), | |
1676 NAME=info.name, | |
1677 ARGS=argument_expressions) | |
1678 else: | |
1679 emitter.Emit('$(INDENT)$(THIS).$NAME($ARGS);\n' | |
1680 '$(INDENT)return;\n', | |
1681 INDENT=indent, | |
1682 THIS=self.DomObjectName(), | |
1683 NAME=info.name, | |
1684 ARGS=argument_expressions) | |
1685 | |
1686 def GenerateDispatch(self, emitter, info, indent, position, overloads): | |
1687 """Generates a dispatch to one of the overloads. | |
1688 | |
1689 Arguments: | |
1690 emitter: an Emitter for the body of a block of code. | |
1691 info: the compound information about the operation and its overloads. | |
1692 indent: an indentation string for generated code. | |
1693 position: the index of the parameter to dispatch on. | |
1694 overloads: a list of the remaining IDLOperations to dispatch. | |
1695 | |
1696 Returns True if the dispatch can fall through on failure, False if the code | |
1697 always dispatches. | |
1698 """ | |
1699 | |
1700 def NullCheck(name): | |
1701 return '%s === null' % name | |
1702 | |
1703 def TypeCheck(name, type): | |
1704 return '%s is %s' % (name, type) | |
1705 | |
1706 if position == len(info.arg_infos): | |
1707 if len(overloads) > 1: | |
1708 raise Exception('Duplicate operations ' + str(overloads)) | |
1709 operation = overloads[0] | |
1710 self.GenerateSingleOperation(emitter, info, indent, operation) | |
1711 return False | |
1712 | |
1713 # FIXME: Consider a simpler dispatch that iterates over the | |
1714 # overloads and generates an overload specific check. Revisit | |
1715 # when we move to named optional arguments. | |
1716 | |
1717 # Partition the overloads to divide and conquer on the dispatch. | |
1718 positive = [] | |
1719 negative = [] | |
1720 first_overload = overloads[0] | |
1721 (param_name, param_type, param_default) = info.arg_infos[position] | |
1722 | |
1723 if position < len(first_overload.arguments): | |
1724 # FIXME: This will not work if the second overload has a more | |
1725 # precise type than the first. E.g., | |
1726 # void foo(Node x); | |
1727 # void foo(Element x); | |
1728 type = DartType(first_overload.arguments[position].type.id) | |
1729 test = TypeCheck(param_name, type) | |
1730 pred = lambda op: (len(op.arguments) > position and | |
1731 DartType(op.arguments[position].type.id) == type) | |
1732 else: | |
1733 type = None | |
1734 test = NullCheck(param_name) | |
1735 pred = lambda op: position >= len(op.arguments) | |
1736 | |
1737 for overload in overloads: | |
1738 if pred(overload): | |
1739 positive.append(overload) | |
1740 else: | |
1741 negative.append(overload) | |
1742 | |
1743 if positive and negative: | |
1744 (true_code, false_code) = emitter.Emit( | |
1745 '$(INDENT)if ($COND) {\n' | |
1746 '$!TRUE' | |
1747 '$(INDENT)} else {\n' | |
1748 '$!FALSE' | |
1749 '$(INDENT)}\n', | |
1750 COND=test, INDENT=indent) | |
1751 fallthrough1 = self.GenerateDispatch( | |
1752 true_code, info, indent + ' ', position + 1, positive) | |
1753 fallthrough2 = self.GenerateDispatch( | |
1754 false_code, info, indent + ' ', position, negative) | |
1755 return fallthrough1 or fallthrough2 | |
1756 | |
1757 if negative: | |
1758 raise Exception('Internal error, must be all positive') | |
1759 | |
1760 # All overloads require the same test. Do we bother? | |
1761 | |
1762 # If the test is the same as the method's formal parameter then checked mode | |
1763 # will have done the test already. (It could be null too but we ignore that | |
1764 # case since all the overload behave the same and we don't know which types | |
1765 # in the IDL are not nullable.) | |
1766 if type == param_type: | |
1767 return self.GenerateDispatch( | |
1768 emitter, info, indent, position + 1, positive) | |
1769 | |
1770 # Otherwise the overloads have the same type but the type is a substype of | |
1771 # the method's synthesized formal parameter. e.g we have overloads f(X) and | |
1772 # f(Y), implemented by the synthesized method f(Z) where X<Z and Y<Z. The | |
1773 # dispatch has removed f(X), leaving only f(Y), but there is no guarantee | |
1774 # that Y = Z-X, so we need to check for Y. | |
1775 true_code = emitter.Emit( | |
1776 '$(INDENT)if ($COND) {\n' | |
1777 '$!TRUE' | |
1778 '$(INDENT)}\n', | |
1779 COND=test, INDENT=indent) | |
1780 self.GenerateDispatch( | |
1781 true_code, info, indent + ' ', position + 1, positive) | |
1782 return True | |
OLD | NEW |