OLD | NEW |
---|---|
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.md file. | 3 // BSD-style license that can be found in the LICENSE.md file. |
4 | 4 |
5 import 'package:front_end/src/base/instrumentation.dart'; | 5 import 'package:front_end/src/base/instrumentation.dart'; |
6 import 'package:front_end/src/fasta/errors.dart'; | |
6 import 'package:front_end/src/fasta/kernel/kernel_shadow_ast.dart'; | 7 import 'package:front_end/src/fasta/kernel/kernel_shadow_ast.dart'; |
7 import 'package:front_end/src/fasta/names.dart' show callName; | 8 import 'package:front_end/src/fasta/names.dart' |
9 show callName, indexGetName, indexSetName; | |
8 import 'package:front_end/src/fasta/type_inference/type_inference_engine.dart'; | 10 import 'package:front_end/src/fasta/type_inference/type_inference_engine.dart'; |
9 import 'package:front_end/src/fasta/type_inference/type_inference_listener.dart' ; | 11 import 'package:front_end/src/fasta/type_inference/type_inference_listener.dart' ; |
10 import 'package:front_end/src/fasta/type_inference/type_promotion.dart'; | 12 import 'package:front_end/src/fasta/type_inference/type_promotion.dart'; |
11 import 'package:front_end/src/fasta/type_inference/type_schema.dart'; | 13 import 'package:front_end/src/fasta/type_inference/type_schema.dart'; |
12 import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart' ; | 14 import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart' ; |
13 import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart' ; | 15 import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart' ; |
14 import 'package:kernel/ast.dart' | 16 import 'package:kernel/ast.dart' |
15 show | 17 show |
16 Arguments, | 18 Arguments, |
17 AsyncMarker, | 19 AsyncMarker, |
18 BottomType, | 20 BottomType, |
19 Class, | 21 Class, |
20 DartType, | 22 DartType, |
21 DynamicType, | 23 DynamicType, |
22 Expression, | 24 Expression, |
23 Field, | 25 Field, |
24 FunctionType, | 26 FunctionType, |
25 Initializer, | 27 Initializer, |
26 InterfaceType, | 28 InterfaceType, |
29 Let, | |
27 Member, | 30 Member, |
31 MethodInvocation, | |
28 Name, | 32 Name, |
29 Procedure, | 33 Procedure, |
30 ProcedureKind, | 34 ProcedureKind, |
31 Statement, | 35 Statement, |
32 TypeParameterType, | 36 TypeParameterType, |
37 VariableDeclaration, | |
38 VariableGet, | |
33 VoidType; | 39 VoidType; |
34 import 'package:kernel/class_hierarchy.dart'; | 40 import 'package:kernel/class_hierarchy.dart'; |
35 import 'package:kernel/core_types.dart'; | 41 import 'package:kernel/core_types.dart'; |
36 import 'package:kernel/type_algebra.dart'; | 42 import 'package:kernel/type_algebra.dart'; |
37 | 43 |
38 /// Keeps track of information about the innermost function or closure being | 44 /// Keeps track of information about the innermost function or closure being |
39 /// inferred. | 45 /// inferred. |
40 class ClosureContext { | 46 class ClosureContext { |
41 final bool isAsync; | 47 final bool isAsync; |
42 | 48 |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
229 strongMode = engine.strongMode, | 235 strongMode = engine.strongMode, |
230 classHierarchy = engine.classHierarchy, | 236 classHierarchy = engine.classHierarchy, |
231 instrumentation = topLevel ? null : engine.instrumentation, | 237 instrumentation = topLevel ? null : engine.instrumentation, |
232 typeSchemaEnvironment = engine.typeSchemaEnvironment, | 238 typeSchemaEnvironment = engine.typeSchemaEnvironment, |
233 isTopLevel = topLevel; | 239 isTopLevel = topLevel; |
234 | 240 |
235 /// Gets the type promoter that should be used to promote types during | 241 /// Gets the type promoter that should be used to promote types during |
236 /// inference. | 242 /// inference. |
237 TypePromoter get typePromoter; | 243 TypePromoter get typePromoter; |
238 | 244 |
245 /// Finds a member of [receiverType] called [name], and if it is found, | |
246 /// reports it through instrumentation using [fileOffset]. | |
247 Member findInterfaceMember(DartType receiverType, Name name, int fileOffset, | |
248 {bool setter: false, bool silent: false}) { | |
249 // Our non-strong golden files currently don't include interface | |
250 // targets, so we can't store the interface target without causing tests | |
251 // to fail. TODO(paulberry): fix this. | |
ahe
2017/06/08 14:16:33
As far as I understand, interface targets are for
Paul Berry
2017/06/08 16:17:04
Acknowledged.
| |
252 if (!strongMode) return null; | |
253 | |
254 if (receiverType is InterfaceType) { | |
255 var interfaceMember = classHierarchy | |
256 .getInterfaceMember(receiverType.classNode, name, setter: setter); | |
257 if (!silent && interfaceMember != null) { | |
258 instrumentation?.record(Uri.parse(uri), fileOffset, 'target', | |
259 new InstrumentationValueForMember(interfaceMember)); | |
260 } | |
261 return interfaceMember; | |
262 } | |
263 return null; | |
264 } | |
265 | |
266 /// Finds a member of [receiverType] called [name], and if it is found, | |
267 /// reports it through instrumentation and records it in [methodInvocation]. | |
268 Member findMethodInvocationMember( | |
269 DartType receiverType, MethodInvocation methodInvocation, | |
270 {bool silent: false}) { | |
271 var interfaceMember = findInterfaceMember( | |
272 receiverType, methodInvocation.name, methodInvocation.fileOffset, | |
273 silent: silent); | |
274 // interfaceTarget is currently required to be a procedure, so we skip | |
275 // if it's anything else. TODO(paulberry): fix this - see | |
276 // https://codereview.chromium.org/2923653003/. | |
277 if (interfaceMember is Procedure) { | |
278 methodInvocation.interfaceTarget = interfaceMember; | |
279 } | |
280 return interfaceMember; | |
281 } | |
282 | |
239 FunctionType getCalleeFunctionType(Member interfaceMember, | 283 FunctionType getCalleeFunctionType(Member interfaceMember, |
240 DartType receiverType, Name methodName, bool followCall) { | 284 DartType receiverType, Name methodName, bool followCall) { |
241 var type = getCalleeType(interfaceMember, receiverType, methodName); | 285 var type = getCalleeType(interfaceMember, receiverType, methodName); |
242 if (type is FunctionType) { | 286 if (type is FunctionType) { |
243 return type; | 287 return type; |
244 } else if (followCall && type is InterfaceType) { | 288 } else if (followCall && type is InterfaceType) { |
245 var member = classHierarchy.getInterfaceMember(type.classNode, callName); | 289 var member = classHierarchy.getInterfaceMember(type.classNode, callName); |
246 var callType = member?.getterType; | 290 var callType = member?.getterType; |
247 if (callType is FunctionType) { | 291 if (callType is FunctionType) { |
248 return callType; | 292 return callType; |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
404 | 448 |
405 @override | 449 @override |
406 void inferFunctionBody( | 450 void inferFunctionBody( |
407 DartType returnType, AsyncMarker asyncMarker, Statement body) { | 451 DartType returnType, AsyncMarker asyncMarker, Statement body) { |
408 assert(closureContext == null); | 452 assert(closureContext == null); |
409 closureContext = new ClosureContext(this, asyncMarker, returnType); | 453 closureContext = new ClosureContext(this, asyncMarker, returnType); |
410 inferStatement(body); | 454 inferStatement(body); |
411 closureContext = null; | 455 closureContext = null; |
412 } | 456 } |
413 | 457 |
458 /// Performs type inference for a (possibly compound) assignment whose LHS is | |
459 /// an index expression. | |
460 DartType inferIndexAssign( | |
461 Expression expression, DartType typeContext, bool typeNeeded) { | |
462 typeNeeded = | |
463 listener.indexAssignEnter(expression, typeContext) || typeNeeded; | |
464 | |
465 // Decompose an expression like `a[b] += c` into subexpressions and record | |
466 // the variables holding those subexpressions (if any): | |
467 // receiver: a | |
468 // index: b | |
469 // read: a[b] | |
470 // rhs: c | |
471 // value: a[b] + c | |
472 // write: a[b] = a[b] + c | |
473 Expression receiver; | |
474 VariableDeclaration receiverVariable; | |
475 Expression index; | |
476 VariableDeclaration indexVariable; | |
477 Expression read; | |
478 VariableDeclaration readVariable; | |
479 Expression rhs; | |
480 VariableDeclaration rhsVariable; | |
481 Expression value; | |
482 VariableDeclaration valueVariable; | |
483 MethodInvocation write; | |
484 KernelConditionalExpression nullAwareCombiner; | |
485 MethodInvocation combiner; | |
486 List<VariableDeclaration> syntheticVariables = []; | |
487 VariableGet finalValue; | |
488 bool isPostIncDec = false; | |
489 | |
490 // Dig in to the tree to find the expression with the main effect. | |
491 Expression e = expression; | |
492 while (true) { | |
493 if (e is Let) { | |
494 Let let = e; | |
495 var variable = let.variable; | |
496 syntheticVariables.add(variable); | |
497 if (KernelVariableDeclaration.isDiscarding(variable)) { | |
498 finalValue = let.body; | |
499 e = variable.initializer; | |
ahe
2017/06/08 14:16:33
I don't understand this. Isn't [e] supposed to be
Paul Berry
2017/06/08 16:17:04
Again considering the example of `var x = a[b] = c
| |
500 } else { | |
501 e = let.body; | |
502 } | |
503 } else if (e is KernelConditionalExpression && | |
504 KernelConditionalExpression.isNullAwareCombiner(e)) { | |
505 KernelConditionalExpression conditional = e; | |
506 nullAwareCombiner = conditional; | |
507 e = conditional.then; | |
508 } else { | |
509 break; | |
510 } | |
511 } | |
512 | |
513 Expression deref(Expression e, void callback(VariableDeclaration v)) { | |
514 if (e is VariableGet) { | |
515 var variable = e.variable; | |
516 if (syntheticVariables.contains(variable)) { | |
517 callback(variable); | |
518 return variable.initializer; | |
519 } | |
520 } | |
521 return e; | |
522 } | |
523 | |
524 if (e is KernelMethodInvocation && identical(e.name.name, '[]=')) { | |
525 write = e; | |
526 receiver = deref(e.receiver, (v) => receiverVariable = v); | |
527 index = deref(e.arguments.positional[0], (v) => indexVariable = v); | |
528 value = deref(e.arguments.positional[1], (v) => valueVariable = v); | |
529 if (value is KernelMethodInvocation && | |
530 KernelMethodInvocation.isCombiner(value)) { | |
531 combiner = value; | |
532 read = deref(value.receiver, (v) => readVariable = v); | |
533 rhs = deref(value.arguments.positional[0], (v) => rhsVariable = v); | |
534 isPostIncDec = | |
535 finalValue is VariableGet && finalValue.variable == readVariable; | |
536 } else { | |
537 rhs = value; | |
538 if (nullAwareCombiner != null) { | |
539 MethodInvocation equalsNullCheck = nullAwareCombiner.condition; | |
540 read = deref(equalsNullCheck.receiver, (v) => readVariable = v); | |
541 } | |
542 } | |
543 } else { | |
544 internalError('Unexpected expression type: $e'); | |
545 } | |
546 | |
547 var receiverType = inferExpression(receiver, null, true); | |
548 receiverVariable?.type = receiverType; | |
549 if (read != null) { | |
550 var readMember = | |
551 findMethodInvocationMember(receiverType, read, silent: true); | |
552 if (readVariable != null) { | |
553 var calleeType = getCalleeType(readMember, receiverType, indexGetName); | |
554 if (calleeType is FunctionType) { | |
555 readVariable.type = calleeType.returnType; | |
556 } | |
557 } | |
558 } | |
559 var writeMember = findMethodInvocationMember(receiverType, write); | |
560 // To replicate analyzer behavior, we base type inference on the write | |
561 // member. TODO(paulberry): would it be better to use the read member | |
562 // when doing compound assignment? | |
563 var calleeType = getCalleeType(writeMember, receiverType, indexSetName); | |
564 DartType indexContext; | |
565 DartType rhsContext; | |
566 DartType inferredType; | |
567 if (calleeType is FunctionType && | |
568 calleeType.positionalParameters.length >= 2) { | |
569 // TODO(paulberry): we ought to get a context for the index expression | |
570 // from the index formal parameter, but analyzer doesn't so for now we | |
571 // replicate its behavior. | |
572 indexContext = null; | |
573 rhsContext = calleeType.positionalParameters[1]; | |
574 if (combiner != null) { | |
575 var combinerMember = | |
576 findMethodInvocationMember(rhsContext, combiner, silent: true); | |
577 if (isPostIncDec) { | |
578 inferredType = rhsContext; | |
579 } else { | |
580 inferredType = getCalleeFunctionType( | |
581 combinerMember, rhsContext, combiner.name, false) | |
582 .returnType; | |
583 } | |
584 // Analyzer uses a null context for the RHS here. | |
585 // TODO(paulberry): improve on this. | |
586 rhsContext = null; | |
587 } else { | |
588 inferredType = rhsContext; | |
589 } | |
590 } else { | |
591 inferredType = const DynamicType(); | |
592 } | |
593 var indexType = inferExpression(index, indexContext, true); | |
594 indexVariable?.type = indexType; | |
595 var rhsType = inferExpression(rhs, rhsContext, true); | |
596 rhsVariable?.type = rhsType; | |
597 valueVariable?.type = rhsContext ?? const DynamicType(); | |
598 if (nullAwareCombiner != null) { | |
599 MethodInvocation equalsInvocation = nullAwareCombiner.condition; | |
600 findMethodInvocationMember(rhsContext, equalsInvocation); | |
601 nullAwareCombiner.staticType = inferredType; | |
602 } | |
603 listener.indexAssignExit(expression, inferredType); | |
604 return inferredType; | |
605 } | |
606 | |
414 /// Performs the type inference steps that are shared by all kinds of | 607 /// Performs the type inference steps that are shared by all kinds of |
415 /// invocations (constructors, instance methods, and static methods). | 608 /// invocations (constructors, instance methods, and static methods). |
416 DartType inferInvocation(DartType typeContext, bool typeNeeded, int offset, | 609 DartType inferInvocation(DartType typeContext, bool typeNeeded, int offset, |
417 FunctionType calleeType, DartType returnType, Arguments arguments, | 610 FunctionType calleeType, DartType returnType, Arguments arguments, |
418 {bool isOverloadedArithmeticOperator: false, | 611 {bool isOverloadedArithmeticOperator: false, |
419 DartType receiverType, | 612 DartType receiverType, |
420 bool skipTypeArgumentInference: false}) { | 613 bool skipTypeArgumentInference: false}) { |
421 var calleeTypeParameters = calleeType.typeParameters; | 614 var calleeTypeParameters = calleeType.typeParameters; |
422 List<DartType> explicitTypeArguments = getExplicitTypeArguments(arguments); | 615 List<DartType> explicitTypeArguments = getExplicitTypeArguments(arguments); |
423 bool inferenceNeeded = !skipTypeArgumentInference && | 616 bool inferenceNeeded = !skipTypeArgumentInference && |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
558 void _forEachArgument( | 751 void _forEachArgument( |
559 Arguments arguments, void callback(String name, Expression expression)) { | 752 Arguments arguments, void callback(String name, Expression expression)) { |
560 for (var expression in arguments.positional) { | 753 for (var expression in arguments.positional) { |
561 callback(null, expression); | 754 callback(null, expression); |
562 } | 755 } |
563 for (var namedExpression in arguments.named) { | 756 for (var namedExpression in arguments.named) { |
564 callback(namedExpression.name, namedExpression.value); | 757 callback(namedExpression.name, namedExpression.value); |
565 } | 758 } |
566 } | 759 } |
567 } | 760 } |
OLD | NEW |