| 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 283e16d1d08c1804d4cedd58eff5807cc71ce5ae..9eedad7698bfc043c950aa6f959e6708b7eb5749 100644
|
| --- a/pkg/compiler/lib/src/js_backend/native_data.dart
|
| +++ b/pkg/compiler/lib/src/js_backend/native_data.dart
|
| @@ -8,6 +8,7 @@ import '../common.dart';
|
| import '../elements/elements.dart' show ClassElement;
|
| import '../elements/entities.dart';
|
| import '../native/behavior.dart' show NativeBehavior;
|
| +import '../util/util.dart';
|
|
|
| /// Basic information for native classes and methods and js-interop
|
| /// classes.
|
| @@ -21,6 +22,12 @@ abstract class NativeClassData {
|
| /// mechanism allowed for user libraries.
|
| bool isNativeClass(ClassEntity element);
|
|
|
| + /// Returns the list of non-directive native tag words for [cls].
|
| + List<String> getNativeTagsOfClass(ClassEntity cls);
|
| +
|
| + /// Returns `true` if [cls] has a `!nonleaf` tag word.
|
| + bool hasNativeTagsForcedNonLeaf(ClassEntity cls);
|
| +
|
| /// Returns `true` if [element] or any of its superclasses is native.
|
| bool isNativeOrExtendsNative(ClassEntity element);
|
|
|
| @@ -66,12 +73,6 @@ abstract class NativeData extends NativeClassData {
|
| /// names for the library and/or the enclosing class.
|
| String getFixedBackendMethodPath(FunctionEntity element);
|
|
|
| - /// Returns the list of non-directive native tag words for [cls].
|
| - List<String> getNativeTagsOfClass(ClassEntity cls);
|
| -
|
| - /// Returns `true` if [cls] has a `!nonleaf` tag word.
|
| - bool hasNativeTagsForcedNonLeaf(ClassEntity cls);
|
| -
|
| /// Returns `true` if [element] is a JsInterop method.
|
| bool isJsInteropMember(MemberEntity element);
|
|
|
| @@ -116,9 +117,6 @@ abstract class NativeDataBuilder {
|
| /// Registers the [behavior] for writing to the native [field].
|
| void setNativeFieldStoreBehavior(FieldEntity field, NativeBehavior behavior);
|
|
|
| - /// Returns the list of native tag words for [cls].
|
| - List<String> getNativeTagsOfClassRaw(ClassEntity cls);
|
| -
|
| /// Returns [element] as an explicit part of JsInterop. The js interop name is
|
| /// expected to be computed later.
|
| void markAsJsInteropMember(MemberEntity element);
|
| @@ -137,21 +135,96 @@ abstract class NativeDataBuilder {
|
| void setJsInteropMemberName(MemberEntity element, String name);
|
| }
|
|
|
| -class NativeDataImpl
|
| - implements NativeData, NativeDataBuilder, NativeClassDataBuilder {
|
| - /// The JavaScript names for elements implemented via typed JavaScript
|
| - /// interop.
|
| - Map<LibraryEntity, String> jsInteropLibraryNames = <LibraryEntity, String>{};
|
| - Map<ClassEntity, String> jsInteropClassNames = <ClassEntity, String>{};
|
| - Map<MemberEntity, String> jsInteropMemberNames = <MemberEntity, String>{};
|
| +class NativeClassDataImpl implements NativeClassDataBuilder, NativeClassData {
|
| + /// Tag info for native JavaScript classes names. See
|
| + /// [setNativeClassTagInfo].
|
| + Map<ClassEntity, NativeClassTag> nativeClassTagInfo =
|
| + <ClassEntity, NativeClassTag>{};
|
| +
|
| + /// The JavaScript libraries implemented via typed JavaScript interop.
|
| + Set<LibraryEntity> jsInteropLibraries = new Set<LibraryEntity>();
|
| +
|
| + /// The JavaScript classes implemented via typed JavaScript interop.
|
| + Set<ClassEntity> jsInteropClasses = new Set<ClassEntity>();
|
| +
|
| + /// Sets the native tag info for [cls].
|
| + ///
|
| + /// The tag info string contains comma-separated 'words' which are either
|
| + /// dispatch tags (having JavaScript identifier syntax) and directives that
|
| + /// begin with `!`.
|
| + void setNativeClassTagInfo(ClassEntity cls, String tagInfo) {
|
| + String tagText = tagInfo.substring(1, tagInfo.length - 1);
|
| + // TODO(johnniwinther): Assert that this is only called once. The memory
|
| + // compiler copies pre-processed elements into a new compiler through
|
| + // [Compiler.onLibraryScanned] and thereby causes multiple calls to this
|
| + // method.
|
| + assert(invariant(
|
| + cls,
|
| + nativeClassTagInfo[cls] == null ||
|
| + nativeClassTagInfo[cls].text == tagText,
|
| + message: "Native tag info set inconsistently on $cls: "
|
| + "Existing tag info '${nativeClassTagInfo[cls]}', "
|
| + "new tag info '$tagText'."));
|
| + nativeClassTagInfo[cls] = new NativeClassTag(tagText);
|
| + }
|
| +
|
| + @override
|
| + void markAsJsInteropLibrary(LibraryEntity element) {
|
| + jsInteropLibraries.add(element);
|
| + }
|
| +
|
| + @override
|
| + void markAsJsInteropClass(ClassEntity element) {
|
| + jsInteropClasses.add(element);
|
| + }
|
| +
|
| + /// Returns `true` if [cls] is a native class.
|
| + bool isNativeClass(ClassEntity element) {
|
| + if (isJsInteropClass(element)) return true;
|
| + return nativeClassTagInfo.containsKey(element);
|
| + }
|
| +
|
| + /// Returns the list of non-directive native tag words for [cls].
|
| + List<String> getNativeTagsOfClass(ClassEntity cls) {
|
| + return nativeClassTagInfo[cls].names;
|
| + }
|
| +
|
| + /// Returns `true` if [cls] has a `!nonleaf` tag word.
|
| + bool hasNativeTagsForcedNonLeaf(ClassEntity cls) {
|
| + return nativeClassTagInfo[cls].isNonLeaf;
|
| + }
|
| +
|
| + /// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
| + bool isJsInteropLibrary(LibraryEntity element) {
|
| + return jsInteropLibraries.contains(element);
|
| + }
|
| +
|
| + /// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
| + bool isJsInteropClass(ClassEntity element) {
|
| + return jsInteropClasses.contains(element);
|
| + }
|
| +
|
| + /// Returns `true` if [element] or any of its superclasses is native.
|
| + bool isNativeOrExtendsNative(ClassElement element) {
|
| + if (element == null) return false;
|
| + if (isNativeClass(element) || isJsInteropClass(element)) {
|
| + return true;
|
| + }
|
| + assert(element.isResolved);
|
| + return isNativeOrExtendsNative(element.superclass);
|
| + }
|
| +}
|
| +
|
| +class NativeDataImpl implements NativeDataBuilder, NativeData {
|
| + /// Prefix used to escape JS names that are not valid Dart names
|
| + /// when using JSInterop.
|
| + static const String _jsInteropEscapePrefix = r'JS$';
|
| +
|
| + final NativeClassData _nativeClassData;
|
|
|
| /// The JavaScript names for native JavaScript elements implemented.
|
| Map<MemberEntity, String> nativeMemberName = <MemberEntity, String>{};
|
|
|
| - /// Tag info for native JavaScript classes names. See
|
| - /// [setNativeClassTagInfo].
|
| - Map<ClassEntity, String> nativeClassTagInfo = <ClassEntity, String>{};
|
| -
|
| /// Cache for [NativeBehavior]s for calling native methods.
|
| Map<FunctionEntity, NativeBehavior> nativeMethodBehavior =
|
| <FunctionEntity, NativeBehavior>{};
|
| @@ -164,43 +237,49 @@ class NativeDataImpl
|
| Map<MemberEntity, NativeBehavior> nativeFieldStoreBehavior =
|
| <FieldEntity, NativeBehavior>{};
|
|
|
| - /// Prefix used to escape JS names that are not valid Dart names
|
| - /// when using JSInterop.
|
| - static const String _jsInteropEscapePrefix = r'JS$';
|
| + /// The JavaScript names for elements implemented via typed JavaScript
|
| + /// interop.
|
| + Map<LibraryEntity, String> jsInteropLibraryNames = <LibraryEntity, String>{};
|
| + Map<ClassEntity, String> jsInteropClassNames = <ClassEntity, String>{};
|
|
|
| - /// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
| - bool _isJsInteropLibrary(LibraryEntity element) {
|
| - return jsInteropLibraryNames.containsKey(element);
|
| - }
|
| + /// The JavaScript names for elements implemented via typed JavaScript
|
| + /// interop.
|
| + Map<MemberEntity, String> jsInteropMemberNames = <MemberEntity, String>{};
|
|
|
| - /// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
| - bool _isJsInteropClass(ClassEntity element) {
|
| - return jsInteropClassNames.containsKey(element);
|
| - }
|
| + NativeDataImpl(this._nativeClassData);
|
|
|
| - /// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
| - bool _isJsInteropMember(MemberEntity element) {
|
| - return jsInteropMemberNames.containsKey(element);
|
| + /// Sets the native [name] for the member [element]. This name is used for
|
| + /// [element] in the generated JavaScript.
|
| + void setNativeMemberName(MemberEntity element, String name) {
|
| + // TODO(johnniwinther): Avoid setting this more than once. The enqueuer
|
| + // might enqueue [element] several times (before processing it) and computes
|
| + // name on each call to `internalAddToWorkList`.
|
| + assert(invariant(element,
|
| + nativeMemberName[element] == null || nativeMemberName[element] == name,
|
| + message: "Native member name set inconsistently on $element: "
|
| + "Existing name '${nativeMemberName[element]}', "
|
| + "new name '$name'."));
|
| + nativeMemberName[element] = name;
|
| }
|
|
|
| - @override
|
| - void markAsJsInteropLibrary(LibraryEntity element) {
|
| - jsInteropLibraryNames[element] = null;
|
| + /// Registers the [behavior] for calling the native [method].
|
| + void setNativeMethodBehavior(FunctionEntity method, NativeBehavior behavior) {
|
| + nativeMethodBehavior[method] = behavior;
|
| }
|
|
|
| - @override
|
| - void markAsJsInteropClass(ClassEntity element) {
|
| - jsInteropClassNames[element] = null;
|
| + /// Registers the [behavior] for reading from the native [field].
|
| + void setNativeFieldLoadBehavior(FieldEntity field, NativeBehavior behavior) {
|
| + nativeFieldLoadBehavior[field] = behavior;
|
| }
|
|
|
| - @override
|
| - void markAsJsInteropMember(MemberEntity element) {
|
| - jsInteropMemberNames[element] = null;
|
| + /// Registers the [behavior] for writing to the native [field].
|
| + void setNativeFieldStoreBehavior(FieldEntity field, NativeBehavior behavior) {
|
| + nativeFieldStoreBehavior[field] = behavior;
|
| }
|
|
|
| /// Sets the explicit js interop [name] for the library [element].
|
| void setJsInteropLibraryName(LibraryEntity element, String name) {
|
| - assert(invariant(element, _isJsInteropLibrary(element),
|
| + assert(invariant(element, _nativeClassData.isJsInteropLibrary(element),
|
| message:
|
| 'Library $element is not js interop but given a js interop name.'));
|
| jsInteropLibraryNames[element] = name;
|
| @@ -208,12 +287,22 @@ class NativeDataImpl
|
|
|
| /// Sets the explicit js interop [name] for the class [element].
|
| void setJsInteropClassName(ClassEntity element, String name) {
|
| - assert(invariant(element, _isJsInteropClass(element),
|
| + assert(invariant(element, _nativeClassData.isJsInteropClass(element),
|
| message:
|
| 'Class $element is not js interop but given a js interop name.'));
|
| jsInteropClassNames[element] = name;
|
| }
|
|
|
| + @override
|
| + void markAsJsInteropMember(MemberEntity element) {
|
| + jsInteropMemberNames[element] = null;
|
| + }
|
| +
|
| + /// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
| + bool _isJsInteropMember(MemberEntity element) {
|
| + return jsInteropMemberNames.containsKey(element);
|
| + }
|
| +
|
| /// Sets the explicit js interop [name] for the member [element].
|
| void setJsInteropMemberName(MemberEntity element, String name) {
|
| assert(invariant(element, _isJsInteropMember(element),
|
| @@ -222,6 +311,30 @@ class NativeDataImpl
|
| jsInteropMemberNames[element] = name;
|
| }
|
|
|
| + /// Returns `true` if [cls] is a native class.
|
| + bool isNativeClass(ClassEntity element) =>
|
| + _nativeClassData.isNativeClass(element);
|
| +
|
| + /// Returns the list of non-directive native tag words for [cls].
|
| + List<String> getNativeTagsOfClass(ClassEntity cls) =>
|
| + _nativeClassData.getNativeTagsOfClass(cls);
|
| +
|
| + /// Returns `true` if [cls] has a `!nonleaf` tag word.
|
| + bool hasNativeTagsForcedNonLeaf(ClassEntity cls) =>
|
| + _nativeClassData.hasNativeTagsForcedNonLeaf(cls);
|
| +
|
| + /// Returns `true` if [element] is a JsInterop library.
|
| + bool isJsInteropLibrary(LibraryEntity element) =>
|
| + _nativeClassData.isJsInteropLibrary(element);
|
| +
|
| + /// Returns `true` if [element] is a JsInterop class.
|
| + bool isJsInteropClass(ClassEntity element) =>
|
| + _nativeClassData.isJsInteropClass(element);
|
| +
|
| + /// Returns `true` if [element] or any of its superclasses is native.
|
| + bool isNativeOrExtendsNative(ClassEntity element) =>
|
| + _nativeClassData.isNativeOrExtendsNative(element);
|
| +
|
| /// Returns the explicit js interop name for library [element].
|
| String getJsInteropLibraryName(LibraryEntity element) {
|
| return jsInteropLibraryNames[element];
|
| @@ -237,13 +350,6 @@ class NativeDataImpl
|
| return jsInteropMemberNames[element];
|
| }
|
|
|
| - /// Returns `true` if [element] is a JsInterop library.
|
| - bool isJsInteropLibrary(LibraryEntity element) =>
|
| - _isJsInteropLibrary(element);
|
| -
|
| - /// Returns `true` if [element] is a JsInterop class.
|
| - bool isJsInteropClass(ClassEntity element) => _isJsInteropClass(element);
|
| -
|
| /// Returns `true` if [element] is a JsInterop method.
|
| bool isJsInteropMember(MemberEntity element) {
|
| if (element.isFunction ||
|
| @@ -255,10 +361,10 @@ class NativeDataImpl
|
|
|
| if (_isJsInteropMember(function)) return true;
|
| if (function.enclosingClass != null) {
|
| - return _isJsInteropClass(function.enclosingClass);
|
| + return isJsInteropClass(function.enclosingClass);
|
| }
|
| if (function.isTopLevel) {
|
| - return _isJsInteropLibrary(function.library);
|
| + return isJsInteropLibrary(function.library);
|
| }
|
| return false;
|
| } else {
|
| @@ -337,78 +443,12 @@ class NativeDataImpl
|
| return _jsLibraryNameHelper(element.library);
|
| }
|
|
|
| - /// Returns `true` if [cls] is a native class.
|
| - bool isNativeClass(ClassEntity element) {
|
| - if (isJsInteropClass(element)) return true;
|
| - return nativeClassTagInfo.containsKey(element);
|
| - }
|
| -
|
| /// Returns `true` if [element] is a native member of a native class.
|
| bool isNativeMember(MemberEntity 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) {
|
| - if (element == null) return false;
|
| - if (isNativeClass(element) || isJsInteropClass(element)) {
|
| - return true;
|
| - }
|
| - assert(element.isResolved);
|
| - return isNativeOrExtendsNative(element.superclass);
|
| - }
|
| -
|
| - /// Sets the native [name] for the member [element]. This name is used for
|
| - /// [element] in the generated JavaScript.
|
| - void setNativeMemberName(MemberEntity element, String name) {
|
| - // TODO(johnniwinther): Avoid setting this more than once. The enqueuer
|
| - // might enqueue [element] several times (before processing it) and computes
|
| - // name on each call to `internalAddToWorkList`.
|
| - assert(invariant(element,
|
| - nativeMemberName[element] == null || nativeMemberName[element] == name,
|
| - message: "Native member name set inconsistently on $element: "
|
| - "Existing name '${nativeMemberName[element]}', "
|
| - "new name '$name'."));
|
| - nativeMemberName[element] = name;
|
| - }
|
| -
|
| - /// Sets the native tag info for [cls].
|
| - ///
|
| - /// The tag info string contains comma-separated 'words' which are either
|
| - /// dispatch tags (having JavaScript identifier syntax) and directives that
|
| - /// begin with `!`.
|
| - void setNativeClassTagInfo(ClassEntity cls, String tagInfo) {
|
| - // TODO(johnniwinther): Assert that this is only called once. The memory
|
| - // compiler copies pre-processed elements into a new compiler through
|
| - // [Compiler.onLibraryScanned] and thereby causes multiple calls to this
|
| - // method.
|
| - assert(invariant(cls,
|
| - nativeClassTagInfo[cls] == null || nativeClassTagInfo[cls] == tagInfo,
|
| - message: "Native tag info set inconsistently on $cls: "
|
| - "Existing tag info '${nativeClassTagInfo[cls]}', "
|
| - "new tag info '$tagInfo'."));
|
| - nativeClassTagInfo[cls] = tagInfo;
|
| - }
|
| -
|
| - /// Returns the list of native tag words for [cls].
|
| - List<String> getNativeTagsOfClassRaw(ClassEntity cls) {
|
| - String quotedName = nativeClassTagInfo[cls];
|
| - return quotedName.substring(1, quotedName.length - 1).split(',');
|
| - }
|
| -
|
| - /// Returns the list of non-directive native tag words for [cls].
|
| - List<String> getNativeTagsOfClass(ClassEntity cls) {
|
| - return getNativeTagsOfClassRaw(cls)
|
| - .where((s) => !s.startsWith('!'))
|
| - .toList();
|
| - }
|
| -
|
| - /// Returns `true` if [cls] has a `!nonleaf` tag word.
|
| - bool hasNativeTagsForcedNonLeaf(ClassEntity cls) {
|
| - return getNativeTagsOfClassRaw(cls).contains('!nonleaf');
|
| - }
|
| -
|
| /// Returns the [NativeBehavior] for calling the native [method].
|
| NativeBehavior getNativeMethodBehavior(FunctionEntity method) {
|
| assert(invariant(method, nativeMethodBehavior.containsKey(method),
|
| @@ -432,21 +472,6 @@ class NativeDataImpl
|
| return nativeFieldStoreBehavior[field];
|
| }
|
|
|
| - /// Registers the [behavior] for calling the native [method].
|
| - void setNativeMethodBehavior(FunctionEntity method, NativeBehavior behavior) {
|
| - nativeMethodBehavior[method] = behavior;
|
| - }
|
| -
|
| - /// Registers the [behavior] for reading from the native [field].
|
| - void setNativeFieldLoadBehavior(FieldEntity field, NativeBehavior behavior) {
|
| - nativeFieldLoadBehavior[field] = behavior;
|
| - }
|
| -
|
| - /// Registers the [behavior] for writing to the native [field].
|
| - void setNativeFieldStoreBehavior(FieldEntity field, NativeBehavior behavior) {
|
| - nativeFieldStoreBehavior[field] = behavior;
|
| - }
|
| -
|
| /// Apply JS$ escaping scheme to convert possible escaped Dart names into
|
| /// JS names.
|
| String computeUnescapedJSInteropName(String name) {
|
| @@ -455,3 +480,39 @@ class NativeDataImpl
|
| : name;
|
| }
|
| }
|
| +
|
| +class NativeClassTag {
|
| + final List<String> names;
|
| + final bool isNonLeaf;
|
| +
|
| + factory NativeClassTag(String tagText) {
|
| + List<String> tags = tagText.split(',');
|
| + List<String> names = tags.where((s) => !s.startsWith('!')).toList();
|
| + bool isNonLeaf = tags.contains('!nonleaf');
|
| + return new NativeClassTag.internal(names, isNonLeaf);
|
| + }
|
| +
|
| + NativeClassTag.internal(this.names, this.isNonLeaf);
|
| +
|
| + String get text {
|
| + StringBuffer sb = new StringBuffer();
|
| + sb.write(names.join(','));
|
| + if (isNonLeaf) {
|
| + if (names.isNotEmpty) {
|
| + sb.write(',');
|
| + }
|
| + sb.write('!nonleaf');
|
| + }
|
| + return sb.toString();
|
| + }
|
| +
|
| + int get hashCode => Hashing.listHash(names, isNonLeaf.hashCode);
|
| +
|
| + bool operator ==(other) {
|
| + if (identical(this, other)) return true;
|
| + if (other is! NativeClassTag) return false;
|
| + return equalElements(names, other.names) && isNonLeaf == other.isNonLeaf;
|
| + }
|
| +
|
| + String toString() => text;
|
| +}
|
|
|