| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library js_backend.native_data; | 5 library js_backend.native_data; |
| 6 | 6 |
| 7 import '../common.dart'; | 7 import '../common.dart'; |
| 8 import '../elements/elements.dart' | 8 import '../elements/elements.dart' show ClassElement; |
| 9 show | |
| 10 ClassElement, | |
| 11 Element, | |
| 12 FieldElement, | |
| 13 LibraryElement, | |
| 14 MemberElement, | |
| 15 MethodElement; | |
| 16 import '../elements/entities.dart'; | 9 import '../elements/entities.dart'; |
| 17 import '../native/behavior.dart' show NativeBehavior; | 10 import '../native/behavior.dart' show NativeBehavior; |
| 18 | 11 |
| 19 /// Basic information for native classes and methods and js-interop | 12 /// Basic information for native classes and methods and js-interop |
| 20 /// classes. | 13 /// classes. |
| 21 /// | 14 /// |
| 22 /// This information is computed during loading using [NativeClassDataBuilder]. | 15 /// This information is computed during loading using [NativeClassDataBuilder]. |
| 23 abstract class NativeClassData { | 16 abstract class NativeClassData { |
| 24 /// Returns `true` if [cls] corresponds to a native JavaScript class. | 17 /// Returns `true` if [cls] corresponds to a native JavaScript class. |
| 25 /// | 18 /// |
| 26 /// A class is marked as native either through the `@Native(...)` annotation | 19 /// A class is marked as native either through the `@Native(...)` annotation |
| 27 /// allowed for internal libraries or via the typed JavaScriptInterop | 20 /// allowed for internal libraries or via the typed JavaScriptInterop |
| 28 /// mechanism allowed for user libraries. | 21 /// mechanism allowed for user libraries. |
| 29 bool isNativeClass(ClassEntity element); | 22 bool isNativeClass(ClassEntity element); |
| 30 | 23 |
| 31 /// Returns `true` if [element] or any of its superclasses is native. | 24 /// Returns `true` if [element] or any of its superclasses is native. |
| 32 bool isNativeOrExtendsNative(ClassElement element); | 25 bool isNativeOrExtendsNative(ClassEntity element); |
| 33 | 26 |
| 34 /// Returns `true` if [element] is a JsInterop library. | 27 /// Returns `true` if [element] is a JsInterop library. |
| 35 bool isJsInteropLibrary(LibraryElement element); | 28 bool isJsInteropLibrary(LibraryEntity element); |
| 36 | 29 |
| 37 /// Returns `true` if [element] is a JsInterop class. | 30 /// Returns `true` if [element] is a JsInterop class. |
| 38 bool isJsInteropClass(ClassElement element); | 31 bool isJsInteropClass(ClassEntity element); |
| 39 } | 32 } |
| 40 | 33 |
| 41 /// Additional element information for native classes and methods and js-interop | 34 /// Additional element information for native classes and methods and js-interop |
| 42 /// methods. | 35 /// methods. |
| 43 /// | 36 /// |
| 44 /// This information is computed during resolution using [NativeDataBuilder]. | 37 /// This information is computed during resolution using [NativeDataBuilder]. |
| 45 abstract class NativeData extends NativeClassData { | 38 abstract class NativeData extends NativeClassData { |
| 46 /// Returns `true` if [element] corresponds to a native JavaScript member. | 39 /// Returns `true` if [element] corresponds to a native JavaScript member. |
| 47 /// | 40 /// |
| 48 /// A member is marked as native either through the native mechanism | 41 /// A member is marked as native either through the native mechanism |
| 49 /// (`@Native(...)` or the `native` pseudo keyword) allowed for internal | 42 /// (`@Native(...)` or the `native` pseudo keyword) allowed for internal |
| 50 /// libraries or via the typed JavaScriptInterop mechanism allowed for user | 43 /// libraries or via the typed JavaScriptInterop mechanism allowed for user |
| 51 /// libraries. | 44 /// libraries. |
| 52 bool isNativeMember(MemberEntity element); | 45 bool isNativeMember(MemberEntity element); |
| 53 | 46 |
| 54 /// Returns the [NativeBehavior] for calling the native [method]. | 47 /// Returns the [NativeBehavior] for calling the native [method]. |
| 55 NativeBehavior getNativeMethodBehavior(MethodElement method); | 48 NativeBehavior getNativeMethodBehavior(FunctionEntity method); |
| 56 | 49 |
| 57 /// Returns the [NativeBehavior] for reading from the native [field]. | 50 /// Returns the [NativeBehavior] for reading from the native [field]. |
| 58 NativeBehavior getNativeFieldLoadBehavior(FieldElement field); | 51 NativeBehavior getNativeFieldLoadBehavior(FieldEntity field); |
| 59 | 52 |
| 60 /// Returns the [NativeBehavior] for writing to the native [field]. | 53 /// Returns the [NativeBehavior] for writing to the native [field]. |
| 61 NativeBehavior getNativeFieldStoreBehavior(FieldElement field); | 54 NativeBehavior getNativeFieldStoreBehavior(FieldEntity field); |
| 62 | 55 |
| 63 /// Returns `true` if the name of [element] is fixed for the generated | 56 /// Returns `true` if the name of [element] is fixed for the generated |
| 64 /// JavaScript. | 57 /// JavaScript. |
| 65 bool hasFixedBackendName(MemberElement element); | 58 bool hasFixedBackendName(MemberEntity element); |
| 66 | 59 |
| 67 /// Computes the name for [element] to use in the generated JavaScript. This | 60 /// Computes the name for [element] to use in the generated JavaScript. This |
| 68 /// is either given through a native annotation or a js interop annotation. | 61 /// is either given through a native annotation or a js interop annotation. |
| 69 String getFixedBackendName(MemberEntity element); | 62 String getFixedBackendName(MemberEntity element); |
| 70 | 63 |
| 71 /// Computes the name prefix for [element to use in the generated JavaScript. | 64 /// Computes the name prefix for [element to use in the generated JavaScript. |
| 72 /// For static and top-level members and constructors this is based on the | 65 /// For static and top-level members and constructors this is based on the |
| 73 /// names for the library and/or the enclosing class. | 66 /// names for the library and/or the enclosing class. |
| 74 String getFixedBackendMethodPath(MethodElement element); | 67 String getFixedBackendMethodPath(FunctionEntity element); |
| 75 | 68 |
| 76 /// Returns the list of non-directive native tag words for [cls]. | 69 /// Returns the list of non-directive native tag words for [cls]. |
| 77 List<String> getNativeTagsOfClass(ClassElement cls); | 70 List<String> getNativeTagsOfClass(ClassEntity cls); |
| 78 | 71 |
| 79 /// Returns `true` if [cls] has a `!nonleaf` tag word. | 72 /// Returns `true` if [cls] has a `!nonleaf` tag word. |
| 80 bool hasNativeTagsForcedNonLeaf(ClassElement cls); | 73 bool hasNativeTagsForcedNonLeaf(ClassEntity cls); |
| 81 | 74 |
| 82 /// Returns `true` if [element] is a JsInterop method. | 75 /// Returns `true` if [element] is a JsInterop method. |
| 83 bool isJsInteropMember(MemberEntity element); | 76 bool isJsInteropMember(MemberEntity element); |
| 84 | 77 |
| 85 /// Returns the explicit js interop name for library [element]. | 78 /// Returns the explicit js interop name for library [element]. |
| 86 String getJsInteropLibraryName(LibraryElement element); | 79 String getJsInteropLibraryName(LibraryEntity element); |
| 87 | 80 |
| 88 /// Returns the explicit js interop name for class [element]. | 81 /// Returns the explicit js interop name for class [element]. |
| 89 String getJsInteropClassName(ClassElement element); | 82 String getJsInteropClassName(ClassEntity element); |
| 90 | 83 |
| 91 /// Returns the explicit js interop name for member [element]. | 84 /// Returns the explicit js interop name for member [element]. |
| 92 String getJsInteropMemberName(MemberElement element); | 85 String getJsInteropMemberName(MemberEntity element); |
| 93 | 86 |
| 94 /// Apply JS$ escaping scheme to convert possible escaped Dart names into | 87 /// Apply JS$ escaping scheme to convert possible escaped Dart names into |
| 95 /// JS names. | 88 /// JS names. |
| 96 String computeUnescapedJSInteropName(String name); | 89 String computeUnescapedJSInteropName(String name); |
| 97 } | 90 } |
| 98 | 91 |
| 99 abstract class NativeClassDataBuilder { | 92 abstract class NativeClassDataBuilder { |
| 100 /// Sets the native tag info for [cls]. | 93 /// Sets the native tag info for [cls]. |
| 101 /// | 94 /// |
| 102 /// The tag info string contains comma-separated 'words' which are either | 95 /// The tag info string contains comma-separated 'words' which are either |
| 103 /// dispatch tags (having JavaScript identifier syntax) and directives that | 96 /// dispatch tags (having JavaScript identifier syntax) and directives that |
| 104 /// begin with `!`. | 97 /// begin with `!`. |
| 105 void setNativeClassTagInfo(ClassElement cls, String tagInfo); | 98 void setNativeClassTagInfo(ClassEntity cls, String tagInfo); |
| 106 | 99 |
| 107 /// Marks [element] as an explicit part of JsInterop. The js interop name is | 100 /// Marks [element] as an explicit part of JsInterop. The js interop name is |
| 108 /// expected to be computed later. | 101 /// expected to be computed later. |
| 109 void markAsJsInteropLibrary(LibraryElement element); | 102 void markAsJsInteropLibrary(LibraryEntity element); |
| 110 | 103 |
| 111 /// Marks [element] as an explicit part of JsInterop. The js interop name is | 104 /// Marks [element] as an explicit part of JsInterop. The js interop name is |
| 112 /// expected to be computed later. | 105 /// expected to be computed later. |
| 113 void markAsJsInteropClass(ClassElement element); | 106 void markAsJsInteropClass(ClassEntity element); |
| 114 } | 107 } |
| 115 | 108 |
| 116 abstract class NativeDataBuilder { | 109 abstract class NativeDataBuilder { |
| 117 /// Registers the [behavior] for calling the native [method]. | 110 /// Registers the [behavior] for calling the native [method]. |
| 118 void setNativeMethodBehavior(MethodElement method, NativeBehavior behavior); | 111 void setNativeMethodBehavior(FunctionEntity method, NativeBehavior behavior); |
| 119 | 112 |
| 120 /// Registers the [behavior] for reading from the native [field]. | 113 /// Registers the [behavior] for reading from the native [field]. |
| 121 void setNativeFieldLoadBehavior(FieldElement field, NativeBehavior behavior); | 114 void setNativeFieldLoadBehavior(FieldEntity field, NativeBehavior behavior); |
| 122 | 115 |
| 123 /// Registers the [behavior] for writing to the native [field]. | 116 /// Registers the [behavior] for writing to the native [field]. |
| 124 void setNativeFieldStoreBehavior(FieldElement field, NativeBehavior behavior); | 117 void setNativeFieldStoreBehavior(FieldEntity field, NativeBehavior behavior); |
| 125 | 118 |
| 126 /// Returns the list of native tag words for [cls]. | 119 /// Returns the list of native tag words for [cls]. |
| 127 List<String> getNativeTagsOfClassRaw(ClassElement cls); | 120 List<String> getNativeTagsOfClassRaw(ClassEntity cls); |
| 128 | 121 |
| 129 /// Returns [element] as an explicit part of JsInterop. The js interop name is | 122 /// Returns [element] as an explicit part of JsInterop. The js interop name is |
| 130 /// expected to be computed later. | 123 /// expected to be computed later. |
| 131 void markAsJsInteropMember(MemberElement element); | 124 void markAsJsInteropMember(MemberEntity element); |
| 132 | 125 |
| 133 /// Sets the native [name] for the member [element]. This name is used for | 126 /// Sets the native [name] for the member [element]. This name is used for |
| 134 /// [element] in the generated JavaScript. | 127 /// [element] in the generated JavaScript. |
| 135 void setNativeMemberName(MemberElement element, String name); | 128 void setNativeMemberName(MemberEntity element, String name); |
| 136 | 129 |
| 137 /// Sets the explicit js interop [name] for the library [element]. | 130 /// Sets the explicit js interop [name] for the library [element]. |
| 138 void setJsInteropLibraryName(LibraryElement element, String name); | 131 void setJsInteropLibraryName(LibraryEntity element, String name); |
| 139 | 132 |
| 140 /// Sets the explicit js interop [name] for the class [element]. | 133 /// Sets the explicit js interop [name] for the class [element]. |
| 141 void setJsInteropClassName(ClassElement element, String name); | 134 void setJsInteropClassName(ClassEntity element, String name); |
| 142 | 135 |
| 143 /// Sets the explicit js interop [name] for the member [element]. | 136 /// Sets the explicit js interop [name] for the member [element]. |
| 144 void setJsInteropMemberName(MemberElement element, String name); | 137 void setJsInteropMemberName(MemberEntity element, String name); |
| 145 } | 138 } |
| 146 | 139 |
| 147 class NativeDataImpl | 140 class NativeDataImpl |
| 148 implements NativeData, NativeDataBuilder, NativeClassDataBuilder { | 141 implements NativeData, NativeDataBuilder, NativeClassDataBuilder { |
| 149 /// The JavaScript names for elements implemented via typed JavaScript | 142 /// The JavaScript names for elements implemented via typed JavaScript |
| 150 /// interop. | 143 /// interop. |
| 151 Map<LibraryElement, String> jsInteropLibraryNames = | 144 Map<LibraryEntity, String> jsInteropLibraryNames = <LibraryEntity, String>{}; |
| 152 <LibraryElement, String>{}; | 145 Map<ClassEntity, String> jsInteropClassNames = <ClassEntity, String>{}; |
| 153 Map<ClassElement, String> jsInteropClassNames = <ClassElement, String>{}; | 146 Map<MemberEntity, String> jsInteropMemberNames = <MemberEntity, String>{}; |
| 154 Map<MemberElement, String> jsInteropMemberNames = <MemberElement, String>{}; | |
| 155 | 147 |
| 156 /// The JavaScript names for native JavaScript elements implemented. | 148 /// The JavaScript names for native JavaScript elements implemented. |
| 157 Map<Element, String> nativeMemberName = <Element, String>{}; | 149 Map<MemberEntity, String> nativeMemberName = <MemberEntity, String>{}; |
| 158 | 150 |
| 159 /// Tag info for native JavaScript classes names. See | 151 /// Tag info for native JavaScript classes names. See |
| 160 /// [setNativeClassTagInfo]. | 152 /// [setNativeClassTagInfo]. |
| 161 Map<ClassElement, String> nativeClassTagInfo = <ClassElement, String>{}; | 153 Map<ClassEntity, String> nativeClassTagInfo = <ClassEntity, String>{}; |
| 162 | 154 |
| 163 /// Cache for [NativeBehavior]s for calling native methods. | 155 /// Cache for [NativeBehavior]s for calling native methods. |
| 164 Map<MethodElement, NativeBehavior> nativeMethodBehavior = | 156 Map<FunctionEntity, NativeBehavior> nativeMethodBehavior = |
| 165 <MethodElement, NativeBehavior>{}; | 157 <FunctionEntity, NativeBehavior>{}; |
| 166 | 158 |
| 167 /// Cache for [NativeBehavior]s for reading from native fields. | 159 /// Cache for [NativeBehavior]s for reading from native fields. |
| 168 Map<MemberElement, NativeBehavior> nativeFieldLoadBehavior = | 160 Map<MemberEntity, NativeBehavior> nativeFieldLoadBehavior = |
| 169 <FieldElement, NativeBehavior>{}; | 161 <FieldEntity, NativeBehavior>{}; |
| 170 | 162 |
| 171 /// Cache for [NativeBehavior]s for writing to native fields. | 163 /// Cache for [NativeBehavior]s for writing to native fields. |
| 172 Map<MemberElement, NativeBehavior> nativeFieldStoreBehavior = | 164 Map<MemberEntity, NativeBehavior> nativeFieldStoreBehavior = |
| 173 <FieldElement, NativeBehavior>{}; | 165 <FieldEntity, NativeBehavior>{}; |
| 174 | 166 |
| 175 /// Prefix used to escape JS names that are not valid Dart names | 167 /// Prefix used to escape JS names that are not valid Dart names |
| 176 /// when using JSInterop. | 168 /// when using JSInterop. |
| 177 static const String _jsInteropEscapePrefix = r'JS$'; | 169 static const String _jsInteropEscapePrefix = r'JS$'; |
| 178 | 170 |
| 179 /// Returns `true` if [element] is explicitly marked as part of JsInterop. | 171 /// Returns `true` if [element] is explicitly marked as part of JsInterop. |
| 180 bool _isJsInteropLibrary(LibraryElement element) { | 172 bool _isJsInteropLibrary(LibraryEntity element) { |
| 181 return jsInteropLibraryNames.containsKey(element); | 173 return jsInteropLibraryNames.containsKey(element); |
| 182 } | 174 } |
| 183 | 175 |
| 184 /// Returns `true` if [element] is explicitly marked as part of JsInterop. | 176 /// Returns `true` if [element] is explicitly marked as part of JsInterop. |
| 185 bool _isJsInteropClass(ClassElement element) { | 177 bool _isJsInteropClass(ClassEntity element) { |
| 186 return jsInteropClassNames.containsKey(element); | 178 return jsInteropClassNames.containsKey(element); |
| 187 } | 179 } |
| 188 | 180 |
| 189 /// Returns `true` if [element] is explicitly marked as part of JsInterop. | 181 /// Returns `true` if [element] is explicitly marked as part of JsInterop. |
| 190 bool _isJsInteropMember(MemberElement element) { | 182 bool _isJsInteropMember(MemberEntity element) { |
| 191 return jsInteropMemberNames.containsKey(element); | 183 return jsInteropMemberNames.containsKey(element); |
| 192 } | 184 } |
| 193 | 185 |
| 194 @override | 186 @override |
| 195 void markAsJsInteropLibrary(LibraryElement element) { | 187 void markAsJsInteropLibrary(LibraryEntity element) { |
| 196 jsInteropLibraryNames[element] = null; | 188 jsInteropLibraryNames[element] = null; |
| 197 } | 189 } |
| 198 | 190 |
| 199 @override | 191 @override |
| 200 void markAsJsInteropClass(ClassElement element) { | 192 void markAsJsInteropClass(ClassEntity element) { |
| 201 jsInteropClassNames[element] = null; | 193 jsInteropClassNames[element] = null; |
| 202 } | 194 } |
| 203 | 195 |
| 204 @override | 196 @override |
| 205 void markAsJsInteropMember(MemberElement element) { | 197 void markAsJsInteropMember(MemberEntity element) { |
| 206 jsInteropMemberNames[element] = null; | 198 jsInteropMemberNames[element] = null; |
| 207 } | 199 } |
| 208 | 200 |
| 209 /// Sets the explicit js interop [name] for the library [element]. | 201 /// Sets the explicit js interop [name] for the library [element]. |
| 210 void setJsInteropLibraryName(LibraryElement element, String name) { | 202 void setJsInteropLibraryName(LibraryEntity element, String name) { |
| 211 assert(invariant(element, _isJsInteropLibrary(element), | 203 assert(invariant(element, _isJsInteropLibrary(element), |
| 212 message: | 204 message: |
| 213 'Library $element is not js interop but given a js interop name.')); | 205 'Library $element is not js interop but given a js interop name.')); |
| 214 jsInteropLibraryNames[element] = name; | 206 jsInteropLibraryNames[element] = name; |
| 215 } | 207 } |
| 216 | 208 |
| 217 /// Sets the explicit js interop [name] for the class [element]. | 209 /// Sets the explicit js interop [name] for the class [element]. |
| 218 void setJsInteropClassName(ClassElement element, String name) { | 210 void setJsInteropClassName(ClassEntity element, String name) { |
| 219 assert(invariant(element, _isJsInteropClass(element), | 211 assert(invariant(element, _isJsInteropClass(element), |
| 220 message: | 212 message: |
| 221 'Class $element is not js interop but given a js interop name.')); | 213 'Class $element is not js interop but given a js interop name.')); |
| 222 jsInteropClassNames[element] = name; | 214 jsInteropClassNames[element] = name; |
| 223 } | 215 } |
| 224 | 216 |
| 225 /// Sets the explicit js interop [name] for the member [element]. | 217 /// Sets the explicit js interop [name] for the member [element]. |
| 226 void setJsInteropMemberName(MemberElement element, String name) { | 218 void setJsInteropMemberName(MemberEntity element, String name) { |
| 227 assert(invariant(element, _isJsInteropMember(element), | 219 assert(invariant(element, _isJsInteropMember(element), |
| 228 message: | 220 message: |
| 229 'Member $element is not js interop but given a js interop name.')); | 221 'Member $element is not js interop but given a js interop name.')); |
| 230 jsInteropMemberNames[element] = name; | 222 jsInteropMemberNames[element] = name; |
| 231 } | 223 } |
| 232 | 224 |
| 233 /// Returns the explicit js interop name for library [element]. | 225 /// Returns the explicit js interop name for library [element]. |
| 234 String getJsInteropLibraryName(LibraryElement element) { | 226 String getJsInteropLibraryName(LibraryEntity element) { |
| 235 return jsInteropLibraryNames[element]; | 227 return jsInteropLibraryNames[element]; |
| 236 } | 228 } |
| 237 | 229 |
| 238 /// Returns the explicit js interop name for class [element]. | 230 /// Returns the explicit js interop name for class [element]. |
| 239 String getJsInteropClassName(ClassElement element) { | 231 String getJsInteropClassName(ClassEntity element) { |
| 240 return jsInteropClassNames[element]; | 232 return jsInteropClassNames[element]; |
| 241 } | 233 } |
| 242 | 234 |
| 243 /// Returns the explicit js interop name for member [element]. | 235 /// Returns the explicit js interop name for member [element]. |
| 244 String getJsInteropMemberName(MemberElement element) { | 236 String getJsInteropMemberName(MemberEntity element) { |
| 245 return jsInteropMemberNames[element]; | 237 return jsInteropMemberNames[element]; |
| 246 } | 238 } |
| 247 | 239 |
| 248 /// Returns `true` if [element] is a JsInterop library. | 240 /// Returns `true` if [element] is a JsInterop library. |
| 249 bool isJsInteropLibrary(LibraryElement element) => | 241 bool isJsInteropLibrary(LibraryEntity element) => |
| 250 _isJsInteropLibrary(element); | 242 _isJsInteropLibrary(element); |
| 251 | 243 |
| 252 /// Returns `true` if [element] is a JsInterop class. | 244 /// Returns `true` if [element] is a JsInterop class. |
| 253 bool isJsInteropClass(ClassElement element) => _isJsInteropClass(element); | 245 bool isJsInteropClass(ClassEntity element) => _isJsInteropClass(element); |
| 254 | 246 |
| 255 /// Returns `true` if [element] is a JsInterop method. | 247 /// Returns `true` if [element] is a JsInterop method. |
| 256 bool isJsInteropMember(MemberElement element) { | 248 bool isJsInteropMember(MemberEntity element) { |
| 257 if (element.isFunction || element.isConstructor || element.isAccessor) { | 249 if (element.isFunction || |
| 258 MethodElement function = element; | 250 element.isConstructor || |
| 251 element.isGetter || |
| 252 element.isSetter) { |
| 253 FunctionEntity function = element; |
| 259 if (!function.isExternal) return false; | 254 if (!function.isExternal) return false; |
| 260 | 255 |
| 261 if (_isJsInteropMember(function)) return true; | 256 if (_isJsInteropMember(function)) return true; |
| 262 if (function.isClassMember) { | 257 if (function.enclosingClass != null) { |
| 263 return _isJsInteropClass(function.enclosingClass); | 258 return _isJsInteropClass(function.enclosingClass); |
| 264 } | 259 } |
| 265 if (function.isTopLevel) { | 260 if (function.isTopLevel) { |
| 266 return _isJsInteropLibrary(function.library); | 261 return _isJsInteropLibrary(function.library); |
| 267 } | 262 } |
| 268 return false; | 263 return false; |
| 269 } else { | 264 } else { |
| 270 return _isJsInteropMember(element); | 265 return _isJsInteropMember(element); |
| 271 } | 266 } |
| 272 } | 267 } |
| 273 | 268 |
| 274 /// Returns `true` if the name of [element] is fixed for the generated | 269 /// Returns `true` if the name of [element] is fixed for the generated |
| 275 /// JavaScript. | 270 /// JavaScript. |
| 276 bool hasFixedBackendName(MemberElement element) { | 271 bool hasFixedBackendName(MemberEntity element) { |
| 277 return isJsInteropMember(element) || | 272 return isJsInteropMember(element) || nativeMemberName.containsKey(element); |
| 278 nativeMemberName.containsKey(element.declaration); | |
| 279 } | 273 } |
| 280 | 274 |
| 281 /// Computes the name for [element] to use in the generated JavaScript. This | 275 /// Computes the name for [element] to use in the generated JavaScript. This |
| 282 /// is either given through a native annotation or a js interop annotation. | 276 /// is either given through a native annotation or a js interop annotation. |
| 283 String getFixedBackendName(MemberElement element) { | 277 String getFixedBackendName(MemberEntity element) { |
| 284 String name = nativeMemberName[element.declaration]; | 278 String name = nativeMemberName[element]; |
| 285 if (name == null && isJsInteropMember(element)) { | 279 if (name == null && isJsInteropMember(element)) { |
| 286 // If an element isJsInterop but _isJsInterop is false that means it is | 280 // If an element isJsInterop but _isJsInterop is false that means it is |
| 287 // considered interop as the parent class is interop. | 281 // considered interop as the parent class is interop. |
| 288 name = element.isConstructor | 282 name = element.isConstructor |
| 289 ? _jsClassNameHelper(element.enclosingClass) | 283 ? _jsClassNameHelper(element.enclosingClass) |
| 290 : _jsMemberNameHelper(element); | 284 : _jsMemberNameHelper(element); |
| 291 nativeMemberName[element.declaration] = name; | 285 nativeMemberName[element] = name; |
| 292 } | 286 } |
| 293 return name; | 287 return name; |
| 294 } | 288 } |
| 295 | 289 |
| 296 String _jsLibraryNameHelper(LibraryElement element) { | 290 String _jsLibraryNameHelper(LibraryEntity element) { |
| 297 String jsInteropName = getJsInteropLibraryName(element); | 291 String jsInteropName = getJsInteropLibraryName(element); |
| 298 if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName; | 292 if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName; |
| 299 return 'self'; | 293 return 'self'; |
| 300 } | 294 } |
| 301 | 295 |
| 302 String _jsClassNameHelper(ClassElement element) { | 296 String _jsClassNameHelper(ClassEntity element) { |
| 303 String jsInteropName = getJsInteropClassName(element); | 297 String jsInteropName = getJsInteropClassName(element); |
| 304 if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName; | 298 if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName; |
| 305 return computeUnescapedJSInteropName(element.name); | 299 return computeUnescapedJSInteropName(element.name); |
| 306 } | 300 } |
| 307 | 301 |
| 308 String _jsMemberNameHelper(MemberElement element) { | 302 String _jsMemberNameHelper(MemberEntity element) { |
| 309 String jsInteropName = jsInteropMemberNames[element]; | 303 String jsInteropName = jsInteropMemberNames[element]; |
| 310 assert(invariant(element, | 304 assert(invariant(element, |
| 311 !(jsInteropMemberNames.containsKey(element) && jsInteropName == null), | 305 !(jsInteropMemberNames.containsKey(element) && jsInteropName == null), |
| 312 message: | 306 message: |
| 313 'Member $element is js interop but js interop name has not yet ' | 307 'Member $element is js interop but js interop name has not yet ' |
| 314 'been computed.')); | 308 'been computed.')); |
| 315 if (jsInteropName != null && jsInteropName.isNotEmpty) { | 309 if (jsInteropName != null && jsInteropName.isNotEmpty) { |
| 316 return jsInteropName; | 310 return jsInteropName; |
| 317 } | 311 } |
| 318 return computeUnescapedJSInteropName(element.name); | 312 return computeUnescapedJSInteropName(element.name); |
| 319 } | 313 } |
| 320 | 314 |
| 321 /// Returns a JavaScript path specifying the context in which | 315 /// Returns a JavaScript path specifying the context in which |
| 322 /// [element.fixedBackendName] should be evaluated. Only applicable for | 316 /// [element.fixedBackendName] should be evaluated. Only applicable for |
| 323 /// elements using typed JavaScript interop. | 317 /// elements using typed JavaScript interop. |
| 324 /// For example: fixedBackendPath for the static method createMap in the | 318 /// For example: fixedBackendPath for the static method createMap in the |
| 325 /// Map class of the goog.map JavaScript library would have path | 319 /// Map class of the goog.map JavaScript library would have path |
| 326 /// "goog.maps.Map". | 320 /// "goog.maps.Map". |
| 327 String getFixedBackendMethodPath(MethodElement element) { | 321 String getFixedBackendMethodPath(FunctionEntity element) { |
| 328 if (!isJsInteropMember(element)) return null; | 322 if (!isJsInteropMember(element)) return null; |
| 329 if (element.isInstanceMember) return 'this'; | 323 if (element.isInstanceMember) return 'this'; |
| 330 if (element.isConstructor) { | 324 if (element.isConstructor) { |
| 331 return _fixedBackendClassPath(element.enclosingClass); | 325 return _fixedBackendClassPath(element.enclosingClass); |
| 332 } | 326 } |
| 333 StringBuffer sb = new StringBuffer(); | 327 StringBuffer sb = new StringBuffer(); |
| 334 sb.write(_jsLibraryNameHelper(element.library)); | 328 sb.write(_jsLibraryNameHelper(element.library)); |
| 335 if (element.enclosingClass != null) { | 329 if (element.enclosingClass != null) { |
| 336 sb..write('.')..write(_jsClassNameHelper(element.enclosingClass)); | 330 sb..write('.')..write(_jsClassNameHelper(element.enclosingClass)); |
| 337 } | 331 } |
| 338 return sb.toString(); | 332 return sb.toString(); |
| 339 } | 333 } |
| 340 | 334 |
| 341 String _fixedBackendClassPath(ClassElement element) { | 335 String _fixedBackendClassPath(ClassEntity element) { |
| 342 if (!isJsInteropClass(element)) return null; | 336 if (!isJsInteropClass(element)) return null; |
| 343 return _jsLibraryNameHelper(element.library); | 337 return _jsLibraryNameHelper(element.library); |
| 344 } | 338 } |
| 345 | 339 |
| 346 /// Returns `true` if [cls] is a native class. | 340 /// Returns `true` if [cls] is a native class. |
| 347 bool isNativeClass(ClassElement element) { | 341 bool isNativeClass(ClassEntity element) { |
| 348 if (isJsInteropClass(element)) return true; | 342 if (isJsInteropClass(element)) return true; |
| 349 return nativeClassTagInfo.containsKey(element); | 343 return nativeClassTagInfo.containsKey(element); |
| 350 } | 344 } |
| 351 | 345 |
| 352 /// Returns `true` if [element] is a native member of a native class. | 346 /// Returns `true` if [element] is a native member of a native class. |
| 353 bool isNativeMember(MemberElement element) { | 347 bool isNativeMember(MemberEntity element) { |
| 354 if (isJsInteropMember(element)) return true; | 348 if (isJsInteropMember(element)) return true; |
| 355 return nativeMemberName.containsKey(element); | 349 return nativeMemberName.containsKey(element); |
| 356 } | 350 } |
| 357 | 351 |
| 358 /// Returns `true` if [element] or any of its superclasses is native. | 352 /// Returns `true` if [element] or any of its superclasses is native. |
| 359 bool isNativeOrExtendsNative(ClassElement element) { | 353 bool isNativeOrExtendsNative(ClassElement element) { |
| 360 if (element == null) return false; | 354 if (element == null) return false; |
| 361 if (isNativeClass(element) || isJsInteropClass(element)) { | 355 if (isNativeClass(element) || isJsInteropClass(element)) { |
| 362 return true; | 356 return true; |
| 363 } | 357 } |
| 364 assert(element.isResolved); | 358 assert(element.isResolved); |
| 365 return isNativeOrExtendsNative(element.superclass); | 359 return isNativeOrExtendsNative(element.superclass); |
| 366 } | 360 } |
| 367 | 361 |
| 368 /// Sets the native [name] for the member [element]. This name is used for | 362 /// Sets the native [name] for the member [element]. This name is used for |
| 369 /// [element] in the generated JavaScript. | 363 /// [element] in the generated JavaScript. |
| 370 void setNativeMemberName(MemberElement element, String name) { | 364 void setNativeMemberName(MemberEntity element, String name) { |
| 371 // TODO(johnniwinther): Avoid setting this more than once. The enqueuer | 365 // TODO(johnniwinther): Avoid setting this more than once. The enqueuer |
| 372 // might enqueue [element] several times (before processing it) and computes | 366 // might enqueue [element] several times (before processing it) and computes |
| 373 // name on each call to `internalAddToWorkList`. | 367 // name on each call to `internalAddToWorkList`. |
| 374 assert(invariant( | 368 assert(invariant(element, |
| 375 element, | 369 nativeMemberName[element] == null || nativeMemberName[element] == name, |
| 376 nativeMemberName[element.declaration] == null || | |
| 377 nativeMemberName[element.declaration] == name, | |
| 378 message: "Native member name set inconsistently on $element: " | 370 message: "Native member name set inconsistently on $element: " |
| 379 "Existing name '${nativeMemberName[element.declaration]}', " | 371 "Existing name '${nativeMemberName[element]}', " |
| 380 "new name '$name'.")); | 372 "new name '$name'.")); |
| 381 nativeMemberName[element.declaration] = name; | 373 nativeMemberName[element] = name; |
| 382 } | 374 } |
| 383 | 375 |
| 384 /// Sets the native tag info for [cls]. | 376 /// Sets the native tag info for [cls]. |
| 385 /// | 377 /// |
| 386 /// The tag info string contains comma-separated 'words' which are either | 378 /// The tag info string contains comma-separated 'words' which are either |
| 387 /// dispatch tags (having JavaScript identifier syntax) and directives that | 379 /// dispatch tags (having JavaScript identifier syntax) and directives that |
| 388 /// begin with `!`. | 380 /// begin with `!`. |
| 389 void setNativeClassTagInfo(ClassElement cls, String tagInfo) { | 381 void setNativeClassTagInfo(ClassEntity cls, String tagInfo) { |
| 390 // TODO(johnniwinther): Assert that this is only called once. The memory | 382 // TODO(johnniwinther): Assert that this is only called once. The memory |
| 391 // compiler copies pre-processed elements into a new compiler through | 383 // compiler copies pre-processed elements into a new compiler through |
| 392 // [Compiler.onLibraryScanned] and thereby causes multiple calls to this | 384 // [Compiler.onLibraryScanned] and thereby causes multiple calls to this |
| 393 // method. | 385 // method. |
| 394 assert(invariant( | 386 assert(invariant(cls, |
| 395 cls, | 387 nativeClassTagInfo[cls] == null || nativeClassTagInfo[cls] == tagInfo, |
| 396 nativeClassTagInfo[cls.declaration] == null || | |
| 397 nativeClassTagInfo[cls.declaration] == tagInfo, | |
| 398 message: "Native tag info set inconsistently on $cls: " | 388 message: "Native tag info set inconsistently on $cls: " |
| 399 "Existing tag info '${nativeClassTagInfo[cls.declaration]}', " | 389 "Existing tag info '${nativeClassTagInfo[cls]}', " |
| 400 "new tag info '$tagInfo'.")); | 390 "new tag info '$tagInfo'.")); |
| 401 nativeClassTagInfo[cls.declaration] = tagInfo; | 391 nativeClassTagInfo[cls] = tagInfo; |
| 402 } | 392 } |
| 403 | 393 |
| 404 /// Returns the list of native tag words for [cls]. | 394 /// Returns the list of native tag words for [cls]. |
| 405 List<String> getNativeTagsOfClassRaw(ClassElement cls) { | 395 List<String> getNativeTagsOfClassRaw(ClassEntity cls) { |
| 406 String quotedName = nativeClassTagInfo[cls.declaration]; | 396 String quotedName = nativeClassTagInfo[cls]; |
| 407 return quotedName.substring(1, quotedName.length - 1).split(','); | 397 return quotedName.substring(1, quotedName.length - 1).split(','); |
| 408 } | 398 } |
| 409 | 399 |
| 410 /// Returns the list of non-directive native tag words for [cls]. | 400 /// Returns the list of non-directive native tag words for [cls]. |
| 411 List<String> getNativeTagsOfClass(ClassElement cls) { | 401 List<String> getNativeTagsOfClass(ClassEntity cls) { |
| 412 return getNativeTagsOfClassRaw(cls) | 402 return getNativeTagsOfClassRaw(cls) |
| 413 .where((s) => !s.startsWith('!')) | 403 .where((s) => !s.startsWith('!')) |
| 414 .toList(); | 404 .toList(); |
| 415 } | 405 } |
| 416 | 406 |
| 417 /// Returns `true` if [cls] has a `!nonleaf` tag word. | 407 /// Returns `true` if [cls] has a `!nonleaf` tag word. |
| 418 bool hasNativeTagsForcedNonLeaf(ClassElement cls) { | 408 bool hasNativeTagsForcedNonLeaf(ClassEntity cls) { |
| 419 return getNativeTagsOfClassRaw(cls).contains('!nonleaf'); | 409 return getNativeTagsOfClassRaw(cls).contains('!nonleaf'); |
| 420 } | 410 } |
| 421 | 411 |
| 422 /// Returns the [NativeBehavior] for calling the native [method]. | 412 /// Returns the [NativeBehavior] for calling the native [method]. |
| 423 NativeBehavior getNativeMethodBehavior(MethodElement method) { | 413 NativeBehavior getNativeMethodBehavior(FunctionEntity method) { |
| 424 assert(invariant(method, nativeMethodBehavior.containsKey(method), | 414 assert(invariant(method, nativeMethodBehavior.containsKey(method), |
| 425 message: "No native method behavior has been computed for $method.")); | 415 message: "No native method behavior has been computed for $method.")); |
| 426 return nativeMethodBehavior[method]; | 416 return nativeMethodBehavior[method]; |
| 427 } | 417 } |
| 428 | 418 |
| 429 /// Returns the [NativeBehavior] for reading from the native [field]. | 419 /// Returns the [NativeBehavior] for reading from the native [field]. |
| 430 NativeBehavior getNativeFieldLoadBehavior(FieldElement field) { | 420 NativeBehavior getNativeFieldLoadBehavior(FieldEntity field) { |
| 431 assert(invariant(field, nativeFieldLoadBehavior.containsKey(field), | 421 assert(invariant(field, nativeFieldLoadBehavior.containsKey(field), |
| 432 message: "No native field load behavior has been " | 422 message: "No native field load behavior has been " |
| 433 "computed for $field.")); | 423 "computed for $field.")); |
| 434 return nativeFieldLoadBehavior[field]; | 424 return nativeFieldLoadBehavior[field]; |
| 435 } | 425 } |
| 436 | 426 |
| 437 /// Returns the [NativeBehavior] for writing to the native [field]. | 427 /// Returns the [NativeBehavior] for writing to the native [field]. |
| 438 NativeBehavior getNativeFieldStoreBehavior(FieldElement field) { | 428 NativeBehavior getNativeFieldStoreBehavior(FieldEntity field) { |
| 439 assert(invariant(field, nativeFieldStoreBehavior.containsKey(field), | 429 assert(invariant(field, nativeFieldStoreBehavior.containsKey(field), |
| 440 message: "No native field store behavior has been " | 430 message: "No native field store behavior has been " |
| 441 "computed for $field.")); | 431 "computed for $field.")); |
| 442 return nativeFieldStoreBehavior[field]; | 432 return nativeFieldStoreBehavior[field]; |
| 443 } | 433 } |
| 444 | 434 |
| 445 /// Registers the [behavior] for calling the native [method]. | 435 /// Registers the [behavior] for calling the native [method]. |
| 446 void setNativeMethodBehavior(MethodElement method, NativeBehavior behavior) { | 436 void setNativeMethodBehavior(FunctionEntity method, NativeBehavior behavior) { |
| 447 nativeMethodBehavior[method] = behavior; | 437 nativeMethodBehavior[method] = behavior; |
| 448 } | 438 } |
| 449 | 439 |
| 450 /// Registers the [behavior] for reading from the native [field]. | 440 /// Registers the [behavior] for reading from the native [field]. |
| 451 void setNativeFieldLoadBehavior(FieldElement field, NativeBehavior behavior) { | 441 void setNativeFieldLoadBehavior(FieldEntity field, NativeBehavior behavior) { |
| 452 nativeFieldLoadBehavior[field] = behavior; | 442 nativeFieldLoadBehavior[field] = behavior; |
| 453 } | 443 } |
| 454 | 444 |
| 455 /// Registers the [behavior] for writing to the native [field]. | 445 /// Registers the [behavior] for writing to the native [field]. |
| 456 void setNativeFieldStoreBehavior( | 446 void setNativeFieldStoreBehavior(FieldEntity field, NativeBehavior behavior) { |
| 457 FieldElement field, NativeBehavior behavior) { | |
| 458 nativeFieldStoreBehavior[field] = behavior; | 447 nativeFieldStoreBehavior[field] = behavior; |
| 459 } | 448 } |
| 460 | 449 |
| 461 /// Apply JS$ escaping scheme to convert possible escaped Dart names into | 450 /// Apply JS$ escaping scheme to convert possible escaped Dart names into |
| 462 /// JS names. | 451 /// JS names. |
| 463 String computeUnescapedJSInteropName(String name) { | 452 String computeUnescapedJSInteropName(String name) { |
| 464 return name.startsWith(_jsInteropEscapePrefix) | 453 return name.startsWith(_jsInteropEscapePrefix) |
| 465 ? name.substring(_jsInteropEscapePrefix.length) | 454 ? name.substring(_jsInteropEscapePrefix.length) |
| 466 : name; | 455 : name; |
| 467 } | 456 } |
| 468 } | 457 } |
| OLD | NEW |