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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/inferrer/node_tracer.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, 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 part of type_graph_inferrer;
6
7 // A set of selectors we know do not escape the elements inside the
8 // list.
9 Set<String> doesNotEscapeListSet = new Set<String>.from(
10 const <String>[
11 // From Object.
12 '==',
13 'hashCode',
14 'toString',
15 'noSuchMethod',
16 'runtimeType',
17
18 // From Iterable.
19 'isEmpty',
20 'isNotEmpty',
21 'length',
22 'any',
23 'contains',
24 'every',
25 'join',
26
27 // From List.
28 'add',
29 'addAll',
30 'clear',
31 'fillRange',
32 'indexOf',
33 'insert',
34 'insertAll',
35 'lastIndexOf',
36 'remove',
37 'removeRange',
38 'replaceRange',
39 'setAll',
40 'setRange',
41 'shuffle',
42 '[]=',
43
44 // From JSArray.
45 'checkMutable',
46 'checkGrowable',
47 ]);
48
49 Set<String> doesNotEscapeMapSet = new Set<String>.from(
50 const <String>[
51 // From Object.
52 '==',
53 'hashCode',
54 'toString',
55 'noSuchMethod',
56 'runtimeType',
57 // from Map.
58 'isEmpty',
59 'isNotEmpty',
60 'length',
61 'clear',
62 'containsKey',
63 'containsValue',
64 '[]=',
65 // [keys] only allows key values to escape, which we do not track.
66 'keys'
67 ]);
68
69 abstract class TracerVisitor<T extends TypeInformation>
70 implements TypeInformationVisitor {
71 final T tracedType;
72 final TypeGraphInferrerEngine inferrer;
73 final Compiler compiler;
74
75 static const int MAX_ANALYSIS_COUNT = 16;
76 final Setlet<Element> analyzedElements = new Setlet<Element>();
77
78 TracerVisitor(this.tracedType, inferrer)
79 : this.inferrer = inferrer, this.compiler = inferrer.compiler;
80
81 // Work list that gets populated with [TypeInformation] that could
82 // contain the container.
83 final List<TypeInformation> workList = <TypeInformation>[];
84
85 // Work list of lists to analyze after analyzing the users of a
86 // [TypeInformation]. We know the [tracedType] has been stored in these
87 // lists and we must check how it escapes from these lists.
88 final List<ListTypeInformation> listsToAnalyze =
89 <ListTypeInformation>[];
90 // Work list of maps to analyze after analyzing the users of a
91 // [TypeInformation]. We know the [tracedType] has been stored in these
92 // maps and we must check how it escapes from these maps.
93 final List<MapTypeInformation> mapsToAnalyze = <MapTypeInformation>[];
94
95 final Setlet<TypeInformation> flowsInto = new Setlet<TypeInformation>();
96
97 // The current [TypeInformation] in the analysis.
98 TypeInformation currentUser;
99 bool continueAnalyzing = true;
100
101 void addNewEscapeInformation(TypeInformation info) {
102 if (flowsInto.contains(info)) return;
103 flowsInto.add(info);
104 workList.add(info);
105 }
106
107 void analyze() {
108 // Collect the [TypeInformation] where the list can flow in,
109 // as well as the operations done on all these [TypeInformation]s.
110 addNewEscapeInformation(tracedType);
111 while (!workList.isEmpty) {
112 currentUser = workList.removeLast();
113 int expectedWork = analyzedElements.length + currentUser.users.length;
114 if (expectedWork > MAX_ANALYSIS_COUNT) {
115 bailout('Too many users');
116 break;
117 }
118 for (TypeInformation info in currentUser.users) {
119 analyzedElements.add(info.owner);
120 info.accept(this);
121 }
122 while (!listsToAnalyze.isEmpty) {
123 analyzeStoredIntoList(listsToAnalyze.removeLast());
124 }
125 while (!mapsToAnalyze.isEmpty) {
126 analyzeStoredIntoMap(mapsToAnalyze.removeLast());
127 }
128 if (!continueAnalyzing) break;
129 }
130 }
131
132 void bailout(String reason) {
133 if (_VERBOSE) {
134 print('Bailing out on $tracedType because: $reason');
135 }
136 continueAnalyzing = false;
137 }
138
139 void visitNarrowTypeInformation(NarrowTypeInformation info) {
140 addNewEscapeInformation(info);
141 }
142
143 void visitPhiElementTypeInformation(PhiElementTypeInformation info) {
144 addNewEscapeInformation(info);
145 }
146
147 void visitElementInContainerTypeInformation(
148 ElementInContainerTypeInformation info) {
149 addNewEscapeInformation(info);
150 }
151
152 void visitKeyInMapTypeInformation(KeyInMapTypeInformation info) {
153 // We do not track the use of keys from a map, so we have to bail.
154 bailout('Used as key in Map');
155 }
156
157 void visitValueInMapTypeInformation(ValueInMapTypeInformation info) {
158 addNewEscapeInformation(info);
159 }
160
161 void visitListTypeInformation(ListTypeInformation info) {
162 listsToAnalyze.add(info);
163 }
164
165 void visitMapTypeInformation(MapTypeInformation info) {
166 mapsToAnalyze.add(info);
167 }
168 void visitConcreteTypeInformation(ConcreteTypeInformation info) {}
169
170 void visitStringLiteralTypeInformation(StringLiteralTypeInformation info) {}
171
172 void visitClosureTypeInformation(ClosureTypeInformation info) {}
173
174 void visitClosureCallSiteTypeInformation(
175 ClosureCallSiteTypeInformation info) {}
176
177 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) {
178 Element called = info.calledElement;
179 if (inferrer.types.getInferredTypeOf(called) == currentUser) {
180 addNewEscapeInformation(info);
181 }
182 }
183
184 void analyzeStoredIntoList(ListTypeInformation list) {
185 inferrer.analyzeListAndEnqueue(list);
186 if (list.bailedOut) {
187 bailout('Stored in a list that bailed out');
188 } else {
189 list.flowsInto.forEach((flow) {
190 flow.users.forEach((user) {
191 if (user is !DynamicCallSiteTypeInformation) return;
192 if (user.receiver != flow) return;
193 if (inferrer._returnsListElementTypeSet.contains(user.selector)) {
194 addNewEscapeInformation(user);
195 } else if (!doesNotEscapeListSet.contains(user.selector.name)) {
196 bailout('Escape from a list via [${user.selector.name}]');
197 }
198 });
199 });
200 }
201 }
202
203 void analyzeStoredIntoMap(MapTypeInformation map) {
204 inferrer.analyzeMapAndEnqueue(map);
205 if (map.bailedOut) {
206 bailout('Stored in a map that bailed out');
207 } else {
208 map.flowsInto.forEach((flow) {
209 flow.users.forEach((user) {
210 if (user is !DynamicCallSiteTypeInformation) return;
211 if (user.receiver != flow) return;
212 if (user.selector.isIndex) {
213 addNewEscapeInformation(user);
214 } else if (!doesNotEscapeMapSet.contains(user.selector.name)) {
215 bailout('Escape from a map via [${user.selector.name}]');
216 }
217 });
218 });
219 }
220 }
221
222 /**
223 * Checks whether this is a call to a list adding method. The definition
224 * of what list adding means has to stay in sync with
225 * [isParameterOfListAddingMethod].
226 */
227 bool isAddedToContainer(DynamicCallSiteTypeInformation info) {
228 if (info.arguments == null) return false;
229 var receiverType = info.receiver.type;
230 if (!receiverType.isContainer) return false;
231 String selectorName = info.selector.name;
232 List<TypeInformation> arguments = info.arguments.positional;
233 return (selectorName == '[]=' && currentUser == arguments[1])
234 || (selectorName == 'insert' && currentUser == arguments[1])
235 || (selectorName == 'add' && currentUser == arguments[0]);
236 }
237
238 bool isIndexSetOnMap(DynamicCallSiteTypeInformation info) {
239 if (info.arguments == null) return false;
240 var receiverType = info.receiver.type;
241 if (!receiverType.isMap) return false;
242 return info.selector.name == '[]=';
243 }
244
245 /**
246 * Checks whether this is a call to a map adding method for values. The
247 * definition of map adding method has to stay in sync with
248 * [isParameterOfMapAddingMethod].
249 */
250 bool isValueAddedToMap(DynamicCallSiteTypeInformation info) {
251 return isIndexSetOnMap(info) &&
252 currentUser == info.arguments.positional[1];
253 }
254
255 /**
256 * Checks whether this is a call to a map adding method for keys. The
257 * definition of map adding method has to stay in sync with
258 * [isParameterOfMapAddingMethod].
259 */
260 bool isKeyAddedToMap(DynamicCallSiteTypeInformation info) {
261 return isIndexSetOnMap(info) &&
262 currentUser == info.arguments.positional[0];
263 }
264
265 void visitDynamicCallSiteTypeInformation(
266 DynamicCallSiteTypeInformation info) {
267 if (isAddedToContainer(info)) {
268 ContainerTypeMask mask = info.receiver.type;
269
270 if (mask.allocationNode != null) {
271 ListTypeInformation list =
272 inferrer.types.allocatedLists[mask.allocationNode];
273 listsToAnalyze.add(list);
274 } else {
275 // The [ContainerTypeMask] is a union of two containers, and
276 // we lose track of where these containers have been allocated
277 // at this point.
278 bailout('Stored in too many containers');
279 }
280 } else if (isValueAddedToMap(info)) {
281 MapTypeMask mask = info.receiver.type;
282 if (mask.allocationNode != null) {
283 MapTypeInformation map =
284 inferrer.types.allocatedMaps[mask.allocationNode];
285 mapsToAnalyze.add(map);
286 } else {
287 // The [MapTypeMask] is a union. See comment for
288 // [ContainerTypeMask] above.
289 bailout('Stored in too many maps');
290 }
291 } else if (isKeyAddedToMap(info)) {
292 // We do not track the use of keys from a map, so we have to bail.
293 bailout('Used as key in Map');
294 }
295
296 if (info.targetsIncludeNoSuchMethod &&
297 info.arguments != null &&
298 info.arguments.contains(currentUser)) {
299 bailout('Passed to noSuchMethod');
300 }
301
302 Iterable<Element> inferredTargetTypes = info.targets.map((element) {
303 return inferrer.types.getInferredTypeOf(element);
304 });
305 if (inferredTargetTypes.any((user) => user == currentUser)) {
306 addNewEscapeInformation(info);
307 }
308 }
309
310 /**
311 * Check whether element is the parameter of a list adding method.
312 * The definition of what a list adding method is has to stay in sync with
313 * [isAddedToContainer].
314 */
315 bool isParameterOfListAddingMethod(Element element) {
316 if (!element.isParameter) return false;
317 if (element.enclosingClass != compiler.backend.listImplementation) {
318 return false;
319 }
320 Element method = element.enclosingElement;
321 return (method.name == '[]=')
322 || (method.name == 'add')
323 || (method.name == 'insert');
324 }
325
326 /**
327 * Check whether element is the parameter of a list adding method.
328 * The definition of what a list adding method is has to stay in sync with
329 * [isValueAddedToMap] and [isKeyAddedToMap].
330 */
331 bool isParameterOfMapAddingMethod(Element element) {
332 if (!element.isParameter) return false;
333 if (element.enclosingClass != compiler.backend.mapImplementation) {
334 return false;
335 }
336 Element method = element.enclosingElement;
337 return (method.name == '[]=');
338 }
339
340 bool isClosure(Element element) {
341 if (!element.isFunction) return false;
342 /// Creating an instance of a class that implements [Function] also
343 /// closurizes the corresponding [call] member. We do not currently
344 /// track these, thus the check for [isClosurized] on such a method will
345 /// return false. Instead we catch that case here for now.
346 // TODO(herhut): Handle creation of closures from instances of Function.
347 if (element.isInstanceMember &&
348 element.name == Compiler.CALL_OPERATOR_NAME) {
349 return true;
350 }
351 Element outermost = element.outermostEnclosingMemberOrTopLevel;
352 return outermost.declaration != element.declaration;
353 }
354
355 void visitMemberTypeInformation(MemberTypeInformation info) {
356 Element element = info.element;
357 if (info.isClosurized) {
358 bailout('Returned from a closurized method');
359 }
360 if (isClosure(info.element)) {
361 bailout('Returned from a closure');
362 }
363 if (!inferrer.compiler.backend
364 .canBeUsedForGlobalOptimizations(info.element)) {
365 bailout('Escape to code that has special backend treatment');
366 }
367 addNewEscapeInformation(info);
368 }
369
370 void visitParameterTypeInformation(ParameterTypeInformation info) {
371 ParameterElement element = info.element;
372 if (inferrer.isNativeElement(element.functionDeclaration)) {
373 bailout('Passed to a native method');
374 }
375 if (!inferrer.compiler.backend
376 .canBeUsedForGlobalOptimizations(info.element)) {
377 bailout('Escape to code that has special backend treatment');
378 }
379 if (isParameterOfListAddingMethod(info.element) ||
380 isParameterOfMapAddingMethod(info.element)) {
381 // These elements are being handled in
382 // [visitDynamicCallSiteTypeInformation].
383 return;
384 }
385 addNewEscapeInformation(info);
386 }
387 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698