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