Chromium Code Reviews| 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 |