| OLD | NEW |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, 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.interceptor_data; | 5 library js_backend.interceptor_data; |
| 6 | 6 |
| 7 import '../common/names.dart' show Identifiers; | 7 import '../common/names.dart' show Identifiers; |
| 8 import '../core_types.dart' show CommonElements; | 8 import '../core_types.dart' show CommonElements; |
| 9 import '../elements/elements.dart'; | 9 import '../elements/elements.dart'; |
| 10 import '../js/js.dart' as jsAst; | 10 import '../js/js.dart' as jsAst; |
| 11 import '../types/types.dart' show TypeMask; | 11 import '../types/types.dart' show TypeMask; |
| 12 import '../universe/selector.dart'; | 12 import '../universe/selector.dart'; |
| 13 import '../world.dart' show ClosedWorld; | 13 import '../world.dart' show ClosedWorld; |
| 14 import 'backend_helpers.dart'; | 14 import 'backend_helpers.dart'; |
| 15 import 'namer.dart'; | 15 import 'namer.dart'; |
| 16 import 'native_data.dart'; | 16 import 'native_data.dart'; |
| 17 | 17 |
| 18 class InterceptorData { | 18 abstract class InterceptorData { |
| 19 /// Returns `true` if [cls] is an intercepted class. |
| 20 // TODO(johnniwinther): Rename this to `isInterceptedClass`. |
| 21 bool isInterceptorClass(ClassElement element); |
| 22 |
| 23 bool isInterceptedMethod(MemberElement element); |
| 24 bool fieldHasInterceptedGetter(Element element); |
| 25 bool fieldHasInterceptedSetter(Element element); |
| 26 bool isInterceptedName(String name); |
| 27 bool isInterceptedSelector(Selector selector); |
| 28 bool isInterceptedMixinSelector(Selector selector, TypeMask mask); |
| 29 Iterable<ClassElement> get interceptedClasses; |
| 30 bool isMixedIntoInterceptedClass(ClassElement element); |
| 31 |
| 32 /// Returns a set of interceptor classes that contain a member named [name] |
| 33 /// |
| 34 /// Returns an empty set if there is no class. Do not modify the returned set. |
| 35 Set<ClassElement> getInterceptedClassesOn(String name); |
| 36 } |
| 37 |
| 38 abstract class InterceptorDataBuilder { |
| 39 void addInterceptors(ClassElement cls); |
| 40 void addInterceptorsForNativeClassMembers(ClassElement cls); |
| 41 InterceptorData onResolutionComplete(ClosedWorld closedWorld); |
| 42 } |
| 43 |
| 44 class InterceptorDataImpl implements InterceptorData { |
| 19 final NativeData _nativeData; | 45 final NativeData _nativeData; |
| 20 final BackendHelpers _helpers; | 46 final BackendHelpers _helpers; |
| 21 final CommonElements _commonElements; | 47 final ClosedWorld _closedWorld; |
| 22 ClosedWorld _closedWorld; | |
| 23 | |
| 24 /// A collection of selectors that must have a one shot interceptor generated. | |
| 25 final Map<jsAst.Name, Selector> oneShotInterceptors = | |
| 26 <jsAst.Name, Selector>{}; | |
| 27 | 48 |
| 28 /// The members of instantiated interceptor classes: maps a member name to the | 49 /// The members of instantiated interceptor classes: maps a member name to the |
| 29 /// list of members that have that name. This map is used by the codegen to | 50 /// list of members that have that name. This map is used by the codegen to |
| 30 /// know whether a send must be intercepted or not. | 51 /// know whether a send must be intercepted or not. |
| 31 final Map<String, Set<Element>> interceptedElements = | 52 final Map<String, Set<Element>> _interceptedElements; |
| 32 <String, Set<Element>>{}; | 53 |
| 54 /// Set of classes whose methods are intercepted. |
| 55 final Set<ClassElement> _interceptedClasses; |
| 56 |
| 57 /// Set of classes used as mixins on intercepted (native and primitive) |
| 58 /// classes. Methods on these classes might also be mixed in to regular Dart |
| 59 /// (unintercepted) classes. |
| 60 final Set<ClassElement> _classesMixedIntoInterceptedClasses; |
| 33 | 61 |
| 34 /// The members of mixin classes that are mixed into an instantiated | 62 /// The members of mixin classes that are mixed into an instantiated |
| 35 /// interceptor class. This is a cached subset of [interceptedElements]. | 63 /// interceptor class. This is a cached subset of [_interceptedElements]. |
| 36 /// | 64 /// |
| 37 /// Mixin methods are not specialized for the class they are mixed into. | 65 /// Mixin methods are not specialized for the class they are mixed into. |
| 38 /// Methods mixed into intercepted classes thus always make use of the | 66 /// Methods mixed into intercepted classes thus always make use of the |
| 39 /// explicit receiver argument, even when mixed into non-interceptor classes. | 67 /// explicit receiver argument, even when mixed into non-interceptor classes. |
| 40 /// | 68 /// |
| 41 /// These members must be invoked with a correct explicit receiver even when | 69 /// These members must be invoked with a correct explicit receiver even when |
| 42 /// the receiver is not an intercepted class. | 70 /// the receiver is not an intercepted class. |
| 43 final Map<String, Set<Element>> interceptedMixinElements = | 71 final Map<String, Set<Element>> _interceptedMixinElements = |
| 44 new Map<String, Set<Element>>(); | 72 new Map<String, Set<Element>>(); |
| 45 | 73 |
| 46 /// A map of specialized versions of the [getInterceptorMethod]. | 74 final Map<String, Set<ClassElement>> _interceptedClassesCache = |
| 47 /// | 75 new Map<String, Set<ClassElement>>(); |
| 48 /// Since [getInterceptorMethod] is a hot method at runtime, we're always | |
| 49 /// specializing it based on the incoming type. The keys in the map are the | |
| 50 /// names of these specialized versions. Note that the generic version that | |
| 51 /// contains all possible type checks is also stored in this map. | |
| 52 final Map<jsAst.Name, Set<ClassElement>> specializedGetInterceptors = | |
| 53 <jsAst.Name, Set<ClassElement>>{}; | |
| 54 | 76 |
| 55 /// Set of classes whose methods are intercepted. | 77 final Set<ClassElement> _noClasses = new Set<ClassElement>(); |
| 56 final Set<ClassElement> _interceptedClasses = new Set<ClassElement>(); | |
| 57 | 78 |
| 58 /// Set of classes used as mixins on intercepted (native and primitive) | 79 InterceptorDataImpl( |
| 59 /// classes. Methods on these classes might also be mixed in to regular Dart | 80 this._nativeData, |
| 60 /// (unintercepted) classes. | 81 this._helpers, |
| 61 final Set<ClassElement> classesMixedIntoInterceptedClasses = | 82 this._closedWorld, |
| 62 new Set<ClassElement>(); | 83 this._interceptedElements, |
| 63 | 84 this._interceptedClasses, |
| 64 InterceptorData(this._nativeData, this._helpers, this._commonElements); | 85 this._classesMixedIntoInterceptedClasses); |
| 65 | |
| 66 void onResolutionComplete(ClosedWorld closedWorld) { | |
| 67 _closedWorld = closedWorld; | |
| 68 } | |
| 69 | 86 |
| 70 bool isInterceptedMethod(MemberElement element) { | 87 bool isInterceptedMethod(MemberElement element) { |
| 71 if (!element.isInstanceMember) return false; | 88 if (!element.isInstanceMember) return false; |
| 72 if (element.isGenerativeConstructorBody) { | 89 if (element.isGenerativeConstructorBody) { |
| 73 return _nativeData.isNativeOrExtendsNative(element.enclosingClass); | 90 return _nativeData.isNativeOrExtendsNative(element.enclosingClass); |
| 74 } | 91 } |
| 75 return interceptedElements[element.name] != null; | 92 return _interceptedElements[element.name] != null; |
| 76 } | 93 } |
| 77 | 94 |
| 78 bool fieldHasInterceptedGetter(Element element) { | 95 bool fieldHasInterceptedGetter(Element element) { |
| 79 assert(element.isField); | 96 assert(element.isField); |
| 80 return interceptedElements[element.name] != null; | 97 return _interceptedElements[element.name] != null; |
| 81 } | 98 } |
| 82 | 99 |
| 83 bool fieldHasInterceptedSetter(Element element) { | 100 bool fieldHasInterceptedSetter(Element element) { |
| 84 assert(element.isField); | 101 assert(element.isField); |
| 85 return interceptedElements[element.name] != null; | 102 return _interceptedElements[element.name] != null; |
| 86 } | 103 } |
| 87 | 104 |
| 88 bool isInterceptedName(String name) { | 105 bool isInterceptedName(String name) { |
| 89 return interceptedElements[name] != null; | 106 return _interceptedElements[name] != null; |
| 90 } | 107 } |
| 91 | 108 |
| 92 bool isInterceptedSelector(Selector selector) { | 109 bool isInterceptedSelector(Selector selector) { |
| 93 return interceptedElements[selector.name] != null; | 110 return _interceptedElements[selector.name] != null; |
| 94 } | 111 } |
| 95 | 112 |
| 96 /// Returns `true` iff [selector] matches an element defined in a class mixed | 113 /// Returns `true` iff [selector] matches an element defined in a class mixed |
| 97 /// into an intercepted class. These selectors are not eligible for the | 114 /// into an intercepted class. These selectors are not eligible for the |
| 98 /// 'dummy explicit receiver' optimization. | 115 /// 'dummy explicit receiver' optimization. |
| 99 bool isInterceptedMixinSelector(Selector selector, TypeMask mask) { | 116 bool isInterceptedMixinSelector(Selector selector, TypeMask mask) { |
| 100 Set<Element> elements = | 117 Set<Element> elements = |
| 101 interceptedMixinElements.putIfAbsent(selector.name, () { | 118 _interceptedMixinElements.putIfAbsent(selector.name, () { |
| 102 Set<Element> elements = interceptedElements[selector.name]; | 119 Set<Element> elements = _interceptedElements[selector.name]; |
| 103 if (elements == null) return null; | 120 if (elements == null) return null; |
| 104 return elements | 121 return elements |
| 105 .where((element) => classesMixedIntoInterceptedClasses | 122 .where((element) => _classesMixedIntoInterceptedClasses |
| 106 .contains(element.enclosingClass)) | 123 .contains(element.enclosingClass)) |
| 107 .toSet(); | 124 .toSet(); |
| 108 }); | 125 }); |
| 109 | 126 |
| 110 if (elements == null) return false; | 127 if (elements == null) return false; |
| 111 if (elements.isEmpty) return false; | 128 if (elements.isEmpty) return false; |
| 112 return elements.any((element) { | 129 return elements.any((element) { |
| 113 return selector.applies(element) && | 130 return selector.applies(element) && |
| 114 (mask == null || | 131 (mask == null || |
| 115 mask.canHit(element as MemberElement, selector, _closedWorld)); | 132 mask.canHit(element as MemberElement, selector, _closedWorld)); |
| 116 }); | 133 }); |
| 117 } | 134 } |
| 118 | 135 |
| 119 /// True if the given class is an internal class used for type inference | 136 /// True if the given class is an internal class used for type inference |
| 120 /// and never exists at runtime. | 137 /// and never exists at runtime. |
| 121 bool isCompileTimeOnlyClass(ClassElement class_) { | 138 bool _isCompileTimeOnlyClass(ClassElement class_) { |
| 122 return class_ == _helpers.jsPositiveIntClass || | 139 return class_ == _helpers.jsPositiveIntClass || |
| 123 class_ == _helpers.jsUInt32Class || | 140 class_ == _helpers.jsUInt32Class || |
| 124 class_ == _helpers.jsUInt31Class || | 141 class_ == _helpers.jsUInt31Class || |
| 125 class_ == _helpers.jsFixedArrayClass || | 142 class_ == _helpers.jsFixedArrayClass || |
| 126 class_ == _helpers.jsUnmodifiableArrayClass || | 143 class_ == _helpers.jsUnmodifiableArrayClass || |
| 127 class_ == _helpers.jsMutableArrayClass || | 144 class_ == _helpers.jsMutableArrayClass || |
| 128 class_ == _helpers.jsExtendableArrayClass; | 145 class_ == _helpers.jsExtendableArrayClass; |
| 129 } | 146 } |
| 130 | 147 |
| 131 final Map<String, Set<ClassElement>> interceptedClassesCache = | |
| 132 new Map<String, Set<ClassElement>>(); | |
| 133 final Set<ClassElement> _noClasses = new Set<ClassElement>(); | |
| 134 | |
| 135 /// Returns a set of interceptor classes that contain a member named [name] | 148 /// Returns a set of interceptor classes that contain a member named [name] |
| 136 /// | 149 /// |
| 137 /// Returns an empty set if there is no class. Do not modify the returned set. | 150 /// Returns an empty set if there is no class. Do not modify the returned set. |
| 138 Set<ClassElement> getInterceptedClassesOn(String name) { | 151 Set<ClassElement> getInterceptedClassesOn(String name) { |
| 139 Set<Element> intercepted = interceptedElements[name]; | 152 Set<Element> intercepted = _interceptedElements[name]; |
| 140 if (intercepted == null) return _noClasses; | 153 if (intercepted == null) return _noClasses; |
| 141 return interceptedClassesCache.putIfAbsent(name, () { | 154 return _interceptedClassesCache.putIfAbsent(name, () { |
| 142 // Populate the cache by running through all the elements and | 155 // Populate the cache by running through all the elements and |
| 143 // determine if the given selector applies to them. | 156 // determine if the given selector applies to them. |
| 144 Set<ClassElement> result = new Set<ClassElement>(); | 157 Set<ClassElement> result = new Set<ClassElement>(); |
| 145 for (Element element in intercepted) { | 158 for (Element element in intercepted) { |
| 146 ClassElement classElement = element.enclosingClass; | 159 ClassElement classElement = element.enclosingClass; |
| 147 if (isCompileTimeOnlyClass(classElement)) continue; | 160 if (_isCompileTimeOnlyClass(classElement)) continue; |
| 148 if (_nativeData.isNativeOrExtendsNative(classElement) || | 161 if (_nativeData.isNativeOrExtendsNative(classElement) || |
| 149 interceptedClasses.contains(classElement)) { | 162 interceptedClasses.contains(classElement)) { |
| 150 result.add(classElement); | 163 result.add(classElement); |
| 151 } | 164 } |
| 152 if (classesMixedIntoInterceptedClasses.contains(classElement)) { | 165 if (_classesMixedIntoInterceptedClasses.contains(classElement)) { |
| 153 Set<ClassElement> nativeSubclasses = | 166 Set<ClassElement> nativeSubclasses = |
| 154 nativeSubclassesOfMixin(classElement); | 167 nativeSubclassesOfMixin(classElement); |
| 155 if (nativeSubclasses != null) result.addAll(nativeSubclasses); | 168 if (nativeSubclasses != null) result.addAll(nativeSubclasses); |
| 156 } | 169 } |
| 157 } | 170 } |
| 158 return result; | 171 return result; |
| 159 }); | 172 }); |
| 160 } | 173 } |
| 161 | 174 |
| 162 Set<ClassElement> nativeSubclassesOfMixin(ClassElement mixin) { | 175 Set<ClassElement> nativeSubclassesOfMixin(ClassElement mixin) { |
| 163 Iterable<MixinApplicationElement> uses = _closedWorld.mixinUsesOf(mixin); | 176 Iterable<MixinApplicationElement> uses = _closedWorld.mixinUsesOf(mixin); |
| 164 Set<ClassElement> result = null; | 177 Set<ClassElement> result = null; |
| 165 for (MixinApplicationElement use in uses) { | 178 for (MixinApplicationElement use in uses) { |
| 166 _closedWorld.forEachStrictSubclassOf(use, (ClassElement subclass) { | 179 _closedWorld.forEachStrictSubclassOf(use, (ClassElement subclass) { |
| 167 if (_nativeData.isNativeOrExtendsNative(subclass)) { | 180 if (_nativeData.isNativeOrExtendsNative(subclass)) { |
| 168 if (result == null) result = new Set<ClassElement>(); | 181 if (result == null) result = new Set<ClassElement>(); |
| 169 result.add(subclass); | 182 result.add(subclass); |
| 170 } | 183 } |
| 171 }); | 184 }); |
| 172 } | 185 } |
| 173 return result; | 186 return result; |
| 174 } | 187 } |
| 175 | 188 |
| 176 bool isInterceptorClass(ClassElement element) { | 189 bool isInterceptorClass(ClassElement element) { |
| 177 if (element == null) return false; | 190 if (element == null) return false; |
| 178 if (_nativeData.isNativeOrExtendsNative(element)) return true; | 191 if (_nativeData.isNativeOrExtendsNative(element)) return true; |
| 179 if (interceptedClasses.contains(element)) return true; | 192 if (interceptedClasses.contains(element)) return true; |
| 180 if (classesMixedIntoInterceptedClasses.contains(element)) return true; | 193 if (_classesMixedIntoInterceptedClasses.contains(element)) return true; |
| 181 return false; | 194 return false; |
| 182 } | 195 } |
| 183 | 196 |
| 184 jsAst.Name registerOneShotInterceptor(Selector selector, Namer namer) { | 197 bool isMixedIntoInterceptedClass(ClassElement element) => |
| 185 Set<ClassElement> classes = getInterceptedClassesOn(selector.name); | 198 _classesMixedIntoInterceptedClasses.contains(element); |
| 186 jsAst.Name name = namer.nameForGetOneShotInterceptor(selector, classes); | 199 |
| 187 if (!oneShotInterceptors.containsKey(name)) { | 200 Iterable<ClassElement> get interceptedClasses => _interceptedClasses; |
| 188 registerSpecializedGetInterceptor(classes, namer); | 201 } |
| 189 oneShotInterceptors[name] = selector; | 202 |
| 190 } | 203 class InterceptorDataBuilderImpl implements InterceptorDataBuilder { |
| 191 return name; | 204 final NativeData _nativeData; |
| 205 final BackendHelpers _helpers; |
| 206 final CommonElements _commonElements; |
| 207 |
| 208 /// The members of instantiated interceptor classes: maps a member name to the |
| 209 /// list of members that have that name. This map is used by the codegen to |
| 210 /// know whether a send must be intercepted or not. |
| 211 final Map<String, Set<Element>> _interceptedElements = |
| 212 <String, Set<Element>>{}; |
| 213 |
| 214 /// Set of classes whose methods are intercepted. |
| 215 final Set<ClassElement> _interceptedClasses = new Set<ClassElement>(); |
| 216 |
| 217 /// Set of classes used as mixins on intercepted (native and primitive) |
| 218 /// classes. Methods on these classes might also be mixed in to regular Dart |
| 219 /// (unintercepted) classes. |
| 220 final Set<ClassElement> _classesMixedIntoInterceptedClasses = |
| 221 new Set<ClassElement>(); |
| 222 |
| 223 InterceptorDataBuilderImpl( |
| 224 this._nativeData, this._helpers, this._commonElements); |
| 225 |
| 226 InterceptorData onResolutionComplete(ClosedWorld closedWorld) { |
| 227 return new InterceptorDataImpl( |
| 228 _nativeData, |
| 229 _helpers, |
| 230 closedWorld, |
| 231 _interceptedElements, |
| 232 _interceptedClasses, |
| 233 _classesMixedIntoInterceptedClasses); |
| 192 } | 234 } |
| 193 | 235 |
| 194 void addInterceptorsForNativeClassMembers(ClassElement cls) { | 236 void addInterceptorsForNativeClassMembers(ClassElement cls) { |
| 195 cls.forEachMember((ClassElement classElement, Element member) { | 237 cls.forEachMember((ClassElement classElement, Element member) { |
| 196 if (member.name == Identifiers.call) { | 238 if (member.name == Identifiers.call) { |
| 197 return; | 239 return; |
| 198 } | 240 } |
| 199 if (member.isSynthesized) return; | 241 if (member.isSynthesized) return; |
| 200 // All methods on [Object] are shadowed by [Interceptor]. | 242 // All methods on [Object] are shadowed by [Interceptor]. |
| 201 if (classElement == _commonElements.objectClass) return; | 243 if (classElement == _commonElements.objectClass) return; |
| 202 Set<Element> set = interceptedElements.putIfAbsent( | 244 Set<Element> set = _interceptedElements.putIfAbsent( |
| 203 member.name, () => new Set<Element>()); | 245 member.name, () => new Set<Element>()); |
| 204 set.add(member); | 246 set.add(member); |
| 205 }, includeSuperAndInjectedMembers: true); | 247 }, includeSuperAndInjectedMembers: true); |
| 206 | 248 |
| 207 // Walk superclass chain to find mixins. | 249 // Walk superclass chain to find mixins. |
| 208 for (; cls != null; cls = cls.superclass) { | 250 for (; cls != null; cls = cls.superclass) { |
| 209 if (cls.isMixinApplication) { | 251 if (cls.isMixinApplication) { |
| 210 MixinApplicationElement mixinApplication = cls; | 252 MixinApplicationElement mixinApplication = cls; |
| 211 classesMixedIntoInterceptedClasses.add(mixinApplication.mixin); | 253 _classesMixedIntoInterceptedClasses.add(mixinApplication.mixin); |
| 212 } | 254 } |
| 213 } | 255 } |
| 214 } | 256 } |
| 215 | 257 |
| 216 void addInterceptors(ClassElement cls) { | 258 void addInterceptors(ClassElement cls) { |
| 217 if (_interceptedClasses.add(cls)) { | 259 if (_interceptedClasses.add(cls)) { |
| 218 cls.forEachMember((ClassElement classElement, Element member) { | 260 cls.forEachMember((ClassElement classElement, Element member) { |
| 219 // All methods on [Object] are shadowed by [Interceptor]. | 261 // All methods on [Object] are shadowed by [Interceptor]. |
| 220 if (classElement == _commonElements.objectClass) return; | 262 if (classElement == _commonElements.objectClass) return; |
| 221 Set<Element> set = interceptedElements.putIfAbsent( | 263 Set<Element> set = _interceptedElements.putIfAbsent( |
| 222 member.name, () => new Set<Element>()); | 264 member.name, () => new Set<Element>()); |
| 223 set.add(member); | 265 set.add(member); |
| 224 }, includeSuperAndInjectedMembers: true); | 266 }, includeSuperAndInjectedMembers: true); |
| 225 } | 267 } |
| 226 _interceptedClasses.add(_helpers.jsInterceptorClass); | 268 _interceptedClasses.add(_helpers.jsInterceptorClass); |
| 227 } | 269 } |
| 270 } |
| 228 | 271 |
| 229 Set<ClassElement> get interceptedClasses { | 272 class OneShotInterceptorData { |
| 230 assert(_closedWorld != null); | 273 final InterceptorData _interceptorData; |
| 231 return _interceptedClasses; | 274 final BackendHelpers _helpers; |
| 275 |
| 276 OneShotInterceptorData(this._interceptorData, this._helpers); |
| 277 |
| 278 /// A collection of selectors that must have a one shot interceptor generated. |
| 279 final Map<jsAst.Name, Selector> _oneShotInterceptors = |
| 280 <jsAst.Name, Selector>{}; |
| 281 |
| 282 Selector getOneShotInterceptorSelector(jsAst.Name name) => |
| 283 _oneShotInterceptors[name]; |
| 284 |
| 285 Iterable<jsAst.Name> get oneShotInterceptorNames => |
| 286 _oneShotInterceptors.keys.toList()..sort(); |
| 287 |
| 288 /// A map of specialized versions of the [getInterceptorMethod]. |
| 289 /// |
| 290 /// Since [getInterceptorMethod] is a hot method at runtime, we're always |
| 291 /// specializing it based on the incoming type. The keys in the map are the |
| 292 /// names of these specialized versions. Note that the generic version that |
| 293 /// contains all possible type checks is also stored in this map. |
| 294 final Map<jsAst.Name, Set<ClassElement>> _specializedGetInterceptors = |
| 295 <jsAst.Name, Set<ClassElement>>{}; |
| 296 |
| 297 Iterable<jsAst.Name> get specializedGetInterceptorNames => |
| 298 _specializedGetInterceptors.keys.toList()..sort(); |
| 299 |
| 300 Set<ClassElement> getSpecializedGetInterceptorsFor(jsAst.Name name) => |
| 301 _specializedGetInterceptors[name]; |
| 302 |
| 303 jsAst.Name registerOneShotInterceptor(Selector selector, Namer namer) { |
| 304 Set<ClassElement> classes = |
| 305 _interceptorData.getInterceptedClassesOn(selector.name); |
| 306 jsAst.Name name = namer.nameForGetOneShotInterceptor(selector, classes); |
| 307 if (!_oneShotInterceptors.containsKey(name)) { |
| 308 registerSpecializedGetInterceptor(classes, namer); |
| 309 _oneShotInterceptors[name] = selector; |
| 310 } |
| 311 return name; |
| 232 } | 312 } |
| 233 | 313 |
| 234 void registerSpecializedGetInterceptor( | 314 void registerSpecializedGetInterceptor( |
| 235 Set<ClassElement> classes, Namer namer) { | 315 Set<ClassElement> classes, Namer namer) { |
| 236 jsAst.Name name = namer.nameForGetInterceptor(classes); | 316 jsAst.Name name = namer.nameForGetInterceptor(classes); |
| 237 if (classes.contains(_helpers.jsInterceptorClass)) { | 317 if (classes.contains(_helpers.jsInterceptorClass)) { |
| 238 // We can't use a specialized [getInterceptorMethod], so we make | 318 // We can't use a specialized [getInterceptorMethod], so we make |
| 239 // sure we emit the one with all checks. | 319 // sure we emit the one with all checks. |
| 240 specializedGetInterceptors[name] = interceptedClasses; | 320 _specializedGetInterceptors[name] = _interceptorData.interceptedClasses; |
| 241 } else { | 321 } else { |
| 242 specializedGetInterceptors[name] = classes; | 322 _specializedGetInterceptors[name] = classes; |
| 243 } | 323 } |
| 244 } | 324 } |
| 245 } | 325 } |
| OLD | NEW |