Chromium Code Reviews| 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..6480e32885b5ae2474f7c3f7a322b699e9365b5f 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,16 @@ 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. |
|
Siggi Cherem (dart-lang)
2017/03/14 04:48:19
nit: add missing ]
Johnni Winther
2017/03/15 11:42:14
Done.
|
| + /// For static and top-level members and constructors this is based on the |
| + /// names for the library and/or the enclosing class. |
|
Siggi Cherem (dart-lang)
2017/03/14 04:48:19
might be worth clarifying that here you mean JS na
Johnni Winther
2017/03/15 11:42:14
Done.
|
| + String getFixedBackendMethodPath(MethodElement element); |
| /// Returns the list of non-directive native tag words for [cls]. |
| List<String> getNativeTagsOfClass(ClassElement cls); |
| @@ -75,22 +79,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 library [element]. |
| + String getJsInteropLibraryName(LibraryElement element); |
| - /// Returns the explicit js interop name for [element]. |
| - String getJsInteropName(Element 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 +134,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 +177,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 the library [element]. |
| + void setJsInteropLibraryName(LibraryElement element, String name) { |
| + assert(invariant(element, _isJsInteropLibrary(element), |
| + message: |
| + 'Library $element is not js interop but given a js interop name.')); |
| + jsInteropLibraryNames[element] = name; |
| } |
| - /// 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 class [element]. |
| + void setJsInteropClassName(ClassElement element, String name) { |
| + assert(invariant(element, _isJsInteropClass(element), |
| message: |
| - 'Element $element is not js interop but given a js interop name.')); |
| - jsInteropNames[element.declaration] = name; |
| + 'Class $element is not js interop but given a js interop name.')); |
| + jsInteropClassNames[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 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; |
| } |
| - /// 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; |
| + /// Returns the explicit js interop name for library [element]. |
| + String getJsInteropLibraryName(LibraryElement element) { |
| + return jsInteropLibraryNames[element]; |
| + } |
| - 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 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 +460,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; |