| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| /// This file declares a "shadow hierarchy" of concrete classes which extend |
| /// the kernel class hierarchy, adding methods and fields needed by the |
| /// BodyBuilder. |
| /// |
| /// Instances of these classes may be created using the factory methods in |
| /// `ast_factory.dart`. |
| /// |
| /// Note that these classes represent the Dart language prior to desugaring. |
| /// When a single Dart construct desugars to a tree containing multiple kernel |
| /// AST nodes, the shadow class extends the kernel object at the top of the |
| /// desugared tree. |
| /// |
| /// This means that in some cases multiple shadow classes may extend the same |
| /// kernel class, because multiple constructs in Dart may desugar to a tree |
| /// with the same kind of root node. |
| import 'package:front_end/src/base/instrumentation.dart'; |
| import 'package:front_end/src/fasta/type_inference/dependency_collector.dart'; |
| import 'package:front_end/src/fasta/type_inference/type_inference_engine.dart'; |
| import 'package:front_end/src/fasta/type_inference/type_inference_listener.dart'; |
| import 'package:front_end/src/fasta/type_inference/type_inferrer.dart'; |
| import 'package:front_end/src/fasta/type_inference/type_promotion.dart'; |
| import 'package:front_end/src/fasta/type_inference/type_schema.dart'; |
| import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart'; |
| import 'package:kernel/ast.dart' |
| hide InvalidExpression, InvalidInitializer, InvalidStatement; |
| import 'package:kernel/frontend/accessors.dart'; |
| import 'package:kernel/type_environment.dart'; |
| |
| import '../problems.dart' show unhandled, unsupported; |
| |
| /// Computes the return type of a (possibly factory) constructor. |
| InterfaceType computeConstructorReturnType(Member constructor) { |
| if (constructor is Constructor) { |
| return constructor.enclosingClass.thisType; |
| } else { |
| return constructor.function.returnType; |
| } |
| } |
| |
| List<DartType> getExplicitTypeArguments(Arguments arguments) { |
| if (arguments is KernelArguments) { |
| return arguments._hasExplicitTypeArguments ? arguments.types : null; |
| } else { |
| // This code path should only be taken in situations where there are no |
| // type arguments at all, e.g. calling a user-definable operator. |
| assert(arguments.types.isEmpty); |
| return null; |
| } |
| } |
| |
| /// Concrete shadow object representing a set of invocation arguments. |
| class KernelArguments extends Arguments { |
| bool _hasExplicitTypeArguments; |
| |
| KernelArguments(List<Expression> positional, |
| {List<DartType> types, List<NamedExpression> named}) |
| : _hasExplicitTypeArguments = types != null && types.isNotEmpty, |
| super(positional, types: types, named: named); |
| |
| static void setExplicitArgumentTypes( |
| KernelArguments arguments, List<DartType> types) { |
| arguments.types.clear(); |
| arguments.types.addAll(types); |
| arguments._hasExplicitTypeArguments = true; |
| } |
| } |
| |
| /// Shadow object for [AsExpression]. |
| class KernelAsExpression extends AsExpression implements KernelExpression { |
| KernelAsExpression(Expression operand, DartType type) : super(operand, type); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.asExpressionEnter(this, typeContext) || typeNeeded; |
| inferrer.inferExpression(operand, null, false); |
| var inferredType = typeNeeded ? type : null; |
| inferrer.listener.asExpressionExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing an assertion statement in kernel form. |
| class KernelAssertStatement extends AssertStatement implements KernelStatement { |
| KernelAssertStatement(Expression condition, |
| {Expression message, int conditionStartOffset, int conditionEndOffset}) |
| : super(condition, |
| message: message, |
| conditionStartOffset: conditionStartOffset, |
| conditionEndOffset: conditionEndOffset); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.assertStatementEnter(this); |
| inferrer.inferExpression(condition, null, false); |
| if (message != null) { |
| inferrer.inferExpression(message, null, false); |
| } |
| inferrer.listener.assertStatementExit(this); |
| } |
| } |
| |
| /// Shadow object for [AwaitExpression]. |
| class KernelAwaitExpression extends AwaitExpression |
| implements KernelExpression { |
| KernelAwaitExpression(Expression operand) : super(operand); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // Inference dependencies are the dependencies of the awaited expression. |
| collector.collectDependencies(operand); |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.awaitExpressionEnter(this, typeContext) || typeNeeded; |
| if (!inferrer.typeSchemaEnvironment.isEmptyContext(typeContext)) { |
| typeContext = inferrer.wrapFutureOrType(typeContext); |
| } |
| var inferredType = |
| inferrer.inferExpression(operand, typeContext, typeNeeded); |
| inferredType = inferrer.typeSchemaEnvironment.flattenFutures(inferredType); |
| inferrer.listener.awaitExpressionExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a statement block in kernel form. |
| class KernelBlock extends Block implements KernelStatement { |
| KernelBlock(List<Statement> statements) : super(statements); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.blockEnter(this); |
| for (var statement in statements) { |
| inferrer.inferStatement(statement); |
| } |
| inferrer.listener.blockExit(this); |
| } |
| } |
| |
| /// Concrete shadow object representing a boolean literal in kernel form. |
| class KernelBoolLiteral extends BoolLiteral implements KernelExpression { |
| KernelBoolLiteral(bool value) : super(value); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.boolLiteralEnter(this, typeContext) || typeNeeded; |
| var inferredType = typeNeeded ? inferrer.coreTypes.boolClass.rawType : null; |
| inferrer.listener.boolLiteralExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a break or continue statement in kernel |
| /// form. |
| class KernelBreakStatement extends BreakStatement implements KernelStatement { |
| KernelBreakStatement(LabeledStatement target) : super(target); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.breakStatementEnter(this); |
| // No inference needs to be done. |
| inferrer.listener.breakStatementExit(this); |
| } |
| } |
| |
| /// Concrete shadow object representing a cascade expression. |
| /// |
| /// A cascade expression of the form `a..b()..c()` is represented as the kernel |
| /// expression: |
| /// |
| /// let v = a in |
| /// let _ = v.b() in |
| /// let _ = v.c() in |
| /// v |
| /// |
| /// In the documentation that follows, `v` is referred to as the "cascade |
| /// variable"--this is the variable that remembers the value of the expression |
| /// preceding the first `..` while the cascades are being evaluated. |
| /// |
| /// After constructing a [KernelCascadeExpression], the caller should |
| /// call [finalize] with an expression representing the expression after the |
| /// `..`. If a further `..` follows that expression, the caller should call |
| /// [extend] followed by [finalize] for each subsequent cascade. |
| class KernelCascadeExpression extends Let implements KernelExpression { |
| /// Pointer to the last "let" expression in the cascade. |
| Let nextCascade; |
| |
| /// Creates a [KernelCascadeExpression] using [variable] as the cascade |
| /// variable. Caller is responsible for ensuring that [variable]'s |
| /// initializer is the expression preceding the first `..` of the cascade |
| /// expression. |
| KernelCascadeExpression(KernelVariableDeclaration variable) |
| : super( |
| variable, |
| makeLet(new VariableDeclaration.forValue(new _UnfinishedCascade()), |
| new VariableGet(variable))) { |
| nextCascade = body; |
| } |
| |
| /// Adds a new unfinalized section to the end of the cascade. Should be |
| /// called after the previous cascade section has been finalized. |
| void extend() { |
| assert(nextCascade.variable.initializer is! _UnfinishedCascade); |
| Let newCascade = makeLet( |
| new VariableDeclaration.forValue(new _UnfinishedCascade()), |
| nextCascade.body); |
| nextCascade.body = newCascade; |
| newCascade.parent = nextCascade; |
| nextCascade = newCascade; |
| } |
| |
| /// Finalizes the last cascade section with the given [expression]. |
| void finalize(Expression expression) { |
| assert(nextCascade.variable.initializer is _UnfinishedCascade); |
| nextCascade.variable.initializer = expression; |
| expression.parent = nextCascade.variable; |
| } |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // The inference dependencies are the inference dependencies of the cascade |
| // target. |
| collector.collectDependencies(variable.initializer); |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = inferrer.listener.cascadeExpressionEnter(this, typeContext) || |
| typeNeeded; |
| var lhsType = inferrer.inferExpression( |
| variable.initializer, typeContext, typeNeeded || inferrer.strongMode); |
| if (inferrer.strongMode) { |
| variable.type = lhsType; |
| } |
| Let section = body; |
| while (true) { |
| inferrer.inferExpression(section.variable.initializer, null, false); |
| if (section.body is! Let) break; |
| section = section.body; |
| } |
| inferrer.listener.cascadeExpressionExit(this, lhsType); |
| return lhsType; |
| } |
| } |
| |
| /// Abstract shadow object representing a complex assignment in kernel form. |
| /// |
| /// Since there are many forms a complex assignment might have been desugared |
| /// to, this class wraps the desugared assignment rather than extending it. |
| /// |
| /// TODO(paulberry): once we know exactly what constitutes a "complex |
| /// assignment", document it here. |
| abstract class KernelComplexAssignment extends KernelSyntheticExpression { |
| /// In a compound assignment, the expression that reads the old value, or |
| /// `null` if this is not a compound assignment. |
| Expression read; |
| |
| /// The expression appearing on the RHS of the assignment. |
| final Expression rhs; |
| |
| /// The expression that performs the write (e.g. `a.[]=(b, a.[](b) + 1)` in |
| /// `++a[b]`). |
| Expression write; |
| |
| /// In a compound assignment without shortcut semantics, the expression that |
| /// combines the old and new values, or `null` if this is not a compound |
| /// assignment. |
| /// |
| /// Note that in a compound assignment with shortcut semantics, this is not |
| /// used; [nullAwareCombiner] is used instead. |
| MethodInvocation combiner; |
| |
| /// In a compound assignment with shortcut semantics, the conditional |
| /// expression that determines whether the assignment occurs. |
| /// |
| /// Note that in a compound assignment without shortcut semantics, this is not |
| /// used; [combiner] is used instead. |
| ConditionalExpression nullAwareCombiner; |
| |
| /// Indicates whether the expression arose from a post-increment or |
| /// post-decrement. |
| bool isPostIncDec = false; |
| |
| /// Indicates whether the expression arose from a pre-increment or |
| /// pre-decrement. |
| bool isPreIncDec = false; |
| |
| KernelComplexAssignment(this.rhs) : super(null); |
| |
| String toString() { |
| var parts = _getToStringParts(); |
| return '${runtimeType}(${parts.join(', ')})'; |
| } |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // Assignment expressions are not immediately evident expressions. |
| collector.recordNotImmediatelyEvident(fileOffset); |
| } |
| |
| List<String> _getToStringParts() { |
| List<String> parts = []; |
| if (desugared != null) parts.add('desugared=$desugared'); |
| if (read != null) parts.add('read=$read'); |
| if (rhs != null) parts.add('rhs=$rhs'); |
| if (write != null) parts.add('write=$write'); |
| if (combiner != null) parts.add('combiner=$combiner'); |
| if (nullAwareCombiner != null) { |
| parts.add('nullAwareCombiner=$nullAwareCombiner'); |
| } |
| if (isPostIncDec) parts.add('isPostIncDec=true'); |
| if (isPreIncDec) parts.add('isPreIncDec=true'); |
| return parts; |
| } |
| |
| DartType _inferRhs(KernelTypeInferrer inferrer, DartType writeContext) { |
| DartType inferredType = writeContext ?? const DynamicType(); |
| if (nullAwareCombiner != null) { |
| var rhsType = inferrer.inferExpression(rhs, writeContext, true); |
| MethodInvocation equalsInvocation = nullAwareCombiner.condition; |
| inferrer.findMethodInvocationMember(writeContext, equalsInvocation, |
| silent: true); |
| return inferrer.typeSchemaEnvironment |
| .getLeastUpperBound(inferredType, rhsType); |
| } else if (combiner != null) { |
| bool isOverloadedArithmeticOperator = false; |
| var combinerMember = inferrer |
| .findMethodInvocationMember(writeContext, combiner, silent: true); |
| if (combinerMember is Procedure) { |
| isOverloadedArithmeticOperator = inferrer.typeSchemaEnvironment |
| .isOverloadedArithmeticOperatorAndType( |
| combinerMember, writeContext); |
| } |
| if (isPostIncDec) { |
| return inferredType; |
| } else { |
| DartType rhsType; |
| if (isPreIncDec) { |
| rhsType = inferrer.coreTypes.intClass.rawType; |
| } else { |
| // Analyzer uses a null context for the RHS here. |
| // TODO(paulberry): improve on this. |
| rhsType = inferrer.inferExpression(rhs, null, true); |
| } |
| if (isOverloadedArithmeticOperator) { |
| return inferrer.typeSchemaEnvironment |
| .getTypeOfOverloadedArithmetic(inferredType, rhsType); |
| } else { |
| return inferrer |
| .getCalleeFunctionType( |
| combinerMember, writeContext, combiner.name, false) |
| .returnType; |
| } |
| } |
| } else { |
| return inferrer.inferExpression(rhs, writeContext, true); |
| } |
| } |
| } |
| |
| /// Abstract shadow object representing a complex assignment involving a |
| /// receiver. |
| abstract class KernelComplexAssignmentWithReceiver |
| extends KernelComplexAssignment { |
| /// The receiver of the assignment target (e.g. `a` in `a[b] = c`). |
| final Expression receiver; |
| |
| /// Indicates whether this assignment uses `super`. |
| final bool isSuper; |
| |
| KernelComplexAssignmentWithReceiver( |
| this.receiver, Expression rhs, this.isSuper) |
| : super(rhs); |
| |
| @override |
| List<String> _getToStringParts() { |
| var parts = super._getToStringParts(); |
| if (receiver != null) parts.add('receiver=$receiver'); |
| if (isSuper) parts.add('isSuper=true'); |
| return parts; |
| } |
| |
| DartType _inferReceiver(KernelTypeInferrer inferrer) { |
| if (receiver != null) { |
| return inferrer.inferExpression(receiver, null, true); |
| } else if (isSuper) { |
| return inferrer.classHierarchy.getTypeAsInstanceOf( |
| inferrer.thisType, inferrer.thisType.classNode.supertype.classNode); |
| } else { |
| return inferrer.thisType; |
| } |
| } |
| } |
| |
| /// Concrete shadow object representing a conditional expression in kernel form. |
| /// Shadow object for [ConditionalExpression]. |
| class KernelConditionalExpression extends ConditionalExpression |
| implements KernelExpression { |
| KernelConditionalExpression( |
| Expression condition, Expression then, Expression otherwise) |
| : super(condition, then, otherwise, null); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // Inference dependencies are the union of the inference dependencies of the |
| // two returned sub-expressions. |
| collector.collectDependencies(then); |
| collector.collectDependencies(otherwise); |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.conditionalExpressionEnter(this, typeContext) || |
| typeNeeded; |
| if (!inferrer.isTopLevel || |
| TypeInferenceEngineImpl.expandedTopLevelInference) { |
| inferrer.inferExpression( |
| condition, inferrer.coreTypes.boolClass.rawType, false); |
| } |
| DartType thenType = inferrer.inferExpression(then, typeContext, true); |
| DartType otherwiseType = |
| inferrer.inferExpression(otherwise, typeContext, true); |
| DartType type = inferrer.typeSchemaEnvironment |
| .getLeastUpperBound(thenType, otherwiseType); |
| if (inferrer.strongMode) { |
| staticType = type; |
| } |
| var inferredType = typeNeeded ? type : null; |
| inferrer.listener.conditionalExpressionExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Shadow object for [ConstructorInvocation]. |
| class KernelConstructorInvocation extends ConstructorInvocation |
| implements KernelExpression { |
| final Member _initialTarget; |
| |
| KernelConstructorInvocation( |
| Constructor target, this._initialTarget, Arguments arguments, |
| {bool isConst: false}) |
| : super(target, arguments, isConst: isConst); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.constructorInvocationEnter(this, typeContext) || |
| typeNeeded; |
| var inferredType = inferrer.inferInvocation( |
| typeContext, |
| typeNeeded, |
| fileOffset, |
| _initialTarget.function.functionType, |
| computeConstructorReturnType(_initialTarget), |
| arguments); |
| inferrer.listener.constructorInvocationExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a continue statement from a switch |
| /// statement, in kernel form. |
| class KernelContinueSwitchStatement extends ContinueSwitchStatement |
| implements KernelStatement { |
| KernelContinueSwitchStatement(SwitchCase target) : super(target); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.continueSwitchStatementEnter(this); |
| // No inference needs to be done. |
| inferrer.listener.continueSwitchStatementExit(this); |
| } |
| } |
| |
| /// Concrete implementation of [DependencyCollector] specialized to work with |
| /// kernel objects. |
| class KernelDependencyCollector extends DependencyCollectorImpl { |
| @override |
| void collectDependencies(Expression expression) { |
| if (expression is KernelExpression) { |
| // Use polymorphic dispatch on [KernelExpression] to perform whatever kind |
| // of type inference is correct for this kind of statement. |
| // TODO(paulberry): experiment to see if dynamic dispatch would be better, |
| // so that the type hierarchy will be simpler (which may speed up "is" |
| // checks). |
| expression._collectDependencies(this); |
| } else { |
| // Encountered an expression type for which type inference is not yet |
| // implemented, so just assume the expression does not have an immediately |
| // evident type for now. |
| // TODO(paulberry): once the BodyBuilder uses shadow classes for |
| // everything, this case should no longer be needed. |
| recordNotImmediatelyEvident(expression.fileOffset); |
| } |
| } |
| } |
| |
| /// Shadow object for [DirectMethodInvocation]. |
| class KernelDirectMethodInvocation extends DirectMethodInvocation |
| implements KernelExpression { |
| KernelDirectMethodInvocation( |
| Expression receiver, Procedure target, Arguments arguments) |
| : super(receiver, target, arguments); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // DirectMethodInvocation can only occur as a result of a use of `super`, |
| // and `super` can't appear inside a field initializer. So this code should |
| // never be reached. |
| unsupported( |
| "DirectMethodInvocation._collectDependencies", fileOffset, null); |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| inferrer.instrumentation?.record(Uri.parse(inferrer.uri), fileOffset, |
| 'target', new InstrumentationValueForMember(target)); |
| return inferrer.inferMethodInvocation( |
| this, receiver, fileOffset, false, typeContext, typeNeeded, |
| interfaceMember: target, methodName: target.name, arguments: arguments); |
| } |
| } |
| |
| /// Shadow object for [DirectPropertyGet]. |
| class KernelDirectPropertyGet extends DirectPropertyGet |
| implements KernelExpression { |
| KernelDirectPropertyGet(Expression receiver, Member target) |
| : super(receiver, target); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // DirectPropertyGet can only occur as a result of a use of `super`, and |
| // `super` can't appear inside a field initializer. So this code should |
| // never be reached. |
| unsupported("DirectPropertyGet._collectDependencies", fileOffset, null); |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| return inferrer.inferPropertyGet( |
| this, receiver, fileOffset, typeContext, typeNeeded, |
| propertyName: target.name); |
| } |
| } |
| |
| /// Concrete shadow object representing a do loop in kernel form. |
| class KernelDoStatement extends DoStatement implements KernelStatement { |
| KernelDoStatement(Statement body, Expression condition) |
| : super(body, condition); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.doStatementEnter(this); |
| inferrer.inferStatement(body); |
| inferrer.inferExpression( |
| condition, inferrer.coreTypes.boolClass.rawType, false); |
| inferrer.listener.doStatementExit(this); |
| } |
| } |
| |
| /// Concrete shadow object representing a double literal in kernel form. |
| class KernelDoubleLiteral extends DoubleLiteral implements KernelExpression { |
| KernelDoubleLiteral(double value) : super(value); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.doubleLiteralEnter(this, typeContext) || typeNeeded; |
| var inferredType = |
| typeNeeded ? inferrer.coreTypes.doubleClass.rawType : null; |
| inferrer.listener.doubleLiteralExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Common base class for shadow objects representing expressions in kernel |
| /// form. |
| abstract class KernelExpression implements Expression { |
| /// Collects any dependencies of [expression], and reports errors if the |
| /// expression does not have an immediately evident type. |
| void _collectDependencies(KernelDependencyCollector collector); |
| |
| /// Calls back to [inferrer] to perform type inference for whatever concrete |
| /// type of [KernelExpression] this is. |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded); |
| } |
| |
| /// Concrete shadow object representing an expression statement in kernel form. |
| class KernelExpressionStatement extends ExpressionStatement |
| implements KernelStatement { |
| KernelExpressionStatement(Expression expression) : super(expression); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.expressionStatementEnter(this); |
| inferrer.inferExpression(expression, null, false); |
| inferrer.listener.expressionStatementExit(this); |
| } |
| } |
| |
| /// Shadow object for [StaticInvocation] when the procedure being invoked is a |
| /// factory constructor. |
| class KernelFactoryConstructorInvocation extends StaticInvocation |
| implements KernelExpression { |
| final Member _initialTarget; |
| |
| KernelFactoryConstructorInvocation( |
| Procedure target, this._initialTarget, Arguments arguments, |
| {bool isConst: false}) |
| : super(target, arguments, isConst: isConst); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.constructorInvocationEnter(this, typeContext) || |
| typeNeeded; |
| var inferredType = inferrer.inferInvocation( |
| typeContext, |
| typeNeeded, |
| fileOffset, |
| _initialTarget.function.functionType, |
| computeConstructorReturnType(_initialTarget), |
| arguments); |
| inferrer.listener.constructorInvocationExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a field in kernel form. |
| class KernelField extends Field implements KernelMember { |
| @override |
| AccessorNode _accessorNode; |
| |
| @override |
| KernelTypeInferrer _typeInferrer; |
| |
| KernelField(Name name, {String fileUri}) : super(name, fileUri: fileUri) {} |
| |
| @override |
| void setInferredType( |
| TypeInferenceEngineImpl engine, String uri, DartType inferredType) { |
| engine.instrumentation?.record(Uri.parse(uri), fileOffset, 'topType', |
| new InstrumentationValueForType(inferredType)); |
| type = inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a for-in loop in kernel form. |
| class KernelForInStatement extends ForInStatement implements KernelStatement { |
| final bool _declaresVariable; |
| |
| KernelForInStatement(VariableDeclaration variable, Expression iterable, |
| Statement body, this._declaresVariable, |
| {bool isAsync: false}) |
| : super(variable, iterable, body, isAsync: isAsync); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.forInStatementEnter(this); |
| var iterableClass = isAsync |
| ? inferrer.coreTypes.streamClass |
| : inferrer.coreTypes.iterableClass; |
| DartType context; |
| bool typeNeeded = false; |
| KernelVariableDeclaration variable; |
| if (_declaresVariable) { |
| variable = this.variable; |
| if (inferrer.strongMode && variable._implicitlyTyped) { |
| typeNeeded = true; |
| // TODO(paulberry): In this case, should the context be `Iterable<?>`? |
| } else { |
| context = inferrer.wrapType(variable.type, iterableClass); |
| } |
| } else { |
| // TODO(paulberry): In this case, should the context be based on the |
| // declared type of the loop variable? |
| // TODO(paulberry): Note that when [_declaresVariable] is `false`, the |
| // body starts with an assignment from the synthetic loop variable to |
| // another variable. We need to make sure any type inference diagnostics |
| // that occur related to this assignment are reported at the correct |
| // locations. |
| } |
| var inferredExpressionType = inferrer.resolveTypeParameter( |
| inferrer.inferExpression(iterable, context, typeNeeded)); |
| if (typeNeeded) { |
| var inferredType = const DynamicType(); |
| if (inferredExpressionType is InterfaceType) { |
| InterfaceType supertype = inferrer.classHierarchy |
| .getTypeAsInstanceOf(inferredExpressionType, iterableClass); |
| if (supertype != null) { |
| inferredType = supertype.typeArguments[0]; |
| } |
| } |
| inferrer.instrumentation?.record( |
| Uri.parse(inferrer.uri), |
| variable.fileOffset, |
| 'type', |
| new InstrumentationValueForType(inferredType)); |
| variable.type = inferredType; |
| } |
| inferrer.inferStatement(body); |
| inferrer.listener.forInStatementExit(this); |
| } |
| } |
| |
| /// Concrete shadow object representing a classic for loop in kernel form. |
| class KernelForStatement extends ForStatement implements KernelStatement { |
| KernelForStatement(List<VariableDeclaration> variables, Expression condition, |
| List<Expression> updates, Statement body) |
| : super(variables, condition, updates, body); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.forStatementEnter(this); |
| variables.forEach(inferrer.inferStatement); |
| if (condition != null) { |
| inferrer.inferExpression( |
| condition, inferrer.coreTypes.boolClass.rawType, false); |
| } |
| for (var update in updates) { |
| inferrer.inferExpression(update, null, false); |
| } |
| inferrer.inferStatement(body); |
| inferrer.listener.forStatementExit(this); |
| } |
| } |
| |
| /// Concrete shadow object representing a local function declaration in kernel |
| /// form. |
| class KernelFunctionDeclaration extends FunctionDeclaration |
| implements KernelStatement { |
| bool _hasImplicitReturnType = false; |
| |
| KernelFunctionDeclaration(VariableDeclaration variable, FunctionNode function) |
| : super(variable, function); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.functionDeclarationEnter(this); |
| inferrer.inferLocalFunction(function, null, false, fileOffset, |
| _hasImplicitReturnType ? null : function.returnType); |
| variable.type = function.functionType; |
| inferrer.listener.functionDeclarationExit(this); |
| } |
| |
| static void setHasImplicitReturnType( |
| KernelFunctionDeclaration declaration, bool hasImplicitReturnType) { |
| declaration._hasImplicitReturnType = hasImplicitReturnType; |
| } |
| } |
| |
| /// Concrete shadow object representing a function expression in kernel form. |
| class KernelFunctionExpression extends FunctionExpression |
| implements KernelExpression { |
| KernelFunctionExpression(FunctionNode function) : super(function); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| for (KernelVariableDeclaration parameter in function.positionalParameters) { |
| if (parameter._implicitlyTyped) { |
| collector.recordNotImmediatelyEvident(parameter.fileOffset); |
| } |
| } |
| for (KernelVariableDeclaration parameter in function.namedParameters) { |
| if (parameter._implicitlyTyped) { |
| collector.recordNotImmediatelyEvident(parameter.fileOffset); |
| } |
| } |
| var body = function.body; |
| if (body is ReturnStatement) { |
| // The inference dependencies are the inference dependencies of the return |
| // expression. |
| collector.collectDependencies(body.expression); |
| } else { |
| collector.recordNotImmediatelyEvident(fileOffset); |
| } |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = inferrer.listener.functionExpressionEnter(this, typeContext) || |
| typeNeeded; |
| var inferredType = inferrer.inferLocalFunction( |
| function, typeContext, typeNeeded, fileOffset, null); |
| inferrer.listener.functionExpressionExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing an if-null expression. |
| /// |
| /// An if-null expression of the form `a ?? b` is represented as the kernel |
| /// expression: |
| /// |
| /// let v = a in v == null ? b : v |
| class KernelIfNullExpression extends Let implements KernelExpression { |
| KernelIfNullExpression(VariableDeclaration variable, Expression body) |
| : super(variable, body); |
| |
| @override |
| ConditionalExpression get body => super.body; |
| |
| /// Returns the expression to the left of `??`. |
| Expression get _lhs => variable.initializer; |
| |
| /// Returns the expression to the right of `??`. |
| Expression get _rhs => body.then; |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // If-null expressions are not immediately evident expressions. |
| collector.recordNotImmediatelyEvident(fileOffset); |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = inferrer.listener.ifNullEnter(this, typeContext) || typeNeeded; |
| // To infer `e0 ?? e1` in context K: |
| // - Infer e0 in context K to get T0 |
| var lhsType = inferrer.inferExpression(_lhs, typeContext, true); |
| if (inferrer.strongMode) { |
| variable.type = lhsType; |
| } |
| // - Let J = T0 if K is `_` else K. |
| var rhsContext = typeContext ?? lhsType; |
| // - Infer e1 in context J to get T1 |
| var rhsType = |
| inferrer.inferExpression(_rhs, rhsContext, typeContext == null); |
| // - Let T = greatest closure of K with respect to `?` if K is not `_`, else |
| // UP(t0, t1) |
| // - Then the inferred type is T. |
| var inferredType = typeContext == null |
| ? inferrer.typeSchemaEnvironment.getLeastUpperBound(lhsType, rhsType) |
| : greatestClosure(inferrer.coreTypes, typeContext); |
| if (inferrer.strongMode) { |
| body.staticType = inferredType; |
| } |
| inferrer.listener.ifNullExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing an if statement in kernel form. |
| class KernelIfStatement extends IfStatement implements KernelStatement { |
| KernelIfStatement(Expression condition, Statement then, Statement otherwise) |
| : super(condition, then, otherwise); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.ifStatementEnter(this); |
| inferrer.inferExpression( |
| condition, inferrer.coreTypes.boolClass.rawType, false); |
| inferrer.inferStatement(then); |
| if (otherwise != null) inferrer.inferStatement(otherwise); |
| inferrer.listener.ifStatementExit(this); |
| } |
| } |
| |
| /// Concrete shadow object representing an assignment to a target for which |
| /// assignment is not allowed. |
| class KernelIllegalAssignment extends KernelComplexAssignment { |
| KernelIllegalAssignment(Expression rhs) : super(rhs); |
| } |
| |
| /// Concrete shadow object representing an assignment to a target of the form |
| /// `a[b]`. |
| class KernelIndexAssign extends KernelComplexAssignmentWithReceiver { |
| /// In an assignment to an index expression, the index expression. |
| Expression index; |
| |
| KernelIndexAssign(Expression receiver, this.index, Expression rhs, |
| {bool isSuper: false}) |
| : super(receiver, rhs, isSuper); |
| |
| @override |
| List<String> _getToStringParts() { |
| var parts = super._getToStringParts(); |
| if (index != null) parts.add('index=$index'); |
| return parts; |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = inferrer.listener.indexAssignEnter(desugared, typeContext) || |
| typeNeeded; |
| // TODO(paulberry): record the appropriate types on let variables and |
| // conditional expressions. |
| var receiverType = _inferReceiver(inferrer); |
| if (read != null) { |
| inferrer.findMethodInvocationMember(receiverType, read, silent: true); |
| } |
| var writeMember = inferrer.findMethodInvocationMember(receiverType, write); |
| // To replicate analyzer behavior, we base type inference on the write |
| // member. TODO(paulberry): would it be better to use the read member |
| // when doing compound assignment? |
| var calleeType = |
| inferrer.getCalleeType(writeMember, receiverType, indexSetName); |
| DartType indexContext; |
| DartType writeContext; |
| if (calleeType is FunctionType && |
| calleeType.positionalParameters.length >= 2) { |
| // TODO(paulberry): we ought to get a context for the index expression |
| // from the index formal parameter, but analyzer doesn't so for now we |
| // replicate its behavior. |
| indexContext = null; |
| writeContext = calleeType.positionalParameters[1]; |
| } |
| inferrer.inferExpression(index, indexContext, false); |
| var inferredType = _inferRhs(inferrer, writeContext); |
| inferrer.listener.indexAssignExit(desugared, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Common base class for shadow objects representing initializers in kernel |
| /// form. |
| abstract class KernelInitializer implements Initializer { |
| /// Performs type inference for whatever concrete type of [KernelInitializer] |
| /// this is. |
| void _inferInitializer(KernelTypeInferrer inferrer); |
| } |
| |
| /// Concrete shadow object representing an integer literal in kernel form. |
| class KernelIntLiteral extends IntLiteral implements KernelExpression { |
| KernelIntLiteral(int value) : super(value); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.intLiteralEnter(this, typeContext) || typeNeeded; |
| var inferredType = typeNeeded ? inferrer.coreTypes.intClass.rawType : null; |
| inferrer.listener.intLiteralExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a non-inverted "is" test in kernel form. |
| class KernelIsExpression extends IsExpression implements KernelExpression { |
| KernelIsExpression(Expression operand, DartType type) : super(operand, type); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.isExpressionEnter(this, typeContext) || typeNeeded; |
| inferrer.inferExpression(operand, null, false); |
| var inferredType = typeNeeded ? inferrer.coreTypes.boolClass.rawType : null; |
| inferrer.listener.isExpressionExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing an inverted "is" test in kernel form. |
| class KernelIsNotExpression extends Not implements KernelExpression { |
| KernelIsNotExpression(Expression operand, DartType type, int charOffset) |
| : super(new IsExpression(operand, type)..fileOffset = charOffset); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| IsExpression isExpression = this.operand; |
| typeNeeded = |
| inferrer.listener.isNotExpressionEnter(this, typeContext) || typeNeeded; |
| inferrer.inferExpression(isExpression.operand, null, false); |
| var inferredType = typeNeeded ? inferrer.coreTypes.boolClass.rawType : null; |
| inferrer.listener.isNotExpressionExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a labeled statement in kernel form. |
| class KernelLabeledStatement extends LabeledStatement |
| implements KernelStatement { |
| KernelLabeledStatement(Statement body) : super(body); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.labeledStatementEnter(this); |
| inferrer.inferStatement(body); |
| inferrer.listener.labeledStatementExit(this); |
| } |
| } |
| |
| /// Concrete shadow object representing a list literal in kernel form. |
| class KernelListLiteral extends ListLiteral implements KernelExpression { |
| final DartType _declaredTypeArgument; |
| |
| KernelListLiteral(List<Expression> expressions, |
| {DartType typeArgument, bool isConst: false}) |
| : _declaredTypeArgument = typeArgument, |
| super(expressions, |
| typeArgument: typeArgument ?? const DynamicType(), |
| isConst: isConst); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| if (_declaredTypeArgument == null) { |
| expressions.forEach(collector.collectDependencies); |
| } |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.listLiteralEnter(this, typeContext) || typeNeeded; |
| var listClass = inferrer.coreTypes.listClass; |
| var listType = listClass.thisType; |
| List<DartType> inferredTypes; |
| DartType inferredTypeArgument; |
| List<DartType> formalTypes; |
| List<DartType> actualTypes; |
| bool inferenceNeeded = _declaredTypeArgument == null && inferrer.strongMode; |
| if (inferenceNeeded) { |
| inferredTypes = [const UnknownType()]; |
| inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(listType, |
| listClass.typeParameters, null, null, typeContext, inferredTypes); |
| inferredTypeArgument = inferredTypes[0]; |
| formalTypes = []; |
| actualTypes = []; |
| } else { |
| inferredTypeArgument = _declaredTypeArgument ?? const DynamicType(); |
| } |
| if (inferenceNeeded || !inferrer.isTopLevel) { |
| for (var expression in expressions) { |
| var expressionType = inferrer.inferExpression( |
| expression, inferredTypeArgument, inferenceNeeded); |
| if (inferenceNeeded) { |
| formalTypes.add(listType.typeArguments[0]); |
| actualTypes.add(expressionType); |
| } |
| } |
| } |
| if (inferenceNeeded) { |
| inferrer.typeSchemaEnvironment.inferGenericFunctionOrType( |
| listType, |
| listClass.typeParameters, |
| formalTypes, |
| actualTypes, |
| typeContext, |
| inferredTypes); |
| inferredTypeArgument = inferredTypes[0]; |
| inferrer.instrumentation?.record( |
| Uri.parse(inferrer.uri), |
| fileOffset, |
| 'typeArgs', |
| new InstrumentationValueForTypeArgs([inferredTypeArgument])); |
| typeArgument = inferredTypeArgument; |
| } |
| var inferredType = typeNeeded |
| ? new InterfaceType(listClass, [inferredTypeArgument]) |
| : null; |
| inferrer.listener.listLiteralExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Shadow object for [LogicalExpression]. |
| class KernelLogicalExpression extends LogicalExpression |
| implements KernelExpression { |
| KernelLogicalExpression(Expression left, String operator, Expression right) |
| : super(left, operator, right); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = inferrer.listener.logicalExpressionEnter(this, typeContext) || |
| typeNeeded; |
| var boolType = inferrer.coreTypes.boolClass.rawType; |
| inferrer.inferExpression(left, boolType, false); |
| inferrer.inferExpression(right, boolType, false); |
| var inferredType = typeNeeded ? boolType : null; |
| inferrer.listener.logicalExpressionExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Shadow object for [MapLiteral]. |
| class KernelMapLiteral extends MapLiteral implements KernelExpression { |
| final DartType _declaredKeyType; |
| final DartType _declaredValueType; |
| |
| KernelMapLiteral(List<MapEntry> entries, |
| {DartType keyType, DartType valueType, bool isConst: false}) |
| : _declaredKeyType = keyType, |
| _declaredValueType = valueType, |
| super(entries, |
| keyType: keyType ?? const DynamicType(), |
| valueType: valueType ?? const DynamicType(), |
| isConst: isConst); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| assert((_declaredKeyType == null) == (_declaredValueType == null)); |
| if (_declaredKeyType == null) { |
| for (var entry in entries) { |
| collector.collectDependencies(entry.key); |
| collector.collectDependencies(entry.value); |
| } |
| } |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.mapLiteralEnter(this, typeContext) || typeNeeded; |
| var mapClass = inferrer.coreTypes.mapClass; |
| var mapType = mapClass.thisType; |
| List<DartType> inferredTypes; |
| DartType inferredKeyType; |
| DartType inferredValueType; |
| List<DartType> formalTypes; |
| List<DartType> actualTypes; |
| assert((_declaredKeyType == null) == (_declaredValueType == null)); |
| bool inferenceNeeded = _declaredKeyType == null && inferrer.strongMode; |
| if (inferenceNeeded) { |
| inferredTypes = [const UnknownType(), const UnknownType()]; |
| inferrer.typeSchemaEnvironment.inferGenericFunctionOrType(mapType, |
| mapClass.typeParameters, null, null, typeContext, inferredTypes); |
| inferredKeyType = inferredTypes[0]; |
| inferredValueType = inferredTypes[1]; |
| formalTypes = []; |
| actualTypes = []; |
| } else { |
| inferredKeyType = _declaredKeyType ?? const DynamicType(); |
| inferredValueType = _declaredValueType ?? const DynamicType(); |
| } |
| if (inferenceNeeded || !inferrer.isTopLevel) { |
| for (var entry in entries) { |
| var keyType = inferrer.inferExpression( |
| entry.key, inferredKeyType, inferenceNeeded); |
| var valueType = inferrer.inferExpression( |
| entry.value, inferredValueType, inferenceNeeded); |
| if (inferenceNeeded) { |
| formalTypes.addAll(mapType.typeArguments); |
| actualTypes.add(keyType); |
| actualTypes.add(valueType); |
| } |
| } |
| } |
| if (inferenceNeeded) { |
| inferrer.typeSchemaEnvironment.inferGenericFunctionOrType( |
| mapType, |
| mapClass.typeParameters, |
| formalTypes, |
| actualTypes, |
| typeContext, |
| inferredTypes); |
| inferredKeyType = inferredTypes[0]; |
| inferredValueType = inferredTypes[1]; |
| inferrer.instrumentation?.record( |
| Uri.parse(inferrer.uri), |
| fileOffset, |
| 'typeArgs', |
| new InstrumentationValueForTypeArgs( |
| [inferredKeyType, inferredValueType])); |
| keyType = inferredKeyType; |
| valueType = inferredValueType; |
| } |
| var inferredType = typeNeeded |
| ? new InterfaceType(mapClass, [inferredKeyType, inferredValueType]) |
| : null; |
| inferrer.listener.mapLiteralExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Abstract shadow object representing a field or procedure in kernel form. |
| abstract class KernelMember implements Member { |
| String get fileUri; |
| |
| AccessorNode get _accessorNode; |
| |
| void set _accessorNode(AccessorNode value); |
| |
| KernelTypeInferrer get _typeInferrer; |
| |
| void set _typeInferrer(KernelTypeInferrer value); |
| |
| void setInferredType( |
| TypeInferenceEngineImpl engine, String uri, DartType inferredType); |
| |
| static AccessorNode getAccessorNode(Member member) { |
| if (member is KernelMember) return member._accessorNode; |
| return null; |
| } |
| |
| static void recordCrossOverride( |
| KernelMember member, Member overriddenMember) { |
| if (member._accessorNode != null) { |
| member._accessorNode.crossOverrides.add(overriddenMember); |
| } |
| } |
| |
| static void recordOverride(KernelMember member, Member overriddenMember) { |
| if (member._accessorNode != null) { |
| member._accessorNode.overrides.add(overriddenMember); |
| } |
| if (member is KernelProcedure && member._methodNode != null) { |
| member._methodNode.overrides.add(overriddenMember); |
| } |
| } |
| } |
| |
| /// Shadow object for [MethodInvocation]. |
| class KernelMethodInvocation extends MethodInvocation |
| implements KernelExpression { |
| /// Indicates whether this method invocation is a call to a `call` method |
| /// resulting from the invocation of a function expression. |
| final bool _isImplicitCall; |
| |
| KernelMethodInvocation(Expression receiver, Name name, Arguments arguments, |
| {bool isImplicitCall: false, Member interfaceTarget}) |
| : _isImplicitCall = isImplicitCall, |
| super(receiver, name, arguments, interfaceTarget); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // The inference dependencies are the inference dependencies of the |
| // receiver. |
| collector.collectDependencies(receiver); |
| if (isOverloadableArithmeticOperator(name.name)) { |
| collector.collectDependencies(arguments.positional[0]); |
| } |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| return inferrer.inferMethodInvocation( |
| this, receiver, fileOffset, _isImplicitCall, typeContext, typeNeeded, |
| desugaredInvocation: this); |
| } |
| } |
| |
| /// Shadow object for [Not]. |
| class KernelNot extends Not implements KernelExpression { |
| KernelNot(Expression operand) : super(operand); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| collector.collectDependencies(operand); |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = inferrer.listener.notEnter(this, typeContext) || typeNeeded; |
| // First infer the receiver so we can look up the method that was invoked. |
| var boolType = inferrer.coreTypes.boolClass.rawType; |
| inferrer.inferExpression(operand, boolType, false); |
| DartType inferredType = typeNeeded ? boolType : null; |
| inferrer.listener.notExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a null-aware method invocation. |
| /// |
| /// A null-aware method invocation of the form `a?.b(...)` is represented as the |
| /// expression: |
| /// |
| /// let v = a in v == null ? null : v.b(...) |
| class KernelNullAwareMethodInvocation extends Let implements KernelExpression { |
| KernelNullAwareMethodInvocation(VariableDeclaration variable, Expression body) |
| : super(variable, body); |
| |
| @override |
| ConditionalExpression get body => super.body; |
| |
| MethodInvocation get _desugaredInvocation => body.otherwise; |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // Null aware expressions are not immediately evident. |
| collector.recordNotImmediatelyEvident(fileOffset); |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| var inferredType = inferrer.inferMethodInvocation( |
| this, |
| variable.initializer, |
| fileOffset, |
| false, |
| typeContext, |
| typeNeeded || inferrer.strongMode, |
| receiverVariable: variable, |
| desugaredInvocation: _desugaredInvocation); |
| if (inferrer.strongMode) { |
| body.staticType = inferredType; |
| } |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a null-aware read from a property. |
| /// |
| /// A null-aware property get of the form `a?.b` is represented as the kernel |
| /// expression: |
| /// |
| /// let v = a in v == null ? null : v.b |
| class KernelNullAwarePropertyGet extends Let implements KernelExpression { |
| KernelNullAwarePropertyGet( |
| VariableDeclaration variable, ConditionalExpression body) |
| : super(variable, body); |
| |
| @override |
| ConditionalExpression get body => super.body; |
| |
| PropertyGet get _desugaredGet => body.otherwise; |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // Null aware expressions are not immediately evident. |
| collector.recordNotImmediatelyEvident(fileOffset); |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| var inferredType = inferrer.inferPropertyGet(this, variable.initializer, |
| fileOffset, typeContext, typeNeeded || inferrer.strongMode, |
| receiverVariable: variable, desugaredGet: _desugaredGet); |
| if (inferrer.strongMode) { |
| body.staticType = inferredType; |
| } |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a null literal in kernel form. |
| class KernelNullLiteral extends NullLiteral implements KernelExpression { |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.nullLiteralEnter(this, typeContext) || typeNeeded; |
| var inferredType = typeNeeded ? inferrer.coreTypes.nullClass.rawType : null; |
| inferrer.listener.nullLiteralExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a procedure in kernel form. |
| class KernelProcedure extends Procedure implements KernelMember { |
| @override |
| AccessorNode _accessorNode; |
| |
| MethodNode _methodNode; |
| |
| @override |
| KernelTypeInferrer _typeInferrer; |
| |
| final bool _hasImplicitReturnType; |
| |
| KernelProcedure(Name name, ProcedureKind kind, FunctionNode function, |
| this._hasImplicitReturnType, |
| {String fileUri}) |
| : super(name, kind, function, fileUri: fileUri); |
| |
| @override |
| void setInferredType( |
| TypeInferenceEngineImpl engine, String uri, DartType inferredType) { |
| if (isSetter) { |
| if (function.positionalParameters.length > 0) { |
| var parameter = function.positionalParameters[0]; |
| engine.instrumentation?.record(Uri.parse(uri), parameter.fileOffset, |
| 'topType', new InstrumentationValueForType(inferredType)); |
| parameter.type = inferredType; |
| } |
| } else if (isGetter) { |
| engine.instrumentation?.record(Uri.parse(uri), fileOffset, 'topType', |
| new InstrumentationValueForType(inferredType)); |
| function.returnType = inferredType; |
| } else { |
| unhandled("setInferredType", "not accessor", fileOffset, Uri.parse(uri)); |
| } |
| } |
| |
| static MethodNode getMethodNode(Procedure procedure) { |
| if (procedure is KernelProcedure) return procedure._methodNode; |
| return null; |
| } |
| |
| static bool hasImplicitReturnType(KernelProcedure procedure) { |
| return procedure._hasImplicitReturnType; |
| } |
| |
| static void inferSetterReturnType( |
| KernelProcedure procedure, TypeInferenceEngineImpl engine, String uri) { |
| assert(procedure.isSetter); |
| if (procedure._hasImplicitReturnType) { |
| var inferredType = const VoidType(); |
| engine.instrumentation?.record(Uri.parse(uri), procedure.fileOffset, |
| 'topType', new InstrumentationValueForType(inferredType)); |
| procedure.function?.returnType = inferredType; |
| } |
| } |
| } |
| |
| /// Concrete shadow object representing an assignment to a property. |
| class KernelPropertyAssign extends KernelComplexAssignmentWithReceiver { |
| /// If this assignment uses null-aware access (`?.`), the conditional |
| /// expression that guards the access; otherwise `null`. |
| Expression nullAwareGuard; |
| |
| KernelPropertyAssign(Expression receiver, Expression rhs, |
| {bool isSuper: false}) |
| : super(receiver, rhs, isSuper); |
| |
| @override |
| List<String> _getToStringParts() { |
| var parts = super._getToStringParts(); |
| if (nullAwareGuard != null) parts.add('nullAwareGuard=$nullAwareGuard'); |
| return parts; |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.propertyAssignEnter(desugared, typeContext) || |
| typeNeeded; |
| // TODO(paulberry): record the appropriate types on let variables and |
| // conditional expressions. |
| var receiverType = _inferReceiver(inferrer); |
| if (read != null) { |
| inferrer.findPropertyGetMember(receiverType, read, silent: true); |
| } |
| Member writeMember; |
| if (write != null) { |
| writeMember = inferrer.findPropertySetMember(receiverType, write); |
| if (inferrer.isTopLevel && |
| ((writeMember is Procedure && |
| writeMember.kind == ProcedureKind.Setter) || |
| writeMember is Field)) { |
| if (TypeInferenceEngineImpl.fullTopLevelInference) { |
| if (writeMember is KernelField && writeMember._accessorNode != null) { |
| inferrer.engine.inferAccessorFused( |
| writeMember._accessorNode, inferrer.accessorNode); |
| } |
| } else { |
| // References to fields and setters can't be relied upon for top level |
| // inference. |
| inferrer.recordNotImmediatelyEvident(fileOffset); |
| } |
| } |
| } |
| // To replicate analyzer behavior, we base type inference on the write |
| // member. TODO(paulberry): would it be better to use the read member when |
| // doing compound assignment? |
| var writeContext = inferrer.getSetterType(writeMember, receiverType); |
| var inferredType = _inferRhs(inferrer, writeContext); |
| inferrer.listener.propertyAssignExit(desugared, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Shadow object for [PropertyGet]. |
| class KernelPropertyGet extends PropertyGet implements KernelExpression { |
| KernelPropertyGet(Expression receiver, Name name, [Member interfaceTarget]) |
| : super(receiver, name, interfaceTarget); |
| |
| KernelPropertyGet.byReference( |
| Expression receiver, Name name, Reference interfaceTargetReference) |
| : super.byReference(receiver, name, interfaceTargetReference); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // A simple or qualified identifier referring to a top level function, |
| // static variable, field, getter; or a static class variable, static getter |
| // or method; or an instance method; has the inferred type of the referent. |
| // - Otherwise, if the identifier has no inferred or annotated type then it |
| // is an error. |
| // - Note: specifically, references to instance fields and instance getters |
| // are disallowed here. |
| // - The inference dependency of the identifier is the referent if the |
| // referent is a candidate for inference. Otherwise there are no |
| // inference dependencies. |
| |
| // For a property get, the only things we could be looking at are an |
| // instance field, an instance getter, or an instance method. For the first |
| // two, we disallow them in [_inferExpression]. For the last, there are no |
| // field dependencies. So we don't need to do anything here. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| return inferrer.inferPropertyGet( |
| this, receiver, fileOffset, typeContext, typeNeeded, |
| desugaredGet: this); |
| } |
| } |
| |
| /// Concrete shadow object representing a redirecting initializer in kernel |
| /// form. |
| class KernelRedirectingInitializer extends RedirectingInitializer |
| implements KernelInitializer { |
| KernelRedirectingInitializer(Constructor target, Arguments arguments) |
| : super(target, arguments); |
| |
| @override |
| _inferInitializer(KernelTypeInferrer inferrer) { |
| inferrer.listener.redirectingInitializerEnter(this); |
| inferrer.inferInvocation(null, false, fileOffset, |
| target.function.functionType, target.enclosingClass.thisType, arguments, |
| skipTypeArgumentInference: true); |
| inferrer.listener.redirectingInitializerExit(this); |
| } |
| } |
| |
| /// Shadow object for [Rethrow]. |
| class KernelRethrow extends Rethrow implements KernelExpression { |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.rethrowEnter(this, typeContext) || typeNeeded; |
| var inferredType = typeNeeded ? const BottomType() : null; |
| inferrer.listener.rethrowExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a return statement in kernel form. |
| class KernelReturnStatement extends ReturnStatement implements KernelStatement { |
| KernelReturnStatement([Expression expression]) : super(expression); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.returnStatementEnter(this); |
| var closureContext = inferrer.closureContext; |
| var typeContext = |
| !closureContext.isGenerator ? closureContext.returnContext : null; |
| var inferredType = expression != null |
| ? inferrer.inferExpression(expression, typeContext, true) |
| : const VoidType(); |
| // Analyzer treats bare `return` statements as having no effect on the |
| // inferred type of the closure. TODO(paulberry): is this what we want |
| // for Fasta? |
| if (expression != null) { |
| closureContext.handleReturn(inferrer, inferredType); |
| } |
| inferrer.listener.returnStatementExit(this); |
| } |
| } |
| |
| /// Common base class for shadow objects representing statements in kernel |
| /// form. |
| abstract class KernelStatement extends Statement { |
| /// Calls back to [inferrer] to perform type inference for whatever concrete |
| /// type of [KernelStatement] this is. |
| void _inferStatement(KernelTypeInferrer inferrer); |
| } |
| |
| /// Concrete shadow object representing an assignment to a static variable. |
| class KernelStaticAssignment extends KernelComplexAssignment { |
| KernelStaticAssignment(Expression rhs) : super(rhs); |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = inferrer.listener.staticAssignEnter(desugared, typeContext) || |
| typeNeeded; |
| // TODO(paulberry): record the appropriate types on let variables and |
| // conditional expressions. |
| DartType writeContext; |
| var write = this.write; |
| if (write is StaticSet) { |
| writeContext = write.target.setterType; |
| var target = write.target; |
| if (target is KernelField && target._accessorNode != null) { |
| if (inferrer.isDryRun) { |
| inferrer.recordDryRunDependency(target._accessorNode); |
| } |
| if (TypeInferenceEngineImpl.fusedTopLevelInference && |
| inferrer.isTopLevel) { |
| inferrer.engine |
| .inferAccessorFused(target._accessorNode, inferrer.accessorNode); |
| } |
| } |
| } |
| var inferredType = _inferRhs(inferrer, writeContext); |
| inferrer.listener.staticAssignExit(desugared, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a read of a static variable in kernel |
| /// form. |
| class KernelStaticGet extends StaticGet implements KernelExpression { |
| KernelStaticGet(Member target) : super(target); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // A simple or qualified identifier referring to a top level function, |
| // static variable, field, getter; or a static class variable, static getter |
| // or method; or an instance method; has the inferred type of the referent. |
| // - Otherwise, if the identifier has no inferred or annotated type then it |
| // is an error. |
| // - Note: specifically, references to instance fields and instance getters |
| // are disallowed here. |
| // - The inference dependency of the identifier is the referent if the |
| // referent is a candidate for inference. Otherwise there are no |
| // inference dependencies. |
| // TODO(paulberry): implement the proper error checking logic. |
| var target = this.target; |
| if (target is KernelField && target._accessorNode != null) { |
| collector.recordDependency(target._accessorNode); |
| } |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.staticGetEnter(this, typeContext) || typeNeeded; |
| var target = this.target; |
| if (target is KernelField && target._accessorNode != null) { |
| if (inferrer.isDryRun) { |
| inferrer.recordDryRunDependency(target._accessorNode); |
| } |
| if (TypeInferenceEngineImpl.fusedTopLevelInference && |
| inferrer.isTopLevel) { |
| inferrer.engine |
| .inferAccessorFused(target._accessorNode, inferrer.accessorNode); |
| } |
| } |
| var inferredType = typeNeeded ? target.getterType : null; |
| inferrer.listener.staticGetExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Shadow object for [StaticInvocation]. |
| class KernelStaticInvocation extends StaticInvocation |
| implements KernelExpression { |
| KernelStaticInvocation(Procedure target, Arguments arguments, |
| {bool isConst: false}) |
| : super(target, arguments, isConst: isConst); |
| |
| KernelStaticInvocation.byReference( |
| Reference targetReference, Arguments arguments) |
| : super.byReference(targetReference, arguments); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = inferrer.listener.staticInvocationEnter(this, typeContext) || |
| typeNeeded; |
| var calleeType = target.function.functionType; |
| var inferredType = inferrer.inferInvocation(typeContext, typeNeeded, |
| fileOffset, calleeType, calleeType.returnType, arguments); |
| inferrer.listener.staticInvocationExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a string concatenation in kernel form. |
| class KernelStringConcatenation extends StringConcatenation |
| implements KernelExpression { |
| KernelStringConcatenation(List<Expression> expressions) : super(expressions); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.stringConcatenationEnter(this, typeContext) || |
| typeNeeded; |
| if (!inferrer.isTopLevel) { |
| for (Expression expression in expressions) { |
| inferrer.inferExpression(expression, null, false); |
| } |
| } |
| var inferredType = |
| typeNeeded ? inferrer.coreTypes.stringClass.rawType : null; |
| inferrer.listener.stringConcatenationExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a string literal in kernel form. |
| class KernelStringLiteral extends StringLiteral implements KernelExpression { |
| KernelStringLiteral(String value) : super(value); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.stringLiteralEnter(this, typeContext) || typeNeeded; |
| var inferredType = |
| typeNeeded ? inferrer.coreTypes.stringClass.rawType : null; |
| inferrer.listener.stringLiteralExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Shadow object for [SuperMethodInvocation]. |
| class KernelSuperMethodInvocation extends SuperMethodInvocation |
| implements KernelExpression { |
| KernelSuperMethodInvocation(Name name, Arguments arguments, |
| [Procedure interfaceTarget]) |
| : super(name, arguments, interfaceTarget); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // Super expressions should never occur in top level type inference. |
| // TODO(paulberry): but could they occur due to invalid code? |
| assert(false); |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| if (interfaceTarget != null) { |
| inferrer.instrumentation?.record(Uri.parse(inferrer.uri), fileOffset, |
| 'target', new InstrumentationValueForMember(interfaceTarget)); |
| } |
| return inferrer.inferMethodInvocation(this, new KernelThisExpression(), |
| fileOffset, false, typeContext, typeNeeded, |
| interfaceMember: interfaceTarget, |
| methodName: name, |
| arguments: arguments); |
| } |
| } |
| |
| /// Shadow object for [SuperPropertyGet]. |
| class KernelSuperPropertyGet extends SuperPropertyGet |
| implements KernelExpression { |
| KernelSuperPropertyGet(Name name, [Member interfaceTarget]) |
| : super(name, interfaceTarget); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // Super expressions should never occur in top level type inference. |
| // TODO(paulberry): but could they occur due to invalid code? |
| assert(false); |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| return inferrer.inferPropertyGet( |
| this, new KernelThisExpression(), fileOffset, typeContext, typeNeeded, |
| propertyName: name); |
| } |
| } |
| |
| /// Concrete shadow object representing a switch statement in kernel form. |
| class KernelSwitchStatement extends SwitchStatement implements KernelStatement { |
| KernelSwitchStatement(Expression expression, List<SwitchCase> cases) |
| : super(expression, cases); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.switchStatementEnter(this); |
| var expressionType = inferrer.inferExpression(expression, null, true); |
| for (var switchCase in cases) { |
| for (var caseExpression in switchCase.expressions) { |
| inferrer.inferExpression(caseExpression, expressionType, false); |
| } |
| inferrer.inferStatement(switchCase.body); |
| } |
| inferrer.listener.switchStatementExit(this); |
| } |
| } |
| |
| /// Shadow object for [SymbolLiteral]. |
| class KernelSymbolLiteral extends SymbolLiteral implements KernelExpression { |
| KernelSymbolLiteral(String value) : super(value); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.symbolLiteralEnter(this, typeContext) || typeNeeded; |
| var inferredType = |
| typeNeeded ? inferrer.coreTypes.symbolClass.rawType : null; |
| inferrer.listener.symbolLiteralExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Shadow object for expressions that are introduced by the front end as part |
| /// of desugaring or the handling of error conditions. |
| /// |
| /// By default, type inference skips these expressions entirely. Some derived |
| /// classes have type inference behaviors. |
| /// |
| /// Visitors skip over objects of this type, so it is not included in serialized |
| /// output. |
| class KernelSyntheticExpression extends Expression implements KernelExpression { |
| /// The desugared kernel representation of this synthetic expression. |
| Expression desugared; |
| |
| KernelSyntheticExpression(this.desugared); |
| |
| @override |
| void set parent(TreeNode node) { |
| super.parent = node; |
| desugared?.parent = node; |
| } |
| |
| @override |
| accept(ExpressionVisitor v) => desugared.accept(v); |
| |
| @override |
| accept1(ExpressionVisitor1 v, arg) => desugared.accept1(v, arg); |
| |
| @override |
| DartType getStaticType(TypeEnvironment types) => |
| desugared.getStaticType(types); |
| |
| @override |
| transformChildren(Transformer v) => desugared.transformChildren(v); |
| |
| @override |
| visitChildren(Visitor v) => desugared.visitChildren(v); |
| |
| @override |
| _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| return typeNeeded ? const DynamicType() : null; |
| } |
| } |
| |
| /// Shadow object for statements that are introduced by the front end as part |
| /// of desugaring or the handling of error conditions. |
| /// |
| /// By default, type inference skips these statements entirely. Some derived |
| /// classes may have type inference behaviors. |
| /// |
| /// Visitors skip over objects of this type, so it is not included in serialized |
| /// output. |
| class KernelSyntheticStatement extends Statement implements KernelStatement { |
| /// The desugared kernel representation of this synthetic statement. |
| Statement desugared; |
| |
| KernelSyntheticStatement(this.desugared); |
| |
| @override |
| void set parent(TreeNode node) { |
| super.parent = node; |
| desugared?.parent = node; |
| } |
| |
| @override |
| accept(StatementVisitor v) => desugared.accept(v); |
| |
| @override |
| accept1(StatementVisitor1 v, arg) => desugared.accept1(v, arg); |
| |
| @override |
| transformChildren(Transformer v) => desugared.transformChildren(v); |
| |
| @override |
| visitChildren(Visitor v) => desugared.visitChildren(v); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) {} |
| } |
| |
| /// Shadow object for [ThisExpression]. |
| class KernelThisExpression extends ThisExpression implements KernelExpression { |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // Field initializers are not allowed to refer to [this]. But if it |
| // happens, we can still proceed; no additional type inference dependencies |
| // are introduced. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.thisExpressionEnter(this, typeContext) || typeNeeded; |
| var inferredType = |
| typeNeeded ? (inferrer.thisType ?? const DynamicType()) : null; |
| inferrer.listener.thisExpressionExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Shadow object for [Throw]. |
| class KernelThrow extends Throw implements KernelExpression { |
| KernelThrow(Expression expression) : super(expression); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = inferrer.listener.throwEnter(this, typeContext) || typeNeeded; |
| inferrer.inferExpression(expression, null, false); |
| var inferredType = typeNeeded ? const BottomType() : null; |
| inferrer.listener.throwExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a try-catch block in kernel form. |
| class KernelTryCatch extends TryCatch implements KernelStatement { |
| KernelTryCatch(Statement body, List<Catch> catches) : super(body, catches); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.tryCatchEnter(this); |
| inferrer.inferStatement(body); |
| for (var catch_ in catches) { |
| inferrer.inferStatement(catch_.body); |
| } |
| inferrer.listener.tryCatchExit(this); |
| } |
| } |
| |
| /// Concrete shadow object representing a try-finally block in kernel form. |
| class KernelTryFinally extends TryFinally implements KernelStatement { |
| KernelTryFinally(Statement body, Statement finalizer) |
| : super(body, finalizer); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.tryFinallyEnter(this); |
| inferrer.inferStatement(body); |
| inferrer.inferStatement(finalizer); |
| inferrer.listener.tryFinallyExit(this); |
| } |
| } |
| |
| /// Concrete implementation of [TypeInferenceEngine] specialized to work with |
| /// kernel objects. |
| class KernelTypeInferenceEngine extends TypeInferenceEngineImpl { |
| KernelTypeInferenceEngine(Instrumentation instrumentation, bool strongMode) |
| : super(instrumentation, strongMode); |
| |
| @override |
| AccessorNode createAccessorNode(KernelMember member) { |
| AccessorNode accessorNode = new AccessorNode(this, member); |
| member._accessorNode = accessorNode; |
| return accessorNode; |
| } |
| |
| @override |
| KernelTypeInferrer createLocalTypeInferrer( |
| Uri uri, TypeInferenceListener listener, InterfaceType thisType) { |
| return new KernelTypeInferrer._( |
| this, uri.toString(), listener, false, thisType, null); |
| } |
| |
| @override |
| MethodNode createMethodNode(KernelProcedure procedure) { |
| MethodNode methodNode = new MethodNode(procedure); |
| procedure._methodNode = methodNode; |
| return methodNode; |
| } |
| |
| @override |
| KernelTypeInferrer createTopLevelTypeInferrer(TypeInferenceListener listener, |
| InterfaceType thisType, KernelMember member) { |
| return member._typeInferrer = new KernelTypeInferrer._( |
| this, member.fileUri, listener, true, thisType, member._accessorNode); |
| } |
| |
| @override |
| KernelTypeInferrer getMemberTypeInferrer(KernelMember member) { |
| return member._typeInferrer; |
| } |
| } |
| |
| /// Concrete implementation of [TypeInferrer] specialized to work with kernel |
| /// objects. |
| class KernelTypeInferrer extends TypeInferrerImpl { |
| @override |
| final typePromoter = new KernelTypePromoter(); |
| |
| KernelTypeInferrer._( |
| KernelTypeInferenceEngine engine, |
| String uri, |
| TypeInferenceListener listener, |
| bool topLevel, |
| InterfaceType thisType, |
| AccessorNode accessorNode) |
| : super(engine, uri, listener, topLevel, thisType, accessorNode); |
| |
| @override |
| Expression getFieldInitializer(KernelField field) { |
| return field.initializer; |
| } |
| |
| @override |
| DartType inferExpression( |
| Expression expression, DartType typeContext, bool typeNeeded) { |
| // When doing top level inference, we skip subexpressions whose type isn't |
| // needed so that we don't induce bogus dependencies on fields mentioned in |
| // those subexpressions. |
| if (!typeNeeded && isTopLevel) return null; |
| |
| if (expression is KernelExpression) { |
| // Use polymorphic dispatch on [KernelExpression] to perform whatever kind |
| // of type inference is correct for this kind of statement. |
| // TODO(paulberry): experiment to see if dynamic dispatch would be better, |
| // so that the type hierarchy will be simpler (which may speed up "is" |
| // checks). |
| return expression._inferExpression(this, typeContext, typeNeeded); |
| } else { |
| // Encountered an expression type for which type inference is not yet |
| // implemented, so just infer dynamic for now. |
| // TODO(paulberry): once the BodyBuilder uses shadow classes for |
| // everything, this case should no longer be needed. |
| return typeNeeded ? const DynamicType() : null; |
| } |
| } |
| |
| @override |
| DartType inferFieldTopLevel( |
| KernelField field, DartType type, bool typeNeeded) { |
| if (field.initializer == null) return const DynamicType(); |
| return inferExpression(field.initializer, type, typeNeeded); |
| } |
| |
| @override |
| void inferInitializer(Initializer initializer) { |
| if (initializer is KernelInitializer) { |
| // Use polymorphic dispatch on [KernelInitializer] to perform whatever |
| // kind of type inference is correct for this kind of initializer. |
| // TODO(paulberry): experiment to see if dynamic dispatch would be better, |
| // so that the type hierarchy will be simpler (which may speed up "is" |
| // checks). |
| return initializer._inferInitializer(this); |
| } else { |
| // Encountered an initializer type for which type inference is not yet |
| // implemented, so just skip it for now. |
| // TODO(paulberry): once the BodyBuilder uses shadow classes for |
| // everything, this case should no longer be needed. |
| } |
| } |
| |
| @override |
| void inferStatement(Statement statement) { |
| if (statement is KernelStatement) { |
| // Use polymorphic dispatch on [KernelStatement] to perform whatever kind |
| // of type inference is correct for this kind of statement. |
| // TODO(paulberry): experiment to see if dynamic dispatch would be better, |
| // so that the type hierarchy will be simpler (which may speed up "is" |
| // checks). |
| return statement._inferStatement(this); |
| } else { |
| // Encountered a statement type for which type inference is not yet |
| // implemented, so just skip it for now. |
| // TODO(paulberry): once the BodyBuilder uses shadow classes for |
| // everything, this case should no longer be needed. |
| } |
| } |
| } |
| |
| /// Shadow object for [TypeLiteral]. |
| class KernelTypeLiteral extends TypeLiteral implements KernelExpression { |
| KernelTypeLiteral(DartType type) : super(type); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.typeLiteralEnter(this, typeContext) || typeNeeded; |
| var inferredType = typeNeeded ? inferrer.coreTypes.typeClass.rawType : null; |
| inferrer.listener.typeLiteralExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete implementation of [TypePromoter] specialized to work with kernel |
| /// objects. |
| class KernelTypePromoter extends TypePromoterImpl { |
| @override |
| int getVariableFunctionNestingLevel(VariableDeclaration variable) { |
| if (variable is KernelVariableDeclaration) { |
| return variable._functionNestingLevel; |
| } else { |
| // Hack to deal with the fact that BodyBuilder still creates raw |
| // VariableDeclaration objects sometimes. |
| // TODO(paulberry): get rid of this once the type parameter is |
| // KernelVariableDeclaration. |
| return 0; |
| } |
| } |
| |
| @override |
| bool isPromotionCandidate(VariableDeclaration variable) { |
| assert(variable is KernelVariableDeclaration); |
| KernelVariableDeclaration kernelVariableDeclaration = variable; |
| return !kernelVariableDeclaration._isLocalFunction; |
| } |
| |
| @override |
| bool sameExpressions(Expression a, Expression b) { |
| return identical(a, b); |
| } |
| |
| @override |
| void setVariableMutatedAnywhere(VariableDeclaration variable) { |
| if (variable is KernelVariableDeclaration) { |
| variable._mutatedAnywhere = true; |
| } else { |
| // Hack to deal with the fact that BodyBuilder still creates raw |
| // VariableDeclaration objects sometimes. |
| // TODO(paulberry): get rid of this once the type parameter is |
| // KernelVariableDeclaration. |
| } |
| } |
| |
| @override |
| void setVariableMutatedInClosure(VariableDeclaration variable) { |
| if (variable is KernelVariableDeclaration) { |
| variable._mutatedInClosure = true; |
| } else { |
| // Hack to deal with the fact that BodyBuilder still creates raw |
| // VariableDeclaration objects sometimes. |
| // TODO(paulberry): get rid of this once the type parameter is |
| // KernelVariableDeclaration. |
| } |
| } |
| |
| @override |
| bool wasVariableMutatedAnywhere(VariableDeclaration variable) { |
| if (variable is KernelVariableDeclaration) { |
| return variable._mutatedAnywhere; |
| } else { |
| // Hack to deal with the fact that BodyBuilder still creates raw |
| // VariableDeclaration objects sometimes. |
| // TODO(paulberry): get rid of this once the type parameter is |
| // KernelVariableDeclaration. |
| return true; |
| } |
| } |
| } |
| |
| /// Concrete shadow object representing an assignment to a local variable. |
| class KernelVariableAssignment extends KernelComplexAssignment { |
| KernelVariableAssignment(Expression rhs) : super(rhs); |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| typeNeeded = |
| inferrer.listener.variableAssignEnter(desugared, typeContext) || |
| typeNeeded; |
| // TODO(paulberry): record the appropriate types on let variables and |
| // conditional expressions. |
| DartType writeContext; |
| var write = this.write; |
| if (write is VariableSet) { |
| writeContext = write.variable.type; |
| } |
| var inferredType = _inferRhs(inferrer, writeContext); |
| inferrer.listener.variableAssignExit(desugared, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a variable declaration in kernel form. |
| class KernelVariableDeclaration extends VariableDeclaration |
| implements KernelStatement { |
| final bool _implicitlyTyped; |
| |
| final int _functionNestingLevel; |
| |
| bool _mutatedInClosure = false; |
| |
| bool _mutatedAnywhere = false; |
| |
| final bool _isLocalFunction; |
| |
| KernelVariableDeclaration(String name, this._functionNestingLevel, |
| {Expression initializer, |
| DartType type, |
| bool isFinal: false, |
| bool isConst: false, |
| bool isLocalFunction: false}) |
| : _implicitlyTyped = type == null, |
| _isLocalFunction = isLocalFunction, |
| super(name, |
| initializer: initializer, |
| type: type ?? const DynamicType(), |
| isFinal: isFinal, |
| isConst: isConst); |
| |
| KernelVariableDeclaration.forValue( |
| Expression initializer, this._functionNestingLevel) |
| : _implicitlyTyped = true, |
| _isLocalFunction = false, |
| super.forValue(initializer); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.variableDeclarationEnter(this); |
| var declaredType = _implicitlyTyped ? null : type; |
| DartType inferredType; |
| if (initializer != null) { |
| inferredType = inferrer.inferDeclarationType(inferrer.inferExpression( |
| initializer, declaredType, _implicitlyTyped)); |
| } else { |
| inferredType = const DynamicType(); |
| } |
| if (inferrer.strongMode && _implicitlyTyped) { |
| inferrer.instrumentation?.record(Uri.parse(inferrer.uri), fileOffset, |
| 'type', new InstrumentationValueForType(inferredType)); |
| type = inferredType; |
| } |
| inferrer.listener.variableDeclarationExit(this); |
| } |
| |
| /// Determine whether the given [KernelVariableDeclaration] had an implicit |
| /// type. |
| /// |
| /// This is static to avoid introducing a method that would be visible to |
| /// the kernel. |
| static bool isImplicitlyTyped(KernelVariableDeclaration variable) => |
| variable._implicitlyTyped; |
| } |
| |
| /// Concrete shadow object representing a read from a variable in kernel form. |
| class KernelVariableGet extends VariableGet implements KernelExpression { |
| final TypePromotionFact _fact; |
| |
| final TypePromotionScope _scope; |
| |
| KernelVariableGet(VariableDeclaration variable, this._fact, this._scope) |
| : super(variable); |
| |
| @override |
| void _collectDependencies(KernelDependencyCollector collector) { |
| // No inference dependencies. |
| } |
| |
| @override |
| DartType _inferExpression( |
| KernelTypeInferrer inferrer, DartType typeContext, bool typeNeeded) { |
| var variable = this.variable as KernelVariableDeclaration; |
| bool mutatedInClosure = variable._mutatedInClosure; |
| DartType declaredOrInferredType = variable.type; |
| typeNeeded = |
| inferrer.listener.variableGetEnter(this, typeContext) || typeNeeded; |
| DartType promotedType = inferrer.typePromoter |
| .computePromotedType(_fact, _scope, mutatedInClosure); |
| if (promotedType != null) { |
| inferrer.instrumentation?.record(Uri.parse(inferrer.uri), fileOffset, |
| 'promotedType', new InstrumentationValueForType(promotedType)); |
| } |
| this.promotedType = promotedType; |
| var inferredType = |
| typeNeeded ? (promotedType ?? declaredOrInferredType) : null; |
| inferrer.listener.variableGetExit(this, inferredType); |
| return inferredType; |
| } |
| } |
| |
| /// Concrete shadow object representing a while loop in kernel form. |
| class KernelWhileStatement extends WhileStatement implements KernelStatement { |
| KernelWhileStatement(Expression condition, Statement body) |
| : super(condition, body); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.whileStatementEnter(this); |
| inferrer.inferExpression( |
| condition, inferrer.coreTypes.boolClass.rawType, false); |
| inferrer.inferStatement(body); |
| inferrer.listener.whileStatementExit(this); |
| } |
| } |
| |
| /// Concrete shadow object representing a yield statement in kernel form. |
| class KernelYieldStatement extends YieldStatement implements KernelStatement { |
| KernelYieldStatement(Expression expression, {bool isYieldStar: false}) |
| : super(expression, isYieldStar: isYieldStar); |
| |
| @override |
| void _inferStatement(KernelTypeInferrer inferrer) { |
| inferrer.listener.yieldStatementEnter(this); |
| var closureContext = inferrer.closureContext; |
| var typeContext = |
| closureContext.isGenerator ? closureContext.returnContext : null; |
| if (isYieldStar && typeContext != null) { |
| typeContext = inferrer.wrapType( |
| typeContext, |
| closureContext.isAsync |
| ? inferrer.coreTypes.streamClass |
| : inferrer.coreTypes.iterableClass); |
| } |
| var inferredType = inferrer.inferExpression(expression, typeContext, true); |
| closureContext.handleYield(inferrer, isYieldStar, inferredType); |
| inferrer.listener.yieldStatementExit(this); |
| } |
| } |
| |
| class _UnfinishedCascade extends Expression { |
| accept(v) => unsupported("accept", -1, null); |
| |
| accept1(v, arg) => unsupported("accept1", -1, null); |
| |
| getStaticType(types) => unsupported("getStaticType", -1, null); |
| |
| transformChildren(v) => unsupported("transformChildren", -1, null); |
| |
| visitChildren(v) => unsupported("visitChildren", -1, null); |
| } |