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

Side by Side Diff: pkg/compiler/lib/src/inferrer/ast_inferrer_engine.dart

Issue 2991313002: Split ast based parts from InferrerEngineImpl (Closed)
Patch Set: Created 3 years, 4 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
« no previous file with comments | « no previous file | pkg/compiler/lib/src/inferrer/builder.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 import '../common.dart';
6 import '../common/names.dart';
7 import '../compiler.dart';
8 import '../constants/expressions.dart';
9 import '../constants/values.dart';
10 import '../elements/elements.dart';
11 import '../elements/entities.dart';
12 import '../resolution/tree_elements.dart';
13 import '../tree/nodes.dart' as ast;
14 import '../types/constants.dart';
15 import '../types/types.dart';
16 import '../universe/selector.dart';
17 import '../util/util.dart';
18 import '../world.dart';
19 import 'closure_tracer.dart';
20 import 'debug.dart' as debug;
21 import 'locals_handler.dart';
22 import 'builder.dart';
23 import 'builder_kernel.dart';
24 import 'inferrer_engine.dart';
25 import 'type_graph_dump.dart';
26 import 'type_graph_nodes.dart';
27 import 'type_system.dart';
28
29 class AstInferrerEngine extends InferrerEngineImpl<ast.Node> {
30 AstInferrerEngine(Compiler compiler, ClosedWorld closedWorld,
31 ClosedWorldRefiner closedWorldRefiner, FunctionEntity mainElement)
32 : super(compiler, closedWorld, closedWorldRefiner, mainElement,
33 const TypeSystemStrategyImpl());
34
35 GlobalTypeInferenceElementData<ast.Node> createElementData() =>
36 new AstGlobalTypeInferenceElementData();
37
38 void runOverAllElements() {
39 if (compiler.disableTypeInference) return;
40 if (compiler.options.verbose) {
41 compiler.progress.reset();
42 }
43 sortResolvedAsts().forEach((ResolvedAst resolvedAst) {
44 if (compiler.shouldPrintProgress) {
45 reporter.log('Added $addedInGraph elements in inferencing graph.');
46 compiler.progress.reset();
47 }
48 // This also forces the creation of the [ElementTypeInformation] to ensure
49 // it is in the graph.
50 MemberElement member = resolvedAst.element;
51 ast.Node body;
52 if (resolvedAst.kind == ResolvedAstKind.PARSED) {
53 body = resolvedAst.body;
54 }
55 types.withMember(member, () => analyze(member, body, null));
56 });
57 reporter.log('Added $addedInGraph elements in inferencing graph.');
58
59 TypeGraphDump dump = debug.PRINT_GRAPH ? new TypeGraphDump(this) : null;
60
61 dump?.beforeAnalysis();
62 buildWorkQueue();
63 refine();
64
65 // Try to infer element types of lists and compute their escape information.
66 types.allocatedLists.values.forEach((TypeInformation info) {
67 analyzeListAndEnqueue(info);
68 });
69
70 // Try to infer the key and value types for maps and compute the values'
71 // escape information.
72 types.allocatedMaps.values.forEach((TypeInformation info) {
73 analyzeMapAndEnqueue(info);
74 });
75
76 Set<FunctionEntity> bailedOutOn = new Set<FunctionEntity>();
77
78 // Trace closures to potentially infer argument types.
79 types.allocatedClosures.forEach((dynamic info) {
80 void trace(
81 Iterable<FunctionEntity> elements, ClosureTracerVisitor tracer) {
82 tracer.run();
83 if (!tracer.continueAnalyzing) {
84 elements.forEach((FunctionEntity _element) {
85 MethodElement element = _element;
86 MethodElement implementation = element.implementation;
87 closedWorldRefiner.registerMightBePassedToApply(element);
88 if (debug.VERBOSE) {
89 print("traced closure $element as ${true} (bail)");
90 }
91 implementation.functionSignature
92 .forEachParameter((FormalElement _parameter) {
93 ParameterElement parameter = _parameter;
94 types
95 .getInferredTypeOfParameter(parameter)
96 .giveUp(this, clearAssignments: false);
97 });
98 });
99 bailedOutOn.addAll(elements);
100 return;
101 }
102 elements
103 .where((e) => !bailedOutOn.contains(e))
104 .forEach((FunctionEntity _element) {
105 MethodElement element = _element;
106 MethodElement implementation = element.implementation;
107 implementation.functionSignature
108 .forEachParameter((FormalElement _parameter) {
109 ParameterElement parameter = _parameter;
110 ParameterTypeInformation info =
111 types.getInferredTypeOfParameter(parameter);
112 info.maybeResume();
113 workQueue.add(info);
114 });
115 if (tracer.tracedType.mightBePassedToFunctionApply) {
116 closedWorldRefiner.registerMightBePassedToApply(element);
117 }
118 if (debug.VERBOSE) {
119 print("traced closure $element as "
120 "${closedWorldRefiner
121 .getCurrentlyKnownMightBePassedToApply(element)}");
122 }
123 });
124 }
125
126 if (info is ClosureTypeInformation) {
127 Iterable<FunctionEntity> elements = [info.closure];
128 trace(elements, new ClosureTracerVisitor(elements, info, this));
129 } else if (info is CallSiteTypeInformation) {
130 if (info is StaticCallSiteTypeInformation &&
131 info.selector != null &&
132 info.selector.isCall) {
133 // This is a constructor call to a class with a call method. So we
134 // need to trace the call method here.
135 MethodElement calledElement = info.calledElement;
136 assert(calledElement.isGenerativeConstructor);
137 ClassElement cls = calledElement.enclosingClass;
138 MethodElement callMethod = cls.lookupMember(Identifiers.call);
139 if (callMethod == null) {
140 callMethod = cls.lookupMember(Identifiers.noSuchMethod_);
141 }
142 assert(callMethod != null, failedAt(cls));
143 Iterable<FunctionEntity> elements = [callMethod];
144 trace(elements, new ClosureTracerVisitor(elements, info, this));
145 } else {
146 // We only are interested in functions here, as other targets
147 // of this closure call are not a root to trace but an intermediate
148 // for some other function.
149 Iterable<FunctionEntity> elements = new List<FunctionEntity>.from(
150 info.callees.where((e) => e.isFunction));
151 trace(elements, new ClosureTracerVisitor(elements, info, this));
152 }
153 } else if (info is MemberTypeInformation) {
154 trace(<FunctionEntity>[info.member],
155 new StaticTearOffClosureTracerVisitor(info.member, info, this));
156 } else if (info is ParameterTypeInformation) {
157 failedAt(
158 NO_LOCATION_SPANNABLE, 'Unexpected closure allocation info $info');
159 }
160 });
161
162 dump?.beforeTracing();
163
164 // Reset all nodes that use lists/maps that have been inferred, as well
165 // as nodes that use elements fetched from these lists/maps. The
166 // workset for a new run of the analysis will be these nodes.
167 Set<TypeInformation> seenTypes = new Set<TypeInformation>();
168 while (!workQueue.isEmpty) {
169 TypeInformation info = workQueue.remove();
170 if (seenTypes.contains(info)) continue;
171 // If the node cannot be reset, we do not need to update its users either.
172 if (!info.reset(this)) continue;
173 seenTypes.add(info);
174 workQueue.addAll(info.users);
175 }
176
177 workQueue.addAll(seenTypes);
178 refine();
179
180 if (debug.PRINT_SUMMARY) {
181 types.allocatedLists.values.forEach((_info) {
182 ListTypeInformation info = _info;
183 print('${info.type} '
184 'for ${info.originalType.allocationNode} '
185 'at ${info.originalType.allocationElement} '
186 'after ${info.refineCount}');
187 });
188 types.allocatedMaps.values.forEach((_info) {
189 MapTypeInformation info = _info;
190 print('${info.type} '
191 'for ${info.originalType.allocationNode} '
192 'at ${info.originalType.allocationElement} '
193 'after ${info.refineCount}');
194 });
195 types.allocatedClosures.forEach((TypeInformation info) {
196 if (info is ElementTypeInformation) {
197 print('${info.getInferredSignature(types)} for '
198 '${info.debugName}');
199 } else if (info is ClosureTypeInformation) {
200 print('${info.getInferredSignature(types)} for '
201 '${info.debugName}');
202 } else if (info is DynamicCallSiteTypeInformation) {
203 for (MemberEntity target in info.targets) {
204 if (target is FunctionEntity) {
205 print(
206 '${types.getInferredSignatureOfMethod(target)} for ${target}') ;
207 } else {
208 print(
209 '${types.getInferredTypeOfMember(target).type} for ${target}') ;
210 }
211 }
212 } else if (info is StaticCallSiteTypeInformation) {
213 ClassElement cls = info.calledElement.enclosingClass;
214 MethodElement callMethod = cls.lookupMember(Identifiers.call);
215 print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}');
216 } else {
217 print('${info.type} for some unknown kind of closure');
218 }
219 });
220 analyzedElements.forEach((MemberEntity elem) {
221 TypeInformation type = types.getInferredTypeOfMember(elem);
222 print('${elem} :: ${type} from ${type.assignments} ');
223 });
224 }
225 dump?.afterAnalysis();
226
227 reporter.log('Inferred $overallRefineCount types.');
228
229 processLoopInformation();
230 }
231
232 void analyze(MemberEntity element, ast.Node body, ArgumentsTypes arguments) {
233 assert(!(element is MemberElement && !element.isDeclaration));
234 if (analyzedElements.contains(element)) return;
235 analyzedElements.add(element);
236
237 dynamic visitor = compiler.options.kernelGlobalInference
238 ? new KernelTypeGraphBuilder(element, compiler, this)
239 : new ElementGraphBuilder(element, compiler, this);
240 TypeInformation type;
241 reporter.withCurrentElement(element, () {
242 // ignore: UNDEFINED_METHOD
243 type = visitor.run();
244 });
245 addedInGraph++;
246
247 if (element.isField) {
248 FieldElement field = element;
249 if (field.isFinal || field.isConst) {
250 // If [element] is final and has an initializer, we record
251 // the inferred type.
252 if (body != null) {
253 if (type is! ListTypeInformation && type is! MapTypeInformation) {
254 // For non-container types, the constant handler does
255 // constant folding that could give more precise results.
256 ConstantExpression constant = field.constant;
257 if (constant != null) {
258 ConstantValue value =
259 compiler.backend.constants.getConstantValue(constant);
260 if (value != null) {
261 if (value.isFunction) {
262 FunctionConstantValue functionConstant = value;
263 MethodElement function = functionConstant.element;
264 type = types.allocateClosure(function);
265 } else {
266 // Although we might find a better type, we have to keep
267 // the old type around to ensure that we get a complete view
268 // of the type graph and do not drop any flow edges.
269 TypeMask refinedType = computeTypeMask(closedWorld, value);
270 assert(TypeMask.assertIsNormalized(refinedType, closedWorld));
271 type = new NarrowTypeInformation(type, refinedType);
272 types.allocatedTypes.add(type);
273 }
274 } else {
275 assert(
276 field.isInstanceMember ||
277 constant.isImplicit ||
278 constant.isPotential,
279 failedAt(
280 field,
281 "Constant expression without value: "
282 "${constant.toStructuredText()}."));
283 }
284 }
285 }
286 recordTypeOfField(field, type);
287 } else if (!element.isInstanceMember) {
288 recordTypeOfField(field, types.nullType);
289 }
290 } else if (body == null) {
291 // Only update types of static fields if there is no
292 // assignment. Instance fields are dealt with in the constructor.
293 if (element.isStatic || element.isTopLevel) {
294 recordTypeOfField(field, type);
295 }
296 } else {
297 recordTypeOfField(field, type);
298 }
299 if ((element.isStatic || element.isTopLevel) &&
300 body != null &&
301 !element.isConst) {
302 dynamic argument = body;
303 // TODO(13429): We could do better here by using the
304 // constant handler to figure out if it's a lazy field or not.
305 if (argument.asSend() != null ||
306 (argument.asNewExpression() != null && !argument.isConst)) {
307 recordTypeOfField(field, types.nullType);
308 }
309 }
310 } else {
311 FunctionEntity method = element;
312 recordReturnType(method, type);
313 }
314 }
315
316 void updateParameterAssignments(TypeInformation caller, MemberEntity callee,
317 ArgumentsTypes arguments, Selector selector, TypeMask mask,
318 {bool remove, bool addToQueue: true}) {
319 if (callee.name == Identifiers.noSuchMethod_) return;
320 if (callee.isField) {
321 if (selector.isSetter) {
322 ElementTypeInformation info = types.getInferredTypeOfMember(callee);
323 if (remove) {
324 info.removeAssignment(arguments.positional[0]);
325 } else {
326 info.addAssignment(arguments.positional[0]);
327 }
328 if (addToQueue) workQueue.add(info);
329 }
330 } else if (callee.isGetter) {
331 return;
332 } else if (selector != null && selector.isGetter) {
333 // We are tearing a function off and thus create a closure.
334 assert(callee.isFunction);
335 MethodElement method = callee;
336 MemberTypeInformation info = types.getInferredTypeOfMember(method);
337 if (remove) {
338 info.closurizedCount--;
339 } else {
340 info.closurizedCount++;
341 if (Elements.isStaticOrTopLevel(method)) {
342 types.allocatedClosures.add(info);
343 } else {
344 // We add the call-site type information here so that we
345 // can benefit from further refinement of the selector.
346 types.allocatedClosures.add(caller);
347 }
348 FunctionElement function = method.implementation;
349 FunctionSignature signature = function.functionSignature;
350 signature.forEachParameter((FormalElement _parameter) {
351 ParameterElement parameter = _parameter;
352 ParameterTypeInformation info =
353 types.getInferredTypeOfParameter(parameter);
354 info.tagAsTearOffClosureParameter(this);
355 if (addToQueue) workQueue.add(info);
356 });
357 }
358 } else {
359 MethodElement method = callee;
360 FunctionElement function = method.implementation;
361 FunctionSignature signature = function.functionSignature;
362 int parameterIndex = 0;
363 bool visitingRequiredParameter = true;
364 signature.forEachParameter((FormalElement _parameter) {
365 ParameterElement parameter = _parameter;
366 if (signature.hasOptionalParameters &&
367 parameter == signature.optionalParameters.first) {
368 visitingRequiredParameter = false;
369 }
370 TypeInformation type = visitingRequiredParameter
371 ? arguments.positional[parameterIndex]
372 : signature.optionalParametersAreNamed
373 ? arguments.named[parameter.name]
374 : parameterIndex < arguments.positional.length
375 ? arguments.positional[parameterIndex]
376 : null;
377 if (type == null) type = getDefaultTypeOfParameter(parameter);
378 TypeInformation info = types.getInferredTypeOfParameter(parameter);
379 if (remove) {
380 info.removeAssignment(type);
381 } else {
382 info.addAssignment(type);
383 }
384 parameterIndex++;
385 if (addToQueue) workQueue.add(info);
386 });
387 }
388 }
389
390 // Sorts the resolved elements by size. We do this for this inferrer
391 // to get the same results for [ListTracer] compared to the
392 // [SimpleTypesInferrer].
393 Iterable<ResolvedAst> sortResolvedAsts() {
394 int max = 0;
395 Map<int, Setlet<ResolvedAst>> methodSizes = <int, Setlet<ResolvedAst>>{};
396 compiler.enqueuer.resolution.processedEntities.forEach((_element) {
397 MemberElement element = _element;
398 ResolvedAst resolvedAst = element.resolvedAst;
399 element = element.implementation;
400 if (element.impliesType) return;
401 assert(
402 element.isField ||
403 element.isFunction ||
404 element.isConstructor ||
405 element.isGetter ||
406 element.isSetter,
407 failedAt(element, 'Unexpected element kind: ${element.kind}'));
408 if (element.isAbstract) return;
409 // Put the other operators in buckets by length, later to be added in
410 // length order.
411 int length = 0;
412 if (resolvedAst.kind == ResolvedAstKind.PARSED) {
413 TreeElementMapping mapping = resolvedAst.elements;
414 length = mapping.getSelectorCount();
415 }
416 max = length > max ? length : max;
417 Setlet<ResolvedAst> set =
418 methodSizes.putIfAbsent(length, () => new Setlet<ResolvedAst>());
419 set.add(resolvedAst);
420 });
421
422 List<ResolvedAst> result = <ResolvedAst>[];
423 for (int i = 0; i <= max; i++) {
424 Setlet<ResolvedAst> set = methodSizes[i];
425 if (set != null) result.addAll(set);
426 }
427 return result;
428 }
429 }
430
431 class TypeSystemStrategyImpl implements TypeSystemStrategy<ast.Node> {
432 const TypeSystemStrategyImpl();
433
434 @override
435 MemberTypeInformation createMemberTypeInformation(
436 covariant MemberElement member) {
437 assert(member.isDeclaration, failedAt(member));
438 if (member.isField) {
439 FieldElement field = member;
440 return new FieldTypeInformation(field, field.type);
441 } else if (member.isGetter) {
442 GetterElement getter = member;
443 return new GetterTypeInformation(getter, getter.type);
444 } else if (member.isSetter) {
445 SetterElement setter = member;
446 return new SetterTypeInformation(setter);
447 } else if (member.isFunction) {
448 MethodElement method = member;
449 return new MethodTypeInformation(method, method.type);
450 } else {
451 ConstructorElement constructor = member;
452 if (constructor.isFactoryConstructor) {
453 return new FactoryConstructorTypeInformation(
454 constructor, constructor.type);
455 } else {
456 return new GenerativeConstructorTypeInformation(constructor);
457 }
458 }
459 }
460
461 @override
462 ParameterTypeInformation createParameterTypeInformation(
463 covariant ParameterElement parameter, TypeSystem<ast.Node> types) {
464 assert(parameter.isImplementation, failedAt(parameter));
465 FunctionTypedElement function = parameter.functionDeclaration.declaration;
466 if (function.isLocal) {
467 LocalFunctionElement localFunction = function;
468 MethodElement callMethod = localFunction.callMethod;
469 return new ParameterTypeInformation.localFunction(
470 types.getInferredTypeOfMember(callMethod),
471 parameter,
472 parameter.type,
473 callMethod);
474 } else if (function.isInstanceMember) {
475 MethodElement method = function;
476 return new ParameterTypeInformation.instanceMember(
477 types.getInferredTypeOfMember(method),
478 parameter,
479 parameter.type,
480 method,
481 new ParameterAssignments());
482 } else {
483 MethodElement method = function;
484 return new ParameterTypeInformation.static(
485 types.getInferredTypeOfMember(method),
486 parameter,
487 parameter.type,
488 method,
489 // TODO(johnniwinther): Is this still valid now that initializing
490 // formals also introduce locals?
491 isInitializingFormal: parameter.isInitializingFormal);
492 }
493 }
494
495 @override
496 void forEachParameter(
497 covariant MethodElement function, void f(Local parameter)) {
498 MethodElement impl = function.implementation;
499 FunctionSignature signature = impl.functionSignature;
500 signature.forEachParameter((FormalElement _parameter) {
501 ParameterElement parameter = _parameter;
502 f(parameter);
503 });
504 }
505
506 @override
507 bool checkMapNode(ast.Node node) {
508 return node is ast.LiteralMap;
509 }
510
511 @override
512 bool checkListNode(ast.Node node) {
513 return node is ast.LiteralList || node is ast.Send;
514 }
515
516 @override
517 bool checkLoopPhiNode(ast.Node node) {
518 return node is ast.Loop || node is ast.SwitchStatement;
519 }
520
521 @override
522 bool checkPhiNode(ast.Node node) {
523 return true;
524 }
525
526 @override
527 bool checkClassEntity(covariant ClassElement cls) {
528 return cls.isDeclaration;
529 }
530 }
OLDNEW
« no previous file with comments | « no previous file | pkg/compiler/lib/src/inferrer/builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698