| 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 '../common_elements.dart' show CommonElements, ElementEnvironment; | 8 import '../common_elements.dart' show CommonElements, ElementEnvironment; |
| 9 import '../elements/elements.dart' show MemberElement; | 9 import '../elements/elements.dart' show MemberElement; |
| 10 import '../elements/entities.dart'; | 10 import '../elements/entities.dart'; |
| 11 import '../elements/types.dart'; | 11 import '../elements/types.dart'; |
| 12 import '../js/js.dart' as jsAst; | 12 import '../js/js.dart' as jsAst; |
| 13 import '../types/types.dart' show TypeMask; | 13 import '../types/types.dart' show TypeMask; |
| 14 import '../universe/selector.dart'; | 14 import '../universe/selector.dart'; |
| 15 import '../world.dart' show ClosedWorld; | 15 import '../world.dart' show ClosedWorld; |
| 16 import 'namer.dart'; | 16 import 'namer.dart'; |
| 17 import 'native_data.dart'; | 17 import 'native_data.dart'; |
| 18 | 18 |
| 19 abstract class InterceptorData { | 19 abstract class InterceptorData { |
| 20 /// Returns `true` if [cls] is an intercepted class. | 20 /// Returns `true` if [cls] is an intercepted class. |
| 21 bool isInterceptedClass(ClassEntity element); | 21 bool isInterceptedClass(ClassEntity element); |
| 22 | 22 |
| 23 bool isInterceptedMethod(MemberEntity element); | 23 bool isInterceptedMethod(MemberEntity element); |
| 24 bool fieldHasInterceptedGetter(FieldEntity element); | 24 bool fieldHasInterceptedGetter(FieldEntity element); |
| 25 bool fieldHasInterceptedSetter(FieldEntity element); | 25 bool fieldHasInterceptedSetter(FieldEntity element); |
| 26 bool isInterceptedName(String name); | 26 bool isInterceptedName(String name); |
| 27 bool isInterceptedSelector(Selector selector); | 27 bool isInterceptedSelector(Selector selector); |
| 28 bool isInterceptedMixinSelector(Selector selector, TypeMask mask); | 28 bool isInterceptedMixinSelector( |
| 29 Selector selector, TypeMask mask, ClosedWorld closedWorld); |
| 29 Iterable<ClassEntity> get interceptedClasses; | 30 Iterable<ClassEntity> get interceptedClasses; |
| 30 bool isMixedIntoInterceptedClass(ClassEntity element); | 31 bool isMixedIntoInterceptedClass(ClassEntity element); |
| 31 | 32 |
| 32 /// Returns a set of interceptor classes that contain a member named [name] | 33 /// Returns a set of interceptor classes that contain a member named [name] |
| 33 /// | 34 /// |
| 34 /// Returns an empty set if there is no class. Do not modify the returned set. | 35 /// Returns an empty set if there is no class. Do not modify the returned set. |
| 35 Set<ClassEntity> getInterceptedClassesOn(String name); | 36 Set<ClassEntity> getInterceptedClassesOn( |
| 37 String name, ClosedWorld closedWorld); |
| 36 | 38 |
| 37 /// Whether the compiler can use the native `instanceof` check to test for | 39 /// Whether the compiler can use the native `instanceof` check to test for |
| 38 /// instances of [type]. This is true for types that are not used as mixins or | 40 /// instances of [type]. This is true for types that are not used as mixins or |
| 39 /// interfaces. | 41 /// interfaces. |
| 40 bool mayGenerateInstanceofCheck(DartType type); | 42 bool mayGenerateInstanceofCheck(DartType type, ClosedWorld closedWorld); |
| 41 } | 43 } |
| 42 | 44 |
| 43 abstract class InterceptorDataBuilder { | 45 abstract class InterceptorDataBuilder { |
| 44 void addInterceptors(ClassEntity cls); | 46 void addInterceptors(ClassEntity cls); |
| 45 void addInterceptorsForNativeClassMembers(ClassEntity cls); | 47 void addInterceptorsForNativeClassMembers(ClassEntity cls); |
| 46 InterceptorData onResolutionComplete(ClosedWorld closedWorld); | 48 InterceptorData onResolutionComplete(); |
| 47 } | 49 } |
| 48 | 50 |
| 49 class InterceptorDataImpl implements InterceptorData { | 51 class InterceptorDataImpl implements InterceptorData { |
| 50 final NativeBasicData _nativeData; | 52 final NativeBasicData _nativeData; |
| 51 final CommonElements _commonElements; | 53 final CommonElements _commonElements; |
| 52 final ClosedWorld _closedWorld; | |
| 53 | 54 |
| 54 /// The members of instantiated interceptor classes: maps a member name to the | 55 /// The members of instantiated interceptor classes: maps a member name to the |
| 55 /// list of members that have that name. This map is used by the codegen to | 56 /// list of members that have that name. This map is used by the codegen to |
| 56 /// know whether a send must be intercepted or not. | 57 /// know whether a send must be intercepted or not. |
| 57 final Map<String, Set<MemberEntity>> _interceptedElements; | 58 final Map<String, Set<MemberEntity>> _interceptedElements; |
| 58 | 59 |
| 59 /// Set of classes whose methods are intercepted. | 60 /// Set of classes whose methods are intercepted. |
| 60 final Set<ClassEntity> _interceptedClasses; | 61 final Set<ClassEntity> _interceptedClasses; |
| 61 | 62 |
| 62 /// Set of classes used as mixins on intercepted (native and primitive) | 63 /// Set of classes used as mixins on intercepted (native and primitive) |
| (...skipping 14 matching lines...) Expand all Loading... |
| 77 new Map<String, Set<MemberEntity>>(); | 78 new Map<String, Set<MemberEntity>>(); |
| 78 | 79 |
| 79 final Map<String, Set<ClassEntity>> _interceptedClassesCache = | 80 final Map<String, Set<ClassEntity>> _interceptedClassesCache = |
| 80 new Map<String, Set<ClassEntity>>(); | 81 new Map<String, Set<ClassEntity>>(); |
| 81 | 82 |
| 82 final Set<ClassEntity> _noClasses = new Set<ClassEntity>(); | 83 final Set<ClassEntity> _noClasses = new Set<ClassEntity>(); |
| 83 | 84 |
| 84 InterceptorDataImpl( | 85 InterceptorDataImpl( |
| 85 this._nativeData, | 86 this._nativeData, |
| 86 this._commonElements, | 87 this._commonElements, |
| 87 this._closedWorld, | |
| 88 this._interceptedElements, | 88 this._interceptedElements, |
| 89 this._interceptedClasses, | 89 this._interceptedClasses, |
| 90 this._classesMixedIntoInterceptedClasses); | 90 this._classesMixedIntoInterceptedClasses); |
| 91 | 91 |
| 92 bool isInterceptedMethod(MemberElement element) { | 92 bool isInterceptedMethod(MemberElement element) { |
| 93 if (!element.isInstanceMember) return false; | 93 if (!element.isInstanceMember) return false; |
| 94 if (element.isGenerativeConstructorBody) { | 94 if (element.isGenerativeConstructorBody) { |
| 95 return _nativeData.isNativeOrExtendsNative(element.enclosingClass); | 95 return _nativeData.isNativeOrExtendsNative(element.enclosingClass); |
| 96 } | 96 } |
| 97 return _interceptedElements[element.name] != null; | 97 return _interceptedElements[element.name] != null; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 109 return _interceptedElements[name] != null; | 109 return _interceptedElements[name] != null; |
| 110 } | 110 } |
| 111 | 111 |
| 112 bool isInterceptedSelector(Selector selector) { | 112 bool isInterceptedSelector(Selector selector) { |
| 113 return _interceptedElements[selector.name] != null; | 113 return _interceptedElements[selector.name] != null; |
| 114 } | 114 } |
| 115 | 115 |
| 116 /// Returns `true` iff [selector] matches an element defined in a class mixed | 116 /// Returns `true` iff [selector] matches an element defined in a class mixed |
| 117 /// into an intercepted class. These selectors are not eligible for the | 117 /// into an intercepted class. These selectors are not eligible for the |
| 118 /// 'dummy explicit receiver' optimization. | 118 /// 'dummy explicit receiver' optimization. |
| 119 bool isInterceptedMixinSelector(Selector selector, TypeMask mask) { | 119 bool isInterceptedMixinSelector( |
| 120 Selector selector, TypeMask mask, ClosedWorld closedWorld) { |
| 120 Set<MemberEntity> elements = | 121 Set<MemberEntity> elements = |
| 121 _interceptedMixinElements.putIfAbsent(selector.name, () { | 122 _interceptedMixinElements.putIfAbsent(selector.name, () { |
| 122 Set<MemberEntity> elements = _interceptedElements[selector.name]; | 123 Set<MemberEntity> elements = _interceptedElements[selector.name]; |
| 123 if (elements == null) return null; | 124 if (elements == null) return null; |
| 124 return elements | 125 return elements |
| 125 .where((element) => _classesMixedIntoInterceptedClasses | 126 .where((element) => _classesMixedIntoInterceptedClasses |
| 126 .contains(element.enclosingClass)) | 127 .contains(element.enclosingClass)) |
| 127 .toSet(); | 128 .toSet(); |
| 128 }); | 129 }); |
| 129 | 130 |
| 130 if (elements == null) return false; | 131 if (elements == null) return false; |
| 131 if (elements.isEmpty) return false; | 132 if (elements.isEmpty) return false; |
| 132 return elements.any((element) { | 133 return elements.any((element) { |
| 133 return selector.applies(element) && | 134 return selector.applies(element) && |
| 134 (mask == null || mask.canHit(element, selector, _closedWorld)); | 135 (mask == null || mask.canHit(element, selector, closedWorld)); |
| 135 }); | 136 }); |
| 136 } | 137 } |
| 137 | 138 |
| 138 /// True if the given class is an internal class used for type inference | 139 /// True if the given class is an internal class used for type inference |
| 139 /// and never exists at runtime. | 140 /// and never exists at runtime. |
| 140 bool _isCompileTimeOnlyClass(ClassEntity class_) { | 141 bool _isCompileTimeOnlyClass(ClassEntity class_) { |
| 141 return class_ == _commonElements.jsPositiveIntClass || | 142 return class_ == _commonElements.jsPositiveIntClass || |
| 142 class_ == _commonElements.jsUInt32Class || | 143 class_ == _commonElements.jsUInt32Class || |
| 143 class_ == _commonElements.jsUInt31Class || | 144 class_ == _commonElements.jsUInt31Class || |
| 144 class_ == _commonElements.jsFixedArrayClass || | 145 class_ == _commonElements.jsFixedArrayClass || |
| 145 class_ == _commonElements.jsUnmodifiableArrayClass || | 146 class_ == _commonElements.jsUnmodifiableArrayClass || |
| 146 class_ == _commonElements.jsMutableArrayClass || | 147 class_ == _commonElements.jsMutableArrayClass || |
| 147 class_ == _commonElements.jsExtendableArrayClass; | 148 class_ == _commonElements.jsExtendableArrayClass; |
| 148 } | 149 } |
| 149 | 150 |
| 150 /// Returns a set of interceptor classes that contain a member named [name] | 151 /// Returns a set of interceptor classes that contain a member named [name] |
| 151 /// | 152 /// |
| 152 /// Returns an empty set if there is no class. Do not modify the returned set. | 153 /// Returns an empty set if there is no class. Do not modify the returned set. |
| 153 Set<ClassEntity> getInterceptedClassesOn(String name) { | 154 Set<ClassEntity> getInterceptedClassesOn( |
| 155 String name, ClosedWorld closedWorld) { |
| 154 Set<MemberEntity> intercepted = _interceptedElements[name]; | 156 Set<MemberEntity> intercepted = _interceptedElements[name]; |
| 155 if (intercepted == null) return _noClasses; | 157 if (intercepted == null) return _noClasses; |
| 156 return _interceptedClassesCache.putIfAbsent(name, () { | 158 return _interceptedClassesCache.putIfAbsent(name, () { |
| 157 // Populate the cache by running through all the elements and | 159 // Populate the cache by running through all the elements and |
| 158 // determine if the given selector applies to them. | 160 // determine if the given selector applies to them. |
| 159 Set<ClassEntity> result = new Set<ClassEntity>(); | 161 Set<ClassEntity> result = new Set<ClassEntity>(); |
| 160 for (MemberEntity element in intercepted) { | 162 for (MemberEntity element in intercepted) { |
| 161 ClassEntity classElement = element.enclosingClass; | 163 ClassEntity classElement = element.enclosingClass; |
| 162 if (_isCompileTimeOnlyClass(classElement)) continue; | 164 if (_isCompileTimeOnlyClass(classElement)) continue; |
| 163 if (_nativeData.isNativeOrExtendsNative(classElement) || | 165 if (_nativeData.isNativeOrExtendsNative(classElement) || |
| 164 interceptedClasses.contains(classElement)) { | 166 interceptedClasses.contains(classElement)) { |
| 165 result.add(classElement); | 167 result.add(classElement); |
| 166 } | 168 } |
| 167 if (_classesMixedIntoInterceptedClasses.contains(classElement)) { | 169 if (_classesMixedIntoInterceptedClasses.contains(classElement)) { |
| 168 Set<ClassEntity> nativeSubclasses = | 170 Set<ClassEntity> nativeSubclasses = |
| 169 nativeSubclassesOfMixin(classElement); | 171 nativeSubclassesOfMixin(classElement, closedWorld); |
| 170 if (nativeSubclasses != null) result.addAll(nativeSubclasses); | 172 if (nativeSubclasses != null) result.addAll(nativeSubclasses); |
| 171 } | 173 } |
| 172 } | 174 } |
| 173 return result; | 175 return result; |
| 174 }); | 176 }); |
| 175 } | 177 } |
| 176 | 178 |
| 177 Set<ClassEntity> nativeSubclassesOfMixin(ClassEntity mixin) { | 179 Set<ClassEntity> nativeSubclassesOfMixin( |
| 178 Iterable<ClassEntity> uses = _closedWorld.mixinUsesOf(mixin); | 180 ClassEntity mixin, ClosedWorld closedWorld) { |
| 181 Iterable<ClassEntity> uses = closedWorld.mixinUsesOf(mixin); |
| 179 Set<ClassEntity> result = null; | 182 Set<ClassEntity> result = null; |
| 180 for (ClassEntity use in uses) { | 183 for (ClassEntity use in uses) { |
| 181 _closedWorld.forEachStrictSubclassOf(use, (ClassEntity subclass) { | 184 closedWorld.forEachStrictSubclassOf(use, (ClassEntity subclass) { |
| 182 if (_nativeData.isNativeOrExtendsNative(subclass)) { | 185 if (_nativeData.isNativeOrExtendsNative(subclass)) { |
| 183 if (result == null) result = new Set<ClassEntity>(); | 186 if (result == null) result = new Set<ClassEntity>(); |
| 184 result.add(subclass); | 187 result.add(subclass); |
| 185 } | 188 } |
| 186 }); | 189 }); |
| 187 } | 190 } |
| 188 return result; | 191 return result; |
| 189 } | 192 } |
| 190 | 193 |
| 191 bool isInterceptedClass(ClassEntity element) { | 194 bool isInterceptedClass(ClassEntity element) { |
| 192 if (element == null) return false; | 195 if (element == null) return false; |
| 193 if (_nativeData.isNativeOrExtendsNative(element)) return true; | 196 if (_nativeData.isNativeOrExtendsNative(element)) return true; |
| 194 if (interceptedClasses.contains(element)) return true; | 197 if (interceptedClasses.contains(element)) return true; |
| 195 if (_classesMixedIntoInterceptedClasses.contains(element)) return true; | 198 if (_classesMixedIntoInterceptedClasses.contains(element)) return true; |
| 196 return false; | 199 return false; |
| 197 } | 200 } |
| 198 | 201 |
| 199 bool isMixedIntoInterceptedClass(ClassEntity element) => | 202 bool isMixedIntoInterceptedClass(ClassEntity element) => |
| 200 _classesMixedIntoInterceptedClasses.contains(element); | 203 _classesMixedIntoInterceptedClasses.contains(element); |
| 201 | 204 |
| 202 Iterable<ClassEntity> get interceptedClasses => _interceptedClasses; | 205 Iterable<ClassEntity> get interceptedClasses => _interceptedClasses; |
| 203 | 206 |
| 204 bool mayGenerateInstanceofCheck(DartType type) { | 207 bool mayGenerateInstanceofCheck(DartType type, ClosedWorld closedWorld) { |
| 205 // We can use an instanceof check for raw types that have no subclass that | 208 // We can use an instanceof check for raw types that have no subclass that |
| 206 // is mixed-in or in an implements clause. | 209 // is mixed-in or in an implements clause. |
| 207 | 210 |
| 208 if (!type.treatAsRaw) return false; | 211 if (!type.treatAsRaw) return false; |
| 209 InterfaceType interfaceType = type; | 212 InterfaceType interfaceType = type; |
| 210 ClassEntity classElement = interfaceType.element; | 213 ClassEntity classElement = interfaceType.element; |
| 211 if (isInterceptedClass(classElement)) return false; | 214 if (isInterceptedClass(classElement)) return false; |
| 212 return _closedWorld.hasOnlySubclasses(classElement); | 215 return closedWorld.hasOnlySubclasses(classElement); |
| 213 } | 216 } |
| 214 } | 217 } |
| 215 | 218 |
| 216 class InterceptorDataBuilderImpl implements InterceptorDataBuilder { | 219 class InterceptorDataBuilderImpl implements InterceptorDataBuilder { |
| 217 final NativeBasicData _nativeData; | 220 final NativeBasicData _nativeData; |
| 218 final ElementEnvironment _elementEnvironment; | 221 final ElementEnvironment _elementEnvironment; |
| 219 final CommonElements _commonElements; | 222 final CommonElements _commonElements; |
| 220 | 223 |
| 221 /// The members of instantiated interceptor classes: maps a member name to the | 224 /// The members of instantiated interceptor classes: maps a member name to the |
| 222 /// list of members that have that name. This map is used by the codegen to | 225 /// list of members that have that name. This map is used by the codegen to |
| 223 /// know whether a send must be intercepted or not. | 226 /// know whether a send must be intercepted or not. |
| 224 final Map<String, Set<MemberEntity>> _interceptedElements = | 227 final Map<String, Set<MemberEntity>> _interceptedElements = |
| 225 <String, Set<MemberEntity>>{}; | 228 <String, Set<MemberEntity>>{}; |
| 226 | 229 |
| 227 /// Set of classes whose methods are intercepted. | 230 /// Set of classes whose methods are intercepted. |
| 228 final Set<ClassEntity> _interceptedClasses = new Set<ClassEntity>(); | 231 final Set<ClassEntity> _interceptedClasses = new Set<ClassEntity>(); |
| 229 | 232 |
| 230 /// Set of classes used as mixins on intercepted (native and primitive) | 233 /// Set of classes used as mixins on intercepted (native and primitive) |
| 231 /// classes. Methods on these classes might also be mixed in to regular Dart | 234 /// classes. Methods on these classes might also be mixed in to regular Dart |
| 232 /// (unintercepted) classes. | 235 /// (unintercepted) classes. |
| 233 final Set<ClassEntity> _classesMixedIntoInterceptedClasses = | 236 final Set<ClassEntity> _classesMixedIntoInterceptedClasses = |
| 234 new Set<ClassEntity>(); | 237 new Set<ClassEntity>(); |
| 235 | 238 |
| 236 InterceptorDataBuilderImpl( | 239 InterceptorDataBuilderImpl( |
| 237 this._nativeData, this._elementEnvironment, this._commonElements); | 240 this._nativeData, this._elementEnvironment, this._commonElements); |
| 238 | 241 |
| 239 InterceptorData onResolutionComplete(ClosedWorld closedWorld) { | 242 InterceptorData onResolutionComplete() { |
| 240 return new InterceptorDataImpl( | 243 return new InterceptorDataImpl( |
| 241 _nativeData, | 244 _nativeData, |
| 242 _commonElements, | 245 _commonElements, |
| 243 closedWorld, | |
| 244 _interceptedElements, | 246 _interceptedElements, |
| 245 _interceptedClasses, | 247 _interceptedClasses, |
| 246 _classesMixedIntoInterceptedClasses); | 248 _classesMixedIntoInterceptedClasses); |
| 247 } | 249 } |
| 248 | 250 |
| 249 void addInterceptorsForNativeClassMembers(ClassEntity cls) { | 251 void addInterceptorsForNativeClassMembers(ClassEntity cls) { |
| 250 _elementEnvironment.forEachClassMember(cls, | 252 _elementEnvironment.forEachClassMember(cls, |
| 251 (ClassEntity cls, MemberEntity member) { | 253 (ClassEntity cls, MemberEntity member) { |
| 252 if (member.name == Identifiers.call) return; | 254 if (member.name == Identifiers.call) return; |
| 253 // All methods on [Object] are shadowed by [Interceptor]. | 255 // All methods on [Object] are shadowed by [Interceptor]. |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 302 /// contains all possible type checks is also stored in this map. | 304 /// contains all possible type checks is also stored in this map. |
| 303 final Map<jsAst.Name, Set<ClassEntity>> _specializedGetInterceptors = | 305 final Map<jsAst.Name, Set<ClassEntity>> _specializedGetInterceptors = |
| 304 <jsAst.Name, Set<ClassEntity>>{}; | 306 <jsAst.Name, Set<ClassEntity>>{}; |
| 305 | 307 |
| 306 Iterable<jsAst.Name> get specializedGetInterceptorNames => | 308 Iterable<jsAst.Name> get specializedGetInterceptorNames => |
| 307 _specializedGetInterceptors.keys.toList()..sort(); | 309 _specializedGetInterceptors.keys.toList()..sort(); |
| 308 | 310 |
| 309 Set<ClassEntity> getSpecializedGetInterceptorsFor(jsAst.Name name) => | 311 Set<ClassEntity> getSpecializedGetInterceptorsFor(jsAst.Name name) => |
| 310 _specializedGetInterceptors[name]; | 312 _specializedGetInterceptors[name]; |
| 311 | 313 |
| 312 jsAst.Name registerOneShotInterceptor(Selector selector, Namer namer) { | 314 jsAst.Name registerOneShotInterceptor( |
| 315 Selector selector, Namer namer, ClosedWorld closedWorld) { |
| 313 Set<ClassEntity> classes = | 316 Set<ClassEntity> classes = |
| 314 _interceptorData.getInterceptedClassesOn(selector.name); | 317 _interceptorData.getInterceptedClassesOn(selector.name, closedWorld); |
| 315 jsAst.Name name = namer.nameForGetOneShotInterceptor(selector, classes); | 318 jsAst.Name name = namer.nameForGetOneShotInterceptor(selector, classes); |
| 316 if (!_oneShotInterceptors.containsKey(name)) { | 319 if (!_oneShotInterceptors.containsKey(name)) { |
| 317 registerSpecializedGetInterceptor(classes, namer); | 320 registerSpecializedGetInterceptor(classes, namer); |
| 318 _oneShotInterceptors[name] = selector; | 321 _oneShotInterceptors[name] = selector; |
| 319 } | 322 } |
| 320 return name; | 323 return name; |
| 321 } | 324 } |
| 322 | 325 |
| 323 void registerSpecializedGetInterceptor( | 326 void registerSpecializedGetInterceptor( |
| 324 Set<ClassEntity> classes, Namer namer) { | 327 Set<ClassEntity> classes, Namer namer) { |
| 325 jsAst.Name name = namer.nameForGetInterceptor(classes); | 328 jsAst.Name name = namer.nameForGetInterceptor(classes); |
| 326 if (classes.contains(_commonElements.jsInterceptorClass)) { | 329 if (classes.contains(_commonElements.jsInterceptorClass)) { |
| 327 // We can't use a specialized [getInterceptorMethod], so we make | 330 // We can't use a specialized [getInterceptorMethod], so we make |
| 328 // sure we emit the one with all checks. | 331 // sure we emit the one with all checks. |
| 329 _specializedGetInterceptors[name] = _interceptorData.interceptedClasses; | 332 _specializedGetInterceptors[name] = _interceptorData.interceptedClasses; |
| 330 } else { | 333 } else { |
| 331 _specializedGetInterceptors[name] = classes; | 334 _specializedGetInterceptors[name] = classes; |
| 332 } | 335 } |
| 333 } | 336 } |
| 334 } | 337 } |
| OLD | NEW |