| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library js_backend.interceptor_data; |
| 6 |
| 7 import '../common/names.dart' show Identifiers; |
| 8 import '../core_types.dart' show CommonElements; |
| 9 import '../elements/elements.dart'; |
| 10 import '../js/js.dart' as jsAst; |
| 11 import '../types/types.dart' show TypeMask; |
| 12 import '../universe/selector.dart'; |
| 13 import '../world.dart' show ClosedWorld; |
| 14 import 'backend_helpers.dart'; |
| 15 import 'namer.dart'; |
| 16 import 'native_data.dart'; |
| 17 |
| 18 class InterceptorData { |
| 19 final NativeData _nativeData; |
| 20 final BackendHelpers _helpers; |
| 21 final CommonElements _commonElements; |
| 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 |
| 28 /// 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 |
| 30 /// know whether a send must be intercepted or not. |
| 31 final Map<String, Set<Element>> interceptedElements = |
| 32 <String, Set<Element>>{}; |
| 33 |
| 34 /// The members of mixin classes that are mixed into an instantiated |
| 35 /// interceptor class. This is a cached subset of [interceptedElements]. |
| 36 /// |
| 37 /// Mixin methods are not specialized for the class they are mixed into. |
| 38 /// Methods mixed into intercepted classes thus always make use of the |
| 39 /// explicit receiver argument, even when mixed into non-interceptor classes. |
| 40 /// |
| 41 /// These members must be invoked with a correct explicit receiver even when |
| 42 /// the receiver is not an intercepted class. |
| 43 final Map<String, Set<Element>> interceptedMixinElements = |
| 44 new Map<String, Set<Element>>(); |
| 45 |
| 46 /// A map of specialized versions of the [getInterceptorMethod]. |
| 47 /// |
| 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 |
| 55 /// Set of classes whose methods are intercepted. |
| 56 final Set<ClassElement> _interceptedClasses = new Set<ClassElement>(); |
| 57 |
| 58 /// Set of classes used as mixins on intercepted (native and primitive) |
| 59 /// classes. Methods on these classes might also be mixed in to regular Dart |
| 60 /// (unintercepted) classes. |
| 61 final Set<ClassElement> classesMixedIntoInterceptedClasses = |
| 62 new Set<ClassElement>(); |
| 63 |
| 64 InterceptorData(this._nativeData, this._helpers, this._commonElements); |
| 65 |
| 66 void onResolutionComplete(ClosedWorld closedWorld) { |
| 67 _closedWorld = closedWorld; |
| 68 } |
| 69 |
| 70 bool isInterceptedMethod(MemberElement element) { |
| 71 if (!element.isInstanceMember) return false; |
| 72 if (element.isGenerativeConstructorBody) { |
| 73 return _nativeData.isNativeOrExtendsNative(element.enclosingClass); |
| 74 } |
| 75 return interceptedElements[element.name] != null; |
| 76 } |
| 77 |
| 78 bool fieldHasInterceptedGetter(Element element) { |
| 79 assert(element.isField); |
| 80 return interceptedElements[element.name] != null; |
| 81 } |
| 82 |
| 83 bool fieldHasInterceptedSetter(Element element) { |
| 84 assert(element.isField); |
| 85 return interceptedElements[element.name] != null; |
| 86 } |
| 87 |
| 88 bool isInterceptedName(String name) { |
| 89 return interceptedElements[name] != null; |
| 90 } |
| 91 |
| 92 bool isInterceptedSelector(Selector selector) { |
| 93 return interceptedElements[selector.name] != null; |
| 94 } |
| 95 |
| 96 /// Returns `true` iff [selector] matches an element defined in a class mixed |
| 97 /// into an intercepted class. These selectors are not eligible for the |
| 98 /// 'dummy explicit receiver' optimization. |
| 99 bool isInterceptedMixinSelector(Selector selector, TypeMask mask) { |
| 100 Set<Element> elements = |
| 101 interceptedMixinElements.putIfAbsent(selector.name, () { |
| 102 Set<Element> elements = interceptedElements[selector.name]; |
| 103 if (elements == null) return null; |
| 104 return elements |
| 105 .where((element) => classesMixedIntoInterceptedClasses |
| 106 .contains(element.enclosingClass)) |
| 107 .toSet(); |
| 108 }); |
| 109 |
| 110 if (elements == null) return false; |
| 111 if (elements.isEmpty) return false; |
| 112 return elements.any((element) { |
| 113 return selector.applies(element) && |
| 114 (mask == null || |
| 115 mask.canHit(element as MemberElement, selector, _closedWorld)); |
| 116 }); |
| 117 } |
| 118 |
| 119 /// True if the given class is an internal class used for type inference |
| 120 /// and never exists at runtime. |
| 121 bool isCompileTimeOnlyClass(ClassElement class_) { |
| 122 return class_ == _helpers.jsPositiveIntClass || |
| 123 class_ == _helpers.jsUInt32Class || |
| 124 class_ == _helpers.jsUInt31Class || |
| 125 class_ == _helpers.jsFixedArrayClass || |
| 126 class_ == _helpers.jsUnmodifiableArrayClass || |
| 127 class_ == _helpers.jsMutableArrayClass || |
| 128 class_ == _helpers.jsExtendableArrayClass; |
| 129 } |
| 130 |
| 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] |
| 136 /// |
| 137 /// Returns an empty set if there is no class. Do not modify the returned set. |
| 138 Set<ClassElement> getInterceptedClassesOn(String name) { |
| 139 Set<Element> intercepted = interceptedElements[name]; |
| 140 if (intercepted == null) return _noClasses; |
| 141 return interceptedClassesCache.putIfAbsent(name, () { |
| 142 // Populate the cache by running through all the elements and |
| 143 // determine if the given selector applies to them. |
| 144 Set<ClassElement> result = new Set<ClassElement>(); |
| 145 for (Element element in intercepted) { |
| 146 ClassElement classElement = element.enclosingClass; |
| 147 if (isCompileTimeOnlyClass(classElement)) continue; |
| 148 if (_nativeData.isNativeOrExtendsNative(classElement) || |
| 149 interceptedClasses.contains(classElement)) { |
| 150 result.add(classElement); |
| 151 } |
| 152 if (classesMixedIntoInterceptedClasses.contains(classElement)) { |
| 153 Set<ClassElement> nativeSubclasses = |
| 154 nativeSubclassesOfMixin(classElement); |
| 155 if (nativeSubclasses != null) result.addAll(nativeSubclasses); |
| 156 } |
| 157 } |
| 158 return result; |
| 159 }); |
| 160 } |
| 161 |
| 162 Set<ClassElement> nativeSubclassesOfMixin(ClassElement mixin) { |
| 163 Iterable<MixinApplicationElement> uses = _closedWorld.mixinUsesOf(mixin); |
| 164 Set<ClassElement> result = null; |
| 165 for (MixinApplicationElement use in uses) { |
| 166 _closedWorld.forEachStrictSubclassOf(use, (ClassElement subclass) { |
| 167 if (_nativeData.isNativeOrExtendsNative(subclass)) { |
| 168 if (result == null) result = new Set<ClassElement>(); |
| 169 result.add(subclass); |
| 170 } |
| 171 }); |
| 172 } |
| 173 return result; |
| 174 } |
| 175 |
| 176 bool isInterceptorClass(ClassElement element) { |
| 177 if (element == null) return false; |
| 178 if (_nativeData.isNativeOrExtendsNative(element)) return true; |
| 179 if (interceptedClasses.contains(element)) return true; |
| 180 if (classesMixedIntoInterceptedClasses.contains(element)) return true; |
| 181 return false; |
| 182 } |
| 183 |
| 184 jsAst.Name registerOneShotInterceptor(Selector selector, Namer namer) { |
| 185 Set<ClassElement> classes = getInterceptedClassesOn(selector.name); |
| 186 jsAst.Name name = namer.nameForGetOneShotInterceptor(selector, classes); |
| 187 if (!oneShotInterceptors.containsKey(name)) { |
| 188 registerSpecializedGetInterceptor(classes, namer); |
| 189 oneShotInterceptors[name] = selector; |
| 190 } |
| 191 return name; |
| 192 } |
| 193 |
| 194 void addInterceptorsForNativeClassMembers(ClassElement cls) { |
| 195 cls.forEachMember((ClassElement classElement, Element member) { |
| 196 if (member.name == Identifiers.call) { |
| 197 return; |
| 198 } |
| 199 if (member.isSynthesized) return; |
| 200 // All methods on [Object] are shadowed by [Interceptor]. |
| 201 if (classElement == _commonElements.objectClass) return; |
| 202 Set<Element> set = interceptedElements.putIfAbsent( |
| 203 member.name, () => new Set<Element>()); |
| 204 set.add(member); |
| 205 }, includeSuperAndInjectedMembers: true); |
| 206 |
| 207 // Walk superclass chain to find mixins. |
| 208 for (; cls != null; cls = cls.superclass) { |
| 209 if (cls.isMixinApplication) { |
| 210 MixinApplicationElement mixinApplication = cls; |
| 211 classesMixedIntoInterceptedClasses.add(mixinApplication.mixin); |
| 212 } |
| 213 } |
| 214 } |
| 215 |
| 216 void addInterceptors(ClassElement cls) { |
| 217 if (_interceptedClasses.add(cls)) { |
| 218 cls.forEachMember((ClassElement classElement, Element member) { |
| 219 // All methods on [Object] are shadowed by [Interceptor]. |
| 220 if (classElement == _commonElements.objectClass) return; |
| 221 Set<Element> set = interceptedElements.putIfAbsent( |
| 222 member.name, () => new Set<Element>()); |
| 223 set.add(member); |
| 224 }, includeSuperAndInjectedMembers: true); |
| 225 } |
| 226 _interceptedClasses.add(_helpers.jsInterceptorClass); |
| 227 } |
| 228 |
| 229 Set<ClassElement> get interceptedClasses { |
| 230 assert(_closedWorld != null); |
| 231 return _interceptedClasses; |
| 232 } |
| 233 |
| 234 void registerSpecializedGetInterceptor( |
| 235 Set<ClassElement> classes, Namer namer) { |
| 236 jsAst.Name name = namer.nameForGetInterceptor(classes); |
| 237 if (classes.contains(_helpers.jsInterceptorClass)) { |
| 238 // We can't use a specialized [getInterceptorMethod], so we make |
| 239 // sure we emit the one with all checks. |
| 240 specializedGetInterceptors[name] = interceptedClasses; |
| 241 } else { |
| 242 specializedGetInterceptors[name] = classes; |
| 243 } |
| 244 } |
| 245 } |
| OLD | NEW |