blob: 78e4277a72a62914dc130387f7b363c42e96cee5 [file] [log] [blame]
// 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.
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/class_hierarchy.dart' as ir;
import 'package:kernel/core_types.dart' as ir;
import 'package:kernel/type_algebra.dart' as ir;
import 'package:kernel/type_environment.dart' as ir;
import 'scope.dart';
import 'static_type_base.dart';
/// Enum values for how the target of a static type should be interpreted.
enum ClassRelation {
/// The target is any subtype of the static type.
subtype,
/// The target is a subclass or mixin application of the static type.
///
/// This corresponds to accessing a member through a this expression.
thisExpression,
/// The target is an exact instance of the static type.
exact,
}
/// Visitor that computes and caches the static type of expression while
/// visiting the full tree at expression level.
///
/// To ensure that the traversal only visits and computes the expression type
/// for each expression once, this class performs the traversal explicitly and
/// adds 'handleX' hooks for subclasses to handle individual expressions using
/// the readily compute static types of subexpressions.
// TODO(johnniwinther): Add improved type promotion to handle negative
// reasoning.
abstract class StaticTypeVisitor extends StaticTypeBase {
Map<ir.Expression, ir.DartType> _cache = {};
StaticTypeVisitor(ir.TypeEnvironment typeEnvironment)
: super(typeEnvironment);
Map<ir.Expression, ir.DartType> get staticTypeCacheForTesting => _cache;
VariableScopeModel get variableScopeModel => null;
@override
ir.DartType defaultNode(ir.Node node) =>
throw UnsupportedError('Unhandled node $node (${node.runtimeType})');
@override
Null visitComponent(ir.Component node) {
visitNodes(node.libraries);
}
@override
Null visitLibrary(ir.Library node) {
visitNodes(node.classes);
visitNodes(node.procedures);
visitNodes(node.fields);
}
@override
Null visitClass(ir.Class node) {
visitNodes(node.constructors);
visitNodes(node.procedures);
visitNodes(node.fields);
}
/// Returns the static type of the expression as an instantiation of
/// [superclass].
///
/// Should only be used on code compiled in strong mode, as this method
/// assumes the IR is strongly typed.
///
/// This method furthermore assumes that the type of the expression actually
/// is a subtype of (some instantiation of) the given [superclass].
/// If this is not the case the raw type of [superclass] is returned.
///
/// This method is derived from `ir.Expression.getStaticTypeAsInstanceOf`.
ir.InterfaceType getTypeAsInstanceOf(ir.DartType type, ir.Class superclass) {
// This method assumes the program is correctly typed, so if the superclass
// is not generic, we can just return its raw type without computing the
// type of this expression. It also ensures that all types are considered
// subtypes of Object (not just interface types), and function types are
// considered subtypes of Function.
if (superclass.typeParameters.isEmpty) {
return superclass.rawType;
}
while (type is ir.TypeParameterType) {
type = (type as ir.TypeParameterType).parameter.bound;
}
if (type is ir.InterfaceType) {
ir.InterfaceType upcastType =
typeEnvironment.hierarchy.getTypeAsInstanceOf(type, superclass);
if (upcastType != null) return upcastType;
} else if (type is ir.BottomType) {
return superclass.bottomType;
}
return superclass.rawType;
}
/// Computes the result type of the property access [node] on a receiver of
/// type [receiverType].
///
/// If the `node.interfaceTarget` is `null` but matches an `Object` member
/// it is updated to target this member.
ir.DartType _computePropertyGetType(
ir.PropertyGet node, ir.DartType receiverType) {
ir.Member interfaceTarget = node.interfaceTarget;
if (interfaceTarget == null && receiverType is ir.InterfaceType) {
interfaceTarget = node.interfaceTarget = typeEnvironment.hierarchy
.getInterfaceMember(receiverType.classNode, node.name);
}
if (interfaceTarget != null) {
ir.Class superclass = interfaceTarget.enclosingClass;
receiverType = getTypeAsInstanceOf(receiverType, superclass);
return ir.Substitution.fromInterfaceType(receiverType)
.substituteType(interfaceTarget.getterType);
}
// Treat the properties of Object specially.
String nameString = node.name.name;
if (nameString == 'hashCode') {
return typeEnvironment.intType;
} else if (nameString == 'runtimeType') {
return typeEnvironment.typeType;
}
return const ir.DynamicType();
}
void handlePropertyGet(
ir.PropertyGet node, ir.DartType receiverType, ir.DartType resultType) {}
@override
ir.DartType visitPropertyGet(ir.PropertyGet node) {
ir.DartType receiverType = visitNode(node.receiver);
ir.DartType resultType =
_cache[node] = _computePropertyGetType(node, receiverType);
receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
handlePropertyGet(node, receiverType, resultType);
return resultType;
}
void handlePropertySet(
ir.PropertySet node, ir.DartType receiverType, ir.DartType valueType) {}
@override
ir.DartType visitPropertySet(ir.PropertySet node) {
ir.DartType receiverType = visitNode(node.receiver);
ir.DartType valueType = super.visitPropertySet(node);
if (node.interfaceTarget == null && receiverType is ir.InterfaceType) {
node.interfaceTarget = typeEnvironment.hierarchy
.getInterfaceMember(receiverType.classNode, node.name, setter: true);
}
receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
handlePropertySet(node, receiverType, valueType);
return valueType;
}
void handleDirectPropertyGet(ir.DirectPropertyGet node,
ir.DartType receiverType, ir.DartType resultType) {}
@override
ir.DartType visitDirectPropertyGet(ir.DirectPropertyGet node) {
ir.DartType receiverType = visitNode(node.receiver);
ir.Class superclass = node.target.enclosingClass;
receiverType = getTypeAsInstanceOf(receiverType, superclass);
ir.DartType resultType = ir.Substitution.fromInterfaceType(receiverType)
.substituteType(node.target.getterType);
_cache[node] = resultType;
handleDirectPropertyGet(node, receiverType, resultType);
return resultType;
}
void handleDirectMethodInvocation(
ir.DirectMethodInvocation node,
ir.DartType receiverType,
ArgumentTypes argumentTypes,
ir.DartType returnType) {}
@override
ir.DartType visitDirectMethodInvocation(ir.DirectMethodInvocation node) {
ir.DartType receiverType = visitNode(node.receiver);
ArgumentTypes argumentTypes = _visitArguments(node.arguments);
ir.DartType returnType;
if (typeEnvironment.isOverloadedArithmeticOperator(node.target)) {
ir.DartType argumentType = argumentTypes.positional[0];
returnType = typeEnvironment.getTypeOfOverloadedArithmetic(
receiverType, argumentType);
} else {
ir.Class superclass = node.target.enclosingClass;
receiverType = getTypeAsInstanceOf(receiverType, superclass);
ir.DartType returnType = ir.Substitution.fromInterfaceType(receiverType)
.substituteType(node.target.function.returnType);
returnType = ir.Substitution.fromPairs(
node.target.function.typeParameters, node.arguments.types)
.substituteType(returnType);
}
_cache[node] = returnType;
handleDirectMethodInvocation(node, receiverType, argumentTypes, returnType);
return returnType;
}
void handleDirectPropertySet(ir.DirectPropertySet node,
ir.DartType receiverType, ir.DartType valueType) {}
@override
ir.DartType visitDirectPropertySet(ir.DirectPropertySet node) {
ir.DartType receiverType = visitNode(node.receiver);
ir.DartType valueType = super.visitDirectPropertySet(node);
handleDirectPropertySet(node, receiverType, valueType);
return valueType;
}
/// Returns `true` if [interfaceTarget] is an arithmetic operator whose result
/// type is computed using both the receiver type and the argument type.
///
/// Visitors that subclass the [StaticTypeVisitor] must special case this
/// target as to avoid visiting the argument twice.
bool isSpecialCasedBinaryOperator(ir.Member interfaceTarget) {
return interfaceTarget is ir.Procedure &&
typeEnvironment.isOverloadedArithmeticOperator(interfaceTarget);
}
ir.Member _getMember(ir.Class cls, String name) {
for (ir.Member member in cls.members) {
if (member.name.name == name) return member;
}
throw fail("Member '$name' not found in $cls");
}
ir.Procedure _objectEquals;
ir.Procedure get objectEquals =>
_objectEquals ??= _getMember(typeEnvironment.objectType.classNode, '==');
/// Returns [receiverType] narrowed to enclosing class of [interfaceTarget].
///
/// If [interfaceTarget] is `null` or `receiverType` is _not_ `dynamic` no
/// narrowing is performed.
ir.DartType _narrowInstanceReceiver(
ir.Member interfaceTarget, ir.DartType receiverType) {
if (interfaceTarget != null && receiverType == const ir.DynamicType()) {
receiverType = interfaceTarget.enclosingClass.thisType;
}
return receiverType;
}
/// Returns `true` if [member] can be called with the structure of
/// [arguments].
bool _isApplicable(ir.Arguments arguments, ir.Member member) {
/// Returns `true` if [arguments] are applicable to the function type
/// structure.
bool isFunctionTypeApplicable(
int typeParameterCount,
int requiredParameterCount,
int positionalParameterCount,
Iterable<String> Function() getNamedParameters) {
if (arguments.types.isNotEmpty &&
arguments.types.length != typeParameterCount) {
return false;
}
if (arguments.positional.length < requiredParameterCount) {
return false;
}
if (arguments.positional.length > positionalParameterCount) {
return false;
}
Iterable<String> namedParameters = getNamedParameters();
if (arguments.named.length > namedParameters.length) {
return false;
}
if (arguments.named.isNotEmpty) {
for (ir.NamedExpression namedArguments in arguments.named) {
if (!namedParameters.contains(namedArguments.name)) {
return false;
}
}
}
return true;
}
/// Returns `true` if [arguments] are applicable to a value of the static
/// [type].
bool isTypeApplicable(ir.DartType type) {
if (type is ir.DynamicType) return true;
if (type == typeEnvironment.rawFunctionType) return true;
if (type is ir.FunctionType) {
return isFunctionTypeApplicable(
type.typeParameters.length,
type.requiredParameterCount,
type.positionalParameters.length,
() => type.namedParameters.map((p) => p.name).toSet());
}
return false;
}
if (member is ir.Procedure) {
if (member.kind == ir.ProcedureKind.Setter ||
member.kind == ir.ProcedureKind.Factory) {
return false;
} else if (member.kind == ir.ProcedureKind.Getter) {
return isTypeApplicable(member.getterType);
} else if (member.kind == ir.ProcedureKind.Method ||
member.kind == ir.ProcedureKind.Operator) {
return isFunctionTypeApplicable(
member.function.typeParameters.length,
member.function.requiredParameterCount,
member.function.positionalParameters.length,
() => member.function.namedParameters.map((p) => p.name).toSet());
}
} else if (member is ir.Field) {
return isTypeApplicable(member.type);
}
return false;
}
/// Computes the result type of the method invocation [node] on a receiver of
/// type [receiverType].
///
/// If the `node.interfaceTarget` is `null` but matches an `Object` member
/// it is updated to target this member.
ir.DartType _computeMethodInvocationType(ir.MethodInvocation node,
ir.DartType receiverType, ArgumentTypes argumentTypes) {
ir.Member interfaceTarget = node.interfaceTarget;
// TODO(34602): Remove when `interfaceTarget` is set on synthetic calls to
// ==.
if (interfaceTarget == null &&
node.name.name == '==' &&
node.arguments.types.isEmpty &&
node.arguments.positional.length == 1 &&
node.arguments.named.isEmpty) {
interfaceTarget = node.interfaceTarget = objectEquals;
}
if (interfaceTarget == null && receiverType is ir.InterfaceType) {
ir.Member member = typeEnvironment.hierarchy
.getInterfaceMember(receiverType.classNode, node.name);
if (_isApplicable(node.arguments, member)) {
interfaceTarget = node.interfaceTarget = member;
}
}
if (interfaceTarget != null) {
if (isSpecialCasedBinaryOperator(interfaceTarget)) {
ir.DartType argumentType = argumentTypes.positional[0];
return typeEnvironment.getTypeOfOverloadedArithmetic(
receiverType, argumentType);
}
ir.Class superclass = interfaceTarget.enclosingClass;
receiverType = getTypeAsInstanceOf(receiverType, superclass);
ir.DartType getterType = ir.Substitution.fromInterfaceType(receiverType)
.substituteType(interfaceTarget.getterType);
if (getterType is ir.FunctionType) {
List<ir.DartType> typeArguments = node.arguments.types;
if (interfaceTarget is ir.Procedure &&
interfaceTarget.function.typeParameters.isNotEmpty &&
typeArguments.isEmpty) {
// If this was a dynamic call the invocation does not have the
// inferred default type arguments so we need to create them here
// to perform a valid substitution.
ir.Substitution substitution =
ir.Substitution.fromInterfaceType(receiverType);
typeArguments = interfaceTarget.function.typeParameters
.map((t) => substitution.substituteType(t.defaultType))
.toList();
}
return ir.Substitution.fromPairs(
getterType.typeParameters, typeArguments)
.substituteType(getterType.returnType);
} else {
return const ir.DynamicType();
}
}
if (node.name.name == 'call') {
if (receiverType is ir.FunctionType) {
if (receiverType.typeParameters.length != node.arguments.types.length) {
return const ir.BottomType();
}
return ir.Substitution.fromPairs(
receiverType.typeParameters, node.arguments.types)
.substituteType(receiverType.returnType);
}
}
if (node.name.name == '==') {
// We use this special case to simplify generation of '==' checks.
return typeEnvironment.boolType;
}
return const ir.DynamicType();
}
ArgumentTypes _visitArguments(ir.Arguments arguments) {
List<ir.DartType> positional;
List<ir.DartType> named;
if (arguments.positional.isEmpty) {
positional = const <ir.DartType>[];
} else {
positional = new List<ir.DartType>(arguments.positional.length);
int index = 0;
for (ir.Expression argument in arguments.positional) {
positional[index++] = visitNode(argument);
}
}
if (arguments.named.isEmpty) {
named = const <ir.DartType>[];
} else {
named = new List<ir.DartType>(arguments.named.length);
int index = 0;
for (ir.NamedExpression argument in arguments.named) {
named[index++] = visitNode(argument);
}
}
return new ArgumentTypes(positional, named);
}
void handleMethodInvocation(
ir.MethodInvocation node,
ir.DartType receiverType,
ArgumentTypes argumentTypes,
ir.DartType returnType) {}
@override
ir.DartType visitMethodInvocation(ir.MethodInvocation node) {
ArgumentTypes argumentTypes = _visitArguments(node.arguments);
ir.DartType receiverType = visitNode(node.receiver);
ir.DartType returnType =
_computeMethodInvocationType(node, receiverType, argumentTypes);
receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
_cache[node] = returnType;
handleMethodInvocation(node, receiverType, argumentTypes, returnType);
return returnType;
}
void handleStaticGet(ir.StaticGet node, ir.DartType resultType) {}
@override
ir.DartType visitStaticGet(ir.StaticGet node) {
ir.DartType resultType = super.visitStaticGet(node);
handleStaticGet(node, resultType);
return resultType;
}
void handleStaticSet(ir.StaticSet node, ir.DartType valueType) {}
@override
ir.DartType visitStaticSet(ir.StaticSet node) {
ir.DartType valueType = super.visitStaticSet(node);
handleStaticSet(node, valueType);
return valueType;
}
void handleStaticInvocation(ir.StaticInvocation node,
ArgumentTypes argumentTypes, ir.DartType returnType) {}
@override
ir.DartType visitStaticInvocation(ir.StaticInvocation node) {
ArgumentTypes argumentTypes = _visitArguments(node.arguments);
ir.DartType returnType = ir.Substitution.fromPairs(
node.target.function.typeParameters, node.arguments.types)
.substituteType(node.target.function.returnType);
_cache[node] = returnType;
handleStaticInvocation(node, argumentTypes, returnType);
return returnType;
}
void handleConstructorInvocation(ir.ConstructorInvocation node,
ArgumentTypes argumentTypes, ir.DartType resultType) {}
@override
ir.DartType visitConstructorInvocation(ir.ConstructorInvocation node) {
ArgumentTypes argumentTypes = _visitArguments(node.arguments);
ir.DartType resultType = node.arguments.types.isEmpty
? node.target.enclosingClass.rawType
: new ir.InterfaceType(
node.target.enclosingClass, node.arguments.types);
_cache[node] = resultType;
handleConstructorInvocation(node, argumentTypes, resultType);
return resultType;
}
void handleSuperPropertyGet(
ir.SuperPropertyGet node, ir.DartType resultType) {}
@override
ir.DartType visitSuperPropertyGet(ir.SuperPropertyGet node) {
ir.DartType resultType;
if (node.interfaceTarget == null) {
// TODO(johnniwinther): Resolve and set the target here.
resultType = const ir.DynamicType();
} else {
ir.Class declaringClass = node.interfaceTarget.enclosingClass;
if (declaringClass.typeParameters.isEmpty) {
resultType = node.interfaceTarget.getterType;
} else {
ir.DartType receiver = typeEnvironment.hierarchy
.getTypeAsInstanceOf(typeEnvironment.thisType, declaringClass);
resultType = ir.Substitution.fromInterfaceType(receiver)
.substituteType(node.interfaceTarget.getterType);
}
}
_cache[node] = resultType;
handleSuperPropertyGet(node, resultType);
return resultType;
}
void handleSuperPropertySet(
ir.SuperPropertySet node, ir.DartType valueType) {}
@override
ir.DartType visitSuperPropertySet(ir.SuperPropertySet node) {
ir.DartType valueType = super.visitSuperPropertySet(node);
handleSuperPropertySet(node, valueType);
return valueType;
}
void handleSuperMethodInvocation(ir.SuperMethodInvocation node,
ArgumentTypes argumentTypes, ir.DartType returnType) {}
@override
ir.DartType visitSuperMethodInvocation(ir.SuperMethodInvocation node) {
ArgumentTypes argumentTypes = _visitArguments(node.arguments);
ir.DartType returnType;
if (node.interfaceTarget == null) {
// TODO(johnniwinther): Resolve and set the target here.
returnType = const ir.DynamicType();
} else {
ir.Class superclass = node.interfaceTarget.enclosingClass;
ir.InterfaceType receiverType = typeEnvironment.hierarchy
.getTypeAsInstanceOf(typeEnvironment.thisType, superclass);
returnType = ir.Substitution.fromInterfaceType(receiverType)
.substituteType(node.interfaceTarget.function.returnType);
returnType = ir.Substitution.fromPairs(
node.interfaceTarget.function.typeParameters,
node.arguments.types)
.substituteType(returnType);
}
_cache[node] = returnType;
handleSuperMethodInvocation(node, argumentTypes, returnType);
return returnType;
}
@override
ir.DartType visitLogicalExpression(ir.LogicalExpression node) {
visitNode(node.left);
visitNode(node.right);
return super.visitLogicalExpression(node);
}
@override
ir.DartType visitNot(ir.Not node) {
visitNode(node.operand);
return super.visitNot(node);
}
@override
ir.DartType visitConditionalExpression(ir.ConditionalExpression node) {
visitNode(node.condition);
visitNode(node.then);
visitNode(node.otherwise);
return super.visitConditionalExpression(node);
}
void handleIsExpression(ir.IsExpression node) {}
@override
ir.DartType visitIsExpression(ir.IsExpression node) {
visitNode(node.operand);
handleIsExpression(node);
return super.visitIsExpression(node);
}
@override
ir.DartType visitLet(ir.Let node) {
visitNode(node.variable.initializer);
return super.visitLet(node);
}
ir.DartType _computeInstantiationType(
ir.Instantiation node, ir.FunctionType expressionType) {
return ir.Substitution.fromPairs(
expressionType.typeParameters, node.typeArguments)
.substituteType(expressionType.withoutTypeParameters);
}
void handleInstantiation(ir.Instantiation node,
ir.FunctionType expressionType, ir.DartType resultType) {}
@override
ir.DartType visitInstantiation(ir.Instantiation node) {
ir.FunctionType expressionType = visitNode(node.expression);
ir.DartType resultType = _computeInstantiationType(node, expressionType);
_cache[node] = resultType;
handleInstantiation(node, expressionType, resultType);
return resultType;
}
@override
Null visitBlock(ir.Block node) => visitNodes(node.statements);
ir.DartType visitExpressionStatement(ir.ExpressionStatement node) {
visitNode(node.expression);
return null;
}
void handleAsExpression(ir.AsExpression node) {}
@override
ir.DartType visitAsExpression(ir.AsExpression node) {
visitNode(node.operand);
handleAsExpression(node);
return super.visitAsExpression(node);
}
void handleStringConcatenation(ir.StringConcatenation node) {}
@override
ir.DartType visitStringConcatenation(ir.StringConcatenation node) {
visitNodes(node.expressions);
handleStringConcatenation(node);
return super.visitStringConcatenation(node);
}
void handleIntLiteral(ir.IntLiteral node) {}
@override
ir.DartType visitIntLiteral(ir.IntLiteral node) {
handleIntLiteral(node);
return super.visitIntLiteral(node);
}
void handleDoubleLiteral(ir.DoubleLiteral node) {}
@override
ir.DartType visitDoubleLiteral(ir.DoubleLiteral node) {
handleDoubleLiteral(node);
return super.visitDoubleLiteral(node);
}
void handleBoolLiteral(ir.BoolLiteral node) {}
@override
ir.DartType visitBoolLiteral(ir.BoolLiteral node) {
handleBoolLiteral(node);
return super.visitBoolLiteral(node);
}
void handleStringLiteral(ir.StringLiteral node) {}
@override
ir.DartType visitStringLiteral(ir.StringLiteral node) {
handleStringLiteral(node);
return super.visitStringLiteral(node);
}
void handleSymbolLiteral(ir.SymbolLiteral node) {}
@override
ir.DartType visitSymbolLiteral(ir.SymbolLiteral node) {
handleSymbolLiteral(node);
return super.visitSymbolLiteral(node);
}
void handleNullLiteral(ir.NullLiteral node) {}
@override
ir.DartType visitNullLiteral(ir.NullLiteral node) {
handleNullLiteral(node);
return super.visitNullLiteral(node);
}
void handleListLiteral(ir.ListLiteral node) {}
@override
ir.DartType visitListLiteral(ir.ListLiteral node) {
visitNodes(node.expressions);
handleListLiteral(node);
return super.visitListLiteral(node);
}
void handleMapLiteral(ir.MapLiteral node) {}
@override
ir.DartType visitMapLiteral(ir.MapLiteral node) {
visitNodes(node.entries);
handleMapLiteral(node);
return super.visitMapLiteral(node);
}
@override
Null visitMapEntry(ir.MapEntry entry) {
visitNode(entry.key);
visitNode(entry.value);
}
void handleFunctionExpression(ir.FunctionExpression node) {}
@override
ir.DartType visitFunctionExpression(ir.FunctionExpression node) {
visitSignature(node.function);
visitNode(node.function.body);
handleFunctionExpression(node);
return super.visitFunctionExpression(node);
}
void handleThrow(ir.Throw node) {}
@override
ir.DartType visitThrow(ir.Throw node) {
visitNode(node.expression);
handleThrow(node);
return super.visitThrow(node);
}
@override
Null visitSwitchCase(ir.SwitchCase node) {
visitNodes(node.expressions);
visitNode(node.body);
}
@override
Null visitContinueSwitchStatement(ir.ContinueSwitchStatement node) {}
@override
Null visitLabeledStatement(ir.LabeledStatement node) {
visitNode(node.body);
}
@override
Null visitBreakStatement(ir.BreakStatement node) {}
@override
Null visitYieldStatement(ir.YieldStatement node) {
visitNode(node.expression);
}
@override
Null visitAssertInitializer(ir.AssertInitializer node) {
visitNode(node.statement);
}
void handleFieldInitializer(ir.FieldInitializer node) {}
@override
Null visitFieldInitializer(ir.FieldInitializer node) {
visitNode(node.value);
handleFieldInitializer(node);
}
void handleRedirectingInitializer(
ir.RedirectingInitializer node, ArgumentTypes argumentTypes) {}
@override
Null visitRedirectingInitializer(ir.RedirectingInitializer node) {
ArgumentTypes argumentTypes = _visitArguments(node.arguments);
handleRedirectingInitializer(node, argumentTypes);
}
void handleSuperInitializer(
ir.SuperInitializer node, ArgumentTypes argumentTypes) {}
@override
Null visitSuperInitializer(ir.SuperInitializer node) {
ArgumentTypes argumentTypes = _visitArguments(node.arguments);
handleSuperInitializer(node, argumentTypes);
}
@override
Null visitLocalInitializer(ir.LocalInitializer node) {
visitNode(node.variable);
}
@override
ir.DartType visitNamedExpression(ir.NamedExpression node) =>
visitNode(node.value);
@override
Null visitEmptyStatement(ir.EmptyStatement node) {}
@override
Null visitForStatement(ir.ForStatement node) {
visitNodes(node.variables);
visitNode(node.condition);
visitNodes(node.updates);
visitNode(node.body);
}
void handleForInStatement(ir.ForInStatement node, ir.DartType iterableType) {}
@override
Null visitForInStatement(ir.ForInStatement node) {
visitNode(node.variable);
ir.DartType iterableType = visitNode(node.iterable);
visitNode(node.body);
handleForInStatement(node, iterableType);
}
@override
Null visitDoStatement(ir.DoStatement node) {
visitNode(node.body);
visitNode(node.condition);
}
@override
Null visitWhileStatement(ir.WhileStatement node) {
visitNode(node.condition);
visitNode(node.body);
}
void handleSwitchStatement(ir.SwitchStatement node) {}
@override
Null visitSwitchStatement(ir.SwitchStatement node) {
visitNode(node.expression);
visitNodes(node.cases);
handleSwitchStatement(node);
}
@override
Null visitReturnStatement(ir.ReturnStatement node) {
visitNode(node.expression);
}
@override
Null visitIfStatement(ir.IfStatement node) {
visitNode(node.condition);
visitNode(node.then);
visitNode(node.otherwise);
}
@override
Null visitTryCatch(ir.TryCatch node) {
visitNode(node.body);
visitNodes(node.catches);
}
void handleCatch(ir.Catch node) {}
@override
Null visitCatch(ir.Catch node) {
handleCatch(node);
visitNode(node.body);
}
@override
Null visitTryFinally(ir.TryFinally node) {
visitNode(node.body);
visitNode(node.finalizer);
}
void handleTypeLiteral(ir.TypeLiteral node) {}
@override
ir.DartType visitTypeLiteral(ir.TypeLiteral node) {
handleTypeLiteral(node);
return super.visitTypeLiteral(node);
}
void handleLoadLibrary(ir.LoadLibrary node) {}
@override
ir.DartType visitLoadLibrary(ir.LoadLibrary node) {
handleLoadLibrary(node);
return super.visitLoadLibrary(node);
}
void handleAssertStatement(ir.AssertStatement node) {}
@override
Null visitAssertStatement(ir.AssertStatement node) {
visitNode(node.condition);
visitNode(node.message);
handleAssertStatement(node);
}
void handleFunctionDeclaration(ir.FunctionDeclaration node) {}
@override
Null visitFunctionDeclaration(ir.FunctionDeclaration node) {
visitSignature(node.function);
visitNode(node.function.body);
handleFunctionDeclaration(node);
}
void handleParameter(ir.VariableDeclaration node) {}
void visitParameter(ir.VariableDeclaration node) {
visitNode(node.initializer);
handleParameter(node);
}
void handleSignature(ir.FunctionNode node) {}
void visitSignature(ir.FunctionNode node) {
node.positionalParameters.forEach(visitParameter);
node.namedParameters.forEach(visitParameter);
handleSignature(node);
}
void handleProcedure(ir.Procedure node) {}
@override
Null visitProcedure(ir.Procedure node) {
typeEnvironment.thisType =
node.enclosingClass != null ? node.enclosingClass.thisType : null;
visitSignature(node.function);
visitNode(node.function.body);
handleProcedure(node);
typeEnvironment.thisType = null;
}
void handleConstructor(ir.Constructor node) {}
@override
Null visitConstructor(ir.Constructor node) {
typeEnvironment.thisType = node.enclosingClass.thisType;
visitSignature(node.function);
visitNodes(node.initializers);
visitNode(node.function.body);
handleConstructor(node);
typeEnvironment.thisType = null;
}
void handleField(ir.Field node) {}
@override
Null visitField(ir.Field node) {
typeEnvironment.thisType =
node.enclosingClass != null ? node.enclosingClass.thisType : null;
visitNode(node.initializer);
handleField(node);
typeEnvironment.thisType = null;
}
void handleVariableDeclaration(ir.VariableDeclaration node) {}
@override
Null visitVariableDeclaration(ir.VariableDeclaration node) {
ir.DartType type = visitNode(node.initializer);
if (node.initializer != null &&
variableScopeModel != null &&
variableScopeModel.isEffectivelyFinal(node)) {
node.type = type;
}
handleVariableDeclaration(node);
}
}
class ArgumentTypes {
final List<ir.DartType> positional;
final List<ir.DartType> named;
ArgumentTypes(this.positional, this.named);
}