| Index: pkg/compiler/lib/src/js_backend/native_data.dart
|
| diff --git a/pkg/compiler/lib/src/js_backend/native_data.dart b/pkg/compiler/lib/src/js_backend/native_data.dart
|
| index 0d6b5ea5d38e0e4ae500f41fe01a5c5faeb62270..8401fb7e1c513b7002101ca712647bccf19e5720 100644
|
| --- a/pkg/compiler/lib/src/js_backend/native_data.dart
|
| +++ b/pkg/compiler/lib/src/js_backend/native_data.dart
|
| @@ -10,7 +10,6 @@ import '../elements/elements.dart'
|
| ClassElement,
|
| Element,
|
| FieldElement,
|
| - FunctionElement,
|
| LibraryElement,
|
| MemberElement,
|
| MethodElement;
|
| @@ -63,11 +62,17 @@ abstract class NativeData extends NativeClassData {
|
|
|
| /// Returns `true` if the name of [element] is fixed for the generated
|
| /// JavaScript.
|
| - bool hasFixedBackendName(Element element);
|
| + bool hasFixedBackendName(MemberElement element);
|
|
|
| /// Computes the name for [element] to use in the generated JavaScript. This
|
| /// is either given through a native annotation or a js interop annotation.
|
| - String getFixedBackendName(Entity entity);
|
| + String getFixedBackendName(MemberEntity element);
|
| +
|
| + /// Computes the name prefix for [element] to use in the generated JavaScript.
|
| + ///
|
| + /// For static and top-level members and constructors this is based on the
|
| + /// JavaScript names for the library and/or the enclosing class.
|
| + String getFixedBackendMethodPath(MethodElement element);
|
|
|
| /// Returns the list of non-directive native tag words for [cls].
|
| List<String> getNativeTagsOfClass(ClassElement cls);
|
| @@ -75,22 +80,21 @@ abstract class NativeData extends NativeClassData {
|
| /// Returns `true` if [cls] has a `!nonleaf` tag word.
|
| bool hasNativeTagsForcedNonLeaf(ClassElement cls);
|
|
|
| - /// Returns `true` if [element] is part of JsInterop.
|
| - ///
|
| - /// Deprecated: Use [isJsInteropLibrary], [isJsInteropClass] or
|
| - /// [isJsInteropMember] instead.
|
| - @deprecated
|
| - bool isJsInterop(Element element);
|
| -
|
| /// Returns `true` if [element] is a JsInterop method.
|
| - bool isJsInteropMember(MethodElement element);
|
| + bool isJsInteropMember(MemberEntity element);
|
|
|
| - /// Returns the explicit js interop name for [element].
|
| - String getJsInteropName(Element element);
|
| + /// Returns the explicit js interop name for library [element].
|
| + String getJsInteropLibraryName(LibraryElement element);
|
| +
|
| + /// Returns the explicit js interop name for class [element].
|
| + String getJsInteropClassName(ClassElement element);
|
| +
|
| + /// Returns the explicit js interop name for member [element].
|
| + String getJsInteropMemberName(MemberElement element);
|
|
|
| /// Apply JS$ escaping scheme to convert possible escaped Dart names into
|
| /// JS names.
|
| - String getUnescapedJSInteropName(String name);
|
| + String computeUnescapedJSInteropName(String name);
|
| }
|
|
|
| abstract class NativeClassDataBuilder {
|
| @@ -131,15 +135,24 @@ abstract class NativeDataBuilder {
|
| /// [element] in the generated JavaScript.
|
| void setNativeMemberName(MemberElement element, String name);
|
|
|
| - /// Sets the explicit js interop [name] for [element].
|
| - void setJsInteropName(Element element, String name);
|
| + /// Sets the explicit js interop [name] for the library [element].
|
| + void setJsInteropLibraryName(LibraryElement element, String name);
|
| +
|
| + /// Sets the explicit js interop [name] for the class [element].
|
| + void setJsInteropClassName(ClassElement element, String name);
|
| +
|
| + /// Sets the explicit js interop [name] for the member [element].
|
| + void setJsInteropMemberName(MemberElement element, String name);
|
| }
|
|
|
| class NativeDataImpl
|
| implements NativeData, NativeDataBuilder, NativeClassDataBuilder {
|
| /// The JavaScript names for elements implemented via typed JavaScript
|
| /// interop.
|
| - Map<Element, String> jsInteropNames = <Element, String>{};
|
| + Map<LibraryElement, String> jsInteropLibraryNames =
|
| + <LibraryElement, String>{};
|
| + Map<ClassElement, String> jsInteropClassNames = <ClassElement, String>{};
|
| + Map<MemberElement, String> jsInteropMemberNames = <MemberElement, String>{};
|
|
|
| /// The JavaScript names for native JavaScript elements implemented.
|
| Map<Element, String> nativeMemberName = <Element, String>{};
|
| @@ -165,125 +178,183 @@ class NativeDataImpl
|
| static const String _jsInteropEscapePrefix = r'JS$';
|
|
|
| /// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
| - bool _isJsInterop(Element element) {
|
| - return jsInteropNames.containsKey(element.declaration);
|
| + bool _isJsInteropLibrary(LibraryElement element) {
|
| + return jsInteropLibraryNames.containsKey(element);
|
| }
|
|
|
| - /// Marks [element] as an explicit part of JsInterop. The js interop name is
|
| - /// expected to be computed later.
|
| - void markAsJsInterop(Element element) {
|
| - jsInteropNames[element.declaration] = null;
|
| + /// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
| + bool _isJsInteropClass(ClassElement element) {
|
| + return jsInteropClassNames.containsKey(element);
|
| + }
|
| +
|
| + /// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
| + bool _isJsInteropMember(MemberElement element) {
|
| + return jsInteropMemberNames.containsKey(element);
|
| }
|
|
|
| @override
|
| void markAsJsInteropLibrary(LibraryElement element) {
|
| - markAsJsInterop(element);
|
| + jsInteropLibraryNames[element] = null;
|
| }
|
|
|
| @override
|
| void markAsJsInteropClass(ClassElement element) {
|
| - markAsJsInterop(element);
|
| + jsInteropClassNames[element] = null;
|
| }
|
|
|
| @override
|
| void markAsJsInteropMember(MemberElement element) {
|
| - markAsJsInterop(element);
|
| + jsInteropMemberNames[element] = null;
|
| }
|
|
|
| - /// Sets the explicit js interop [name] for [element].
|
| - void setJsInteropName(Element element, String name) {
|
| - assert(invariant(element, isJsInterop(element),
|
| + /// Sets the explicit js interop [name] for the library [element].
|
| + void setJsInteropLibraryName(LibraryElement element, String name) {
|
| + assert(invariant(element, _isJsInteropLibrary(element),
|
| message:
|
| - 'Element $element is not js interop but given a js interop name.'));
|
| - jsInteropNames[element.declaration] = name;
|
| + 'Library $element is not js interop but given a js interop name.'));
|
| + jsInteropLibraryNames[element] = name;
|
| }
|
|
|
| - /// Returns the explicit js interop name for [element].
|
| - String getJsInteropName(Element element) {
|
| - return jsInteropNames[element.declaration];
|
| + /// Sets the explicit js interop [name] for the class [element].
|
| + void setJsInteropClassName(ClassElement element, String name) {
|
| + assert(invariant(element, _isJsInteropClass(element),
|
| + message:
|
| + 'Class $element is not js interop but given a js interop name.'));
|
| + jsInteropClassNames[element] = name;
|
| }
|
|
|
| - /// Returns `true` if [element] is part of JsInterop.
|
| - bool isJsInterop(Element element) {
|
| - // An function is part of JsInterop in the following cases:
|
| - // * It has a jsInteropName annotation
|
| - // * It is external member of a class or library tagged as JsInterop.
|
| - if (element.isFunction || element.isConstructor || element.isAccessor) {
|
| - FunctionElement function = element;
|
| - if (!function.isExternal) return false;
|
| + /// Sets the explicit js interop [name] for the member [element].
|
| + void setJsInteropMemberName(MemberElement element, String name) {
|
| + assert(invariant(element, _isJsInteropMember(element),
|
| + message:
|
| + 'Member $element is not js interop but given a js interop name.'));
|
| + jsInteropMemberNames[element] = name;
|
| + }
|
|
|
| - if (_isJsInterop(function)) return true;
|
| - if (function.isClassMember) return isJsInterop(function.contextClass);
|
| - if (function.isTopLevel) return isJsInterop(function.library);
|
| - return false;
|
| - } else {
|
| - return _isJsInterop(element);
|
| - }
|
| + /// Returns the explicit js interop name for library [element].
|
| + String getJsInteropLibraryName(LibraryElement element) {
|
| + return jsInteropLibraryNames[element];
|
| + }
|
| +
|
| + /// Returns the explicit js interop name for class [element].
|
| + String getJsInteropClassName(ClassElement element) {
|
| + return jsInteropClassNames[element];
|
| + }
|
| +
|
| + /// Returns the explicit js interop name for member [element].
|
| + String getJsInteropMemberName(MemberElement element) {
|
| + return jsInteropMemberNames[element];
|
| }
|
|
|
| /// Returns `true` if [element] is a JsInterop library.
|
| - bool isJsInteropLibrary(LibraryElement element) => isJsInterop(element);
|
| + bool isJsInteropLibrary(LibraryElement element) =>
|
| + _isJsInteropLibrary(element);
|
|
|
| /// Returns `true` if [element] is a JsInterop class.
|
| - bool isJsInteropClass(ClassElement element) => isJsInterop(element);
|
| + bool isJsInteropClass(ClassElement element) => _isJsInteropClass(element);
|
|
|
| /// Returns `true` if [element] is a JsInterop method.
|
| - bool isJsInteropMember(MethodElement element) => isJsInterop(element);
|
| + bool isJsInteropMember(MemberElement element) {
|
| + if (element.isFunction || element.isConstructor || element.isAccessor) {
|
| + MethodElement function = element;
|
| + if (!function.isExternal) return false;
|
| +
|
| + if (_isJsInteropMember(function)) return true;
|
| + if (function.isClassMember) {
|
| + return _isJsInteropClass(function.enclosingClass);
|
| + }
|
| + if (function.isTopLevel) {
|
| + return _isJsInteropLibrary(function.library);
|
| + }
|
| + return false;
|
| + } else {
|
| + return _isJsInteropMember(element);
|
| + }
|
| + }
|
|
|
| /// Returns `true` if the name of [element] is fixed for the generated
|
| /// JavaScript.
|
| - bool hasFixedBackendName(Element element) {
|
| - return isJsInterop(element) ||
|
| + bool hasFixedBackendName(MemberElement element) {
|
| + return isJsInteropMember(element) ||
|
| nativeMemberName.containsKey(element.declaration);
|
| }
|
|
|
| - String _jsNameHelper(Element element) {
|
| - String jsInteropName = jsInteropNames[element.declaration];
|
| - assert(invariant(element, !(_isJsInterop(element) && jsInteropName == null),
|
| - message:
|
| - 'Element $element is js interop but js interop name has not yet '
|
| - 'been computed.'));
|
| - if (jsInteropName != null && jsInteropName.isNotEmpty) {
|
| - return jsInteropName;
|
| - }
|
| - return element.isLibrary ? 'self' : getUnescapedJSInteropName(element.name);
|
| - }
|
| -
|
| /// Computes the name for [element] to use in the generated JavaScript. This
|
| /// is either given through a native annotation or a js interop annotation.
|
| - String getFixedBackendName(Entity entity) {
|
| - // TODO(johnniwinther): Remove this assignment from [Entity] to [Element]
|
| - // when `.declaration` is no longer needed.
|
| - Element element = entity;
|
| + String getFixedBackendName(MemberElement element) {
|
| String name = nativeMemberName[element.declaration];
|
| - if (name == null && isJsInterop(element)) {
|
| + if (name == null && isJsInteropMember(element)) {
|
| // If an element isJsInterop but _isJsInterop is false that means it is
|
| // considered interop as the parent class is interop.
|
| - name = _jsNameHelper(
|
| - element.isConstructor ? element.enclosingClass : element);
|
| + name = element.isConstructor
|
| + ? _jsClassNameHelper(element.enclosingClass)
|
| + : _jsMemberNameHelper(element);
|
| nativeMemberName[element.declaration] = name;
|
| }
|
| return name;
|
| }
|
|
|
| - /// Whether [element] corresponds to a native JavaScript construct either
|
| - /// through the native mechanism (`@Native(...)` or the `native` pseudo
|
| - /// keyword) which is only allowed for internal libraries or via the typed
|
| - /// JavaScriptInterop mechanism which is allowed for user libraries.
|
| - bool isNative(Element element) {
|
| - if (isJsInterop(element)) return true;
|
| - if (element.isClass) {
|
| - return nativeClassTagInfo.containsKey(element.declaration);
|
| - } else {
|
| - return nativeMemberName.containsKey(element.declaration);
|
| + String _jsLibraryNameHelper(LibraryElement element) {
|
| + String jsInteropName = getJsInteropLibraryName(element);
|
| + if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName;
|
| + return 'self';
|
| + }
|
| +
|
| + String _jsClassNameHelper(ClassElement element) {
|
| + String jsInteropName = getJsInteropClassName(element);
|
| + if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName;
|
| + return computeUnescapedJSInteropName(element.name);
|
| + }
|
| +
|
| + String _jsMemberNameHelper(MemberElement element) {
|
| + String jsInteropName = jsInteropMemberNames[element];
|
| + assert(invariant(element,
|
| + !(jsInteropMemberNames.containsKey(element) && jsInteropName == null),
|
| + message:
|
| + 'Member $element is js interop but js interop name has not yet '
|
| + 'been computed.'));
|
| + if (jsInteropName != null && jsInteropName.isNotEmpty) {
|
| + return jsInteropName;
|
| + }
|
| + return computeUnescapedJSInteropName(element.name);
|
| + }
|
| +
|
| + /// Returns a JavaScript path specifying the context in which
|
| + /// [element.fixedBackendName] should be evaluated. Only applicable for
|
| + /// elements using typed JavaScript interop.
|
| + /// For example: fixedBackendPath for the static method createMap in the
|
| + /// Map class of the goog.map JavaScript library would have path
|
| + /// "goog.maps.Map".
|
| + String getFixedBackendMethodPath(MethodElement element) {
|
| + if (!isJsInteropMember(element)) return null;
|
| + if (element.isInstanceMember) return 'this';
|
| + if (element.isConstructor) {
|
| + return _fixedBackendClassPath(element.enclosingClass);
|
| }
|
| + StringBuffer sb = new StringBuffer();
|
| + sb.write(_jsLibraryNameHelper(element.library));
|
| + if (element.enclosingClass != null) {
|
| + sb..write('.')..write(_jsClassNameHelper(element.enclosingClass));
|
| + }
|
| + return sb.toString();
|
| + }
|
| +
|
| + String _fixedBackendClassPath(ClassElement element) {
|
| + if (!isJsInteropClass(element)) return null;
|
| + return _jsLibraryNameHelper(element.library);
|
| }
|
|
|
| /// Returns `true` if [cls] is a native class.
|
| - bool isNativeClass(ClassElement element) => isNative(element);
|
| + bool isNativeClass(ClassElement element) {
|
| + if (isJsInteropClass(element)) return true;
|
| + return nativeClassTagInfo.containsKey(element);
|
| + }
|
|
|
| /// Returns `true` if [element] is a native member of a native class.
|
| - bool isNativeMember(MemberElement element) => isNative(element);
|
| + bool isNativeMember(MemberElement element) {
|
| + if (isJsInteropMember(element)) return true;
|
| + return nativeMemberName.containsKey(element);
|
| + }
|
|
|
| /// Returns `true` if [element] or any of its superclasses is native.
|
| bool isNativeOrExtendsNative(ClassElement element) {
|
| @@ -390,7 +461,7 @@ class NativeDataImpl
|
|
|
| /// Apply JS$ escaping scheme to convert possible escaped Dart names into
|
| /// JS names.
|
| - String getUnescapedJSInteropName(String name) {
|
| + String computeUnescapedJSInteropName(String name) {
|
| return name.startsWith(_jsInteropEscapePrefix)
|
| ? name.substring(_jsInteropEscapePrefix.length)
|
| : name;
|
|
|