67bda7c22b
https://github.com/dart-lang/sdk/issues/56455 The existing native typed data implementation in dart2js/ddc and the JS typed data wrappers in dart2wasm do not support SharedArrayBuffers. In dart2js/ddc, this is because the native type for ByteBuffer is simply ArrayBuffer, leading to type failures when using SharedArrayBuffers. To handle this, this change makes NativeByteBuffer an abstract parent class to NativeArrayBuffer and NativeSharedArrayBuffer. This allows ByteBuffer to support both types. There is a preexisting SharedArrayBuffer type in dart:html that we should avoid breaking, so we add an interface that NativeSharedArrayBuffer implements and expose that interface. In dart2wasm, JSArrayBufferImpl only allows ArrayBuffers as its extern ref. This change makes that wrapper support SharedArrayBuffers as well. In dart:js_interop, the existing toJS conversion on ByteBuffer now throws if the underlying buffer was actually a SharedArrayBuffer. This is to support the return type of JSArrayBuffer. This behavior technically already existed due to type differences in the JS compilers, but was never possible with dart2wasm. CoreLibraryReviewExempt: Backend-specific libraries with no real functional changes to public APIs. Change-Id: I4dac9fb808590bf0c274da815c152cd4637316b1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/437526 Reviewed-by: Stephen Adams <sra@google.com> Commit-Queue: Srujan Gaddam <srujzs@google.com>
2352 lines
93 KiB
Python
2352 lines
93 KiB
Python
#!/usr/bin/env python3
|
|
# 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 logging
|
|
import monitored
|
|
import os
|
|
import re
|
|
from collections import OrderedDict
|
|
from generator import *
|
|
from htmldartgenerator import *
|
|
from htmlrenamer import generateCallbackInterface
|
|
|
|
_logger = logging.getLogger('systemhtml')
|
|
|
|
|
|
def CanUseStaticExtensions(interface, should):
|
|
if not should:
|
|
return False
|
|
static_extension_interfaces = [] # Classes to be migrated
|
|
return interface in static_extension_interfaces
|
|
|
|
|
|
HTML_LIBRARY_NAMES = [
|
|
'html', 'indexed_db', 'svg', 'web_audio', 'web_gl', 'web_sql'
|
|
]
|
|
|
|
_safe_to_ignore_shadowing_members = monitored.Set(
|
|
'systemhtml._safe_to_ignore_shadowing_members', [
|
|
'SVGElement.tabIndex',
|
|
'SVGStyleElement.title',
|
|
])
|
|
|
|
_js_custom_members = monitored.Set(
|
|
'systemhtml._js_custom_members',
|
|
[
|
|
'AudioContext.createGain',
|
|
'AudioContext.createScriptProcessor',
|
|
'CanvasRenderingContext2D.drawImage',
|
|
'CanvasRenderingContext2D.fillText',
|
|
'CanvasRenderingContext2D.lineDashOffset',
|
|
'CanvasRenderingContext2D.setLineDash',
|
|
'Console.memory',
|
|
'ConsoleBase.assertCondition',
|
|
'ConsoleBase.clear',
|
|
'ConsoleBase.count',
|
|
'ConsoleBase.countReset',
|
|
'ConsoleBase.debug',
|
|
'ConsoleBase.dir',
|
|
'ConsoleBase.dirxml',
|
|
'ConsoleBase.error',
|
|
'ConsoleBase.group',
|
|
'ConsoleBase.groupCollapsed',
|
|
'ConsoleBase.groupEnd',
|
|
'ConsoleBase.info',
|
|
'ConsoleBase.log',
|
|
'ConsoleBase.markTimeline',
|
|
'ConsoleBase.profile',
|
|
'ConsoleBase.profileEnd',
|
|
'ConsoleBase.table',
|
|
'ConsoleBase.time',
|
|
'ConsoleBase.timeEnd',
|
|
'ConsoleBase.timeStamp',
|
|
'ConsoleBase.trace',
|
|
'ConsoleBase.warn',
|
|
'WebKitCSSKeyframesRule.insertRule',
|
|
'CSSStyleDeclaration.setProperty',
|
|
'CSSStyleDeclaration.__propertyQuery__',
|
|
'Document.createNodeIterator',
|
|
'Document.createTreeWalker',
|
|
'DOMException.name',
|
|
'DOMException.toString',
|
|
# ListMixin already provides this method although the implementation
|
|
# is slower. As this class is obsolete anyway, we ignore the slowdown in
|
|
# DOMStringList performance.
|
|
'DOMStringList.contains',
|
|
'Element.animate',
|
|
'Element.createShadowRoot',
|
|
'Element.insertAdjacentElement',
|
|
'Element.insertAdjacentHTML',
|
|
'Element.insertAdjacentText',
|
|
'Element.remove',
|
|
'Element.shadowRoot',
|
|
'Element.matches',
|
|
'ElementEvents.mouseWheel',
|
|
'ElementEvents.transitionEnd',
|
|
'FileReader.result',
|
|
'HTMLAnchorElement.toString',
|
|
'HTMLAreaElement.toString',
|
|
'HTMLTableElement.createTBody',
|
|
'IDBCursor.next',
|
|
'IDBDatabase.transaction',
|
|
'IDBDatabase.transactionList',
|
|
'IDBDatabase.transactionStore',
|
|
'IDBDatabase.transactionStores',
|
|
'KeyboardEvent.initKeyboardEvent',
|
|
'Location.origin',
|
|
'Location.toString',
|
|
'MouseEvent.offsetX',
|
|
'MouseEvent.offsetY',
|
|
'Navigator.language',
|
|
'Navigator.webkitGetUserMedia',
|
|
'ScriptProcessorNode._setEventListener',
|
|
'URL.createObjectURL',
|
|
'URL.createObjectUrlFromSource',
|
|
'URL.createObjectUrlFromStream',
|
|
'URL.createObjectUrlFromBlob',
|
|
'URL.revokeObjectURL',
|
|
'URL.toString',
|
|
'WheelEvent.deltaMode',
|
|
'Window.cancelAnimationFrame',
|
|
'Window.console',
|
|
'Window.document',
|
|
'Window.indexedDB',
|
|
'Window.location',
|
|
'Window.open',
|
|
'Window.requestAnimationFrame',
|
|
'Window.scrollX',
|
|
'Window.scrollY'
|
|
# 'WorkerContext.indexedDB', # Workers
|
|
],
|
|
dart2jsOnly=True)
|
|
|
|
_js_custom_constructors = monitored.Set(
|
|
'systemhtml._js_custom_constructors', [
|
|
'AudioContext',
|
|
'Blob',
|
|
'Comment',
|
|
'MutationObserver',
|
|
'PaymentRequest',
|
|
'RTCIceCandidate',
|
|
'RTCPeerConnection',
|
|
'RTCSessionDescription',
|
|
'SpeechRecognition',
|
|
],
|
|
dart2jsOnly=True)
|
|
|
|
# Classes that offer only static methods, and therefore we should suppress
|
|
# constructor creation.
|
|
_static_classes = set(['Url'])
|
|
|
|
# Callback typedefs with generic List (List<nnn>) convert to List
|
|
_callback_list_generics_mapping = monitored.Set(
|
|
'systemhtml._callback_list_generics_mapping', [
|
|
'List<Entry>',
|
|
'List<IntersectionObserverEntry>',
|
|
'List<MutationRecord>',
|
|
'List<_Report>',
|
|
'List<ResizeObserverEntry>',
|
|
])
|
|
|
|
|
|
# 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
|
|
# optional parameters are always nullable
|
|
info.param_infos = [
|
|
ParamInfo(name=tXn[1],
|
|
type_id=tXn[0],
|
|
is_optional=True,
|
|
is_nullable=True,
|
|
default_value=None,
|
|
default_value_is_null=False) for tXn in 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',
|
|
'HTMLMetaElement':
|
|
'meta',
|
|
'HTMLMeterElement':
|
|
'meter',
|
|
'HTMLOListElement':
|
|
'ol',
|
|
'HTMLObjectElement':
|
|
'object',
|
|
'HTMLOptGroupElement':
|
|
'optgroup',
|
|
'HTMLOutputElement':
|
|
'output',
|
|
'HTMLParagraphElement':
|
|
'p',
|
|
'HTMLParamElement':
|
|
'param',
|
|
'HTMLPreElement':
|
|
'pre',
|
|
'HTMLProgressElement':
|
|
'progress',
|
|
'HTMLQuoteElement':
|
|
'q',
|
|
'HTMLScriptElement':
|
|
'script',
|
|
'HTMLSelectElement':
|
|
'select',
|
|
'HTMLShadowElement':
|
|
'shadow',
|
|
'HTMLSourceElement':
|
|
'source',
|
|
'HTMLSpanElement':
|
|
'span',
|
|
'HTMLStyleElement':
|
|
'style',
|
|
'HTMLTableCaptionElement':
|
|
'caption',
|
|
'HTMLTableCellElement':
|
|
'td',
|
|
'HTMLTableColElement':
|
|
'col',
|
|
'HTMLTableElement':
|
|
'table',
|
|
'HTMLTableRowElement':
|
|
'tr',
|
|
#'HTMLTableSectionElement' <thead> <tbody> <tfoot>
|
|
'HTMLTemplateElement':
|
|
'template',
|
|
'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,
|
|
'typed_data': {},
|
|
'web_audio': {},
|
|
'web_gl': {},
|
|
'web_sql': {},
|
|
}
|
|
|
|
_factory_ctr_strings = {
|
|
'html': {
|
|
'provider_name': 'document',
|
|
'constructor_name': 'createElement'
|
|
},
|
|
'indexed_db': {
|
|
'provider_name': 'document',
|
|
'constructor_name': 'createElement'
|
|
},
|
|
'svg': {
|
|
'provider_name': '_SvgElementFactoryProvider',
|
|
'constructor_name': 'createSvgElement_tag',
|
|
},
|
|
'typed_data': {
|
|
'provider_name': 'document',
|
|
'constructor_name': 'createElement'
|
|
},
|
|
'web_audio': {
|
|
'provider_name': 'document',
|
|
'constructor_name': 'createElement'
|
|
},
|
|
'web_gl': {
|
|
'provider_name': 'document',
|
|
'constructor_name': 'createElement'
|
|
},
|
|
'web_sql': {
|
|
'provider_name': 'document',
|
|
'constructor_name': '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',
|
|
'HTMLTemplateElement',
|
|
'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(
|
|
list({
|
|
'Animation':
|
|
"JS('bool', '!!(document.body.animate)')",
|
|
'AudioContext':
|
|
"JS('bool', '!!(window.AudioContext ||"
|
|
" window.webkitAudioContext)')",
|
|
'Crypto':
|
|
"JS('bool', '!!(window.crypto && window.crypto.getRandomValues)')",
|
|
'Database':
|
|
"JS('bool', '!!(window.openDatabase)')",
|
|
'DOMPoint':
|
|
"JS('bool', '!!(window.DOMPoint) || !!(window.WebKitPoint)')",
|
|
'ApplicationCache':
|
|
"JS('bool', '!!(window.applicationCache)')",
|
|
'DOMFileSystem':
|
|
"JS('bool', '!!(window.webkitRequestFileSystem)')",
|
|
'FormData':
|
|
"JS('bool', '!!(window.FormData)')",
|
|
'HashChangeEvent':
|
|
"Device.isEventTypeSupported('HashChangeEvent')",
|
|
'HTMLShadowElement':
|
|
ElemSupportStr('shadow'),
|
|
'HTMLTemplateElement':
|
|
ElemSupportStr('template'),
|
|
'MediaStreamEvent':
|
|
"Device.isEventTypeSupported('MediaStreamEvent')",
|
|
'MediaStreamTrackEvent':
|
|
"Device.isEventTypeSupported('MediaStreamTrackEvent')",
|
|
'MediaSource':
|
|
"JS('bool', '!!(window.MediaSource)')",
|
|
'Notification':
|
|
"JS('bool', '!!(window.Notification)')",
|
|
'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')",
|
|
'WebGLRenderingContext':
|
|
"JS('bool', '!!(window.WebGLRenderingContext)')",
|
|
'WebSocket':
|
|
"JS('bool', 'typeof window.WebSocket != \"undefined\"')",
|
|
'Worker':
|
|
"JS('bool', '(typeof window.Worker != \"undefined\")')",
|
|
'XSLTProcessor':
|
|
"JS('bool', '!!(window.XSLTProcessor)')",
|
|
}.items()) + list(
|
|
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()))
|
|
|
|
# JavaScript element class names of elements for which createElement does not
|
|
# always return exactly the right element, either because it might not be
|
|
# supported, or some browser does something weird.
|
|
_js_unreliable_element_factories = set(
|
|
_js_support_checks_basic_element_with_constructors +
|
|
_js_support_checks_additional_element + [
|
|
'HTMLEmbedElement',
|
|
'HTMLObjectElement',
|
|
'HTMLShadowElement',
|
|
'HTMLTemplateElement',
|
|
])
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
class HtmlDartInterfaceGenerator(object):
|
|
"""Generates dart interface and implementation for the DOM IDL interface."""
|
|
|
|
def __init__(self, options, library_emitter, event_generator,
|
|
prototype_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._prototype_event_generator = prototype_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)
|
|
self._metadata = options.metadata
|
|
|
|
def Generate(self):
|
|
if IsCustomType(self._interface.id):
|
|
pass
|
|
elif 'Callback' in self._interface.ext_attrs:
|
|
if len(GetCallbackHandlers(self._interface)) > 0:
|
|
self.GenerateCallback()
|
|
elif generateCallbackInterface(self._interface.id):
|
|
self.GenerateInterface()
|
|
else:
|
|
return
|
|
else:
|
|
self.GenerateInterface()
|
|
|
|
def GenerateCallback(self):
|
|
"""Generates a typedef for the callback interface."""
|
|
typedef_name = self._renamer.RenameInterface(self._interface)
|
|
if not typedef_name:
|
|
return
|
|
|
|
info = GetCallbackInfo(self._interface)
|
|
code = self._library_emitter.FileEmitter(self._interface.id,
|
|
self._library_name)
|
|
code.Emit(self._template_loader.Load('callback.darttemplate'))
|
|
|
|
annotations = self._metadata.GetFormattedMetadata(
|
|
self._library_name, self._interface)
|
|
|
|
params = info.ParametersAsDeclaration(self._DartType)
|
|
|
|
types = params.split()
|
|
if len(types) > 0:
|
|
mapType = types[0] in _callback_list_generics_mapping
|
|
if mapType is True:
|
|
types[0] = 'List'
|
|
params = " ".join(types)
|
|
|
|
code.Emit(
|
|
'$(ANNOTATIONS)typedef void $NAME($PARAMS);\n',
|
|
ANNOTATIONS=annotations,
|
|
NAME=typedef_name,
|
|
PARAMS=params)
|
|
self._backend.GenerateCallback(info)
|
|
|
|
def GenerateInterface(self):
|
|
interface_name = self._interface_type_info.interface_name()
|
|
if (self._renamer.ShouldNotGenerateInterface(self._interface)):
|
|
return
|
|
|
|
implementation_name = self._interface_type_info.implementation_name()
|
|
self._library_emitter.AddTypeEntry(
|
|
self._library_name, self._interface.id, implementation_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 and factory_provider != info.factory_provider_name:
|
|
_logger.warn('Conflicting factory provider names: %s != %s' %
|
|
(factory_provider, info.factory_provider_name))
|
|
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, self._database):
|
|
base_type_info = self._type_registry.TypeInfo(supertype)
|
|
|
|
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:
|
|
# Get rid of duplicates using OrderedDict.
|
|
implements = list(OrderedDict([(i, None) for i in implements]))
|
|
implements_str = ' implements ' + ', '.join(implements)
|
|
|
|
mixins = self._backend.Mixins()
|
|
|
|
mixins_str = ''
|
|
if mixins:
|
|
mixins_str = ' with ' + ', '.join(mixins)
|
|
if not base_class:
|
|
base_class = 'JavaScriptObject'
|
|
elif (base_class == 'NativeFieldWrapperClass2' and
|
|
self._options.dart_js_interop and
|
|
not (isinstance(self._backend, Dart2JSBackend))):
|
|
base_class = 'DartHtmlDomObject'
|
|
|
|
annotations = self._metadata.GetFormattedMetadata(
|
|
self._library_name, self._interface, None, '')
|
|
|
|
class_modifiers = ''
|
|
if (self._renamer.ShouldSuppressInterface(self._interface) or
|
|
IsPureInterface(self._interface.id, self._database)):
|
|
# XMLHttpRequestProgressEvent can't be abstract we need to instantiate
|
|
# for JsInterop.
|
|
if (not (isinstance(self._backend, Dart2JSBackend)) and
|
|
(self._interface.id == 'XMLHttpRequestProgressEvent' or
|
|
self._interface.id == 'DOMStringMap')):
|
|
# Suppress abstract for XMLHttpRequestProgressEvent and DOMStringMap
|
|
# for Dartium. Need to be able to instantiate the class; can't be abstract.
|
|
class_modifiers = ''
|
|
else:
|
|
# For Dartium w/ JsInterop these suppressed interfaces are needed to
|
|
# instantiate the internal classes.
|
|
if (self._renamer.ShouldSuppressInterface(self._interface) and
|
|
not (isinstance(self._backend, Dart2JSBackend)) and
|
|
self._options.dart_js_interop):
|
|
class_modifiers = ''
|
|
else:
|
|
class_modifiers = 'abstract '
|
|
|
|
native_spec = ''
|
|
if not IsPureInterface(self._interface.id, self._database):
|
|
native_spec = self._backend.NativeSpec()
|
|
|
|
class_name = self._interface_type_info.implementation_name()
|
|
|
|
js_interop_wrapper = '''
|
|
|
|
@Deprecated("Internal Use Only")
|
|
external static Type get instanceRuntimeType;
|
|
|
|
@Deprecated("Internal Use Only")
|
|
{0}.internal_() : super.internal_();
|
|
|
|
'''.format(class_name)
|
|
if base_class == 'NativeFieldWrapperClass2' or base_class == 'DartHtmlDomObject':
|
|
js_interop_wrapper = '''
|
|
|
|
@Deprecated("Internal Use Only")
|
|
external static Type get instanceRuntimeType;
|
|
|
|
@Deprecated("Internal Use Only")
|
|
{0}.internal_() {{ }}
|
|
'''.format(class_name)
|
|
# Change to use the synthesized class so we can construct with a mixin
|
|
# classes prefixed with name of NativeFieldWrapperClass2 don't have a
|
|
# default constructor so classes with mixins can't be new'd.
|
|
if (self._options.templates._conditions['DARTIUM'] and
|
|
self._options.dart_js_interop and
|
|
(self._interface.id == 'NamedNodeMap' or
|
|
self._interface.id == 'CSSStyleDeclaration')):
|
|
base_class = 'DartHtmlDomObject'
|
|
|
|
maplikeKeyType = ''
|
|
maplikeValueType = ''
|
|
if self._interface.isMaplike:
|
|
maplikeKeyType = self._type_registry.\
|
|
_TypeInfo(self._interface.maplike_key_value[0].id).dart_type()
|
|
maplikeValueType = 'dynamic'
|
|
mixins_str = " with MapMixin<%s, %s>" % (maplikeKeyType,
|
|
maplikeValueType)
|
|
|
|
implementation_members_emitter = implementation_emitter.Emit(
|
|
self._backend.ImplementationTemplate(),
|
|
LIBRARYNAME='dart.dom.%s' % self._library_name,
|
|
ANNOTATIONS=annotations,
|
|
CLASS_MODIFIERS=class_modifiers,
|
|
CLASSNAME=class_name,
|
|
EXTENDS=' extends %s' % base_class if base_class else '',
|
|
IMPLEMENTS=implements_str,
|
|
MIXINS=mixins_str,
|
|
DOMNAME=self._interface.doc_js_name,
|
|
NATIVESPEC=native_spec,
|
|
KEYTYPE=maplikeKeyType,
|
|
VALUETYPE=maplikeValueType,
|
|
NULLABLE='?',
|
|
NULLSAFECAST=True,
|
|
NULLASSERT='!')
|
|
if self._interface.doc_js_name is 'RadioNodeList':
|
|
print(self._backend.ImplementationTemplate())
|
|
print(implementation_members_emitter)
|
|
stream_getter_signatures_emitter = None
|
|
element_stream_getters_emitter = None
|
|
class_members_emitter = None
|
|
if type(implementation_members_emitter) == tuple:
|
|
# We add event stream getters for both Element and ElementList, so in
|
|
# impl_Element.darttemplate, we have two additional "holes" for emitters
|
|
# to fill in, with small variations. These store these specialized
|
|
# emitters.
|
|
if (len(implementation_members_emitter) == 3):
|
|
stream_getter_signatures_emitter = \
|
|
implementation_members_emitter[0]
|
|
element_stream_getters_emitter = implementation_members_emitter[
|
|
1]
|
|
implementation_members_emitter = \
|
|
implementation_members_emitter[2]
|
|
|
|
# We add special emitters for classes migrated to static type extensions
|
|
elif (len(implementation_members_emitter) == 2):
|
|
class_members_emitter = \
|
|
implementation_members_emitter[0]
|
|
implementation_members_emitter = \
|
|
implementation_members_emitter[1]
|
|
self._backend.StartInterface(implementation_members_emitter)
|
|
self._backend.EmitHelpers(base_class, class_members_emitter)
|
|
self._event_generator.EmitStreamProviders(
|
|
self._interface, self._backend.CustomJSMembers(),
|
|
implementation_members_emitter, self._library_name)
|
|
self._prototype_event_generator.CollectStreamProviders(
|
|
self._interface, self._backend.CustomJSMembers(),
|
|
self._library_name)
|
|
self._backend.AddConstructors(constructors, factory_provider,
|
|
factory_constructor_name,
|
|
class_members_emitter)
|
|
|
|
# Write out the JsInterop code.
|
|
if (implementation_members_emitter and
|
|
self._options.templates._conditions['DARTIUM'] and
|
|
self._options.dart_js_interop and
|
|
not IsPureInterface(self._interface.id, self._database)):
|
|
implementation_members_emitter.Emit(js_interop_wrapper)
|
|
|
|
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, False,
|
|
self._options.dart_js_interop)
|
|
self._backend.AddSecondaryMembers(self._interface)
|
|
self._event_generator.EmitStreamGetters(
|
|
self._interface, [], implementation_members_emitter,
|
|
self._library_name, stream_getter_signatures_emitter,
|
|
element_stream_getters_emitter)
|
|
self._prototype_event_generator.CollectStreamGetters(
|
|
self._interface, [], 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)
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
''' TODO(terry): Current idl_parser (Chrome) doesn't keep the Promise type e.g.,
|
|
Promise<T> in the AST so there is no way to pull this out. Need
|
|
to investigate getting the Chrome folks to fix. However, they
|
|
don't use this in the C++ code generation and don't have a need
|
|
for this feature. For now I have a table that maps to the
|
|
parameterized Promise type.
|
|
'''
|
|
promise_attributes = monitored.Dict(
|
|
'systemhtml.promise_attr_type', {
|
|
"Animation.finished": {
|
|
"type": "Animation",
|
|
"creates": "Animation"
|
|
},
|
|
"Animation.ready": {
|
|
"type": "Animation",
|
|
"creates": "Animation"
|
|
},
|
|
"BeforeInstallPromptEvent.userChoice": {
|
|
"type": "dictionary"
|
|
},
|
|
"FontFace.loaded": {
|
|
"type": "FontFace",
|
|
"creates": "FontFace"
|
|
},
|
|
"FontFaceSet.ready": {
|
|
"type": "FontFaceSet",
|
|
"creates": "FontFaceSet"
|
|
},
|
|
"MediaKeySession.closed": {
|
|
"type": "void"
|
|
},
|
|
"PresentationReceiver.connectionList": {
|
|
"type": "PresentationConnectionList",
|
|
"creates": "PresentationConnectionList"
|
|
},
|
|
"ServiceWorkerContainer.ready": {
|
|
"type": "ServiceWorkerRegistration",
|
|
"creates": "ServiceWorkerRegistration"
|
|
},
|
|
})
|
|
|
|
promise_operations = monitored.Dict(
|
|
'systemhtml.promise_oper_type', {
|
|
"Clipboard.read": {
|
|
"type": "DataTransfer",
|
|
"creates": "DataTransfer"
|
|
},
|
|
"Clipboard.readText": {
|
|
"type": "String"
|
|
},
|
|
"FontFace.load": {
|
|
"type": "FontFace",
|
|
"creates": "FontFace"
|
|
},
|
|
"FontFaceSet.load": {
|
|
"type": "List<dynamic>"
|
|
},
|
|
"OffscreenCanvas.load": {
|
|
"type": "Blob",
|
|
"creates": "Blob"
|
|
},
|
|
"BackgroundFetchManager.fetch": {
|
|
"type": "BackgroundFetchRegistration",
|
|
"creates": "BackgroundFetchRegistration"
|
|
},
|
|
"BackgroundFetchManager.get": {
|
|
"type": "BackgroundFetchRegistration",
|
|
"creates": "BackgroundFetchRegistration"
|
|
},
|
|
"BackgroundFetchManager.getIds": {
|
|
"type": "List<dynamic>"
|
|
},
|
|
"BackgroundFetchRegistration.abort": {
|
|
"type": "bool"
|
|
},
|
|
"SyncManager.getTags": {
|
|
"type": "List<dynamic>"
|
|
},
|
|
"BudgetService.getCost": {
|
|
"type": "double"
|
|
},
|
|
"BudgetService.getBudget": {
|
|
"type": "BudgetState",
|
|
"creates": "BudgetState"
|
|
},
|
|
"BudgetService.reserve": {
|
|
"type": "bool"
|
|
},
|
|
"Body.blob": {
|
|
"type": "Blob",
|
|
"creates": "Blob"
|
|
},
|
|
"Body.formData": {
|
|
"type": "FormData",
|
|
"creates": "FormData"
|
|
},
|
|
"Body.text": {
|
|
"type": "String"
|
|
},
|
|
"ImageCapture.getPhotoCapabilities": {
|
|
"type": "PhotoCapabilities",
|
|
"creates": "PhotoCapabilities"
|
|
},
|
|
"ImageCapture.getPhotoSettings": {
|
|
"type": "dictionary"
|
|
},
|
|
"ImageCapture.takePhoto": {
|
|
"type": "Blob",
|
|
"creates": "Blob"
|
|
},
|
|
"ImageCapture.grabFrame": {
|
|
"type": "ImageBitmap",
|
|
"creates": "ImageBitmap"
|
|
},
|
|
"Navigator.getInstalledRelatedApps": {
|
|
"type": "RelatedApplication",
|
|
"creates": "RelatedApplication"
|
|
},
|
|
"OffscreenCanvas.convertToBlob": {
|
|
"type": "Blob",
|
|
"creates": "Blob"
|
|
},
|
|
"MediaCapabilities.decodingInfo": {
|
|
"type": "MediaCapabilitiesInfo",
|
|
"creates": "MediaCapabilitiesInfo"
|
|
},
|
|
"MediaCapabilities.encodingInfo": {
|
|
"type": "MediaCapabilitiesInfo",
|
|
"creates": "MediaCapabilitiesInfo"
|
|
},
|
|
"MediaDevices.enumerateDevices": {
|
|
"type": "List<dynamic>"
|
|
},
|
|
"MediaDevices.getUserMedia": {
|
|
"type": "MediaStream",
|
|
"creates": "MediaStream"
|
|
},
|
|
"ServiceWorkerRegistration.getNotifications": {
|
|
"type": "List<dynamic>"
|
|
},
|
|
"PaymentInstruments.delete": {
|
|
"type": "bool"
|
|
},
|
|
"PaymentInstruments.get": {
|
|
"type": "dictionary"
|
|
},
|
|
"PaymentInstruments.keys": {
|
|
"type": "List<dynamic>"
|
|
},
|
|
"PaymentInstrumentshas.": {
|
|
"type": "bool"
|
|
},
|
|
"PaymentRequest.show": {
|
|
"type": "PaymentResponse",
|
|
"creates": "PaymentResponse"
|
|
},
|
|
"PaymentRequest.canMakePayment": {
|
|
"type": "bool"
|
|
},
|
|
"PaymentRequestEvent.openWindow": {
|
|
"type": "WindowClient",
|
|
"creates": "WindowClient"
|
|
},
|
|
"RTCPeerConnection.createOffer": {
|
|
"type": "RtcSessionDescription",
|
|
"creates": "RtcSessionDescription"
|
|
},
|
|
"RTCPeerConnection.createAnswer": {
|
|
"type": "RtcSessionDescription",
|
|
"creates": "RtcSessionDescription"
|
|
},
|
|
"RTCPeerConnection.getStats": {
|
|
"type": "RtcStatsReport",
|
|
"maplike": "RTCStatsReport",
|
|
"creates": "RtcStatsReport"
|
|
},
|
|
"RTCPeerConnection.generateCertificate": {
|
|
"type": "RtcCertificate",
|
|
"creates": "RtcCertificate"
|
|
},
|
|
"Permissions.query": {
|
|
"type": "PermissionStatus",
|
|
"creates": "PermissionStatus"
|
|
},
|
|
"Permissions.request": {
|
|
"type": "PermissionStatus",
|
|
"creates": "PermissionStatus"
|
|
},
|
|
"Permissions.revoke": {
|
|
"type": "PermissionStatus",
|
|
"creates": "PermissionStatus"
|
|
},
|
|
"Permissions.requestAll": {
|
|
"type": "PermissionStatus",
|
|
"creates": "PermissionStatus"
|
|
},
|
|
"PresentationRequest.start": {
|
|
"type": "PresentationConnection",
|
|
"creates": "PresentationConnection"
|
|
},
|
|
"PresentationRequest.reconnect": {
|
|
"type": "PresentationConnection",
|
|
"creates": "PresentationConnection"
|
|
},
|
|
"PresentationRequest.getAvailability": {
|
|
"type": "PresentationAvailability",
|
|
"creates": "PresentationAvailability"
|
|
},
|
|
"PushManager.subscribe": {
|
|
"type": "PushSubscription",
|
|
"creates": "PushSubscription"
|
|
},
|
|
"PushManager.getSubscription": {
|
|
"type": "PushSubscription",
|
|
"creates": "PushSubscription"
|
|
},
|
|
"PushSubscription.unsubscribe": {
|
|
"type": "bool"
|
|
},
|
|
"StorageManager.persisted": {
|
|
"type": "bool"
|
|
},
|
|
"StorageManager.persist": {
|
|
"type": "bool"
|
|
},
|
|
"StorageManager.estimate": {
|
|
"type": "dictionary"
|
|
},
|
|
"RemotePlayback.watchAvailability": {
|
|
"type": "int"
|
|
},
|
|
"Clients.matchAll": {
|
|
"type": "List<dynamic>"
|
|
},
|
|
"Clients.openWindow": {
|
|
"type": "WindowClient",
|
|
"creates": "WindowClient"
|
|
},
|
|
"NavigationPreloadManager.getState": {
|
|
"type": "dictionary"
|
|
},
|
|
"ServiceWorkerContainer.register": {
|
|
"type": "ServiceWorkerRegistration",
|
|
"creates": "ServiceWorkerRegistration"
|
|
},
|
|
"ServiceWorkerContainer.getRegistration": {
|
|
"type": "ServiceWorkerRegistration",
|
|
"creates": "ServiceWorkerRegistration"
|
|
},
|
|
"ServiceWorkerContainer.getRegistrations": {
|
|
"type": "List<dynamic>"
|
|
},
|
|
"ServiceWorkerGlobalScope.fetch": {
|
|
"creates": "_Response"
|
|
},
|
|
"ServiceWorkerRegistration.unregister": {
|
|
"type": "bool"
|
|
},
|
|
"WindowClient.focus": {
|
|
"type": "WindowClient",
|
|
"creates": "WindowClient"
|
|
},
|
|
"WindowClient.navigate": {
|
|
"type": "WindowClient",
|
|
"creates": "WindowClient"
|
|
},
|
|
"BarcodeDetector.detect": {
|
|
"type": "List<dynamic>"
|
|
},
|
|
"FaceDetector.detect": {
|
|
"type": "List<dynamic>"
|
|
},
|
|
"TextDetector.detect": {
|
|
"type": "List<dynamic>"
|
|
},
|
|
"BaseAudioContext.decodeAudioData": {
|
|
"type": "AudioBuffer",
|
|
"creates": "AudioBuffer"
|
|
},
|
|
"OfflineAudioContext.startRendering": {
|
|
"type": "AudioBuffer",
|
|
"creates": "AudioBuffer"
|
|
},
|
|
"CacheStorage.match": {
|
|
"creates": "_Response"
|
|
},
|
|
"CacheStorage.open": {
|
|
"creates": "_Cache"
|
|
},
|
|
"CredentialsContainer.create": {
|
|
"creates": "Credential"
|
|
},
|
|
"CredentialsContainer.get": {
|
|
"creates": "Credential"
|
|
},
|
|
"CredentialsContainer.store": {
|
|
"creates": "Credential"
|
|
},
|
|
"FetchEvent.preloadResponse": {
|
|
"creates": "_Response"
|
|
},
|
|
"MediaKeySystemAccess.createMediaKeys": {
|
|
"creates": "MediaKeys"
|
|
},
|
|
"Navigator.getVRDisplays": {
|
|
"creates": "VRDisplay"
|
|
},
|
|
"Navigator.requestMediaKeySystemAccess": {
|
|
"creates": "MediaKeySystemAccess"
|
|
},
|
|
"VRSession.requestFrameOfReference": {
|
|
"creates": "VRFrameOfReference"
|
|
},
|
|
"Window.fetch": {
|
|
"creates": "_Response"
|
|
},
|
|
"WorkerGlobalScope.fetch": {
|
|
"creates": "_Response"
|
|
},
|
|
})
|
|
|
|
promise_generateCall = monitored.Set('systemhtml.promise_generateCall', [
|
|
"Navigator.requestKeyboardLock",
|
|
])
|
|
|
|
|
|
def _IsPromiseOperationGenerateCall(interface_operation):
|
|
return interface_operation in promise_generateCall
|
|
|
|
|
|
def _GetPromiseOperationType(interface_operation):
|
|
if interface_operation in promise_operations:
|
|
return promise_operations[interface_operation]
|
|
return None
|
|
|
|
|
|
def _GetPromiseAttributeType(interface_operation):
|
|
if interface_operation in promise_attributes:
|
|
return promise_attributes[interface_operation]
|
|
return None
|
|
|
|
# Compatibility is used to help determine attribute nullability i.e. if the
|
|
# attribute is not compatible across all browsers, the getter/setter is marked
|
|
# as nullable. There are cases where the attribute belongs to a class that
|
|
# implements an interface whose methods are not in the IDL, however.
|
|
# Since attribute getters need to match their overridden method declaration,
|
|
# there are conflicts when the overriding method is not compatible, the
|
|
# overriding method is, and they're not already nullable. This dict marks the
|
|
# attributes where there is a conflict that cannot be resolved with code
|
|
# generation or within src/template files.
|
|
compat_conflicts = {
|
|
# These interfaces implement Rectangle, which is a Dart interface. In order
|
|
# to match the interface of Rectangle, they must be marked as non-nullable.
|
|
'DOMRectReadOnly': ['bottom', 'height', 'left', 'right', 'top', 'width'],
|
|
'DOMRect': ['height', 'width'],
|
|
}
|
|
|
|
|
|
class Dart2JSBackend(HtmlDartGenerator):
|
|
"""Generates a dart2js class for the dart:html library from a DOM IDL
|
|
interface.
|
|
"""
|
|
|
|
def __init__(self,
|
|
interface,
|
|
options,
|
|
logging_level=logging.WARNING,
|
|
generate_static_extensions=False):
|
|
super(Dart2JSBackend, self).__init__(interface, options, False, _logger)
|
|
|
|
self._generate_static_extensions = generate_static_extensions
|
|
self._database = options.database
|
|
self._template_loader = options.templates
|
|
self._type_registry = options.type_registry
|
|
self._renamer = options.renamer
|
|
self._metadata = options.metadata
|
|
self._interface_type_info = self._type_registry.TypeInfo(
|
|
self._interface.id)
|
|
self._interface_name = self._interface_type_info.interface_name()
|
|
self._current_secondary_parent = None
|
|
self._library_name = self._renamer.GetLibraryName(self._interface)
|
|
# Global constants for all WebGLRenderingContextBase, WebGL2RenderingContextBase, WebGLDrawBuffers
|
|
self._gl_constants = []
|
|
_logger.setLevel(logging_level)
|
|
|
|
def ImplementsMergedMembers(self):
|
|
return True
|
|
|
|
def GenerateCallback(self, info):
|
|
pass
|
|
|
|
def AdditionalImplementedInterfaces(self):
|
|
implements = super(Dart2JSBackend,
|
|
self).AdditionalImplementedInterfaces()
|
|
if self._interface_type_info.list_item_type() and self.HasIndexedGetter(
|
|
):
|
|
item_type = self._type_registry.TypeInfo(
|
|
self._interface_type_info.list_item_type()).dart_type()
|
|
if self._interface_type_info.list_item_type_nullable():
|
|
item_type += '?'
|
|
implements.append('JavaScriptIndexingBehavior<%s>' % item_type)
|
|
return implements
|
|
|
|
def NativeSpec(self):
|
|
native_spec = MakeNativeSpec(self._interface.javascript_binding_name)
|
|
return '@Native("%s")\n' % native_spec
|
|
|
|
def ImplementationTemplate(self):
|
|
template_file = ('impl_%s.darttemplate' % self._interface.doc_js_name)
|
|
template_file_content = self._template_loader.TryLoad(template_file)
|
|
if not (template_file_content):
|
|
if self._interface.isMaplike and self._interface.isMaplike_ro:
|
|
# TODO(terry): There are no mutable maplikes yet.
|
|
template_file_content = self._template_loader.Load(
|
|
'dart2js_maplike_impl.darttemplate')
|
|
|
|
else:
|
|
if CanUseStaticExtensions(self._interface_name,
|
|
self._generate_static_extensions):
|
|
template_file_content = self._template_loader.Load(
|
|
'dart2js_static_extension_impl.darttemplate')
|
|
else:
|
|
template_file_content = self._template_loader.Load(
|
|
'dart2js_impl.darttemplate')
|
|
return template_file_content
|
|
|
|
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 MakeFactoryCall(self, factory, method, arguments, constructor_info):
|
|
if factory is 'document' and method is 'createElement' \
|
|
and not ',' in arguments \
|
|
and not self._HasUnreliableFactoryConstructor():
|
|
return emitter.Format(
|
|
"JS$CAST("
|
|
"'returns:$INTERFACE_NAME;creates:$INTERFACE_NAME;new:true',"
|
|
" '#.$METHOD(#)', $FACTORY, $ARGUMENTS)",
|
|
CAST='<' + self._interface_type_info.interface_name() + '>',
|
|
INTERFACE_NAME=self._interface_type_info.interface_name(),
|
|
FACTORY=factory,
|
|
METHOD=method,
|
|
ARGUMENTS=arguments)
|
|
return emitter.Format('$FACTORY.$METHOD($ARGUMENTS)$CAST',
|
|
FACTORY=factory,
|
|
METHOD=method,
|
|
ARGUMENTS=arguments,
|
|
CAST=' as ' +
|
|
self._interface_type_info.interface_name())
|
|
|
|
def _HasUnreliableFactoryConstructor(self):
|
|
return self._interface.doc_js_name in _js_unreliable_element_factories
|
|
|
|
def IsConstructorArgumentOptional(self, argument):
|
|
return argument.optional
|
|
|
|
def EmitStaticFactoryOverload(self, constructor_info, name, arguments,
|
|
emitter):
|
|
if self._interface_type_info.has_generated_interface():
|
|
# Use dart_type name, we're generating.
|
|
interface_name = self._interface_type_info.interface_name()
|
|
else:
|
|
# Use the implementation name the interface is suppressed.
|
|
interface_name = self._interface_type_info.implementation_name()
|
|
|
|
index = len(arguments)
|
|
arguments = constructor_info.ParametersAsArgumentList(index)
|
|
if arguments:
|
|
arguments = ', ' + arguments
|
|
(emitter if (emitter != None) else self._members_emitter).Emit(
|
|
" static $INTERFACE_NAME $NAME($PARAMETERS) => "
|
|
"JS('$INTERFACE_NAME', 'new $CTOR_NAME($PLACEHOLDERS)'$ARGUMENTS);\n",
|
|
INTERFACE_NAME=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 HasIndexedGetter(self):
|
|
ext_attrs = self._interface.ext_attrs
|
|
has_indexed_getter = 'CustomIndexedGetter' in ext_attrs
|
|
for operation in self._interface.operations:
|
|
if operation.id == 'item' and 'getter' in operation.specials \
|
|
and not self._OperationRequiresConversions(operation):
|
|
has_indexed_getter = True
|
|
break
|
|
if operation.id == '__getter__' and 'getter' in operation.specials \
|
|
and not self._OperationRequiresConversions(operation):
|
|
has_indexed_getter = True
|
|
break
|
|
return has_indexed_getter
|
|
|
|
def AddIndexer(self, element_type, nullable):
|
|
"""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<T> ...
|
|
#
|
|
# In the non-root case we have to choose between:
|
|
#
|
|
# class YImpl extends XImpl { add List<T> methods; }
|
|
#
|
|
# and
|
|
#
|
|
# class YImpl extends ListBase<T> { copies of transitive XImpl methods; }
|
|
#
|
|
|
|
has_indexed_getter = self.HasIndexedGetter()
|
|
|
|
indexed_getter = False
|
|
indexed_getter_nullable = nullable
|
|
if has_indexed_getter:
|
|
indexed_getter = ('JS("%s%s", "#[#]", this, index)' %
|
|
(self.SecureOutputType(element_type),
|
|
"|Null" if nullable else ""))
|
|
else:
|
|
for op in self._interface.operations:
|
|
if op.id == 'getItem':
|
|
indexed_getter = 'this.getItem(index)'
|
|
indexed_getter_nullable = OperationTypeIsNullable(op)
|
|
break
|
|
if not indexed_getter:
|
|
for op in self._interface.operations:
|
|
if op.id == 'item':
|
|
indexed_getter = 'this.item(index)'
|
|
indexed_getter_nullable = OperationTypeIsNullable(op)
|
|
break
|
|
|
|
if indexed_getter:
|
|
self._members_emitter.Emit(
|
|
'\n'
|
|
' $TYPE operator[](int index) {\n'
|
|
' if (JS("bool", "# >>> 0 !== # || # >= #", index,\n'
|
|
' index, index, length))\n'
|
|
' throw new IndexError.withLength(index, length, indexable: this);\n'
|
|
' return $INDEXED_GETTER$NULLASSERT;\n'
|
|
' }',
|
|
INDEXED_GETTER=indexed_getter,
|
|
TYPE=self.SecureOutputType(element_type,
|
|
is_dart_type=False,
|
|
can_narrow_type=True,
|
|
nullable=nullable),
|
|
# If the type of the operation is not nullable but the getter
|
|
# is, we must assert non-null.
|
|
NULLASSERT='!' if not nullable and indexed_getter_nullable \
|
|
else '')
|
|
|
|
if 'CustomIndexedSetter' in self._interface.ext_attrs:
|
|
self._members_emitter.Emit(
|
|
'\n'
|
|
' void operator[]=(int index, $TYPE$NULLABLE value) {'
|
|
' JS("void", "#[#] = #", this, index, value); }',
|
|
TYPE=self._NarrowInputType(element_type),
|
|
NULLABLE='?' if nullable else '')
|
|
else:
|
|
theType = self._NarrowInputType(element_type)
|
|
if theType == 'DomRectList':
|
|
theType = ''
|
|
|
|
self._members_emitter.Emit(
|
|
'\n'
|
|
' void operator[]=(int index, $TYPE$NULLABLE value) {\n'
|
|
' throw new UnsupportedError("Cannot assign element of immutable List.");\n'
|
|
' }\n',
|
|
TYPE=theType,
|
|
NULLABLE='?' if nullable else '')
|
|
|
|
self.EmitListMixin(self._DartType(element_type), nullable)
|
|
|
|
def EmitAttribute(self, attribute, html_name, read_only):
|
|
if self._HasCustomImplementation(attribute.id):
|
|
return
|
|
|
|
if IsPureInterface(self._interface.id, self._database):
|
|
self._AddAttributeUsingProperties(attribute, html_name, read_only)
|
|
return
|
|
|
|
output_type = self.SecureOutputType(attribute.type.id,
|
|
can_narrow_type=read_only,
|
|
nullable=attribute.type.nullable)
|
|
|
|
rename = self._RenamingAnnotation(attribute.id, html_name)
|
|
metadata = self._Metadata(attribute.type.id, attribute.id, output_type,
|
|
attribute.type.nullable)
|
|
|
|
is_compat = self._mdn_reader.is_compatible(attribute)
|
|
|
|
# If the attribute is shadowing, we can't generate a shadowing
|
|
# getter or setter (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.
|
|
# TODO(srujzs): Determine if logic behind shadowing is still true here
|
|
# and below with the transition to natives.
|
|
(super_attribute,
|
|
super_attribute_interface) = self._FindShadowedAttribute(attribute)
|
|
|
|
if super_attribute:
|
|
if is_compat is None:
|
|
# If there is no compatibility info on this attribute, we use
|
|
# the parent attribute's compatibility info.
|
|
is_compat = self._mdn_reader.is_compatible(super_attribute)
|
|
self._mdn_reader.set_compatible(attribute, is_compat)
|
|
if read_only or self._SafeToIgnoreShadowingMember(html_name):
|
|
if attribute.type.id == super_attribute.type.id:
|
|
# Compatible attribute, use the superclass property. This
|
|
# works because JavaScript will do its own dynamic dispatch.
|
|
|
|
# Nullability is determined by attribute compatibility.
|
|
nullable = not is_compat or attribute.type.nullable
|
|
self._members_emitter.Emit(
|
|
'\n'
|
|
' // Use implementation from $SUPER.\n'
|
|
' // $GET_TYPE get $NAME native;\n'
|
|
' // void set $NAME($SET_TYPE value) native;\n',
|
|
SUPER=super_attribute_interface,
|
|
NAME=html_name,
|
|
GET_TYPE=self.SecureOutputType(attribute.type.id,
|
|
can_narrow_type=read_only,
|
|
nullable=nullable),
|
|
SET_TYPE=self.SecureOutputType(attribute.type.id,
|
|
can_narrow_type=read_only,
|
|
nullable=nullable or \
|
|
'TreatNullAs' in attribute.ext_attrs))
|
|
return
|
|
self._members_emitter.Emit('\n // Shadowing definition.')
|
|
self._AddAttributeUsingProperties(attribute, html_name, read_only,
|
|
rename, metadata)
|
|
return
|
|
|
|
# If the attribute is shadowed incompatibly in a subclass then we also
|
|
# can't just generate it as a getter/setter. In particular, this happens
|
|
# with DomMatrixReadOnly and its subclass DomMatrix. Force the
|
|
# superclass to generate getters. Hardcoding the known problem classes
|
|
# for now.
|
|
# TODO(alanknight): Fix this more generally.
|
|
if (self._interface.id == 'DOMMatrixReadOnly' or
|
|
self._interface.id == 'DOMPointReadOnly' or
|
|
self._interface.id == 'DOMRectReadOnly'):
|
|
self._AddAttributeUsingProperties(attribute, html_name, read_only,
|
|
rename, metadata)
|
|
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,
|
|
rename, metadata)
|
|
return
|
|
|
|
input_type = self._NarrowInputType(attribute.type.id)
|
|
if attribute.type.nullable or not is_compat:
|
|
input_type += '?'
|
|
if not read_only:
|
|
if attribute.type.id == 'Promise':
|
|
_logger.warn('R/W member is a Promise: %s.%s' %
|
|
(self._interface.id, html_name))
|
|
self._AddAttributeUsingProperties(attribute, html_name, read_only,
|
|
rename, metadata)
|
|
else:
|
|
if attribute.type.id == 'Promise':
|
|
lookupOp = "%s.%s" % (self._interface.id, html_name)
|
|
promiseFound = _GetPromiseAttributeType(lookupOp)
|
|
promiseType = 'Future'
|
|
promiseCall = 'promiseToFuture'
|
|
type_description = ''
|
|
if promiseFound is not (None):
|
|
paramType = promiseFound.get('type')
|
|
if 'maplike' in promiseFound:
|
|
promiseCall = 'promiseToFuture<dynamic>'
|
|
promiseType = 'Future'
|
|
elif paramType == 'dictionary':
|
|
# It's a dictionary so return as a Map.
|
|
promiseCall = 'promiseToFutureAsMap'
|
|
output_conversion = self._OutputConversion("Dictionary",
|
|
None)
|
|
nullability = '?' if output_conversion.nullable_output \
|
|
else ''
|
|
promiseType = 'Future<Map<String, dynamic>' + \
|
|
nullability + '>'
|
|
elif paramType:
|
|
promiseCall = 'promiseToFuture<%s>' % paramType
|
|
promiseType = 'Future<%s>' % paramType
|
|
|
|
if 'creates' in promiseFound:
|
|
createsType = promiseFound['creates']
|
|
type_description = 'creates:%s;' % createsType
|
|
|
|
if attribute.type.nullable:
|
|
promiseType += '?'
|
|
|
|
template = '\n $RENAME$(ANNOTATIONS)$TYPE get $NAME => '\
|
|
'$PROMISE_CALL(JS("$TYPE_DESC", "#.$NAME", this));\n'
|
|
|
|
self._members_emitter.Emit(
|
|
template,
|
|
RENAME=rename,
|
|
ANNOTATIONS=metadata,
|
|
TYPE=promiseType,
|
|
PROMISE_CALL=promiseCall,
|
|
TYPE_DESC=type_description,
|
|
NAME=html_name,)
|
|
else:
|
|
# 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=metadata,
|
|
NAME=html_name,
|
|
TYPE=input_type
|
|
if output_type == 'double' else output_type)
|
|
else:
|
|
# Transform to native getters/setters.
|
|
# TODO(srujzs): Should the logic for types and doubles from
|
|
# above and before stay the same here?
|
|
self._AddAttributeUsingProperties(attribute, html_name,
|
|
read_only, rename, metadata)
|
|
|
|
def _IsACompatibilityConflict(self, interface, attr):
|
|
if interface in compat_conflicts and attr.id in compat_conflicts[
|
|
interface]:
|
|
is_compat = self._mdn_reader.is_compatible(attr)
|
|
if is_compat or attr.type.nullable:
|
|
# Only attributes that are not compatible and not nullable
|
|
# belong in this list.
|
|
raise ValueError(
|
|
interface + '.' + attr.id +
|
|
' has no conflict between compatibility and nullability.')
|
|
else:
|
|
return True
|
|
return False
|
|
|
|
def _AddAttributeUsingProperties(self, attribute, html_name, read_only,
|
|
rename=None, metadata=None):
|
|
self._AddRenamingGetter(attribute, html_name, rename, metadata)
|
|
if not read_only:
|
|
# No metadata for setters.
|
|
self._AddRenamingSetter(attribute, html_name, rename)
|
|
|
|
def _AddRenamingGetter(self, attr, html_name, rename, metadata):
|
|
conversion = self._OutputConversion(attr.type.id, attr.id)
|
|
if conversion:
|
|
return self._AddConvertingGetter(attr, html_name, conversion)
|
|
# If the attribute is incompatible, it must be marked nullable.
|
|
is_compat = self._mdn_reader.is_compatible(attr)
|
|
return_type = self.SecureOutputType(attr.type.id,
|
|
nullable=(not is_compat) or
|
|
attr.type.nullable)
|
|
native_type = self._NarrowToImplementationType(attr.type.id)
|
|
non_null_return_type = self.SecureOutputType(attr.type.id,
|
|
nullable=False)
|
|
if self._IsACompatibilityConflict(self._interface.id, attr):
|
|
if not rename:
|
|
rename = '@JSName(\'%s\')' % html_name
|
|
template = """\n
|
|
// The following getter is incompatible with some browsers but
|
|
// must be made non-nullable to match the overridden method.
|
|
\n $RENAME
|
|
\n $METADATA
|
|
\n $STATIC $TYPE get _$HTML_NAME native;
|
|
\n
|
|
\n $STATIC $NONNULLTYPE get $HTML_NAME => _$HTML_NAME$NULLASSERT;"""
|
|
else:
|
|
if CanUseStaticExtensions(self._interface_name,
|
|
self._generate_static_extensions):
|
|
template = """\n $RENAME
|
|
\n $METADATA
|
|
\n $STATIC $TYPE get $HTML_NAME => js_util.getProperty(this, '$JSNAME');
|
|
\n"""
|
|
else:
|
|
template = """\n $RENAME
|
|
\n $METADATA
|
|
\n $STATIC $TYPE get $HTML_NAME native;
|
|
\n"""
|
|
self._members_emitter.Emit(template,
|
|
RENAME=rename if rename else '',
|
|
METADATA=metadata if metadata else '',
|
|
HTML_NAME=html_name,
|
|
STATIC='static' if attr.is_static else '',
|
|
TYPE=return_type,
|
|
NULLASSERT='!',
|
|
NONNULLTYPE=non_null_return_type,
|
|
JSNAME=rename if rename else html_name)
|
|
|
|
def _AddRenamingSetter(self, attr, html_name, rename):
|
|
conversion = self._InputConversion(attr.type.id, attr.id)
|
|
if conversion:
|
|
return self._AddConvertingSetter(attr, html_name, conversion)
|
|
nullable_type = attr.type.nullable or 'TreatNullAs' in attr.ext_attrs
|
|
# If this attr has an output conversion, it is possible that there is a
|
|
# converting getter. We need to make sure the setter type matches the
|
|
# getter type.
|
|
conversion = self._OutputConversion(attr.type.id, attr.id)
|
|
# If the attribute is incompatible, it must be marked nullable.
|
|
is_compat = self._mdn_reader.is_compatible(attr)
|
|
if (conversion and conversion.nullable_output) or not is_compat:
|
|
nullable_type = True
|
|
if self._IsACompatibilityConflict(self._interface.id, attr):
|
|
# Force non-nullable if it's a manual conflict.
|
|
nullable_type = False
|
|
if CanUseStaticExtensions(self._interface_name,
|
|
self._generate_static_extensions):
|
|
template = """\n $RENAME
|
|
\n $STATIC set $HTML_NAME($TYPE value)
|
|
=> js_util.setProperty(this, '$JSNAME', value);
|
|
\n"""
|
|
else:
|
|
template = """\n $RENAME
|
|
\n $STATIC set $HTML_NAME($TYPE value) native;
|
|
\n"""
|
|
self._members_emitter.Emit(template,
|
|
RENAME=rename if rename else '',
|
|
HTML_NAME=html_name,
|
|
STATIC='static ' if attr.is_static else '',
|
|
TYPE=self.SecureOutputType(
|
|
attr.type.id, nullable=nullable_type),
|
|
JSNAME=rename if rename else html_name)
|
|
|
|
def _AddConvertingGetter(self, attr, html_name, conversion):
|
|
# dynamic should not be marked with ?
|
|
nullable_out = conversion.nullable_output and \
|
|
not conversion.output_type == 'dynamic'
|
|
# Nullability is determined by attribute compatibility.
|
|
is_compat = self._mdn_reader.is_compatible(attr)
|
|
nullable_in = (not is_compat or attr.type.nullable) and \
|
|
not conversion.input_type == 'dynamic'
|
|
self._members_emitter.Emit(
|
|
'\n $(METADATA)$RETURN_TYPE$NULLABLE_OUT get $HTML_NAME => '
|
|
'$CONVERT(this._get_$(HTML_NAME)$NULLASSERT);'
|
|
"\n @JSName('$NAME')"
|
|
'\n $(JS_METADATA)$NATIVE_TYPE$NULLABLE_IN get _get_$HTML_NAME native;'
|
|
'\n',
|
|
METADATA=self._metadata.GetFormattedMetadata(
|
|
self._library_name, self._interface, html_name, ' '),
|
|
JS_METADATA=self._Metadata(attr.type.id, html_name,
|
|
conversion.input_type,
|
|
conversion.nullable_output),
|
|
CONVERT=conversion.function_name,
|
|
HTML_NAME=html_name,
|
|
NAME=attr.id,
|
|
RETURN_TYPE=conversion.output_type,
|
|
NULLABLE_OUT='?' if nullable_out else '',
|
|
NATIVE_TYPE=conversion.input_type,
|
|
NULLABLE_IN='?' if nullable_in else '',
|
|
NULLASSERT='!' if nullable_in and \
|
|
not conversion.nullable_input else '')
|
|
|
|
def _AddConvertingSetter(self, attr, html_name, conversion):
|
|
# If the attribute is incompatible, it must be marked nullable.
|
|
is_compat = self._mdn_reader.is_compatible(attr)
|
|
# If the attribute is nullable, the setter should be nullable.
|
|
nullable_in = ((attr.type.nullable or 'TreatNullAs' in attr.ext_attrs) \
|
|
and not conversion.input_type == 'dynamic') or not is_compat
|
|
nullable_out = conversion.nullable_output and \
|
|
not conversion.output_type == 'dynamic'
|
|
self._members_emitter.Emit(
|
|
# TODO(sra): Use metadata to provide native name.
|
|
'\n set $HTML_NAME($INPUT_TYPE$NULLABLE_IN value) {'
|
|
'\n this._set_$HTML_NAME = $CONVERT(value$NULLASSERT);'
|
|
'\n }'
|
|
'\n set _set_$HTML_NAME(/*$NATIVE_TYPE$NULLABLE_OUT*/ 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,
|
|
NULLABLE_IN='?' if nullable_in else '',
|
|
NATIVE_TYPE=conversion.output_type,
|
|
NULLABLE_OUT='?' if nullable_out else '',
|
|
NULLASSERT='!' if nullable_in and \
|
|
not conversion.nullable_input else '')
|
|
|
|
def AmendIndexer(self, element_type):
|
|
pass
|
|
|
|
def RootClassName(self):
|
|
return 'JavaScriptObject'
|
|
|
|
def OmitOperationOverrides(self):
|
|
return True
|
|
|
|
def EmitOperation(self, info, html_name, dart_js_interop=False):
|
|
"""
|
|
Arguments:
|
|
info: An OperationInfo object.
|
|
"""
|
|
if self._HasCustomImplementation(info.name):
|
|
return
|
|
|
|
if IsPureInterface(self._interface.id, self._database):
|
|
self._AddInterfaceOperation(info, html_name)
|
|
elif info.callback_args:
|
|
self._AddFutureifiedOperation(info, html_name)
|
|
else:
|
|
if any(
|
|
self._OperationRequiresConversions(op)
|
|
for op in info.overloads):
|
|
lookupOp = "%s.%s" % (self._interface.id, html_name)
|
|
if (_GetPromiseOperationType(lookupOp) or info.type_name == 'Promise') and \
|
|
not _IsPromiseOperationGenerateCall(lookupOp):
|
|
self._AddDirectNativeOperation(info, html_name)
|
|
else:
|
|
# Any conversions needed?
|
|
self._AddOperationWithConversions(info, html_name)
|
|
else:
|
|
self._AddDirectNativeOperation(info, html_name)
|
|
|
|
def _computeResultType(self, checkType):
|
|
# TODO(terry): Work around bug in dart2js compiler e.g.,
|
|
# typedef void CustomElementConstructor();
|
|
# CustomElementConstructor registerElement(String type, [Map options])
|
|
# Needs to become:
|
|
# Function registerElement(String type, [Map options])
|
|
resultType = checkType
|
|
if self._database.HasInterface(resultType):
|
|
resultInterface = self._database.GetInterface(resultType)
|
|
if 'Callback' in resultInterface.ext_attrs:
|
|
resultType = 'Function'
|
|
return resultType
|
|
|
|
def _zeroArgs(self, argsNames):
|
|
return 'JS("$TYPE_DESC", "#.$JSNAME()", this)'
|
|
|
|
def _manyArgs(self, numberArgs, argsNames):
|
|
argsPound = "#" if numberArgs == 1 else ("#, " * numberArgs)[:-2]
|
|
template = ' JS("$TYPE_DESC", "#.$JSNAME(%s)", this, %s)'
|
|
return template % (argsPound, argsNames)
|
|
|
|
""" If argument conversionsMapToDictionary is a list first entry is argument
|
|
name and second entry signals if argument is optional (True). """
|
|
|
|
def _promiseToFutureCode(self, argsNames, conversionsMapToDictionary=None):
|
|
numberArgs = argsNames.count(',') + 1
|
|
jsCall = self._zeroArgs(argsNames) if len(argsNames) == 0 else \
|
|
self._manyArgs(numberArgs, argsNames)
|
|
|
|
futureTemplate = []
|
|
if conversionsMapToDictionary is None:
|
|
futureTemplate = [
|
|
'\n'
|
|
' $RENAME$METADATA$MODIFIERS $TYPE $NAME($PARAMS) => $PROMISE_CALL(',
|
|
jsCall, ');\n'
|
|
]
|
|
else:
|
|
mapArg = conversionsMapToDictionary[0]
|
|
tempVariable = '%s_dict' % mapArg
|
|
mapArgOptional = conversionsMapToDictionary[1]
|
|
|
|
if argsNames.endswith('%s' % mapArg):
|
|
argsNames = '%s_dict' % argsNames
|
|
jsCall = self._zeroArgs(argsNames) if len(argsNames) == 0 else \
|
|
self._manyArgs(numberArgs, argsNames)
|
|
if mapArgOptional:
|
|
futureTemplate = [
|
|
# We will need to convert the Map argument to a Dictionary, test if mapArg is there (optional) then convert.
|
|
'\n'
|
|
' $RENAME$METADATA$MODIFIERS $TYPE $NAME($PARAMS) {\n',
|
|
' var ',
|
|
tempVariable,
|
|
' = null;\n',
|
|
' if (',
|
|
mapArg,
|
|
' != null) {\n',
|
|
' ',
|
|
tempVariable,
|
|
' = convertDartToNative_Dictionary(',
|
|
mapArg,
|
|
');\n',
|
|
' }\n',
|
|
' return $PROMISE_CALL(',
|
|
jsCall,
|
|
');\n',
|
|
' }\n'
|
|
]
|
|
else:
|
|
futureTemplate = [
|
|
# We will need to convert the Map argument to a Dictionary, the Map argument is not optional.
|
|
'\n'
|
|
' $RENAME$METADATA$MODIFIERS $TYPE $NAME($PARAMS) {\n',
|
|
' var ',
|
|
tempVariable,
|
|
' = convertDartToNative_Dictionary(',
|
|
mapArg,
|
|
');\n',
|
|
' return $PROMISE_CALL(',
|
|
jsCall,
|
|
');\n',
|
|
' }\n'
|
|
]
|
|
|
|
return "".join(futureTemplate)
|
|
|
|
def _AddDirectNativeOperation(self, info, html_name):
|
|
force_optional = True if html_name.startswith('_') else False
|
|
resultType = self._computeResultType(info.type_name)
|
|
|
|
if info.type_name == 'Promise' and not (force_optional):
|
|
lookupOp = "%s.%s" % (self._interface.id, html_name)
|
|
promiseFound = _GetPromiseOperationType(lookupOp)
|
|
promiseType = 'Future'
|
|
promiseCall = 'promiseToFuture'
|
|
type_description = ''
|
|
if promiseFound is not (None):
|
|
paramType = promiseFound.get('type')
|
|
if 'maplike' in promiseFound:
|
|
if paramType == 'dictionary':
|
|
promiseCall = 'promiseToFuture<dynamic>'
|
|
promiseType = 'Future'
|
|
elif paramType:
|
|
promiseCall = 'promiseToFuture<%s>' % paramType
|
|
promiseType = 'Future<%s>' % paramType
|
|
elif paramType == 'dictionary':
|
|
# It's a dictionary so return as a Map.
|
|
promiseCall = 'promiseToFutureAsMap'
|
|
output_conversion = self._OutputConversion("Dictionary",
|
|
None)
|
|
nullability = '?' if output_conversion.nullable_output \
|
|
else ''
|
|
promiseType = 'Future<Map<String, dynamic>' + \
|
|
nullability + '>'
|
|
elif paramType:
|
|
promiseCall = 'promiseToFuture<%s>' % paramType
|
|
promiseType = 'Future<%s>' % paramType
|
|
|
|
if 'creates' in promiseFound:
|
|
createsType = promiseFound['creates']
|
|
type_description = 'creates:%s;' % createsType
|
|
|
|
argsNames = info.ParametersAsArgumentList()
|
|
dictionary_argument = info.dictionaryArgumentName()
|
|
codeTemplate = self._promiseToFutureCode(argsNames,
|
|
dictionary_argument)
|
|
if info.type_nullable:
|
|
promiseType += '?'
|
|
self._members_emitter.Emit(
|
|
codeTemplate,
|
|
RENAME=self._RenamingAnnotation(info.declared_name, html_name),
|
|
METADATA=self._Metadata(info.type_name, info.declared_name,
|
|
self.SecureOutputType(info.type_name,
|
|
nullable=info.type_nullable),
|
|
info.type_nullable),
|
|
MODIFIERS='static ' if info.IsStatic() else '',
|
|
TYPE=promiseType,
|
|
PROMISE_CALL=promiseCall,
|
|
NAME=html_name,
|
|
TYPE_DESC=type_description,
|
|
JSNAME=info.declared_name,
|
|
PARAMS=info.ParametersAsDeclaration(self._NarrowInputType,
|
|
force_optional))
|
|
else:
|
|
self._members_emitter.Emit(
|
|
'\n'
|
|
' $RENAME$METADATA$MODIFIERS$TYPE $NAME($PARAMS) => '\
|
|
'js_util.callMethod(this, \'$JSNAME\', [$ARGS]);\n'
|
|
if CanUseStaticExtensions(self._interface_name, self._generate_static_extensions) else
|
|
'\n $RENAME$METADATA$MODIFIERS$TYPE $NAME($PARAMS) native;\n',
|
|
RENAME=self._RenamingAnnotation(info.declared_name, html_name),
|
|
METADATA=self._Metadata(
|
|
info.type_name, info.declared_name,
|
|
self.SecureOutputType(info.type_name,
|
|
nullable=info.type_nullable),
|
|
info.type_nullable),
|
|
MODIFIERS='static ' if info.IsStatic() else '',
|
|
TYPE=self.SecureOutputType(resultType,
|
|
can_narrow_type=True,
|
|
nullable=info.type_nullable),
|
|
NAME=html_name,
|
|
PARAMS=info.ParametersAsDeclaration(self._NarrowInputType,
|
|
force_optional),
|
|
ARGS=info.ParametersAsArgumentList(),
|
|
JSNAME=info.declared_name if info.declared_name != html_name else html_name)
|
|
|
|
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
|
|
|
|
resultType = self._computeResultType(info.type_name)
|
|
|
|
output_conversion = self._OutputConversion(resultType,
|
|
info.declared_name)
|
|
if output_conversion:
|
|
return_type = output_conversion.output_type
|
|
native_return_type = output_conversion.input_type
|
|
else:
|
|
return_type = resultType if resultType == 'Function' else self._NarrowInputType(
|
|
resultType)
|
|
native_return_type = return_type
|
|
|
|
parameter_names = [param_info.name for param_info in info.param_infos]
|
|
parameter_types = [
|
|
self._InputType(param_info.type_id, info)
|
|
for param_info in info.param_infos
|
|
]
|
|
operations = info.operations
|
|
|
|
def InputType(type_name):
|
|
return self._InputType(type_name, info)
|
|
|
|
def GenerateCall(stmts_emitter, call_emitter, version, operation,
|
|
argument_count):
|
|
target = '_%s_%d' % (html_name[1:] if html_name.startswith('_') else
|
|
html_name, version)
|
|
|
|
(target_parameters, arguments,
|
|
calling_params) = self._ConvertArgumentTypes(
|
|
stmts_emitter, operation.arguments, argument_count, info)
|
|
|
|
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)
|
|
if output_conversion.nullable_output and not info.type_nullable:
|
|
# Return type of operation is not nullable while conversion
|
|
# is, so we need to assert non-null.
|
|
call += '!'
|
|
|
|
call_emitter.Emit(call)
|
|
|
|
if (native_return_type == 'Future'):
|
|
hashArgs = ''
|
|
if argument_count > 0:
|
|
if argument_count < 20:
|
|
hashArgs = '#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#' [:
|
|
argument_count
|
|
*
|
|
2
|
|
-
|
|
1]
|
|
else:
|
|
print(
|
|
"ERROR: Arguments exceede 20 - please fix Python code to handle more."
|
|
)
|
|
self._members_emitter.Emit(
|
|
' $RENAME$METADATA$MODIFIERS$TYPE$TARGET($PARAMS) =>\n'
|
|
' promiseToFuture(JS("", "#.$JSNAME($HASH_STR)"'\
|
|
', this$CALLING_PARAMS));\n',
|
|
RENAME=self._RenamingAnnotation(info.declared_name, target),
|
|
METADATA=self._Metadata(info.type_name, info.declared_name,
|
|
None, info.type_nullable),
|
|
MODIFIERS='static ' if info.IsStatic() else '',
|
|
TYPE=TypeOrNothing(native_return_type,
|
|
nullable=info.type_nullable),
|
|
TARGET=target,
|
|
PARAMS=', '.join(target_parameters),
|
|
JSNAME=operation.id,
|
|
HASH_STR=hashArgs,
|
|
CALLING_PARAMS=calling_params)
|
|
else:
|
|
self._members_emitter.Emit(
|
|
' $RENAME$METADATA$MODIFIERS$TYPE$TARGET($PARAMS) native;\n',
|
|
RENAME=self._RenamingAnnotation(info.declared_name, target),
|
|
METADATA=self._Metadata(info.type_name, info.declared_name,
|
|
None, info.type_nullable),
|
|
MODIFIERS='static ' if info.IsStatic() else '',
|
|
TYPE=TypeOrNothing(native_return_type,
|
|
nullable=info.type_nullable),
|
|
TARGET=target,
|
|
PARAMS=', '.join(target_parameters))
|
|
|
|
# private methods don't need named arguments.
|
|
full_name = '%s.%s' % (self._interface.id, info.declared_name)
|
|
force_optional = False if hasNamedFormals(full_name) and not (
|
|
html_name.startswith('_')) else True
|
|
|
|
nullsafe_return_type = return_type;
|
|
if info.type_nullable:
|
|
nullsafe_return_type += '?'
|
|
|
|
declaration = '%s%s%s %s(%s)' % (
|
|
self._Metadata(info.type_name, info.declared_name, return_type,
|
|
info.type_nullable),
|
|
'static ' if info.IsStatic() else '', nullsafe_return_type,
|
|
html_name, info.ParametersAsDeclaration(InputType, force_optional))
|
|
self._GenerateDispatcherBody(
|
|
info,
|
|
operations,
|
|
declaration,
|
|
GenerateCall,
|
|
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, can_narrow_type=True,
|
|
nullable=info.type_nullable),
|
|
NAME=html_name,
|
|
PARAMS=info.ParametersAsDeclaration(self._NarrowInputType))
|
|
|
|
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 _SafeToIgnoreShadowingMember(self, member_name):
|
|
member_name = '%s.%s' % (self._interface.doc_js_name, member_name)
|
|
return member_name in _safe_to_ignore_shadowing_members
|
|
|
|
def _RenamingAnnotation(self, idl_name, member_name):
|
|
if member_name != idl_name:
|
|
return "@JSName('%s')\n " % idl_name
|
|
return ''
|
|
|
|
def _Metadata(self, idl_type, idl_member_name, dart_type, nullable,
|
|
indent=' '):
|
|
anns = self._metadata.GetDart2JSMetadata(
|
|
idl_type, self._library_name, self._interface, idl_member_name)
|
|
|
|
if not self._metadata.AnyConversionAnnotations(
|
|
idl_type, self._interface.id, idl_member_name):
|
|
return_type = self.SecureOutputType(idl_type)
|
|
native_type = self._NarrowToImplementationType(idl_type)
|
|
|
|
null_union = '' if not nullable else '|Null'
|
|
if native_type != return_type:
|
|
anns = anns + [
|
|
"@Returns('%s%s')" % (native_type, null_union),
|
|
"@Creates('%s')" % native_type,
|
|
]
|
|
if dart_type == 'dynamic' or dart_type == 'Object?':
|
|
# We emit non-nullable Object annotations but exclude nullable
|
|
# Object annotations since that's the default.
|
|
|
|
def js_type_annotation(ann):
|
|
return re.search('^@.*Returns', ann) or re.search(
|
|
'^@.*Creates', ann)
|
|
|
|
if not list(filter(js_type_annotation, anns)):
|
|
_logger.warn('Member with wildcard native type: %s.%s' %
|
|
(self._interface.id, idl_member_name))
|
|
|
|
return self._metadata.FormatMetadata(anns, indent)
|
|
|
|
def CustomJSMembers(self):
|
|
return _js_custom_members
|
|
|
|
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, self._database):
|
|
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)
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
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 AddTypeEntry(self, basename, idl_name, dart_name):
|
|
self._dart_libraries.AddTypeEntry(basename, idl_name, dart_name)
|
|
|
|
def EmitLibraries(self, auxiliary_dir, dart_js_interop):
|
|
self._dart_libraries.Emit(self._multiemitter, auxiliary_dir)
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
class DartLibrary():
|
|
|
|
def __init__(self, name, template_loader, library_type, output_dir,
|
|
dart_js_interop):
|
|
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 = []
|
|
self._typeMap = {}
|
|
self._dart_js_interop = dart_js_interop
|
|
|
|
def AddFile(self, path):
|
|
self._paths.append(path)
|
|
|
|
def AddTypeEntry(self, idl_name, dart_name):
|
|
self._typeMap[idl_name] = dart_name
|
|
|
|
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)
|
|
emitters = library_emitter.Emit(
|
|
self._template,
|
|
AUXILIARY_DIR=massage_path(auxiliary_dir),
|
|
NULLABLE='?')
|
|
if isinstance(emitters, tuple):
|
|
imports_emitter, map_emitter = emitters
|
|
else:
|
|
imports_emitter, map_emitter = emitters, None
|
|
|
|
for path in sorted(self._paths):
|
|
relpath = os.path.relpath(path, library_file_dir)
|
|
imports_emitter.Emit("part '$PATH';\n", PATH=massage_path(relpath))
|
|
|
|
# Emit the $!TYPE_MAP
|
|
if map_emitter:
|
|
items = list(self._typeMap.items())
|
|
items.sort()
|
|
for (idl_name, dart_name) in items:
|
|
map_emitter.Emit(
|
|
" '$IDL_NAME': () => $DART_NAME.instanceRuntimeType,\n",
|
|
IDL_NAME=idl_name,
|
|
DART_NAME=dart_name)
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
class DartLibraries():
|
|
|
|
def __init__(self, libraries, template_loader, library_type, output_dir,
|
|
dart_js_interop):
|
|
self._libraries = {}
|
|
for library_name in libraries:
|
|
self._libraries[library_name] = DartLibrary(
|
|
library_name, template_loader, library_type, output_dir,
|
|
dart_js_interop)
|
|
|
|
def AddFile(self, basename, library_name, path):
|
|
self._libraries[library_name].AddFile(path)
|
|
|
|
def AddTypeEntry(self, library_name, idl_name, dart_name):
|
|
self._libraries[library_name].AddTypeEntry(idl_name, dart_name)
|
|
|
|
def Emit(self, emitter, auxiliary_dir):
|
|
for lib in self._libraries.values():
|
|
lib.Emit(emitter, auxiliary_dir)
|