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 |