#!/usr/bin/python # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file # for details. All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. """This module provides shared functionality for the system to generate Dart:html APIs from the IDL database.""" import emitter import monitored import os from generator import * from htmldartgenerator import * _js_custom_members = monitored.Set('systemhtml._js_custom_members', [ 'ArrayBuffer.slice', 'AudioBufferSourceNode.start', 'AudioBufferSourceNode.stop', 'AudioContext.createGain', 'AudioContext.createScriptProcessor', 'CanvasRenderingContext2D.lineDashOffset', 'Console.memory', 'Console.profiles', 'Console.assertCondition', 'Console.count', 'Console.debug', 'Console.dir', 'Console.dirxml', 'Console.error', 'Console.group', 'Console.groupCollapsed', 'Console.groupEnd', 'Console.info', 'Console.log', 'Console.markTimeline', 'Console.profile', 'Console.profileEnd', 'Console.time', 'Console.timeEnd', 'Console.timeStamp', 'Console.trace', 'Console.warn', 'CSSStyleDeclaration.setProperty', 'Element.insertAdjacentElement', 'Element.insertAdjacentHTML', 'Element.insertAdjacentText', 'Element.webkitMatchesSelector', 'Element.remove', 'ElementEvents.mouseWheel', 'ElementEvents.transitionEnd', 'DOMException.name', 'HTMLTableElement.createTBody', 'IDBDatabase.transaction', 'KeyboardEvent.initKeyboardEvent', 'MouseEvent.offsetX', 'MouseEvent.offsetY', 'Navigator.language', 'Navigator.webkitGetUserMedia', 'URL.createObjectURL', 'URL.revokeObjectURL', 'WheelEvent.wheelDeltaX', 'WheelEvent.wheelDeltaY', 'Window.cancelAnimationFrame', 'Window.console', 'Window.document', 'Window.indexedDB', 'Window.location', 'Window.open', 'Window.requestAnimationFrame', 'WorkerContext.indexedDB', ]) _js_custom_constructors = monitored.Set('systemhtml._js_custom_constructors', [ 'AudioContext', 'Blob', 'MutationObserver', 'Notification', 'RTCIceCandidate', 'RTCPeerConnection', 'RTCSessionDescription', 'SpeechRecognition', ]) # Classes that offer only static methods, and therefore we should suppress # constructor creation. _static_classes = set(['Url']) # Information for generating element constructors. # # TODO(sra): maybe remove all the argument complexity and use cascades. # # var c = new CanvasElement(width: 100, height: 70); # var c = new CanvasElement()..width = 100..height = 70; # class ElementConstructorInfo(object): def __init__(self, name=None, tag=None, params=[], opt_params=[], factory_provider_name='document'): self.name = name # The constructor name 'h1' in 'HeadingElement.h1' self.tag = tag or name # The HTML or SVG tag self.params = params self.opt_params = opt_params self.factory_provider_name = factory_provider_name def ConstructorInfo(self, interface_name): info = OperationInfo() info.overloads = None info.declared_name = interface_name info.name = interface_name info.constructor_name = self.name info.js_name = None info.type_name = interface_name info.param_infos = map(lambda tXn: ParamInfo(tXn[1], tXn[0], True), self.opt_params) info.requires_named_arguments = True info.factory_parameters = ['"%s"' % self.tag] info.pure_dart_constructor = True return info _html_element_constructors = monitored.Dict( 'systemhtml._html_element_constructors', { 'HTMLAnchorElement' : ElementConstructorInfo(tag='a', opt_params=[('DOMString', 'href')]), 'HTMLAreaElement': 'area', 'HTMLButtonElement': 'button', 'HTMLBRElement': 'br', 'HTMLBaseElement': 'base', 'HTMLBodyElement': 'body', 'HTMLButtonElement': 'button', 'HTMLCanvasElement': ElementConstructorInfo(tag='canvas', opt_params=[('int', 'width'), ('int', 'height')]), 'HTMLContentElement': 'content', 'HTMLDataListElement': 'datalist', 'HTMLDListElement': 'dl', 'HTMLDetailsElement': 'details', 'HTMLDivElement': 'div', 'HTMLEmbedElement': 'embed', 'HTMLFieldSetElement': 'fieldset', 'HTMLFormElement': 'form', 'HTMLHRElement': 'hr', 'HTMLHeadElement': 'head', 'HTMLHeadingElement': [ElementConstructorInfo('h1'), ElementConstructorInfo('h2'), ElementConstructorInfo('h3'), ElementConstructorInfo('h4'), ElementConstructorInfo('h5'), ElementConstructorInfo('h6')], 'HTMLHtmlElement': 'html', 'HTMLIFrameElement': 'iframe', 'HTMLImageElement': ElementConstructorInfo(tag='img', opt_params=[('DOMString', 'src'), ('int', 'width'), ('int', 'height')]), 'HTMLKeygenElement': 'keygen', 'HTMLLIElement': 'li', 'HTMLLabelElement': 'label', 'HTMLLegendElement': 'legend', 'HTMLLinkElement': 'link', 'HTMLMapElement': 'map', 'HTMLMenuElement': 'menu', 'HTMLMeterElement': 'meter', 'HTMLOListElement': 'ol', 'HTMLObjectElement': 'object', 'HTMLOptGroupElement': 'optgroup', 'HTMLOutputElement': 'output', 'HTMLParagraphElement': 'p', 'HTMLParamElement': 'param', 'HTMLPreElement': 'pre', 'HTMLProgressElement': 'progress', 'HTMLScriptElement': 'script', 'HTMLSelectElement': 'select', 'HTMLSourceElement': 'source', 'HTMLSpanElement': 'span', 'HTMLStyleElement': 'style', 'HTMLTableCaptionElement': 'caption', 'HTMLTableCellElement': 'td', 'HTMLTableColElement': 'col', 'HTMLTableElement': 'table', 'HTMLTableRowElement': 'tr', #'HTMLTableSectionElement' 'HTMLTextAreaElement': 'textarea', 'HTMLTitleElement': 'title', 'HTMLTrackElement': 'track', 'HTMLUListElement': 'ul', 'HTMLVideoElement': 'video' }) _svg_element_constructors = monitored.Dict( 'systemhtml._svg_element_constructors', { 'SVGAElement': 'a', 'SVGAltGlyphElement': 'altGlyph', 'SVGAnimateElement': 'animate', 'SVGAnimateMotionElement': 'animateMotion', 'SVGAnimateTransformElement': 'animateTransform', 'SVGAnimationElement': 'animation', 'SVGCircleElement': 'circle', 'SVGClipPathElement': 'clipPath', 'SVGCursorElement': 'cursor', 'SVGDefsElement': 'defs', 'SVGDescElement': 'desc', 'SVGEllipseElement': 'ellipse', 'SVGFEBlendElement': 'feBlend', 'SVGFEColorMatrixElement': 'feColorMatrix', 'SVGFEComponentTransferElement': 'feComponentTransfer', 'SVGFEConvolveMatrixElement': 'feConvolveMatrix', 'SVGFEDiffuseLightingElement': 'feDiffuseLighting', 'SVGFEDisplacementMapElement': 'feDisplacementMap', 'SVGFEDistantLightElement': 'feDistantLight', 'SVGFEFloodElement': 'feFlood', 'SVGFEFuncAElement': 'feFuncA', 'SVGFEFuncBElement': 'feFuncB', 'SVGFEFuncGElement': 'feFuncG', 'SVGFEFuncRElement': 'feFuncR', 'SVGFEGaussianBlurElement': 'feGaussianBlur', 'SVGFEImageElement': 'feImage', 'SVGFEMergeElement': 'feMerge', 'SVGFEMergeNodeElement': 'feMergeNode', 'SVGFEMorphology': 'feMorphology', 'SVGFEOffsetElement': 'feOffset', 'SVGFEPointLightElement': 'fePointLight', 'SVGFESpecularLightingElement': 'feSpecularLighting', 'SVGFESpotLightElement': 'feSpotLight', 'SVGFETileElement': 'feTile', 'SVGFETurbulenceElement': 'feTurbulence', 'SVGFilterElement': 'filter', 'SVGForeignObjectElement': 'foreignObject', 'SVGGlyphElement': 'glyph', 'SVGGElement': 'g', 'SVGHKernElement': 'hkern', 'SVGImageElement': 'image', 'SVGLinearGradientElement': 'linearGradient', 'SVGLineElement': 'line', 'SVGMarkerElement': 'marker', 'SVGMaskElement': 'mask', 'SVGMPathElement': 'mpath', 'SVGPathElement': 'path', 'SVGPatternElement': 'pattern', 'SVGPolygonElement': 'polygon', 'SVGPolylineElement': 'polyline', 'SVGRadialGradientElement': 'radialGradient', 'SVGRectElement': 'rect', 'SVGScriptElement': 'script', 'SVGSetElement': 'set', 'SVGStopElement': 'stop', 'SVGStyleElement': 'style', 'SVGSwitchElement': 'switch', 'SVGSymbolElement': 'symbol', 'SVGTextElement': 'text', 'SVGTitleElement': 'title', 'SVGTRefElement': 'tref', 'SVGTSpanElement': 'tspan', 'SVGUseElement': 'use', 'SVGViewElement': 'view', 'SVGVKernElement': 'vkern', }) _element_constructors = { 'html': _html_element_constructors, 'indexed_db': {}, 'svg': _svg_element_constructors, 'web_audio': {}, } _factory_ctr_strings = { 'html': { 'provider_name': 'document', 'constructor_name': '$dom_createElement' }, 'indexed_db': { 'provider_name': 'document', 'constructor_name': '$dom_createElement' }, 'svg': { 'provider_name': '_SvgElementFactoryProvider', 'constructor_name': 'createSvgElement_tag', }, 'web_audio': { 'provider_name': 'document', 'constructor_name': '$dom_createElement' }, } def ElementConstructorInfos(typename, element_constructors, factory_provider_name='_Elements'): """Returns list of ElementConstructorInfos about the convenience constructors for an Element or SvgElement.""" # TODO(sra): Handle multiple and named constructors. if typename not in element_constructors: return [] infos = element_constructors[typename] if isinstance(infos, str): infos = ElementConstructorInfo(tag=infos, factory_provider_name=factory_provider_name) if not isinstance(infos, list): infos = [infos] return infos # ------------------------------------------------------------------------------ def SvgSupportStr(tagName): return 'Svg%s' % ElemSupportStr(tagName) def ElemSupportStr(tagName): return "Element.isTagSupported('%s')" % tagName _js_support_checks_basic_element_with_constructors = [ 'HTMLContentElement', 'HTMLDataListElement', 'HTMLDetailsElement', 'HTMLEmbedElement', 'HTMLMeterElement', 'HTMLObjectElement', 'HTMLOutputElement', 'HTMLProgressElement', 'HTMLTrackElement', ] _js_support_checks_additional_element = [ # IE creates keygen as Block elements 'HTMLKeygenElement', 'SVGAltGlyphElement', 'SVGAnimateElement', 'SVGAnimateMotionElement', 'SVGAnimateTransformElement', 'SVGCursorElement', 'SVGFEBlendElement', 'SVGFEColorMatrixElement', 'SVGFEComponentTransferElement', 'SVGFEConvolveMatrixElement', 'SVGFEDiffuseLightingElement', 'SVGFEDisplacementMapElement', 'SVGFEDistantLightElement', 'SVGFEFloodElement', 'SVGFEFuncAElement', 'SVGFEFuncBElement', 'SVGFEFuncGElement', 'SVGFEFuncRElement', 'SVGFEGaussianBlurElement', 'SVGFEImageElement', 'SVGFEMergeElement', 'SVGFEMergeNodeElement', 'SVGFEMorphology', 'SVGFEOffsetElement', 'SVGFEPointLightElement', 'SVGFESpecularLightingElement', 'SVGFESpotLightElement', 'SVGFETileElement', 'SVGFETurbulenceElement', 'SVGFilterElement', 'SVGForeignObjectElement', 'SVGSetElement', ] js_support_checks = dict({ 'ArrayBuffer': "JS('bool', 'typeof window.ArrayBuffer != \"undefined\"')", 'Database': "JS('bool', '!!(window.openDatabase)')", 'DOMApplicationCache': "JS('bool', '!!(window.applicationCache)')", 'DOMFileSystem': "JS('bool', '!!(window.webkitRequestFileSystem)')", 'FormData': "JS('bool', '!!(window.FormData)')", 'HashChangeEvent': "Event._isTypeSupported('HashChangeEvent')", 'HTMLShadowElement': ElemSupportStr('shadow'), 'MediaStreamEvent': "Event._isTypeSupported('MediaStreamEvent')", 'MediaStreamTrackEvent': "Event._isTypeSupported('MediaStreamTrackEvent')", 'NotificationCenter': "JS('bool', '!!(window.webkitNotifications)')", 'Performance': "JS('bool', '!!(window.performance)')", 'SpeechRecognition': "JS('bool', '!!(window.SpeechRecognition || " "window.webkitSpeechRecognition)')", 'SVGExternalResourcesRequired': ('supported(SvgElement element)', "JS('bool', '#.externalResourcesRequired !== undefined && " "#.externalResourcesRequired.animVal !== undefined', " "element, element)"), 'SVGLangSpace': ('supported(SvgElement element)', "JS('bool', '#.xmlspace !== undefined && #.xmllang !== undefined', " "element, element)"), 'TouchList': "JS('bool', '!!document.createTouchList')", 'XMLHttpRequestProgressEvent': "Event._isTypeSupported('XMLHttpRequestProgressEvent')", 'WebGLRenderingContext': "JS('bool', '!!(window.WebGLRenderingContext)')", 'WebKitCSSMatrix': "JS('bool', '!!(window.WebKitCSSMatrix)')", 'WebKitPoint': "JS('bool', '!!(window.WebKitPoint)')", 'WebSocket': "JS('bool', 'typeof window.WebSocket != \"undefined\"')", 'XSLTProcessor': "JS('bool', '!!(window.XSLTProcessor)')", }.items() + dict((key, SvgSupportStr(_svg_element_constructors[key]) if key.startswith('SVG') else ElemSupportStr(_html_element_constructors[key])) for key in _js_support_checks_basic_element_with_constructors + _js_support_checks_additional_element).items()) # ------------------------------------------------------------------------------ class HtmlDartInterfaceGenerator(object): """Generates dart interface and implementation for the DOM IDL interface.""" def __init__(self, options, library_emitter, event_generator, interface, backend): self._renamer = options.renamer self._database = options.database self._template_loader = options.templates self._type_registry = options.type_registry self._options = options self._library_emitter = library_emitter self._event_generator = event_generator self._interface = interface self._backend = backend self._interface_type_info = self._type_registry.TypeInfo(self._interface.id) self._library_name = self._renamer.GetLibraryName(self._interface) def Generate(self): if 'Callback' in self._interface.ext_attrs: self.GenerateCallback() else: self.GenerateInterface() def GenerateCallback(self): """Generates a typedef for the callback interface.""" handlers = [operation for operation in self._interface.operations if operation.id == 'handleEvent'] info = AnalyzeOperation(self._interface, handlers) code = self._library_emitter.FileEmitter(self._interface.id, self._library_name) code.Emit(self._template_loader.Load('callback.darttemplate')) typedef_name = self._renamer.RenameInterface(self._interface) code.Emit('typedef void $NAME($PARAMS);\n', LIBRARYNAME='dart.dom.%s' % self._library_name, NAME=typedef_name, PARAMS=info.ParametersDeclaration(self._DartType)) self._backend.GenerateCallback(info) def GenerateInterface(self): interface_name = self._interface_type_info.interface_name() factory_provider = None if interface_name in interface_factories: factory_provider = interface_factories[interface_name] factory_constructor_name = None constructors = [] if interface_name in _static_classes: constructor_info = None else: constructor_info = AnalyzeConstructor(self._interface) if constructor_info: constructors.append(constructor_info) # TODO(antonm): consider removing it later. factory_provider = interface_name # HTML Elements and SVG Elements have convenience constructors. infos = ElementConstructorInfos(self._interface.id, _element_constructors[self._library_name], factory_provider_name= _factory_ctr_strings[self._library_name]['provider_name']) if infos: factory_constructor_name = _factory_ctr_strings[ self._library_name]['constructor_name'] for info in infos: constructors.append(info.ConstructorInfo(self._interface.id)) if factory_provider: assert factory_provider == info.factory_provider_name else: factory_provider = info.factory_provider_name implementation_emitter = self._ImplementationEmitter() base_type_info = None if self._interface.parents: supertype = self._interface.parents[0].type.id if not IsDartCollectionType(supertype) and not IsPureInterface(supertype): base_type_info = self._type_registry.TypeInfo(supertype) if base_type_info.merged_into() \ and self._backend.ImplementsMergedMembers(): base_type_info = self._type_registry.TypeInfo( base_type_info.merged_into()) if base_type_info: base_class = base_type_info.implementation_name() else: base_class = self._backend.RootClassName() implements = self._backend.AdditionalImplementedInterfaces() for parent in self._interface.parents: parent_type_info = self._type_registry.TypeInfo(parent.type.id) if parent_type_info.interface_name() != base_class and \ parent_type_info != base_type_info: implements.append(parent_type_info.interface_name()) secure_base_name = self._backend.SecureBaseName(interface_name) if secure_base_name: implements.append(secure_base_name) implements_str = '' if implements: implements_str = ' implements ' + ', '.join(set(implements)) annotations = FormatAnnotationsAndComments( GetAnnotationsAndComments(self._library_name, self._interface.doc_js_name), '') self._implementation_members_emitter = implementation_emitter.Emit( self._backend.ImplementationTemplate(), LIBRARYNAME='dart.dom.%s' % self._library_name, ANNOTATIONS=annotations, CLASSNAME=self._interface_type_info.implementation_name(), EXTENDS=' extends %s' % base_class if base_class else '', IMPLEMENTS=implements_str, DOMNAME=self._interface.doc_js_name, NATIVESPEC=self._backend.NativeSpec()) self._backend.StartInterface(self._implementation_members_emitter) self._backend.EmitHelpers(base_class) self._event_generator.EmitStreamProviders( self._interface, self._backend.CustomJSMembers(), self._implementation_members_emitter, self._library_name) self._backend.AddConstructors( constructors, factory_provider, factory_constructor_name) self._backend.EmitSupportCheck() merged_interface = self._interface_type_info.merged_interface() if merged_interface: self._backend.AddMembers(self._database.GetInterface(merged_interface), not self._backend.ImplementsMergedMembers()) self._backend.AddMembers(self._interface) self._backend.AddSecondaryMembers(self._interface) self._event_generator.EmitStreamGetters( self._interface, [], self._implementation_members_emitter, self._library_name) self._backend.FinishInterface() def _ImplementationEmitter(self): basename = self._interface_type_info.implementation_name() if (self._interface_type_info.merged_into() and self._backend.ImplementsMergedMembers()): # Merged members are implemented in target interface implementation. return emitter.Emitter() return self._library_emitter.FileEmitter(basename, self._library_name) def _DartType(self, type_name): return self._type_registry.DartType(type_name) # ------------------------------------------------------------------------------ class Dart2JSBackend(HtmlDartGenerator): """Generates a dart2js class for the dart:html library from a DOM IDL interface. """ def __init__(self, interface, options): super(Dart2JSBackend, self).__init__(interface, options) self._database = options.database self._template_loader = options.templates self._type_registry = options.type_registry self._renamer = options.renamer self._interface_type_info = self._type_registry.TypeInfo(self._interface.id) self._current_secondary_parent = None self._library_name = self._renamer.GetLibraryName(self._interface) def ImplementsMergedMembers(self): return True def GenerateCallback(self, info): pass def RootClassName(self): return None def AdditionalImplementedInterfaces(self): implements = super(Dart2JSBackend, self).AdditionalImplementedInterfaces() if self._interface_type_info.list_item_type(): implements.append('JavaScriptIndexingBehavior') return implements def NativeSpec(self): native_spec = MakeNativeSpec(self._interface.javascript_binding_name) return ' native "%s"' % native_spec def ImplementationTemplate(self): if IsPureInterface(self._interface.id): return self._template_loader.Load('pure_interface.darttemplate') template_file = ('impl_%s.darttemplate' % self._interface.doc_js_name) return (self._template_loader.TryLoad(template_file) or self._template_loader.Load('dart2js_impl.darttemplate')) def StartInterface(self, members_emitter): self._members_emitter = members_emitter def FinishInterface(self): pass def HasSupportCheck(self): return self._interface.doc_js_name in js_support_checks def GetSupportCheck(self): """Return a tuple of the support check function signature and the support test itself. If no parameters are supplied, we assume the default.""" if self._interface.doc_js_name in _js_support_checks_additional_element: if self._interface.doc_js_name in _svg_element_constructors: lib_prefix = 'Svg' constructors = _svg_element_constructors else: lib_prefix = '' constructors = _html_element_constructors return (js_support_checks.get(self._interface.doc_js_name) + " && (new %sElement.tag('%s') is %s)" % (lib_prefix, constructors[self._interface.doc_js_name], self._renamer.RenameInterface(self._interface))) return js_support_checks.get(self._interface.doc_js_name) def GenerateCustomFactory(self, constructor_info): # Custom factory will be taken from the template. return self._interface.doc_js_name in _js_custom_constructors def IsConstructorArgumentOptional(self, argument): return 'Optional' in argument.ext_attrs def EmitStaticFactoryOverload(self, constructor_info, name, arguments): index = len(arguments) arguments = constructor_info.ParametersAsArgumentList(index) if arguments: arguments = ', ' + arguments self._members_emitter.Emit( " static $INTERFACE_NAME $NAME($PARAMETERS) => " "JS('$INTERFACE_NAME', 'new $CTOR_NAME($PLACEHOLDERS)'$ARGUMENTS);\n", INTERFACE_NAME=self._interface_type_info.interface_name(), NAME=name, # TODO(antonm): add types to parameters. PARAMETERS=constructor_info.ParametersAsArgumentList(index), CTOR_NAME=constructor_info.name or self._interface.doc_js_name, PLACEHOLDERS=','.join(['#'] * index), ARGUMENTS=arguments) def SecondaryContext(self, interface): if interface is not self._current_secondary_parent: self._current_secondary_parent = interface self._members_emitter.Emit('\n // From $WHERE\n', WHERE=interface.id) def AddIndexer(self, element_type): """Adds all the methods required to complete implementation of List.""" # We would like to simply inherit the implementation of everything except # length, [], and maybe []=. It is possible to extend from a base # array implementation class only when there is no other implementation # inheritance. There might be no implementation inheritance other than # DOMBaseWrapper for many classes, but there might be some where the # array-ness is introduced by a non-root interface: # # interface Y extends X, List ... # # In the non-root case we have to choose between: # # class YImpl extends XImpl { add List methods; } # # and # # class YImpl extends ListBase { copies of transitive XImpl methods; } # self._members_emitter.Emit( '\n' ' $TYPE operator[](int index) => JS("$TYPE", "#[#]", this, index);\n', TYPE=self.SecureOutputType(element_type)) if 'CustomIndexedSetter' in self._interface.ext_attrs: self._members_emitter.Emit( '\n' ' void operator[]=(int index, $TYPE value) {' ' JS("void", "#[#] = #", this, index, value); }', TYPE=self._NarrowInputType(element_type)) else: self._members_emitter.Emit( '\n' ' void operator[]=(int index, $TYPE value) {\n' ' throw new UnsupportedError("Cannot assign element of immutable List.");\n' ' }\n', TYPE=self._NarrowInputType(element_type)) self.EmitListMixin(self._DartType(element_type)) def EmitAttribute(self, attribute, html_name, read_only): if self._HasCustomImplementation(attribute.id): return if IsPureInterface(self._interface.id): self._AddInterfaceAttribute(attribute, html_name) return # If the attribute is shadowing, we can't generate a shadowing # field (Issue 1633). # TODO(sra): _FindShadowedAttribute does not take into account the html # renaming. we should be looking for another attribute that has the same # html_name. Two attributes with the same IDL name might not match if one # is renamed. (super_attribute, super_attribute_interface) = self._FindShadowedAttribute( attribute) if super_attribute: if read_only: if attribute.type.id == super_attribute.type.id: # Compatible attribute, use the superclass property. This works # because JavaScript will do its own dynamic dispatch. self._members_emitter.Emit( '\n' ' // Use implementation from $SUPER.\n' ' // final $TYPE $NAME;\n', SUPER=super_attribute_interface, NAME=html_name, TYPE=self.SecureOutputType(attribute.type.id)) return self._members_emitter.Emit('\n // Shadowing definition.') self._AddAttributeUsingProperties(attribute, html_name, read_only) return # If the type has a conversion we need a getter or setter to contain the # conversion code. if (self._OutputConversion(attribute.type.id, attribute.id) or self._InputConversion(attribute.type.id, attribute.id)): self._AddAttributeUsingProperties(attribute, html_name, read_only) return output_type = self.SecureOutputType(attribute.type.id) input_type = self._NarrowInputType(attribute.type.id) annotations = self._Annotations(attribute.type.id, attribute.id) rename = self._RenamingAnnotation(attribute.id, html_name) if not read_only: self._members_emitter.Emit( '\n $RENAME$ANNOTATIONS$TYPE $NAME;' '\n', RENAME=rename, ANNOTATIONS=annotations, NAME=html_name, TYPE=output_type) else: template = '\n $RENAME$(ANNOTATIONS)final $TYPE $NAME;\n' # Need to use a getter for list.length properties so we can add a # setter which throws an exception, satisfying List API. if self._interface_type_info.list_item_type() and html_name == 'length': template = ('\n $RENAME$(ANNOTATIONS)$TYPE get $NAME => ' + 'JS("$TYPE", "#.$NAME", this);\n') self._members_emitter.Emit( template, RENAME=rename, ANNOTATIONS=annotations, NAME=html_name, TYPE=output_type) def _AddAttributeUsingProperties(self, attribute, html_name, read_only): self._AddRenamingGetter(attribute, html_name) if not read_only: self._AddRenamingSetter(attribute, html_name) def _AddInterfaceAttribute(self, attribute, html_name): self._members_emitter.Emit( '\n $TYPE $NAME;' '\n', NAME=html_name, TYPE=self.SecureOutputType(attribute.type.id)) def _AddRenamingGetter(self, attr, html_name): conversion = self._OutputConversion(attr.type.id, attr.id) if conversion: return self._AddConvertingGetter(attr, html_name, conversion) return_type = self.SecureOutputType(attr.type.id) native_type = self._NarrowToImplementationType(attr.type.id) self._members_emitter.Emit( # TODO(sra): Use metadata to provide native name. '\n $TYPE get $HTML_NAME => JS("$NATIVE_TYPE", "#.$NAME", this);' '\n', HTML_NAME=html_name, NAME=attr.id, TYPE=return_type, NATIVE_TYPE=native_type) def _AddRenamingSetter(self, attr, html_name): conversion = self._InputConversion(attr.type.id, attr.id) if conversion: return self._AddConvertingSetter(attr, html_name, conversion) self._members_emitter.Emit( # TODO(sra): Use metadata to provide native name. '\n void set $HTML_NAME($TYPE value) {' '\n JS("void", "#.$NAME = #", this, value);' '\n }' '\n', HTML_NAME=html_name, NAME=attr.id, TYPE=self._NarrowInputType(attr.type.id)) def _AddConvertingGetter(self, attr, html_name, conversion): self._members_emitter.Emit( '\n $RETURN_TYPE get $HTML_NAME => $CONVERT(this._$(HTML_NAME));' "\n @JSName('$NAME')" '\n $(ANNOTATIONS)final $NATIVE_TYPE _$HTML_NAME;' '\n', ANNOTATIONS=self._Annotations(attr.type.id, html_name), CONVERT=conversion.function_name, HTML_NAME=html_name, NAME=attr.id, RETURN_TYPE=conversion.output_type, NATIVE_TYPE=conversion.input_type) def _AddConvertingSetter(self, attr, html_name, conversion): self._members_emitter.Emit( # TODO(sra): Use metadata to provide native name. '\n void set $HTML_NAME($INPUT_TYPE value) {' '\n this._$HTML_NAME = $CONVERT(value);' '\n }' '\n void set _$HTML_NAME(/*$NATIVE_TYPE*/ value) {' '\n JS("void", "#.$NAME = #", this, value);' '\n }' '\n', CONVERT=conversion.function_name, HTML_NAME=html_name, NAME=attr.id, INPUT_TYPE=conversion.input_type, NATIVE_TYPE=conversion.output_type) def AmendIndexer(self, element_type): pass def EmitOperation(self, info, html_name): """ Arguments: info: An OperationInfo object. """ if self._HasCustomImplementation(info.name): return if IsPureInterface(self._interface.id): self._AddInterfaceOperation(info, html_name) elif any(self._OperationRequiresConversions(op) for op in info.overloads): # Any conversions needed? self._AddOperationWithConversions(info, html_name) else: self._AddDirectNativeOperation(info, html_name) def _AddDirectNativeOperation(self, info, html_name): self._members_emitter.Emit( '\n' ' $RENAME$ANNOTATIONS$MODIFIERS$TYPE $NAME($PARAMS) native;\n', RENAME=self._RenamingAnnotation(info.declared_name, html_name), ANNOTATIONS=self._Annotations(info.type_name, info.declared_name), MODIFIERS='static ' if info.IsStatic() else '', TYPE=self.SecureOutputType(info.type_name), NAME=html_name, PARAMS=info.ParametersDeclaration(self._NarrowInputType)) def _AddOperationWithConversions(self, info, html_name): # Assert all operations have same return type. assert len(set([op.type.id for op in info.operations])) == 1 output_conversion = self._OutputConversion(info.type_name, info.declared_name) if output_conversion: return_type = output_conversion.output_type native_return_type = output_conversion.input_type else: return_type = self._NarrowInputType(info.type_name) native_return_type = return_type def InputType(type_name): conversion = self._InputConversion(type_name, info.declared_name) if conversion: return conversion.input_type else: return self._NarrowInputType(type_name) if type_name else 'dynamic' parameter_names = [param_info.name for param_info in info.param_infos] parameter_types = [InputType(param_info.type_id) for param_info in info.param_infos] operations = info.operations temp_version = [0] def GenerateCall( stmts_emitter, call_emitter, version, operation, argument_count): target = '_%s_%d' % (html_name, version); arguments = [] target_parameters = [] for position, arg in enumerate(operation.arguments[:argument_count]): conversion = self._InputConversion(arg.type.id, operation.id) param_name = operation.arguments[position].id if conversion: temp_version[0] += 1 temp_name = '%s_%s' % (param_name, temp_version[0]) temp_type = conversion.output_type stmts_emitter.Emit( '$(INDENT)$TYPE $NAME = $CONVERT($ARG);\n', TYPE=TypeOrVar(temp_type), NAME=temp_name, CONVERT=conversion.function_name, ARG=parameter_names[position]) arguments.append(temp_name) param_type = temp_type verified_type = temp_type # verified by assignment in checked mode. else: arguments.append(parameter_names[position]) param_type = self._NarrowInputType(arg.type.id) # Verified by argument checking on entry to the dispatcher. verified_type = InputType(info.param_infos[position].type_id) # The native method does not need an argument type if we know the type. # But we do need the native methods to have correct function types, so # be conservative. if param_type == verified_type: if param_type in ['String', 'num', 'int', 'double', 'bool', 'Object']: param_type = 'dynamic' target_parameters.append( '%s%s' % (TypeOrNothing(param_type), param_name)) argument_list = ', '.join(arguments) # TODO(sra): If the native method has zero type checks, we can 'inline' is # and call it directly with a JS-expression. call = '%s(%s)' % (target, argument_list) if output_conversion: call = '%s(%s)' % (output_conversion.function_name, call) call_emitter.Emit(call) self._members_emitter.Emit( ' $RENAME$ANNOTATIONS$MODIFIERS$TYPE$TARGET($PARAMS) native;\n', RENAME=self._RenamingAnnotation(info.declared_name, target), ANNOTATIONS=self._Annotations(info.type_name, info.declared_name), MODIFIERS='static ' if info.IsStatic() else '', TYPE=TypeOrNothing(native_return_type), TARGET=target, PARAMS=', '.join(target_parameters)) declaration = '%s%s%s %s(%s)' % ( self._Annotations(info.type_name, info.declared_name), 'static ' if info.IsStatic() else '', return_type, html_name, info.ParametersDeclaration(InputType)) self._GenerateDispatcherBody( operations, parameter_names, declaration, GenerateCall, self._IsOptional, can_omit_type_check=lambda type, pos: type == parameter_types[pos]) def _AddInterfaceOperation(self, info, html_name): self._members_emitter.Emit( '\n' ' $TYPE $NAME($PARAMS);\n', TYPE=self.SecureOutputType(info.type_name), NAME=info.name, PARAMS=info.ParametersDeclaration(self._NarrowInputType)) def _IsOptional(self, operation, argument): return IsOptional(argument) def _OperationRequiresConversions(self, operation): return (self._OperationRequiresOutputConversion(operation) or self._OperationRequiresInputConversions(operation)) def _OperationRequiresOutputConversion(self, operation): return self._OutputConversion(operation.type.id, operation.id) def _OperationRequiresInputConversions(self, operation): return any(self._InputConversion(arg.type.id, operation.id) for arg in operation.arguments) def _OutputConversion(self, idl_type, member): return FindConversion(idl_type, 'get', self._interface.id, member) def _InputConversion(self, idl_type, member): return FindConversion(idl_type, 'set', self._interface.id, member) def _HasCustomImplementation(self, member_name): member_name = '%s.%s' % (self._interface.doc_js_name, member_name) return member_name in _js_custom_members def _RenamingAnnotation(self, idl_name, member_name): if member_name != idl_name: return "@JSName('%s')\n " % idl_name return '' def _Annotations(self, idl_type, idl_member_name, indent=' '): anns = FindDart2JSAnnotationsAndComments(idl_type, self._library_name, self._interface.id, idl_member_name) if not AnyConversionAnnotations(idl_type, self._interface.id, idl_member_name): return_type = self.SecureOutputType(idl_type) native_type = self._NarrowToImplementationType(idl_type) if native_type != return_type: anns = anns + [ "@Returns('%s')" % native_type, "@Creates('%s')" % native_type, ] return FormatAnnotationsAndComments(anns, indent); def CustomJSMembers(self): return _js_custom_members def _NarrowToImplementationType(self, type_name): return self._type_registry.TypeInfo(type_name).narrow_dart_type() def _NarrowInputType(self, type_name): return self._NarrowToImplementationType(type_name) def _FindShadowedAttribute(self, attr): """Returns (attribute, superinterface) or (None, None).""" def FindInParent(interface): """Returns matching attribute in parent, or None.""" if interface.parents: parent = interface.parents[0] if IsDartCollectionType(parent.type.id): return (None, None) if IsPureInterface(parent.type.id): return (None, None) if self._database.HasInterface(parent.type.id): interfaces_to_search_in = [] parent_interface_name = parent.type.id interfaces_to_search_in.append(parent_interface_name) parent_type_info = self._type_registry.TypeInfo(parent_interface_name) if parent_type_info.merged_into(): # IDL parent was merged into another interface, which became a # parent interface in Dart. parent_interface_name = parent_type_info.merged_into() interfaces_to_search_in.append(parent_interface_name) elif parent_type_info.merged_interface(): # IDL parent has another interface that was merged into it. interfaces_to_search_in.append(parent_type_info.merged_interface()) for interface_name in interfaces_to_search_in: interface = self._database.GetInterface(interface_name) attr2 = FindMatchingAttribute(interface, attr) if attr2: return (attr2, parent_interface_name) return FindInParent( self._database.GetInterface(parent_interface_name)) return (None, None) return FindInParent(self._interface) if attr else (None, None) def _DartType(self, type_name): return self._type_registry.DartType(type_name) # ------------------------------------------------------------------------------ class DartLibraryEmitter(): def __init__(self, multiemitter, dart_sources_dir, dart_libraries): self._multiemitter = multiemitter self._dart_sources_dir = dart_sources_dir self._path_to_emitter = {} self._dart_libraries = dart_libraries def FileEmitter(self, basename, library_name, template=None): aux_dir = os.path.join(self._dart_sources_dir, library_name) path = os.path.join(aux_dir, '%s.dart' % basename) if not path in self._path_to_emitter: emitter = self._multiemitter.FileEmitter(path) if not template is None: emitter = emitter.Emit(template) self._path_to_emitter[path] = emitter self._dart_libraries.AddFile(basename, library_name, path) return self._path_to_emitter[path] def EmitLibraries(self, auxiliary_dir): self._dart_libraries.Emit(self._multiemitter, auxiliary_dir) # ------------------------------------------------------------------------------ class DartLibrary(): def __init__(self, name, template_loader, library_type, output_dir): self._template = template_loader.Load( '%s_%s.darttemplate' % (name, library_type)) self._dart_path = os.path.join( output_dir, '%s_%s.dart' % (name, library_type)) self._paths = [] def AddFile(self, path): self._paths.append(path) def Emit(self, emitter, auxiliary_dir): def massage_path(path): # The most robust way to emit path separators is to use / always. return path.replace('\\', '/') library_emitter = emitter.FileEmitter(self._dart_path) library_file_dir = os.path.dirname(self._dart_path) auxiliary_dir = os.path.relpath(auxiliary_dir, library_file_dir) imports_emitter = library_emitter.Emit( self._template, AUXILIARY_DIR=massage_path(auxiliary_dir)) for path in sorted(self._paths): relpath = os.path.relpath(path, library_file_dir) imports_emitter.Emit( "part '$PATH';\n", PATH=massage_path(relpath)) # ------------------------------------------------------------------------------ class DartLibraries(): def __init__(self, libraries, template_loader, library_type, output_dir): self._libraries = {} for library_name in libraries: self._libraries[library_name] = DartLibrary( library_name, template_loader, library_type, output_dir) def AddFile(self, basename, library_name, path): self._libraries[library_name].AddFile(path) def Emit(self, emitter, auxiliary_dir): for lib in self._libraries.values(): lib.Emit(emitter, auxiliary_dir)