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 | 9 show |
10 ClassElement, | 10 ClassElement, |
11 Element, | 11 Element, |
12 FieldElement, | 12 FieldElement, |
13 FunctionElement, | |
14 LibraryElement, | 13 LibraryElement, |
15 MemberElement, | 14 MemberElement, |
16 MethodElement; | 15 MethodElement; |
17 import '../elements/entities.dart'; | 16 import '../elements/entities.dart'; |
18 import '../native/behavior.dart' show NativeBehavior; | 17 import '../native/behavior.dart' show NativeBehavior; |
19 | 18 |
20 /// Basic information for native classes and methods and js-interop | 19 /// Basic information for native classes and methods and js-interop |
21 /// classes. | 20 /// classes. |
22 /// | 21 /// |
23 /// This information is computed during loading using [NativeClassDataBuilder]. | 22 /// This information is computed during loading using [NativeClassDataBuilder]. |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
56 NativeBehavior getNativeMethodBehavior(MethodElement method); | 55 NativeBehavior getNativeMethodBehavior(MethodElement method); |
57 | 56 |
58 /// Returns the [NativeBehavior] for reading from the native [field]. | 57 /// Returns the [NativeBehavior] for reading from the native [field]. |
59 NativeBehavior getNativeFieldLoadBehavior(FieldElement field); | 58 NativeBehavior getNativeFieldLoadBehavior(FieldElement field); |
60 | 59 |
61 /// Returns the [NativeBehavior] for writing to the native [field]. | 60 /// Returns the [NativeBehavior] for writing to the native [field]. |
62 NativeBehavior getNativeFieldStoreBehavior(FieldElement field); | 61 NativeBehavior getNativeFieldStoreBehavior(FieldElement field); |
63 | 62 |
64 /// Returns `true` if the name of [element] is fixed for the generated | 63 /// Returns `true` if the name of [element] is fixed for the generated |
65 /// JavaScript. | 64 /// JavaScript. |
66 bool hasFixedBackendName(Element element); | 65 bool hasFixedBackendName(MemberElement element); |
67 | 66 |
68 /// Computes the name for [element] to use in the generated JavaScript. This | 67 /// Computes the name for [element] to use in the generated JavaScript. This |
69 /// is either given through a native annotation or a js interop annotation. | 68 /// is either given through a native annotation or a js interop annotation. |
70 String getFixedBackendName(Entity entity); | 69 String getFixedBackendName(MemberEntity element); |
| 70 |
| 71 /// Computes the name prefix for [element] to use in the generated JavaScript. |
| 72 /// |
| 73 /// For static and top-level members and constructors this is based on the |
| 74 /// JavaScript names for the library and/or the enclosing class. |
| 75 String getFixedBackendMethodPath(MethodElement element); |
71 | 76 |
72 /// Returns the list of non-directive native tag words for [cls]. | 77 /// Returns the list of non-directive native tag words for [cls]. |
73 List<String> getNativeTagsOfClass(ClassElement cls); | 78 List<String> getNativeTagsOfClass(ClassElement cls); |
74 | 79 |
75 /// Returns `true` if [cls] has a `!nonleaf` tag word. | 80 /// Returns `true` if [cls] has a `!nonleaf` tag word. |
76 bool hasNativeTagsForcedNonLeaf(ClassElement cls); | 81 bool hasNativeTagsForcedNonLeaf(ClassElement cls); |
77 | 82 |
78 /// Returns `true` if [element] is part of JsInterop. | 83 /// Returns `true` if [element] is a JsInterop method. |
79 /// | 84 bool isJsInteropMember(MemberEntity element); |
80 /// Deprecated: Use [isJsInteropLibrary], [isJsInteropClass] or | |
81 /// [isJsInteropMember] instead. | |
82 @deprecated | |
83 bool isJsInterop(Element element); | |
84 | 85 |
85 /// Returns `true` if [element] is a JsInterop method. | 86 /// Returns the explicit js interop name for library [element]. |
86 bool isJsInteropMember(MethodElement element); | 87 String getJsInteropLibraryName(LibraryElement element); |
87 | 88 |
88 /// Returns the explicit js interop name for [element]. | 89 /// Returns the explicit js interop name for class [element]. |
89 String getJsInteropName(Element element); | 90 String getJsInteropClassName(ClassElement element); |
| 91 |
| 92 /// Returns the explicit js interop name for member [element]. |
| 93 String getJsInteropMemberName(MemberElement element); |
90 | 94 |
91 /// Apply JS$ escaping scheme to convert possible escaped Dart names into | 95 /// Apply JS$ escaping scheme to convert possible escaped Dart names into |
92 /// JS names. | 96 /// JS names. |
93 String getUnescapedJSInteropName(String name); | 97 String computeUnescapedJSInteropName(String name); |
94 } | 98 } |
95 | 99 |
96 abstract class NativeClassDataBuilder { | 100 abstract class NativeClassDataBuilder { |
97 /// Sets the native tag info for [cls]. | 101 /// Sets the native tag info for [cls]. |
98 /// | 102 /// |
99 /// The tag info string contains comma-separated 'words' which are either | 103 /// The tag info string contains comma-separated 'words' which are either |
100 /// dispatch tags (having JavaScript identifier syntax) and directives that | 104 /// dispatch tags (having JavaScript identifier syntax) and directives that |
101 /// begin with `!`. | 105 /// begin with `!`. |
102 void setNativeClassTagInfo(ClassElement cls, String tagInfo); | 106 void setNativeClassTagInfo(ClassElement cls, String tagInfo); |
103 | 107 |
(...skipping 20 matching lines...) Expand all Loading... |
124 List<String> getNativeTagsOfClassRaw(ClassElement cls); | 128 List<String> getNativeTagsOfClassRaw(ClassElement cls); |
125 | 129 |
126 /// Returns [element] as an explicit part of JsInterop. The js interop name is | 130 /// Returns [element] as an explicit part of JsInterop. The js interop name is |
127 /// expected to be computed later. | 131 /// expected to be computed later. |
128 void markAsJsInteropMember(MemberElement element); | 132 void markAsJsInteropMember(MemberElement element); |
129 | 133 |
130 /// Sets the native [name] for the member [element]. This name is used for | 134 /// Sets the native [name] for the member [element]. This name is used for |
131 /// [element] in the generated JavaScript. | 135 /// [element] in the generated JavaScript. |
132 void setNativeMemberName(MemberElement element, String name); | 136 void setNativeMemberName(MemberElement element, String name); |
133 | 137 |
134 /// Sets the explicit js interop [name] for [element]. | 138 /// Sets the explicit js interop [name] for the library [element]. |
135 void setJsInteropName(Element element, String name); | 139 void setJsInteropLibraryName(LibraryElement element, String name); |
| 140 |
| 141 /// Sets the explicit js interop [name] for the class [element]. |
| 142 void setJsInteropClassName(ClassElement element, String name); |
| 143 |
| 144 /// Sets the explicit js interop [name] for the member [element]. |
| 145 void setJsInteropMemberName(MemberElement element, String name); |
136 } | 146 } |
137 | 147 |
138 class NativeDataImpl | 148 class NativeDataImpl |
139 implements NativeData, NativeDataBuilder, NativeClassDataBuilder { | 149 implements NativeData, NativeDataBuilder, NativeClassDataBuilder { |
140 /// The JavaScript names for elements implemented via typed JavaScript | 150 /// The JavaScript names for elements implemented via typed JavaScript |
141 /// interop. | 151 /// interop. |
142 Map<Element, String> jsInteropNames = <Element, String>{}; | 152 Map<LibraryElement, String> jsInteropLibraryNames = |
| 153 <LibraryElement, String>{}; |
| 154 Map<ClassElement, String> jsInteropClassNames = <ClassElement, String>{}; |
| 155 Map<MemberElement, String> jsInteropMemberNames = <MemberElement, String>{}; |
143 | 156 |
144 /// The JavaScript names for native JavaScript elements implemented. | 157 /// The JavaScript names for native JavaScript elements implemented. |
145 Map<Element, String> nativeMemberName = <Element, String>{}; | 158 Map<Element, String> nativeMemberName = <Element, String>{}; |
146 | 159 |
147 /// Tag info for native JavaScript classes names. See | 160 /// Tag info for native JavaScript classes names. See |
148 /// [setNativeClassTagInfo]. | 161 /// [setNativeClassTagInfo]. |
149 Map<ClassElement, String> nativeClassTagInfo = <ClassElement, String>{}; | 162 Map<ClassElement, String> nativeClassTagInfo = <ClassElement, String>{}; |
150 | 163 |
151 /// Cache for [NativeBehavior]s for calling native methods. | 164 /// Cache for [NativeBehavior]s for calling native methods. |
152 Map<MethodElement, NativeBehavior> nativeMethodBehavior = | 165 Map<MethodElement, NativeBehavior> nativeMethodBehavior = |
153 <MethodElement, NativeBehavior>{}; | 166 <MethodElement, NativeBehavior>{}; |
154 | 167 |
155 /// Cache for [NativeBehavior]s for reading from native fields. | 168 /// Cache for [NativeBehavior]s for reading from native fields. |
156 Map<MemberElement, NativeBehavior> nativeFieldLoadBehavior = | 169 Map<MemberElement, NativeBehavior> nativeFieldLoadBehavior = |
157 <FieldElement, NativeBehavior>{}; | 170 <FieldElement, NativeBehavior>{}; |
158 | 171 |
159 /// Cache for [NativeBehavior]s for writing to native fields. | 172 /// Cache for [NativeBehavior]s for writing to native fields. |
160 Map<MemberElement, NativeBehavior> nativeFieldStoreBehavior = | 173 Map<MemberElement, NativeBehavior> nativeFieldStoreBehavior = |
161 <FieldElement, NativeBehavior>{}; | 174 <FieldElement, NativeBehavior>{}; |
162 | 175 |
163 /// Prefix used to escape JS names that are not valid Dart names | 176 /// Prefix used to escape JS names that are not valid Dart names |
164 /// when using JSInterop. | 177 /// when using JSInterop. |
165 static const String _jsInteropEscapePrefix = r'JS$'; | 178 static const String _jsInteropEscapePrefix = r'JS$'; |
166 | 179 |
167 /// Returns `true` if [element] is explicitly marked as part of JsInterop. | 180 /// Returns `true` if [element] is explicitly marked as part of JsInterop. |
168 bool _isJsInterop(Element element) { | 181 bool _isJsInteropLibrary(LibraryElement element) { |
169 return jsInteropNames.containsKey(element.declaration); | 182 return jsInteropLibraryNames.containsKey(element); |
170 } | 183 } |
171 | 184 |
172 /// Marks [element] as an explicit part of JsInterop. The js interop name is | 185 /// Returns `true` if [element] is explicitly marked as part of JsInterop. |
173 /// expected to be computed later. | 186 bool _isJsInteropClass(ClassElement element) { |
174 void markAsJsInterop(Element element) { | 187 return jsInteropClassNames.containsKey(element); |
175 jsInteropNames[element.declaration] = null; | 188 } |
| 189 |
| 190 /// Returns `true` if [element] is explicitly marked as part of JsInterop. |
| 191 bool _isJsInteropMember(MemberElement element) { |
| 192 return jsInteropMemberNames.containsKey(element); |
176 } | 193 } |
177 | 194 |
178 @override | 195 @override |
179 void markAsJsInteropLibrary(LibraryElement element) { | 196 void markAsJsInteropLibrary(LibraryElement element) { |
180 markAsJsInterop(element); | 197 jsInteropLibraryNames[element] = null; |
181 } | 198 } |
182 | 199 |
183 @override | 200 @override |
184 void markAsJsInteropClass(ClassElement element) { | 201 void markAsJsInteropClass(ClassElement element) { |
185 markAsJsInterop(element); | 202 jsInteropClassNames[element] = null; |
186 } | 203 } |
187 | 204 |
188 @override | 205 @override |
189 void markAsJsInteropMember(MemberElement element) { | 206 void markAsJsInteropMember(MemberElement element) { |
190 markAsJsInterop(element); | 207 jsInteropMemberNames[element] = null; |
191 } | 208 } |
192 | 209 |
193 /// Sets the explicit js interop [name] for [element]. | 210 /// Sets the explicit js interop [name] for the library [element]. |
194 void setJsInteropName(Element element, String name) { | 211 void setJsInteropLibraryName(LibraryElement element, String name) { |
195 assert(invariant(element, isJsInterop(element), | 212 assert(invariant(element, _isJsInteropLibrary(element), |
196 message: | 213 message: |
197 'Element $element is not js interop but given a js interop name.')); | 214 'Library $element is not js interop but given a js interop name.')); |
198 jsInteropNames[element.declaration] = name; | 215 jsInteropLibraryNames[element] = name; |
199 } | 216 } |
200 | 217 |
201 /// Returns the explicit js interop name for [element]. | 218 /// Sets the explicit js interop [name] for the class [element]. |
202 String getJsInteropName(Element element) { | 219 void setJsInteropClassName(ClassElement element, String name) { |
203 return jsInteropNames[element.declaration]; | 220 assert(invariant(element, _isJsInteropClass(element), |
| 221 message: |
| 222 'Class $element is not js interop but given a js interop name.')); |
| 223 jsInteropClassNames[element] = name; |
204 } | 224 } |
205 | 225 |
206 /// Returns `true` if [element] is part of JsInterop. | 226 /// Sets the explicit js interop [name] for the member [element]. |
207 bool isJsInterop(Element element) { | 227 void setJsInteropMemberName(MemberElement element, String name) { |
208 // An function is part of JsInterop in the following cases: | 228 assert(invariant(element, _isJsInteropMember(element), |
209 // * It has a jsInteropName annotation | 229 message: |
210 // * It is external member of a class or library tagged as JsInterop. | 230 'Member $element is not js interop but given a js interop name.')); |
| 231 jsInteropMemberNames[element] = name; |
| 232 } |
| 233 |
| 234 /// Returns the explicit js interop name for library [element]. |
| 235 String getJsInteropLibraryName(LibraryElement element) { |
| 236 return jsInteropLibraryNames[element]; |
| 237 } |
| 238 |
| 239 /// Returns the explicit js interop name for class [element]. |
| 240 String getJsInteropClassName(ClassElement element) { |
| 241 return jsInteropClassNames[element]; |
| 242 } |
| 243 |
| 244 /// Returns the explicit js interop name for member [element]. |
| 245 String getJsInteropMemberName(MemberElement element) { |
| 246 return jsInteropMemberNames[element]; |
| 247 } |
| 248 |
| 249 /// Returns `true` if [element] is a JsInterop library. |
| 250 bool isJsInteropLibrary(LibraryElement element) => |
| 251 _isJsInteropLibrary(element); |
| 252 |
| 253 /// Returns `true` if [element] is a JsInterop class. |
| 254 bool isJsInteropClass(ClassElement element) => _isJsInteropClass(element); |
| 255 |
| 256 /// Returns `true` if [element] is a JsInterop method. |
| 257 bool isJsInteropMember(MemberElement element) { |
211 if (element.isFunction || element.isConstructor || element.isAccessor) { | 258 if (element.isFunction || element.isConstructor || element.isAccessor) { |
212 FunctionElement function = element; | 259 MethodElement function = element; |
213 if (!function.isExternal) return false; | 260 if (!function.isExternal) return false; |
214 | 261 |
215 if (_isJsInterop(function)) return true; | 262 if (_isJsInteropMember(function)) return true; |
216 if (function.isClassMember) return isJsInterop(function.contextClass); | 263 if (function.isClassMember) { |
217 if (function.isTopLevel) return isJsInterop(function.library); | 264 return _isJsInteropClass(function.enclosingClass); |
| 265 } |
| 266 if (function.isTopLevel) { |
| 267 return _isJsInteropLibrary(function.library); |
| 268 } |
218 return false; | 269 return false; |
219 } else { | 270 } else { |
220 return _isJsInterop(element); | 271 return _isJsInteropMember(element); |
221 } | 272 } |
222 } | 273 } |
223 | 274 |
224 /// Returns `true` if [element] is a JsInterop library. | |
225 bool isJsInteropLibrary(LibraryElement element) => isJsInterop(element); | |
226 | |
227 /// Returns `true` if [element] is a JsInterop class. | |
228 bool isJsInteropClass(ClassElement element) => isJsInterop(element); | |
229 | |
230 /// Returns `true` if [element] is a JsInterop method. | |
231 bool isJsInteropMember(MethodElement element) => isJsInterop(element); | |
232 | |
233 /// Returns `true` if the name of [element] is fixed for the generated | 275 /// Returns `true` if the name of [element] is fixed for the generated |
234 /// JavaScript. | 276 /// JavaScript. |
235 bool hasFixedBackendName(Element element) { | 277 bool hasFixedBackendName(MemberElement element) { |
236 return isJsInterop(element) || | 278 return isJsInteropMember(element) || |
237 nativeMemberName.containsKey(element.declaration); | 279 nativeMemberName.containsKey(element.declaration); |
238 } | 280 } |
239 | 281 |
240 String _jsNameHelper(Element element) { | |
241 String jsInteropName = jsInteropNames[element.declaration]; | |
242 assert(invariant(element, !(_isJsInterop(element) && jsInteropName == null), | |
243 message: | |
244 'Element $element is js interop but js interop name has not yet ' | |
245 'been computed.')); | |
246 if (jsInteropName != null && jsInteropName.isNotEmpty) { | |
247 return jsInteropName; | |
248 } | |
249 return element.isLibrary ? 'self' : getUnescapedJSInteropName(element.name); | |
250 } | |
251 | |
252 /// Computes the name for [element] to use in the generated JavaScript. This | 282 /// Computes the name for [element] to use in the generated JavaScript. This |
253 /// is either given through a native annotation or a js interop annotation. | 283 /// is either given through a native annotation or a js interop annotation. |
254 String getFixedBackendName(Entity entity) { | 284 String getFixedBackendName(MemberElement element) { |
255 // TODO(johnniwinther): Remove this assignment from [Entity] to [Element] | |
256 // when `.declaration` is no longer needed. | |
257 Element element = entity; | |
258 String name = nativeMemberName[element.declaration]; | 285 String name = nativeMemberName[element.declaration]; |
259 if (name == null && isJsInterop(element)) { | 286 if (name == null && isJsInteropMember(element)) { |
260 // If an element isJsInterop but _isJsInterop is false that means it is | 287 // If an element isJsInterop but _isJsInterop is false that means it is |
261 // considered interop as the parent class is interop. | 288 // considered interop as the parent class is interop. |
262 name = _jsNameHelper( | 289 name = element.isConstructor |
263 element.isConstructor ? element.enclosingClass : element); | 290 ? _jsClassNameHelper(element.enclosingClass) |
| 291 : _jsMemberNameHelper(element); |
264 nativeMemberName[element.declaration] = name; | 292 nativeMemberName[element.declaration] = name; |
265 } | 293 } |
266 return name; | 294 return name; |
267 } | 295 } |
268 | 296 |
269 /// Whether [element] corresponds to a native JavaScript construct either | 297 String _jsLibraryNameHelper(LibraryElement element) { |
270 /// through the native mechanism (`@Native(...)` or the `native` pseudo | 298 String jsInteropName = getJsInteropLibraryName(element); |
271 /// keyword) which is only allowed for internal libraries or via the typed | 299 if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName; |
272 /// JavaScriptInterop mechanism which is allowed for user libraries. | 300 return 'self'; |
273 bool isNative(Element element) { | 301 } |
274 if (isJsInterop(element)) return true; | 302 |
275 if (element.isClass) { | 303 String _jsClassNameHelper(ClassElement element) { |
276 return nativeClassTagInfo.containsKey(element.declaration); | 304 String jsInteropName = getJsInteropClassName(element); |
277 } else { | 305 if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName; |
278 return nativeMemberName.containsKey(element.declaration); | 306 return computeUnescapedJSInteropName(element.name); |
| 307 } |
| 308 |
| 309 String _jsMemberNameHelper(MemberElement element) { |
| 310 String jsInteropName = jsInteropMemberNames[element]; |
| 311 assert(invariant(element, |
| 312 !(jsInteropMemberNames.containsKey(element) && jsInteropName == null), |
| 313 message: |
| 314 'Member $element is js interop but js interop name has not yet ' |
| 315 'been computed.')); |
| 316 if (jsInteropName != null && jsInteropName.isNotEmpty) { |
| 317 return jsInteropName; |
279 } | 318 } |
| 319 return computeUnescapedJSInteropName(element.name); |
| 320 } |
| 321 |
| 322 /// Returns a JavaScript path specifying the context in which |
| 323 /// [element.fixedBackendName] should be evaluated. Only applicable for |
| 324 /// elements using typed JavaScript interop. |
| 325 /// For example: fixedBackendPath for the static method createMap in the |
| 326 /// Map class of the goog.map JavaScript library would have path |
| 327 /// "goog.maps.Map". |
| 328 String getFixedBackendMethodPath(MethodElement element) { |
| 329 if (!isJsInteropMember(element)) return null; |
| 330 if (element.isInstanceMember) return 'this'; |
| 331 if (element.isConstructor) { |
| 332 return _fixedBackendClassPath(element.enclosingClass); |
| 333 } |
| 334 StringBuffer sb = new StringBuffer(); |
| 335 sb.write(_jsLibraryNameHelper(element.library)); |
| 336 if (element.enclosingClass != null) { |
| 337 sb..write('.')..write(_jsClassNameHelper(element.enclosingClass)); |
| 338 } |
| 339 return sb.toString(); |
| 340 } |
| 341 |
| 342 String _fixedBackendClassPath(ClassElement element) { |
| 343 if (!isJsInteropClass(element)) return null; |
| 344 return _jsLibraryNameHelper(element.library); |
280 } | 345 } |
281 | 346 |
282 /// Returns `true` if [cls] is a native class. | 347 /// Returns `true` if [cls] is a native class. |
283 bool isNativeClass(ClassElement element) => isNative(element); | 348 bool isNativeClass(ClassElement element) { |
| 349 if (isJsInteropClass(element)) return true; |
| 350 return nativeClassTagInfo.containsKey(element); |
| 351 } |
284 | 352 |
285 /// Returns `true` if [element] is a native member of a native class. | 353 /// Returns `true` if [element] is a native member of a native class. |
286 bool isNativeMember(MemberElement element) => isNative(element); | 354 bool isNativeMember(MemberElement element) { |
| 355 if (isJsInteropMember(element)) return true; |
| 356 return nativeMemberName.containsKey(element); |
| 357 } |
287 | 358 |
288 /// Returns `true` if [element] or any of its superclasses is native. | 359 /// Returns `true` if [element] or any of its superclasses is native. |
289 bool isNativeOrExtendsNative(ClassElement element) { | 360 bool isNativeOrExtendsNative(ClassElement element) { |
290 if (element == null) return false; | 361 if (element == null) return false; |
291 if (isNativeClass(element) || isJsInteropClass(element)) { | 362 if (isNativeClass(element) || isJsInteropClass(element)) { |
292 return true; | 363 return true; |
293 } | 364 } |
294 assert(element.isResolved); | 365 assert(element.isResolved); |
295 return isNativeOrExtendsNative(element.superclass); | 366 return isNativeOrExtendsNative(element.superclass); |
296 } | 367 } |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
383 } | 454 } |
384 | 455 |
385 /// Registers the [behavior] for writing to the native [field]. | 456 /// Registers the [behavior] for writing to the native [field]. |
386 void setNativeFieldStoreBehavior( | 457 void setNativeFieldStoreBehavior( |
387 FieldElement field, NativeBehavior behavior) { | 458 FieldElement field, NativeBehavior behavior) { |
388 nativeFieldStoreBehavior[field] = behavior; | 459 nativeFieldStoreBehavior[field] = behavior; |
389 } | 460 } |
390 | 461 |
391 /// Apply JS$ escaping scheme to convert possible escaped Dart names into | 462 /// Apply JS$ escaping scheme to convert possible escaped Dart names into |
392 /// JS names. | 463 /// JS names. |
393 String getUnescapedJSInteropName(String name) { | 464 String computeUnescapedJSInteropName(String name) { |
394 return name.startsWith(_jsInteropEscapePrefix) | 465 return name.startsWith(_jsInteropEscapePrefix) |
395 ? name.substring(_jsInteropEscapePrefix.length) | 466 ? name.substring(_jsInteropEscapePrefix.length) |
396 : name; | 467 : name; |
397 } | 468 } |
398 } | 469 } |
OLD | NEW |