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

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

Issue 2680823002: Extract InterceptorData from JavaScriptBackend. (Closed)
Patch Set: Updated cf. comments 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
(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 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_backend/custom_elements_analysis.dart ('k') | pkg/compiler/lib/src/js_backend/namer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698