| 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' |
| 9 show ClassElement, Element, FieldElement, FunctionElement, MemberElement; | 9 show |
| 10 ClassElement, |
| 11 Element, |
| 12 FieldElement, |
| 13 FunctionElement, |
| 14 MemberElement, |
| 15 MethodElement; |
| 10 import '../elements/entities.dart'; | 16 import '../elements/entities.dart'; |
| 11 import '../native/behavior.dart' show NativeBehavior; | 17 import '../native/behavior.dart' show NativeBehavior; |
| 12 | 18 |
| 13 /// Additional element information for native classes and methods and js-interop | 19 /// Additional element information for native classes and methods and js-interop |
| 14 /// methods. | 20 /// methods. |
| 15 class NativeData { | 21 abstract class NativeData { |
| 22 /// Returns `true` if [cls] corresponds to a native JavaScript class. |
| 23 /// |
| 24 /// A class is marked as native either through the `@Native(...)` annotation |
| 25 /// allowed for internal libraries or via the typed JavaScriptInterop |
| 26 /// mechanism allowed for user libraries. |
| 27 bool isNativeClass(ClassEntity element); |
| 28 |
| 29 /// Returns `true` if [element] corresponds to a native JavaScript member. |
| 30 /// |
| 31 /// A member is marked as native either through the native mechanism |
| 32 /// (`@Native(...)` or the `native` pseudo keyword) allowed for internal |
| 33 /// libraries or via the typed JavaScriptInterop mechanism allowed for user |
| 34 /// libraries. |
| 35 bool isNativeMember(MemberEntity element); |
| 36 |
| 37 /// Returns `true` if [element] or any of its superclasses is native. |
| 38 bool isNativeOrExtendsNative(ClassElement element); |
| 39 |
| 40 /// Returns `true` if the name of [element] is fixed for the generated |
| 41 /// JavaScript. |
| 42 bool hasFixedBackendName(Element element); |
| 43 |
| 44 /// Computes the name for [element] to use in the generated JavaScript. This |
| 45 /// is either given through a native annotation or a js interop annotation. |
| 46 String getFixedBackendName(Entity entity); |
| 47 |
| 48 /// Returns the list of non-directive native tag words for [cls]. |
| 49 List<String> getNativeTagsOfClass(ClassElement cls); |
| 50 |
| 51 /// Returns `true` if [cls] has a `!nonleaf` tag word. |
| 52 bool hasNativeTagsForcedNonLeaf(ClassElement cls); |
| 53 |
| 54 /// Returns the [NativeBehavior] for calling the native [method]. |
| 55 NativeBehavior getNativeMethodBehavior(MethodElement method); |
| 56 |
| 57 /// Returns the [NativeBehavior] for reading from the native [field]. |
| 58 NativeBehavior getNativeFieldLoadBehavior(FieldElement field); |
| 59 |
| 60 /// Returns the [NativeBehavior] for writing to the native [field]. |
| 61 NativeBehavior getNativeFieldStoreBehavior(FieldElement field); |
| 62 |
| 63 /// Returns `true` if [element] is part of JsInterop. |
| 64 bool isJsInterop(Element element); |
| 65 |
| 66 /// Returns `true` if [element] is a JsInterop class. |
| 67 bool isJsInteropClass(ClassElement element); |
| 68 |
| 69 /// Returns `true` if [element] is a JsInterop method. |
| 70 bool isJsInteropMethod(MethodElement element); |
| 71 |
| 72 /// Returns the explicit js interop name for [element]. |
| 73 String getJsInteropName(Element element); |
| 74 |
| 75 /// Apply JS$ escaping scheme to convert possible escaped Dart names into |
| 76 /// JS names. |
| 77 String getUnescapedJSInteropName(String name); |
| 78 } |
| 79 |
| 80 abstract class NativeDataBuilder { |
| 81 /// Sets the native tag info for [cls]. |
| 82 /// |
| 83 /// The tag info string contains comma-separated 'words' which are either |
| 84 /// dispatch tags (having JavaScript identifier syntax) and directives that |
| 85 /// begin with `!`. |
| 86 void setNativeClassTagInfo(ClassElement cls, String tagInfo); |
| 87 |
| 88 /// Returns the list of native tag words for [cls]. |
| 89 List<String> getNativeTagsOfClassRaw(ClassElement cls); |
| 90 |
| 91 /// Sets the native [name] for the member [element]. This name is used for |
| 92 /// [element] in the generated JavaScript. |
| 93 void setNativeMemberName(MemberElement element, String name); |
| 94 |
| 95 /// Registers the [behavior] for calling the native [method]. |
| 96 void setNativeMethodBehavior(MethodElement method, NativeBehavior behavior); |
| 97 |
| 98 /// Registers the [behavior] for reading from the native [field]. |
| 99 void setNativeFieldLoadBehavior(FieldElement field, NativeBehavior behavior); |
| 100 |
| 101 /// Registers the [behavior] for writing to the native [field]. |
| 102 void setNativeFieldStoreBehavior(FieldElement field, NativeBehavior behavior); |
| 103 |
| 104 /// Marks [element] as an explicit part of JsInterop. The js interop name is |
| 105 /// expected to be computed later. |
| 106 void markAsJsInterop(Element element); |
| 107 |
| 108 /// Sets the explicit js interop [name] for [element]. |
| 109 void setJsInteropName(Element element, String name); |
| 110 } |
| 111 |
| 112 class NativeDataImpl implements NativeData, NativeDataBuilder { |
| 16 /// The JavaScript names for elements implemented via typed JavaScript | 113 /// The JavaScript names for elements implemented via typed JavaScript |
| 17 /// interop. | 114 /// interop. |
| 18 Map<Element, String> jsInteropNames = <Element, String>{}; | 115 Map<Element, String> jsInteropNames = <Element, String>{}; |
| 19 | 116 |
| 20 /// The JavaScript names for native JavaScript elements implemented. | 117 /// The JavaScript names for native JavaScript elements implemented. |
| 21 Map<Element, String> nativeMemberName = <Element, String>{}; | 118 Map<Element, String> nativeMemberName = <Element, String>{}; |
| 22 | 119 |
| 23 /// Tag info for native JavaScript classes names. See | 120 /// Tag info for native JavaScript classes names. See |
| 24 /// [setNativeClassTagInfo]. | 121 /// [setNativeClassTagInfo]. |
| 25 Map<ClassElement, String> nativeClassTagInfo = <ClassElement, String>{}; | 122 Map<ClassElement, String> nativeClassTagInfo = <ClassElement, String>{}; |
| 26 | 123 |
| 27 /// Cache for [NativeBehavior]s for calling native methods. | 124 /// Cache for [NativeBehavior]s for calling native methods. |
| 28 Map<FunctionElement, NativeBehavior> nativeMethodBehavior = | 125 Map<MethodElement, NativeBehavior> nativeMethodBehavior = |
| 29 <FunctionElement, NativeBehavior>{}; | 126 <MethodElement, NativeBehavior>{}; |
| 30 | 127 |
| 31 /// Cache for [NativeBehavior]s for reading from native fields. | 128 /// Cache for [NativeBehavior]s for reading from native fields. |
| 32 Map<MemberElement, NativeBehavior> nativeFieldLoadBehavior = | 129 Map<MemberElement, NativeBehavior> nativeFieldLoadBehavior = |
| 33 <FieldElement, NativeBehavior>{}; | 130 <FieldElement, NativeBehavior>{}; |
| 34 | 131 |
| 35 /// Cache for [NativeBehavior]s for writing to native fields. | 132 /// Cache for [NativeBehavior]s for writing to native fields. |
| 36 Map<MemberElement, NativeBehavior> nativeFieldStoreBehavior = | 133 Map<MemberElement, NativeBehavior> nativeFieldStoreBehavior = |
| 37 <FieldElement, NativeBehavior>{}; | 134 <FieldElement, NativeBehavior>{}; |
| 38 | 135 |
| 39 /// Prefix used to escape JS names that are not valid Dart names | 136 /// Prefix used to escape JS names that are not valid Dart names |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 | 172 |
| 76 if (_isJsInterop(function)) return true; | 173 if (_isJsInterop(function)) return true; |
| 77 if (function.isClassMember) return isJsInterop(function.contextClass); | 174 if (function.isClassMember) return isJsInterop(function.contextClass); |
| 78 if (function.isTopLevel) return isJsInterop(function.library); | 175 if (function.isTopLevel) return isJsInterop(function.library); |
| 79 return false; | 176 return false; |
| 80 } else { | 177 } else { |
| 81 return _isJsInterop(element); | 178 return _isJsInterop(element); |
| 82 } | 179 } |
| 83 } | 180 } |
| 84 | 181 |
| 182 /// Returns `true` if [element] is a JsInterop class. |
| 183 bool isJsInteropClass(ClassElement element) => isJsInterop(element); |
| 184 |
| 185 /// Returns `true` if [element] is a JsInterop method. |
| 186 bool isJsInteropMethod(MethodElement element) => isJsInterop(element); |
| 187 |
| 85 /// Returns `true` if the name of [element] is fixed for the generated | 188 /// Returns `true` if the name of [element] is fixed for the generated |
| 86 /// JavaScript. | 189 /// JavaScript. |
| 87 bool hasFixedBackendName(Element element) { | 190 bool hasFixedBackendName(Element element) { |
| 88 return isJsInterop(element) || | 191 return isJsInterop(element) || |
| 89 nativeMemberName.containsKey(element.declaration); | 192 nativeMemberName.containsKey(element.declaration); |
| 90 } | 193 } |
| 91 | 194 |
| 92 String _jsNameHelper(Element element) { | 195 String _jsNameHelper(Element element) { |
| 93 String jsInteropName = jsInteropNames[element.declaration]; | 196 String jsInteropName = jsInteropNames[element.declaration]; |
| 94 assert(invariant(element, !(_isJsInterop(element) && jsInteropName == null), | 197 assert(invariant(element, !(_isJsInterop(element) && jsInteropName == null), |
| (...skipping 29 matching lines...) Expand all Loading... |
| 124 /// JavaScriptInterop mechanism which is allowed for user libraries. | 227 /// JavaScriptInterop mechanism which is allowed for user libraries. |
| 125 bool isNative(Element element) { | 228 bool isNative(Element element) { |
| 126 if (isJsInterop(element)) return true; | 229 if (isJsInterop(element)) return true; |
| 127 if (element.isClass) { | 230 if (element.isClass) { |
| 128 return nativeClassTagInfo.containsKey(element.declaration); | 231 return nativeClassTagInfo.containsKey(element.declaration); |
| 129 } else { | 232 } else { |
| 130 return nativeMemberName.containsKey(element.declaration); | 233 return nativeMemberName.containsKey(element.declaration); |
| 131 } | 234 } |
| 132 } | 235 } |
| 133 | 236 |
| 237 /// Returns `true` if [cls] is a native class. |
| 238 bool isNativeClass(ClassElement element) => isNative(element); |
| 239 |
| 240 /// Returns `true` if [element] is a native member of a native class. |
| 241 bool isNativeMember(MemberElement element) => isNative(element); |
| 242 |
| 134 /// Returns `true` if [element] or any of its superclasses is native. | 243 /// Returns `true` if [element] or any of its superclasses is native. |
| 135 bool isNativeOrExtendsNative(ClassElement element) { | 244 bool isNativeOrExtendsNative(ClassElement element) { |
| 136 if (element == null) return false; | 245 if (element == null) return false; |
| 137 if (isNative(element) || isJsInterop(element)) { | 246 if (isNativeClass(element) || isJsInteropClass(element)) { |
| 138 return true; | 247 return true; |
| 139 } | 248 } |
| 140 assert(element.isResolved); | 249 assert(element.isResolved); |
| 141 return isNativeOrExtendsNative(element.superclass); | 250 return isNativeOrExtendsNative(element.superclass); |
| 142 } | 251 } |
| 143 | 252 |
| 144 /// Sets the native [name] for the member [element]. This name is used for | 253 /// Sets the native [name] for the member [element]. This name is used for |
| 145 /// [element] in the generated JavaScript. | 254 /// [element] in the generated JavaScript. |
| 146 void setNativeMemberName(MemberElement element, String name) { | 255 void setNativeMemberName(MemberElement element, String name) { |
| 147 // TODO(johnniwinther): Avoid setting this more than once. The enqueuer | 256 // TODO(johnniwinther): Avoid setting this more than once. The enqueuer |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 .where((s) => !s.startsWith('!')) | 298 .where((s) => !s.startsWith('!')) |
| 190 .toList(); | 299 .toList(); |
| 191 } | 300 } |
| 192 | 301 |
| 193 /// Returns `true` if [cls] has a `!nonleaf` tag word. | 302 /// Returns `true` if [cls] has a `!nonleaf` tag word. |
| 194 bool hasNativeTagsForcedNonLeaf(ClassElement cls) { | 303 bool hasNativeTagsForcedNonLeaf(ClassElement cls) { |
| 195 return getNativeTagsOfClassRaw(cls).contains('!nonleaf'); | 304 return getNativeTagsOfClassRaw(cls).contains('!nonleaf'); |
| 196 } | 305 } |
| 197 | 306 |
| 198 /// Returns the [NativeBehavior] for calling the native [method]. | 307 /// Returns the [NativeBehavior] for calling the native [method]. |
| 199 NativeBehavior getNativeMethodBehavior(FunctionElement method) { | 308 NativeBehavior getNativeMethodBehavior(MethodElement method) { |
| 200 assert(invariant(method, nativeMethodBehavior.containsKey(method), | 309 assert(invariant(method, nativeMethodBehavior.containsKey(method), |
| 201 message: "No native method behavior has been computed for $method.")); | 310 message: "No native method behavior has been computed for $method.")); |
| 202 return nativeMethodBehavior[method]; | 311 return nativeMethodBehavior[method]; |
| 203 } | 312 } |
| 204 | 313 |
| 205 /// Returns the [NativeBehavior] for reading from the native [field]. | 314 /// Returns the [NativeBehavior] for reading from the native [field]. |
| 206 NativeBehavior getNativeFieldLoadBehavior(FieldElement field) { | 315 NativeBehavior getNativeFieldLoadBehavior(FieldElement field) { |
| 207 assert(invariant(field, nativeFieldLoadBehavior.containsKey(field), | 316 assert(invariant(field, nativeFieldLoadBehavior.containsKey(field), |
| 208 message: "No native field load behavior has been " | 317 message: "No native field load behavior has been " |
| 209 "computed for $field.")); | 318 "computed for $field.")); |
| 210 return nativeFieldLoadBehavior[field]; | 319 return nativeFieldLoadBehavior[field]; |
| 211 } | 320 } |
| 212 | 321 |
| 213 /// Returns the [NativeBehavior] for writing to the native [field]. | 322 /// Returns the [NativeBehavior] for writing to the native [field]. |
| 214 NativeBehavior getNativeFieldStoreBehavior(FieldElement field) { | 323 NativeBehavior getNativeFieldStoreBehavior(FieldElement field) { |
| 215 assert(invariant(field, nativeFieldStoreBehavior.containsKey(field), | 324 assert(invariant(field, nativeFieldStoreBehavior.containsKey(field), |
| 216 message: "No native field store behavior has been " | 325 message: "No native field store behavior has been " |
| 217 "computed for $field.")); | 326 "computed for $field.")); |
| 218 return nativeFieldStoreBehavior[field]; | 327 return nativeFieldStoreBehavior[field]; |
| 219 } | 328 } |
| 220 | 329 |
| 221 /// Registers the [behavior] for calling the native [method]. | 330 /// Registers the [behavior] for calling the native [method]. |
| 222 void setNativeMethodBehavior( | 331 void setNativeMethodBehavior(MethodElement method, NativeBehavior behavior) { |
| 223 FunctionElement method, NativeBehavior behavior) { | |
| 224 nativeMethodBehavior[method] = behavior; | 332 nativeMethodBehavior[method] = behavior; |
| 225 } | 333 } |
| 226 | 334 |
| 227 /// Registers the [behavior] for reading from the native [field]. | 335 /// Registers the [behavior] for reading from the native [field]. |
| 228 void setNativeFieldLoadBehavior(FieldElement field, NativeBehavior behavior) { | 336 void setNativeFieldLoadBehavior(FieldElement field, NativeBehavior behavior) { |
| 229 nativeFieldLoadBehavior[field] = behavior; | 337 nativeFieldLoadBehavior[field] = behavior; |
| 230 } | 338 } |
| 231 | 339 |
| 232 /// Registers the [behavior] for writing to the native [field]. | 340 /// Registers the [behavior] for writing to the native [field]. |
| 233 void setNativeFieldStoreBehavior( | 341 void setNativeFieldStoreBehavior( |
| 234 FieldElement field, NativeBehavior behavior) { | 342 FieldElement field, NativeBehavior behavior) { |
| 235 nativeFieldStoreBehavior[field] = behavior; | 343 nativeFieldStoreBehavior[field] = behavior; |
| 236 } | 344 } |
| 237 | 345 |
| 238 /// Apply JS$ escaping scheme to convert possible escaped Dart names into | 346 /// Apply JS$ escaping scheme to convert possible escaped Dart names into |
| 239 /// JS names. | 347 /// JS names. |
| 240 String getUnescapedJSInteropName(String name) { | 348 String getUnescapedJSInteropName(String name) { |
| 241 return name.startsWith(_jsInteropEscapePrefix) | 349 return name.startsWith(_jsInteropEscapePrefix) |
| 242 ? name.substring(_jsInteropEscapePrefix.length) | 350 ? name.substring(_jsInteropEscapePrefix.length) |
| 243 : name; | 351 : name; |
| 244 } | 352 } |
| 245 } | 353 } |
| OLD | NEW |