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

Side by Side Diff: pkg/compiler/lib/src/inferrer/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
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 import 'package:kernel/ast.dart' as ir; 5 import 'package:kernel/ast.dart' as ir;
6 6
7 import '../common.dart'; 7 import '../common.dart';
8 import '../common/names.dart'; 8 import '../common/names.dart';
9 import '../compiler.dart'; 9 import '../compiler.dart';
10 import '../constants/expressions.dart';
11 import '../constants/values.dart';
12 import '../common_elements.dart'; 10 import '../common_elements.dart';
13 import '../elements/elements.dart'; 11 import '../elements/elements.dart'
12 show
13 ClassElement,
14 ConstructorElement,
15 Elements,
16 MemberElement,
17 ParameterElement;
14 import '../elements/entities.dart'; 18 import '../elements/entities.dart';
15 import '../elements/names.dart'; 19 import '../elements/names.dart';
16 import '../js_backend/annotations.dart'; 20 import '../js_backend/annotations.dart';
17 import '../js_backend/js_backend.dart'; 21 import '../js_backend/js_backend.dart';
18 import '../native/behavior.dart' as native; 22 import '../native/behavior.dart' as native;
19 import '../resolution/tree_elements.dart';
20 import '../tree/nodes.dart' as ast;
21 import '../types/constants.dart';
22 import '../types/types.dart'; 23 import '../types/types.dart';
23 import '../universe/call_structure.dart'; 24 import '../universe/call_structure.dart';
24 import '../universe/selector.dart'; 25 import '../universe/selector.dart';
25 import '../universe/side_effects.dart'; 26 import '../universe/side_effects.dart';
26 import '../util/util.dart';
27 import '../world.dart'; 27 import '../world.dart';
28 import 'closure_tracer.dart';
29 import 'debug.dart' as debug; 28 import 'debug.dart' as debug;
30 import 'locals_handler.dart'; 29 import 'locals_handler.dart';
31 import 'list_tracer.dart'; 30 import 'list_tracer.dart';
32 import 'map_tracer.dart'; 31 import 'map_tracer.dart';
33 import 'builder.dart'; 32 import 'builder.dart';
34 import 'builder_kernel.dart';
35 import 'type_graph_dump.dart';
36 import 'type_graph_inferrer.dart'; 33 import 'type_graph_inferrer.dart';
37 import 'type_graph_nodes.dart'; 34 import 'type_graph_nodes.dart';
38 import 'type_system.dart'; 35 import 'type_system.dart';
39 36
40 /// An inferencing engine that computes a call graph of [TypeInformation] nodes 37 /// An inferencing engine that computes a call graph of [TypeInformation] nodes
41 /// by visiting the AST of the application, and then does the inferencing on the 38 /// by visiting the AST of the application, and then does the inferencing on the
42 /// graph. 39 /// graph.
43 abstract class InferrerEngine { 40 abstract class InferrerEngine<T> {
44 /// A set of selector names that [List] implements, that we know return their 41 /// A set of selector names that [List] implements, that we know return their
45 /// element type. 42 /// element type.
46 final Set<Selector> returnsListElementTypeSet = 43 final Set<Selector> returnsListElementTypeSet =
47 new Set<Selector>.from(<Selector>[ 44 new Set<Selector>.from(<Selector>[
48 new Selector.getter(const PublicName('first')), 45 new Selector.getter(const PublicName('first')),
49 new Selector.getter(const PublicName('last')), 46 new Selector.getter(const PublicName('last')),
50 new Selector.getter(const PublicName('single')), 47 new Selector.getter(const PublicName('single')),
51 new Selector.call(const PublicName('singleWhere'), CallStructure.ONE_ARG), 48 new Selector.call(const PublicName('singleWhere'), CallStructure.ONE_ARG),
52 new Selector.call(const PublicName('elementAt'), CallStructure.ONE_ARG), 49 new Selector.call(const PublicName('elementAt'), CallStructure.ONE_ARG),
53 new Selector.index(), 50 new Selector.index(),
54 new Selector.call(const PublicName('removeAt'), CallStructure.ONE_ARG), 51 new Selector.call(const PublicName('removeAt'), CallStructure.ONE_ARG),
55 new Selector.call(const PublicName('removeLast'), CallStructure.NO_ARGS) 52 new Selector.call(const PublicName('removeLast'), CallStructure.NO_ARGS)
56 ]); 53 ]);
57 54
58 Compiler get compiler; 55 Compiler get compiler;
59 ClosedWorld get closedWorld; 56 ClosedWorld get closedWorld;
60 ClosedWorldRefiner get closedWorldRefiner; 57 ClosedWorldRefiner get closedWorldRefiner;
61 JavaScriptBackend get backend => compiler.backend; 58 JavaScriptBackend get backend => compiler.backend;
62 OptimizerHintsForTests get optimizerHints => backend.optimizerHints; 59 OptimizerHintsForTests get optimizerHints => backend.optimizerHints;
63 DiagnosticReporter get reporter => compiler.reporter; 60 DiagnosticReporter get reporter => compiler.reporter;
64 CommonMasks get commonMasks => closedWorld.commonMasks; 61 CommonMasks get commonMasks => closedWorld.commonMasks;
65 CommonElements get commonElements => closedWorld.commonElements; 62 CommonElements get commonElements => closedWorld.commonElements;
66 63
67 TypeSystem<ast.Node> get types; 64 TypeSystem<T> get types;
68 Map<ast.Node, TypeInformation> get concreteTypes; 65 Map<T, TypeInformation> get concreteTypes;
69 66
70 /// Parallel structure for concreteTypes. 67 /// Parallel structure for concreteTypes.
71 // TODO(efortuna): Remove concreteTypes and/or parameterize InferrerEngine by 68 // TODO(efortuna): Remove concreteTypes and/or parameterize InferrerEngine by
72 // ir.Node or ast.Node type. Then remove this in favor of `concreteTypes`. 69 // ir.Node or ast.Node type. Then remove this in favor of `concreteTypes`.
73 Map<ir.Node, TypeInformation> get concreteKernelTypes; 70 Map<ir.Node, TypeInformation> get concreteKernelTypes;
74 71
75 FunctionEntity get mainElement; 72 FunctionEntity get mainElement;
76 73
77 void runOverAllElements(); 74 void runOverAllElements();
78 75
79 void analyze(ResolvedAst resolvedAst, ArgumentsTypes arguments); 76 void analyze(MemberEntity member, T node, ArgumentsTypes arguments);
80 void analyzeListAndEnqueue(ListTypeInformation info); 77 void analyzeListAndEnqueue(ListTypeInformation info);
81 void analyzeMapAndEnqueue(MapTypeInformation info); 78 void analyzeMapAndEnqueue(MapTypeInformation info);
82 79
83 /// Notifies to the inferrer that [analyzedElement] can have return type 80 /// Notifies to the inferrer that [analyzedElement] can have return type
84 /// [newType]. [currentType] is the type the [ElementGraphBuilder] currently 81 /// [newType]. [currentType] is the type the [ElementGraphBuilder] currently
85 /// found. 82 /// found.
86 /// 83 ///
87 /// Returns the new type for [analyzedElement]. 84 /// Returns the new type for [analyzedElement].
88 TypeInformation addReturnTypeForMethod( 85 TypeInformation addReturnTypeForMethod(
89 FunctionEntity element, TypeInformation unused, TypeInformation newType); 86 FunctionEntity element, TypeInformation unused, TypeInformation newType);
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 void recordExposesThis(ConstructorEntity element, bool exposesThis); 126 void recordExposesThis(ConstructorEntity element, bool exposesThis);
130 127
131 /// Records that the return type [element] is of type [type]. 128 /// Records that the return type [element] is of type [type].
132 void recordReturnType(FunctionEntity element, TypeInformation type); 129 void recordReturnType(FunctionEntity element, TypeInformation type);
133 130
134 /// Records that [element] is of type [type]. 131 /// Records that [element] is of type [type].
135 void recordTypeOfField(FieldEntity element, TypeInformation type); 132 void recordTypeOfField(FieldEntity element, TypeInformation type);
136 133
137 /// Registers a call to await with an expression of type [argumentType] as 134 /// Registers a call to await with an expression of type [argumentType] as
138 /// argument. 135 /// argument.
139 TypeInformation registerAwait(ast.Node node, TypeInformation argument); 136 TypeInformation registerAwait(T node, TypeInformation argument);
140 137
141 /// Registers a call to yield with an expression of type [argumentType] as 138 /// Registers a call to yield with an expression of type [argumentType] as
142 /// argument. 139 /// argument.
143 TypeInformation registerYield(ast.Node node, TypeInformation argument); 140 TypeInformation registerYield(T node, TypeInformation argument);
144 141
145 /// Registers that [caller] calls [closure] with [arguments]. 142 /// Registers that [caller] calls [closure] with [arguments].
146 /// 143 ///
147 /// [sideEffects] will be updated to incorporate the potential callees' side 144 /// [sideEffects] will be updated to incorporate the potential callees' side
148 /// effects. 145 /// effects.
149 /// 146 ///
150 /// [inLoop] tells whether the call happens in a loop. 147 /// [inLoop] tells whether the call happens in a loop.
151 TypeInformation registerCalledClosure( 148 TypeInformation registerCalledClosure(
152 ast.Node node, 149 T node,
153 Selector selector, 150 Selector selector,
154 TypeMask mask, 151 TypeMask mask,
155 TypeInformation closure, 152 TypeInformation closure,
156 MemberEntity caller, 153 MemberEntity caller,
157 ArgumentsTypes arguments, 154 ArgumentsTypes arguments,
158 SideEffects sideEffects, 155 SideEffects sideEffects,
159 bool inLoop); 156 bool inLoop);
160 157
161 /// Registers that [caller] calls [callee] at location [node], with 158 /// Registers that [caller] calls [callee] at location [node], with
162 /// [selector], and [arguments]. Note that [selector] is null for forwarding 159 /// [selector], and [arguments]. Note that [selector] is null for forwarding
(...skipping 14 matching lines...) Expand all
177 174
178 /// Registers that [caller] calls [selector] with [receiverType] as receiver, 175 /// Registers that [caller] calls [selector] with [receiverType] as receiver,
179 /// and [arguments]. 176 /// and [arguments].
180 /// 177 ///
181 /// [sideEffects] will be updated to incorporate the potential callees' side 178 /// [sideEffects] will be updated to incorporate the potential callees' side
182 /// effects. 179 /// effects.
183 /// 180 ///
184 /// [inLoop] tells whether the call happens in a loop. 181 /// [inLoop] tells whether the call happens in a loop.
185 TypeInformation registerCalledSelector( 182 TypeInformation registerCalledSelector(
186 CallType callType, 183 CallType callType,
187 ast.Node node, 184 T node,
188 Selector selector, 185 Selector selector,
189 TypeMask mask, 186 TypeMask mask,
190 TypeInformation receiverType, 187 TypeInformation receiverType,
191 MemberEntity caller, 188 MemberEntity caller,
192 ArgumentsTypes arguments, 189 ArgumentsTypes arguments,
193 SideEffects sideEffects, 190 SideEffects sideEffects,
194 bool inLoop, 191 bool inLoop,
195 bool isConditional); 192 bool isConditional);
196 193
197 /// Update the assignments to parameters in the graph. [remove] tells whether 194 /// Update the assignments to parameters in the graph. [remove] tells whether
198 /// assignments must be added or removed. If [init] is false, parameters are 195 /// assignments must be added or removed. If [init] is false, parameters are
199 /// added to the work queue. 196 /// added to the work queue.
200 void updateParameterAssignments(TypeInformation caller, MemberEntity callee, 197 void updateParameterAssignments(TypeInformation caller, MemberEntity callee,
201 ArgumentsTypes arguments, Selector selector, TypeMask mask, 198 ArgumentsTypes arguments, Selector selector, TypeMask mask,
202 {bool remove, bool addToQueue: true}); 199 {bool remove, bool addToQueue: true});
203 200
204 void updateSelectorInMember(MemberEntity owner, CallType callType, 201 void updateSelectorInMember(MemberEntity owner, CallType callType, T node,
205 ast.Node node, Selector selector, TypeMask mask); 202 Selector selector, TypeMask mask);
206 203
207 /// Returns the return type of [element]. 204 /// Returns the return type of [element].
208 TypeInformation returnTypeOfMember(MemberEntity element); 205 TypeInformation returnTypeOfMember(MemberEntity element);
209 206
210 /// Returns the type of [element] when being called with [selector]. 207 /// Returns the type of [element] when being called with [selector].
211 TypeInformation typeOfMemberWithSelector( 208 TypeInformation typeOfMemberWithSelector(
212 MemberEntity element, Selector selector); 209 MemberEntity element, Selector selector);
213 210
214 /// Returns the type of [element]. 211 /// Returns the type of [element].
215 TypeInformation typeOfMember(MemberEntity element); 212 TypeInformation typeOfMember(MemberEntity element);
216 213
217 /// Returns the type of [element]. 214 /// Returns the type of [element].
218 TypeInformation typeOfParameter(Local element); 215 TypeInformation typeOfParameter(Local element);
219 216
220 /// Returns the type for [nativeBehavior]. See documentation on 217 /// Returns the type for [nativeBehavior]. See documentation on
221 /// [native.NativeBehavior]. 218 /// [native.NativeBehavior].
222 TypeInformation typeOfNativeBehavior(native.NativeBehavior nativeBehavior); 219 TypeInformation typeOfNativeBehavior(native.NativeBehavior nativeBehavior);
223 220
224 bool returnsListElementType(Selector selector, TypeMask mask); 221 bool returnsListElementType(Selector selector, TypeMask mask);
225 222
226 bool returnsMapValueType(Selector selector, TypeMask mask); 223 bool returnsMapValueType(Selector selector, TypeMask mask);
227 224
228 void clear(); 225 void clear();
229 } 226 }
230 227
231 class InferrerEngineImpl extends InferrerEngine { 228 abstract class InferrerEngineImpl<T> extends InferrerEngine<T> {
232 final Map<Local, TypeInformation> defaultTypeOfParameter = 229 final Map<Local, TypeInformation> defaultTypeOfParameter =
233 new Map<Local, TypeInformation>(); 230 new Map<Local, TypeInformation>();
234 final WorkQueue workQueue = new WorkQueue(); 231 final WorkQueue workQueue = new WorkQueue();
235 final FunctionEntity mainElement; 232 final FunctionEntity mainElement;
236 final Set<MemberEntity> analyzedElements = new Set<MemberEntity>(); 233 final Set<MemberEntity> analyzedElements = new Set<MemberEntity>();
237 234
238 /// The maximum number of times we allow a node in the graph to 235 /// The maximum number of times we allow a node in the graph to
239 /// change types. If a node reaches that limit, we give up 236 /// change types. If a node reaches that limit, we give up
240 /// inferencing on it and give it the dynamic type. 237 /// inferencing on it and give it the dynamic type.
241 final int MAX_CHANGE_COUNT = 6; 238 final int MAX_CHANGE_COUNT = 6;
242 239
243 int overallRefineCount = 0; 240 int overallRefineCount = 0;
244 int addedInGraph = 0; 241 int addedInGraph = 0;
245 242
246 final Compiler compiler; 243 final Compiler compiler;
247 244
248 /// The [ClosedWorld] on which inference reasoning is based. 245 /// The [ClosedWorld] on which inference reasoning is based.
249 final ClosedWorld closedWorld; 246 final ClosedWorld closedWorld;
250 247
251 final ClosedWorldRefiner closedWorldRefiner; 248 final ClosedWorldRefiner closedWorldRefiner;
252 final TypeSystem<ast.Node> types; 249 final TypeSystem<T> types;
253 final Map<ast.Node, TypeInformation> concreteTypes = 250 final Map<T, TypeInformation> concreteTypes = new Map<T, TypeInformation>();
254 new Map<ast.Node, TypeInformation>();
255 251
256 final Map<ir.Node, TypeInformation> concreteKernelTypes = 252 final Map<ir.Node, TypeInformation> concreteKernelTypes =
257 new Map<ir.Node, TypeInformation>(); 253 new Map<ir.Node, TypeInformation>();
258 final Set<ConstructorEntity> generativeConstructorsExposingThis = 254 final Set<ConstructorEntity> generativeConstructorsExposingThis =
259 new Set<ConstructorEntity>(); 255 new Set<ConstructorEntity>();
260 256
261 /// Data computed internally within elements, like the type-mask of a send a 257 /// Data computed internally within elements, like the type-mask of a send a
262 /// list allocation, or a for-in loop. 258 /// list allocation, or a for-in loop.
263 final Map<MemberEntity, GlobalTypeInferenceElementData> _memberData = 259 final Map<MemberEntity, GlobalTypeInferenceElementData> _memberData =
264 new Map<MemberEntity, GlobalTypeInferenceElementData>(); 260 new Map<MemberEntity, GlobalTypeInferenceElementData>();
265 261
266 InferrerEngineImpl(this.compiler, ClosedWorld closedWorld, 262 InferrerEngineImpl(
267 this.closedWorldRefiner, this.mainElement) 263 this.compiler,
268 : this.types = new TypeSystem<ast.Node>( 264 ClosedWorld closedWorld,
269 closedWorld, const TypeSystemStrategyImpl()), 265 this.closedWorldRefiner,
266 this.mainElement,
267 TypeSystemStrategy<T> typeSystemStrategy)
268 : this.types = new TypeSystem<T>(closedWorld, typeSystemStrategy),
270 this.closedWorld = closedWorld; 269 this.closedWorld = closedWorld;
271 270
272 void forEachElementMatching( 271 void forEachElementMatching(
273 Selector selector, TypeMask mask, bool f(MemberEntity element)) { 272 Selector selector, TypeMask mask, bool f(MemberEntity element)) {
274 Iterable<MemberEntity> elements = closedWorld.locateMembers(selector, mask); 273 Iterable<MemberEntity> elements = closedWorld.locateMembers(selector, mask);
275 for (MemberEntity e in elements) { 274 for (MemberEntity e in elements) {
276 if (!f(e)) return; 275 if (!f(e)) return;
277 } 276 }
278 } 277 }
279 278
279 GlobalTypeInferenceElementData<T> createElementData();
280
280 // TODO(johnniwinther): Make this private again. 281 // TODO(johnniwinther): Make this private again.
281 GlobalTypeInferenceElementData dataOfMember(MemberEntity element) => 282 GlobalTypeInferenceElementData<T> dataOfMember(MemberEntity element) =>
282 _memberData.putIfAbsent( 283 _memberData.putIfAbsent(element, createElementData);
283 element, () => new GlobalTypeInferenceElementData());
284 284
285 GlobalTypeInferenceElementData lookupDataOfMember(MemberEntity element) => 285 GlobalTypeInferenceElementData<T> lookupDataOfMember(MemberEntity element) =>
286 _memberData[element]; 286 _memberData[element];
287 287
288 /** 288 /**
289 * Update [sideEffects] with the side effects of [callee] being 289 * Update [sideEffects] with the side effects of [callee] being
290 * called with [selector]. 290 * called with [selector].
291 */ 291 */
292 void updateSideEffects( 292 void updateSideEffects(
293 SideEffects sideEffects, Selector selector, MemberEntity callee) { 293 SideEffects sideEffects, Selector selector, MemberEntity callee) {
294 assert(!(callee is MemberElement && !callee.isDeclaration)); 294 assert(!(callee is MemberElement && !callee.isDeclaration));
295 if (callee.isField) { 295 if (callee.isField) {
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
351 mappedType = types.nonNullSubtype(type.element); 351 mappedType = types.nonNullSubtype(type.element);
352 } 352 }
353 returnType = types.computeLUB(returnType, mappedType); 353 returnType = types.computeLUB(returnType, mappedType);
354 if (returnType == types.dynamicType) { 354 if (returnType == types.dynamicType) {
355 break; 355 break;
356 } 356 }
357 } 357 }
358 return returnType; 358 return returnType;
359 } 359 }
360 360
361 void updateSelectorInMember(MemberEntity owner, CallType callType, 361 void updateSelectorInMember(MemberEntity owner, CallType callType, T node,
362 ast.Node node, Selector selector, TypeMask mask) { 362 Selector selector, TypeMask mask) {
363 GlobalTypeInferenceElementData data = dataOfMember(owner); 363 GlobalTypeInferenceElementData data = dataOfMember(owner);
364 assert(validCallType(callType, node)); 364 assert(validCallType(callType, node));
365 switch (callType) { 365 switch (callType) {
366 case CallType.complex: 366 case CallType.complex:
367 if (selector.isSetter || selector.isIndexSet) { 367 if (selector.isSetter || selector.isIndexSet) {
368 data.setTypeMask(node, mask); 368 data.setTypeMask(node, mask);
369 } else if (selector.isGetter || selector.isIndex) { 369 } else if (selector.isGetter || selector.isIndex) {
370 data.setGetterTypeMaskInComplexSendSet(node, mask); 370 data.setGetterTypeMaskInComplexSendSet(node, mask);
371 } else { 371 } else {
372 assert(selector.isOperator); 372 assert(selector.isOperator);
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
449 workQueue.addAll(info.addMapAssignment(map)); 449 workQueue.addAll(info.addMapAssignment(map));
450 } 450 }
451 451
452 info.markAsInferred(); 452 info.markAsInferred();
453 workQueue.add(info.keyType); 453 workQueue.add(info.keyType);
454 workQueue.add(info.valueType); 454 workQueue.add(info.valueType);
455 workQueue.addAll(info.typeInfoMap.values); 455 workQueue.addAll(info.typeInfoMap.values);
456 workQueue.add(info); 456 workQueue.add(info);
457 } 457 }
458 458
459 void runOverAllElements() { 459 void runOverAllElements();
460 if (compiler.disableTypeInference) return;
461 if (compiler.options.verbose) {
462 compiler.progress.reset();
463 }
464 sortResolvedAsts().forEach((ResolvedAst resolvedAst) {
465 if (compiler.shouldPrintProgress) {
466 reporter.log('Added $addedInGraph elements in inferencing graph.');
467 compiler.progress.reset();
468 }
469 // This also forces the creation of the [ElementTypeInformation] to ensure
470 // it is in the graph.
471 MemberElement member = resolvedAst.element;
472 types.withMember(member, () => analyze(resolvedAst, null));
473 });
474 reporter.log('Added $addedInGraph elements in inferencing graph.');
475 460
476 TypeGraphDump dump = debug.PRINT_GRAPH ? new TypeGraphDump(this) : null; 461 void analyze(MemberEntity element, T body, ArgumentsTypes arguments);
477
478 dump?.beforeAnalysis();
479 buildWorkQueue();
480 refine();
481
482 // Try to infer element types of lists and compute their escape information.
483 types.allocatedLists.values.forEach((TypeInformation info) {
484 analyzeListAndEnqueue(info);
485 });
486
487 // Try to infer the key and value types for maps and compute the values'
488 // escape information.
489 types.allocatedMaps.values.forEach((TypeInformation info) {
490 analyzeMapAndEnqueue(info);
491 });
492
493 Set<FunctionEntity> bailedOutOn = new Set<FunctionEntity>();
494
495 // Trace closures to potentially infer argument types.
496 types.allocatedClosures.forEach((dynamic info) {
497 void trace(
498 Iterable<FunctionEntity> elements, ClosureTracerVisitor tracer) {
499 tracer.run();
500 if (!tracer.continueAnalyzing) {
501 elements.forEach((FunctionEntity _element) {
502 MethodElement element = _element;
503 MethodElement implementation = element.implementation;
504 closedWorldRefiner.registerMightBePassedToApply(element);
505 if (debug.VERBOSE) {
506 print("traced closure $element as ${true} (bail)");
507 }
508 implementation.functionSignature
509 .forEachParameter((FormalElement _parameter) {
510 ParameterElement parameter = _parameter;
511 types
512 .getInferredTypeOfParameter(parameter)
513 .giveUp(this, clearAssignments: false);
514 });
515 });
516 bailedOutOn.addAll(elements);
517 return;
518 }
519 elements
520 .where((e) => !bailedOutOn.contains(e))
521 .forEach((FunctionEntity _element) {
522 MethodElement element = _element;
523 MethodElement implementation = element.implementation;
524 implementation.functionSignature
525 .forEachParameter((FormalElement _parameter) {
526 ParameterElement parameter = _parameter;
527 ParameterTypeInformation info =
528 types.getInferredTypeOfParameter(parameter);
529 info.maybeResume();
530 workQueue.add(info);
531 });
532 if (tracer.tracedType.mightBePassedToFunctionApply) {
533 closedWorldRefiner.registerMightBePassedToApply(element);
534 }
535 if (debug.VERBOSE) {
536 print("traced closure $element as "
537 "${closedWorldRefiner
538 .getCurrentlyKnownMightBePassedToApply(element)}");
539 }
540 });
541 }
542
543 if (info is ClosureTypeInformation) {
544 Iterable<FunctionEntity> elements = [info.closure];
545 trace(elements, new ClosureTracerVisitor(elements, info, this));
546 } else if (info is CallSiteTypeInformation) {
547 if (info is StaticCallSiteTypeInformation &&
548 info.selector != null &&
549 info.selector.isCall) {
550 // This is a constructor call to a class with a call method. So we
551 // need to trace the call method here.
552 MethodElement calledElement = info.calledElement;
553 assert(calledElement.isGenerativeConstructor);
554 ClassElement cls = calledElement.enclosingClass;
555 MethodElement callMethod = cls.lookupMember(Identifiers.call);
556 if (callMethod == null) {
557 callMethod = cls.lookupMember(Identifiers.noSuchMethod_);
558 }
559 assert(callMethod != null, failedAt(cls));
560 Iterable<FunctionEntity> elements = [callMethod];
561 trace(elements, new ClosureTracerVisitor(elements, info, this));
562 } else {
563 // We only are interested in functions here, as other targets
564 // of this closure call are not a root to trace but an intermediate
565 // for some other function.
566 Iterable<FunctionEntity> elements = new List<FunctionEntity>.from(
567 info.callees.where((e) => e.isFunction));
568 trace(elements, new ClosureTracerVisitor(elements, info, this));
569 }
570 } else if (info is MemberTypeInformation) {
571 trace(<FunctionEntity>[info.member],
572 new StaticTearOffClosureTracerVisitor(info.member, info, this));
573 } else if (info is ParameterTypeInformation) {
574 failedAt(
575 NO_LOCATION_SPANNABLE, 'Unexpected closure allocation info $info');
576 }
577 });
578
579 dump?.beforeTracing();
580
581 // Reset all nodes that use lists/maps that have been inferred, as well
582 // as nodes that use elements fetched from these lists/maps. The
583 // workset for a new run of the analysis will be these nodes.
584 Set<TypeInformation> seenTypes = new Set<TypeInformation>();
585 while (!workQueue.isEmpty) {
586 TypeInformation info = workQueue.remove();
587 if (seenTypes.contains(info)) continue;
588 // If the node cannot be reset, we do not need to update its users either.
589 if (!info.reset(this)) continue;
590 seenTypes.add(info);
591 workQueue.addAll(info.users);
592 }
593
594 workQueue.addAll(seenTypes);
595 refine();
596
597 if (debug.PRINT_SUMMARY) {
598 types.allocatedLists.values.forEach((_info) {
599 ListTypeInformation info = _info;
600 print('${info.type} '
601 'for ${info.originalType.allocationNode} '
602 'at ${info.originalType.allocationElement} '
603 'after ${info.refineCount}');
604 });
605 types.allocatedMaps.values.forEach((_info) {
606 MapTypeInformation info = _info;
607 print('${info.type} '
608 'for ${info.originalType.allocationNode} '
609 'at ${info.originalType.allocationElement} '
610 'after ${info.refineCount}');
611 });
612 types.allocatedClosures.forEach((TypeInformation info) {
613 if (info is ElementTypeInformation) {
614 print('${info.getInferredSignature(types)} for '
615 '${info.debugName}');
616 } else if (info is ClosureTypeInformation) {
617 print('${info.getInferredSignature(types)} for '
618 '${info.debugName}');
619 } else if (info is DynamicCallSiteTypeInformation) {
620 for (MemberEntity target in info.targets) {
621 if (target is FunctionEntity) {
622 print(
623 '${types.getInferredSignatureOfMethod(target)} for ${target}') ;
624 } else {
625 print(
626 '${types.getInferredTypeOfMember(target).type} for ${target}') ;
627 }
628 }
629 } else if (info is StaticCallSiteTypeInformation) {
630 ClassElement cls = info.calledElement.enclosingClass;
631 MethodElement callMethod = cls.lookupMember(Identifiers.call);
632 print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}');
633 } else {
634 print('${info.type} for some unknown kind of closure');
635 }
636 });
637 analyzedElements.forEach((MemberEntity elem) {
638 TypeInformation type = types.getInferredTypeOfMember(elem);
639 print('${elem} :: ${type} from ${type.assignments} ');
640 });
641 }
642 dump?.afterAnalysis();
643
644 reporter.log('Inferred $overallRefineCount types.');
645
646 processLoopInformation();
647 }
648
649 void analyze(ResolvedAst resolvedAst, ArgumentsTypes arguments) {
650 MemberElement element = resolvedAst.element;
651 if (analyzedElements.contains(element)) return;
652 analyzedElements.add(element);
653
654 dynamic visitor = compiler.options.kernelGlobalInference
655 ? new KernelTypeGraphBuilder(element, resolvedAst, compiler, this)
656 : new ElementGraphBuilder(element, resolvedAst, compiler, this);
657 TypeInformation type;
658 reporter.withCurrentElement(element, () {
659 // ignore: UNDEFINED_METHOD
660 type = visitor.run();
661 });
662 addedInGraph++;
663
664 if (element.isField) {
665 FieldElement field = element;
666 ast.Node initializer = resolvedAst.body;
667 if (field.isFinal || field.isConst) {
668 // If [element] is final and has an initializer, we record
669 // the inferred type.
670 if (resolvedAst.body != null) {
671 if (type is! ListTypeInformation && type is! MapTypeInformation) {
672 // For non-container types, the constant handler does
673 // constant folding that could give more precise results.
674 ConstantExpression constant = field.constant;
675 if (constant != null) {
676 ConstantValue value =
677 compiler.backend.constants.getConstantValue(constant);
678 if (value != null) {
679 if (value.isFunction) {
680 FunctionConstantValue functionConstant = value;
681 MethodElement function = functionConstant.element;
682 type = types.allocateClosure(function);
683 } else {
684 // Although we might find a better type, we have to keep
685 // the old type around to ensure that we get a complete view
686 // of the type graph and do not drop any flow edges.
687 TypeMask refinedType = computeTypeMask(closedWorld, value);
688 assert(TypeMask.assertIsNormalized(refinedType, closedWorld));
689 type = new NarrowTypeInformation(type, refinedType);
690 types.allocatedTypes.add(type);
691 }
692 } else {
693 assert(
694 field.isInstanceMember ||
695 constant.isImplicit ||
696 constant.isPotential,
697 failedAt(
698 field,
699 "Constant expression without value: "
700 "${constant.toStructuredText()}."));
701 }
702 }
703 }
704 recordTypeOfField(field, type);
705 } else if (!element.isInstanceMember) {
706 recordTypeOfField(field, types.nullType);
707 }
708 } else if (initializer == null) {
709 // Only update types of static fields if there is no
710 // assignment. Instance fields are dealt with in the constructor.
711 if (Elements.isStaticOrTopLevelField(element)) {
712 recordTypeOfField(field, type);
713 }
714 } else {
715 recordTypeOfField(field, type);
716 }
717 if (Elements.isStaticOrTopLevelField(field) &&
718 resolvedAst.body != null &&
719 !element.isConst) {
720 dynamic argument = resolvedAst.body;
721 // TODO(13429): We could do better here by using the
722 // constant handler to figure out if it's a lazy field or not.
723 if (argument.asSend() != null ||
724 (argument.asNewExpression() != null && !argument.isConst)) {
725 recordTypeOfField(field, types.nullType);
726 }
727 }
728 } else {
729 MethodElement method = element;
730 recordReturnType(method, type);
731 }
732 }
733 462
734 void processLoopInformation() { 463 void processLoopInformation() {
735 types.allocatedCalls.forEach((dynamic info) { 464 types.allocatedCalls.forEach((dynamic info) {
736 if (!info.inLoop) return; 465 if (!info.inLoop) return;
737 if (info is StaticCallSiteTypeInformation) { 466 if (info is StaticCallSiteTypeInformation) {
738 MemberEntity member = info.calledElement; 467 MemberEntity member = info.calledElement;
739 closedWorldRefiner.addFunctionCalledInLoop(member); 468 closedWorldRefiner.addFunctionCalledInLoop(member);
740 } else if (info.mask != null && !info.mask.containsAll(closedWorld)) { 469 } else if (info.mask != null && !info.mask.containsAll(closedWorld)) {
741 // For instance methods, we only register a selector called in a 470 // For instance methods, we only register a selector called in a
742 // loop if it is a typed selector, to avoid marking too many 471 // loop if it is a typed selector, to avoid marking too many
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
782 511
783 void buildWorkQueue() { 512 void buildWorkQueue() {
784 workQueue.addAll(types.orderedTypeInformations); 513 workQueue.addAll(types.orderedTypeInformations);
785 workQueue.addAll(types.allocatedTypes); 514 workQueue.addAll(types.allocatedTypes);
786 workQueue.addAll(types.allocatedClosures); 515 workQueue.addAll(types.allocatedClosures);
787 workQueue.addAll(types.allocatedCalls); 516 workQueue.addAll(types.allocatedCalls);
788 } 517 }
789 518
790 void updateParameterAssignments(TypeInformation caller, MemberEntity callee, 519 void updateParameterAssignments(TypeInformation caller, MemberEntity callee,
791 ArgumentsTypes arguments, Selector selector, TypeMask mask, 520 ArgumentsTypes arguments, Selector selector, TypeMask mask,
792 {bool remove, bool addToQueue: true}) { 521 {bool remove, bool addToQueue: true});
793 if (callee.name == Identifiers.noSuchMethod_) return;
794 if (callee.isField) {
795 if (selector.isSetter) {
796 ElementTypeInformation info = types.getInferredTypeOfMember(callee);
797 if (remove) {
798 info.removeAssignment(arguments.positional[0]);
799 } else {
800 info.addAssignment(arguments.positional[0]);
801 }
802 if (addToQueue) workQueue.add(info);
803 }
804 } else if (callee.isGetter) {
805 return;
806 } else if (selector != null && selector.isGetter) {
807 // We are tearing a function off and thus create a closure.
808 assert(callee.isFunction);
809 MethodElement method = callee;
810 MemberTypeInformation info = types.getInferredTypeOfMember(method);
811 if (remove) {
812 info.closurizedCount--;
813 } else {
814 info.closurizedCount++;
815 if (Elements.isStaticOrTopLevel(method)) {
816 types.allocatedClosures.add(info);
817 } else {
818 // We add the call-site type information here so that we
819 // can benefit from further refinement of the selector.
820 types.allocatedClosures.add(caller);
821 }
822 FunctionElement function = method.implementation;
823 FunctionSignature signature = function.functionSignature;
824 signature.forEachParameter((FormalElement _parameter) {
825 ParameterElement parameter = _parameter;
826 ParameterTypeInformation info =
827 types.getInferredTypeOfParameter(parameter);
828 info.tagAsTearOffClosureParameter(this);
829 if (addToQueue) workQueue.add(info);
830 });
831 }
832 } else {
833 MethodElement method = callee;
834 FunctionElement function = method.implementation;
835 FunctionSignature signature = function.functionSignature;
836 int parameterIndex = 0;
837 bool visitingRequiredParameter = true;
838 signature.forEachParameter((FormalElement _parameter) {
839 ParameterElement parameter = _parameter;
840 if (signature.hasOptionalParameters &&
841 parameter == signature.optionalParameters.first) {
842 visitingRequiredParameter = false;
843 }
844 TypeInformation type = visitingRequiredParameter
845 ? arguments.positional[parameterIndex]
846 : signature.optionalParametersAreNamed
847 ? arguments.named[parameter.name]
848 : parameterIndex < arguments.positional.length
849 ? arguments.positional[parameterIndex]
850 : null;
851 if (type == null) type = getDefaultTypeOfParameter(parameter);
852 TypeInformation info = types.getInferredTypeOfParameter(parameter);
853 if (remove) {
854 info.removeAssignment(type);
855 } else {
856 info.addAssignment(type);
857 }
858 parameterIndex++;
859 if (addToQueue) workQueue.add(info);
860 });
861 }
862 }
863 522
864 void setDefaultTypeOfParameter(Local parameter, TypeInformation type, 523 void setDefaultTypeOfParameter(Local parameter, TypeInformation type,
865 {bool isInstanceMember}) { 524 {bool isInstanceMember}) {
866 assert(!(parameter is ParameterElement && !parameter.isImplementation)); 525 assert(!(parameter is ParameterElement && !parameter.isImplementation));
867 TypeInformation existing = defaultTypeOfParameter[parameter]; 526 TypeInformation existing = defaultTypeOfParameter[parameter];
868 defaultTypeOfParameter[parameter] = type; 527 defaultTypeOfParameter[parameter] = type;
869 TypeInformation info = types.getInferredTypeOfParameter(parameter); 528 TypeInformation info = types.getInferredTypeOfParameter(parameter);
870 if (existing != null && existing is PlaceholderTypeInformation) { 529 if (existing != null && existing is PlaceholderTypeInformation) {
871 // Replace references to [existing] to use [type] instead. 530 // Replace references to [existing] to use [type] instead.
872 if (isInstanceMember) { 531 if (isInstanceMember) {
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
975 } 634 }
976 } 635 }
977 info.addToGraph(this); 636 info.addToGraph(this);
978 types.allocatedCalls.add(info); 637 types.allocatedCalls.add(info);
979 updateSideEffects(sideEffects, selector, callee); 638 updateSideEffects(sideEffects, selector, callee);
980 return info; 639 return info;
981 } 640 }
982 641
983 TypeInformation registerCalledSelector( 642 TypeInformation registerCalledSelector(
984 CallType callType, 643 CallType callType,
985 ast.Node node, 644 T node,
986 Selector selector, 645 Selector selector,
987 TypeMask mask, 646 TypeMask mask,
988 TypeInformation receiverType, 647 TypeInformation receiverType,
989 MemberEntity caller, 648 MemberEntity caller,
990 ArgumentsTypes arguments, 649 ArgumentsTypes arguments,
991 SideEffects sideEffects, 650 SideEffects sideEffects,
992 bool inLoop, 651 bool inLoop,
993 bool isConditional) { 652 bool isConditional) {
994 if (selector.isClosureCall) { 653 if (selector.isClosureCall) {
995 return registerCalledClosure(node, selector, mask, receiverType, caller, 654 return registerCalledClosure(node, selector, mask, receiverType, caller,
(...skipping 14 matching lines...) Expand all
1010 receiverType, 669 receiverType,
1011 arguments, 670 arguments,
1012 inLoop, 671 inLoop,
1013 isConditional); 672 isConditional);
1014 673
1015 info.addToGraph(this); 674 info.addToGraph(this);
1016 types.allocatedCalls.add(info); 675 types.allocatedCalls.add(info);
1017 return info; 676 return info;
1018 } 677 }
1019 678
1020 TypeInformation registerAwait(ast.Node node, TypeInformation argument) { 679 TypeInformation registerAwait(T node, TypeInformation argument) {
1021 AwaitTypeInformation info = 680 AwaitTypeInformation info =
1022 new AwaitTypeInformation<ast.Node>(types.currentMember, node); 681 new AwaitTypeInformation<T>(types.currentMember, node);
1023 info.addAssignment(argument); 682 info.addAssignment(argument);
1024 types.allocatedTypes.add(info); 683 types.allocatedTypes.add(info);
1025 return info; 684 return info;
1026 } 685 }
1027 686
1028 TypeInformation registerYield(ast.Node node, TypeInformation argument) { 687 TypeInformation registerYield(T node, TypeInformation argument) {
1029 YieldTypeInformation info = 688 YieldTypeInformation info =
1030 new YieldTypeInformation<ast.Node>(types.currentMember, node); 689 new YieldTypeInformation<T>(types.currentMember, node);
1031 info.addAssignment(argument); 690 info.addAssignment(argument);
1032 types.allocatedTypes.add(info); 691 types.allocatedTypes.add(info);
1033 return info; 692 return info;
1034 } 693 }
1035 694
1036 TypeInformation registerCalledClosure( 695 TypeInformation registerCalledClosure(
1037 ast.Node node, 696 T node,
1038 Selector selector, 697 Selector selector,
1039 TypeMask mask, 698 TypeMask mask,
1040 TypeInformation closure, 699 TypeInformation closure,
1041 MemberEntity caller, 700 MemberEntity caller,
1042 ArgumentsTypes arguments, 701 ArgumentsTypes arguments,
1043 SideEffects sideEffects, 702 SideEffects sideEffects,
1044 bool inLoop) { 703 bool inLoop) {
1045 sideEffects.setDependsOnSomething(); 704 sideEffects.setDependsOnSomething();
1046 sideEffects.setAllSideEffects(); 705 sideEffects.setAllSideEffects();
1047 CallSiteTypeInformation info = new ClosureCallSiteTypeInformation( 706 CallSiteTypeInformation info = new ClosureCallSiteTypeInformation(
1048 types.currentMember, 707 types.currentMember,
1049 node, 708 node,
1050 caller, 709 caller,
1051 selector, 710 selector,
1052 mask, 711 mask,
1053 closure, 712 closure,
1054 arguments, 713 arguments,
1055 inLoop); 714 inLoop);
1056 info.addToGraph(this); 715 info.addToGraph(this);
1057 types.allocatedCalls.add(info); 716 types.allocatedCalls.add(info);
1058 return info; 717 return info;
1059 } 718 }
1060 719
1061 // Sorts the resolved elements by size. We do this for this inferrer
1062 // to get the same results for [ListTracer] compared to the
1063 // [SimpleTypesInferrer].
1064 Iterable<ResolvedAst> sortResolvedAsts() {
1065 int max = 0;
1066 Map<int, Setlet<ResolvedAst>> methodSizes = <int, Setlet<ResolvedAst>>{};
1067 compiler.enqueuer.resolution.processedEntities.forEach((_element) {
1068 MemberElement element = _element;
1069 ResolvedAst resolvedAst = element.resolvedAst;
1070 element = element.implementation;
1071 if (element.impliesType) return;
1072 assert(
1073 element.isField ||
1074 element.isFunction ||
1075 element.isConstructor ||
1076 element.isGetter ||
1077 element.isSetter,
1078 failedAt(element, 'Unexpected element kind: ${element.kind}'));
1079 if (element.isAbstract) return;
1080 // Put the other operators in buckets by length, later to be added in
1081 // length order.
1082 int length = 0;
1083 if (resolvedAst.kind == ResolvedAstKind.PARSED) {
1084 TreeElementMapping mapping = resolvedAst.elements;
1085 length = mapping.getSelectorCount();
1086 }
1087 max = length > max ? length : max;
1088 Setlet<ResolvedAst> set =
1089 methodSizes.putIfAbsent(length, () => new Setlet<ResolvedAst>());
1090 set.add(resolvedAst);
1091 });
1092
1093 List<ResolvedAst> result = <ResolvedAst>[];
1094 for (int i = 0; i <= max; i++) {
1095 Setlet<ResolvedAst> set = methodSizes[i];
1096 if (set != null) result.addAll(set);
1097 }
1098 return result;
1099 }
1100
1101 void clear() { 720 void clear() {
1102 void cleanup(TypeInformation info) => info.cleanup(); 721 void cleanup(TypeInformation info) => info.cleanup();
1103 722
1104 types.allocatedCalls.forEach(cleanup); 723 types.allocatedCalls.forEach(cleanup);
1105 types.allocatedCalls.clear(); 724 types.allocatedCalls.clear();
1106 725
1107 defaultTypeOfParameter.clear(); 726 defaultTypeOfParameter.clear();
1108 727
1109 types.parameterTypeInformations.values.forEach(cleanup); 728 types.parameterTypeInformations.values.forEach(cleanup);
1110 types.memberTypeInformations.values.forEach(cleanup); 729 types.memberTypeInformations.values.forEach(cleanup);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
1155 return returnTypeOfMember(element); 774 return returnTypeOfMember(element);
1156 } 775 }
1157 } else if (element.isGetter || element.isField) { 776 } else if (element.isGetter || element.isField) {
1158 assert(selector.isCall || selector.isSetter); 777 assert(selector.isCall || selector.isSetter);
1159 return types.dynamicType; 778 return types.dynamicType;
1160 } else { 779 } else {
1161 return returnTypeOfMember(element); 780 return returnTypeOfMember(element);
1162 } 781 }
1163 } 782 }
1164 } 783 }
1165
1166 class TypeSystemStrategyImpl implements TypeSystemStrategy<ast.Node> {
1167 const TypeSystemStrategyImpl();
1168
1169 @override
1170 MemberTypeInformation createMemberTypeInformation(
1171 covariant MemberElement member) {
1172 assert(member.isDeclaration, failedAt(member));
1173 if (member.isField) {
1174 FieldElement field = member;
1175 return new FieldTypeInformation(field, field.type);
1176 } else if (member.isGetter) {
1177 GetterElement getter = member;
1178 return new GetterTypeInformation(getter, getter.type);
1179 } else if (member.isSetter) {
1180 SetterElement setter = member;
1181 return new SetterTypeInformation(setter);
1182 } else if (member.isFunction) {
1183 MethodElement method = member;
1184 return new MethodTypeInformation(method, method.type);
1185 } else {
1186 ConstructorElement constructor = member;
1187 if (constructor.isFactoryConstructor) {
1188 return new FactoryConstructorTypeInformation(
1189 constructor, constructor.type);
1190 } else {
1191 return new GenerativeConstructorTypeInformation(constructor);
1192 }
1193 }
1194 }
1195
1196 @override
1197 ParameterTypeInformation createParameterTypeInformation(
1198 covariant ParameterElement parameter, TypeSystem<ast.Node> types) {
1199 assert(parameter.isImplementation, failedAt(parameter));
1200 FunctionTypedElement function = parameter.functionDeclaration.declaration;
1201 if (function.isLocal) {
1202 LocalFunctionElement localFunction = function;
1203 MethodElement callMethod = localFunction.callMethod;
1204 return new ParameterTypeInformation.localFunction(
1205 types.getInferredTypeOfMember(callMethod),
1206 parameter,
1207 parameter.type,
1208 callMethod);
1209 } else if (function.isInstanceMember) {
1210 MethodElement method = function;
1211 return new ParameterTypeInformation.instanceMember(
1212 types.getInferredTypeOfMember(method),
1213 parameter,
1214 parameter.type,
1215 method,
1216 new ParameterAssignments());
1217 } else {
1218 MethodElement method = function;
1219 return new ParameterTypeInformation.static(
1220 types.getInferredTypeOfMember(method),
1221 parameter,
1222 parameter.type,
1223 method,
1224 // TODO(johnniwinther): Is this still valid now that initializing
1225 // formals also introduce locals?
1226 isInitializingFormal: parameter.isInitializingFormal);
1227 }
1228 }
1229
1230 @override
1231 void forEachParameter(
1232 covariant MethodElement function, void f(Local parameter)) {
1233 MethodElement impl = function.implementation;
1234 FunctionSignature signature = impl.functionSignature;
1235 signature.forEachParameter((FormalElement _parameter) {
1236 ParameterElement parameter = _parameter;
1237 f(parameter);
1238 });
1239 }
1240
1241 @override
1242 bool checkMapNode(ast.Node node) {
1243 return node is ast.LiteralMap;
1244 }
1245
1246 @override
1247 bool checkListNode(ast.Node node) {
1248 return node is ast.LiteralList || node is ast.Send;
1249 }
1250
1251 @override
1252 bool checkLoopPhiNode(ast.Node node) {
1253 return node is ast.Loop || node is ast.SwitchStatement;
1254 }
1255
1256 @override
1257 bool checkPhiNode(ast.Node node) {
1258 return true;
1259 }
1260
1261 @override
1262 bool checkClassEntity(covariant ClassElement cls) {
1263 return cls.isDeclaration;
1264 }
1265 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/inferrer/builder_kernel.dart ('k') | pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698