Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(163)

Side by Side Diff: pkg/compiler/lib/src/js_backend/interceptor_data.dart

Issue 2692303002: Split InterceptorData (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_backend/backend.dart ('k') | pkg/compiler/lib/src/js_emitter/full_emitter/interceptor_emitter.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698