| // 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; |
| |
| /// Visitor that computes the static type of an expression. |
| /// |
| /// This visitor doesn't traverse subtrees that are not needed for computing |
| /// the static type. |
| // TODO(johnniwinther): Add improved type promotion to handle negative |
| // reasoning. |
| abstract class StaticTypeVisitor extends ir.Visitor<ir.DartType> { |
| ir.TypeEnvironment _typeEnvironment; |
| |
| StaticTypeVisitor(this._typeEnvironment); |
| |
| fail(String message) => message; |
| |
| ir.TypeEnvironment get typeEnvironment => _typeEnvironment; |
| |
| /// 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; |
| } |
| |
| @override |
| ir.DartType defaultNode(ir.Node node) { |
| return null; |
| } |
| |
| ir.DartType visitNode(ir.Node node) { |
| return node?.accept(this); |
| } |
| |
| Null visitNodes(List<ir.Node> nodes) { |
| for (ir.Node node in nodes) { |
| visitNode(node); |
| } |
| } |
| |
| ir.DartType defaultExpression(ir.Expression node) { |
| throw fail('Unhandled node $node (${node.runtimeType})'); |
| } |
| |
| @override |
| ir.DartType visitAsExpression(ir.AsExpression node) { |
| return node.type; |
| } |
| |
| @override |
| ir.DartType visitAwaitExpression(ir.AwaitExpression node) { |
| return typeEnvironment.unfutureType(visitNode(node.operand)); |
| } |
| |
| @override |
| ir.DartType visitBoolLiteral(ir.BoolLiteral node) => typeEnvironment.boolType; |
| |
| @override |
| ir.DartType visitCheckLibraryIsLoaded(ir.CheckLibraryIsLoaded node) => |
| typeEnvironment.objectType; |
| |
| @override |
| ir.DartType visitStringLiteral(ir.StringLiteral node) => |
| typeEnvironment.stringType; |
| |
| @override |
| ir.DartType visitStringConcatenation(ir.StringConcatenation node) { |
| return typeEnvironment.stringType; |
| } |
| |
| @override |
| ir.DartType visitNullLiteral(ir.NullLiteral node) => const ir.BottomType(); |
| |
| @override |
| ir.DartType visitIntLiteral(ir.IntLiteral node) => typeEnvironment.intType; |
| |
| @override |
| ir.DartType visitDoubleLiteral(ir.DoubleLiteral node) => |
| typeEnvironment.doubleType; |
| |
| @override |
| ir.DartType visitSymbolLiteral(ir.SymbolLiteral node) => |
| typeEnvironment.symbolType; |
| |
| @override |
| ir.DartType visitListLiteral(ir.ListLiteral node) { |
| return typeEnvironment.literalListType(node.typeArgument); |
| } |
| |
| @override |
| ir.DartType visitMapLiteral(ir.MapLiteral node) { |
| return typeEnvironment.literalMapType(node.keyType, node.valueType); |
| } |
| |
| @override |
| ir.DartType visitVariableGet(ir.VariableGet node) => |
| node.promotedType ?? node.variable.type; |
| |
| @override |
| ir.DartType visitVariableSet(ir.VariableSet node) { |
| return visitNode(node.value); |
| } |
| |
| ir.DartType computePropertyGetType( |
| ir.PropertyGet node, ir.DartType receiverType) { |
| ir.Member interfaceTarget = node.interfaceTarget; |
| 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(); |
| } |
| |
| @override |
| ir.DartType visitPropertyGet(ir.PropertyGet node) { |
| ir.DartType receiverType = visitNode(node.receiver); |
| return computePropertyGetType(node, receiverType); |
| } |
| |
| @override |
| ir.DartType visitPropertySet(ir.PropertySet node) { |
| return visitNode(node.value); |
| } |
| |
| @override |
| ir.DartType visitDirectPropertyGet(ir.DirectPropertyGet node) { |
| ir.DartType receiverType = visitNode(node.receiver); |
| ir.Class superclass = node.target.enclosingClass; |
| receiverType = getTypeAsInstanceOf(receiverType, superclass); |
| return ir.Substitution.fromInterfaceType(receiverType) |
| .substituteType(node.target.getterType); |
| } |
| |
| @override |
| ir.DartType visitDirectMethodInvocation(ir.DirectMethodInvocation node) { |
| ir.DartType receiverType = visitNode(node.receiver); |
| if (typeEnvironment.isOverloadedArithmeticOperator(node.target)) { |
| ir.DartType argumentType = visitNode(node.arguments.positional[0]); |
| return typeEnvironment.getTypeOfOverloadedArithmetic( |
| receiverType, argumentType); |
| } |
| ir.Class superclass = node.target.enclosingClass; |
| receiverType = getTypeAsInstanceOf(receiverType, superclass); |
| ir.DartType returnType = ir.Substitution.fromInterfaceType(receiverType) |
| .substituteType(node.target.function.returnType); |
| return ir.Substitution.fromPairs( |
| node.target.function.typeParameters, node.arguments.types) |
| .substituteType(returnType); |
| } |
| |
| @override |
| ir.DartType visitDirectPropertySet(ir.DirectPropertySet node) { |
| return visitNode(node.value); |
| } |
| |
| @override |
| ir.DartType visitThisExpression(ir.ThisExpression node) => |
| typeEnvironment.thisType; |
| |
| /// 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.DartType computeMethodInvocationType( |
| ir.MethodInvocation node, ir.DartType receiverType) { |
| ir.Member interfaceTarget = node.interfaceTarget; |
| if (interfaceTarget != null) { |
| if (isSpecialCasedBinaryOperator(interfaceTarget)) { |
| ir.DartType argumentType = visitNode(node.arguments.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) { |
| return ir.Substitution.fromPairs( |
| getterType.typeParameters, node.arguments.types) |
| .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(); |
| } |
| |
| @override |
| ir.DartType visitMethodInvocation(ir.MethodInvocation node) { |
| ir.DartType receiverType = visitNode(node.receiver); |
| return computeMethodInvocationType(node, receiverType); |
| } |
| |
| @override |
| ir.DartType visitStaticGet(ir.StaticGet node) => node.target.getterType; |
| |
| @override |
| ir.DartType visitStaticSet(ir.StaticSet node) { |
| return visitNode(node.value); |
| } |
| |
| @override |
| ir.DartType visitStaticInvocation(ir.StaticInvocation node) { |
| return ir.Substitution.fromPairs( |
| node.target.function.typeParameters, node.arguments.types) |
| .substituteType(node.target.function.returnType); |
| } |
| |
| @override |
| ir.DartType visitConstructorInvocation(ir.ConstructorInvocation node) { |
| return node.arguments.types.isEmpty |
| ? node.target.enclosingClass.rawType |
| : new ir.InterfaceType( |
| node.target.enclosingClass, node.arguments.types); |
| } |
| |
| @override |
| ir.DartType visitSuperPropertyGet(ir.SuperPropertyGet node) { |
| if (node.interfaceTarget == null) { |
| // TODO(johnniwinther): Resolve and set the target here. |
| return const ir.DynamicType(); |
| } |
| ir.Class declaringClass = node.interfaceTarget.enclosingClass; |
| if (declaringClass.typeParameters.isEmpty) { |
| return node.interfaceTarget.getterType; |
| } |
| ir.DartType receiver = typeEnvironment.hierarchy |
| .getTypeAsInstanceOf(typeEnvironment.thisType, declaringClass); |
| return ir.Substitution.fromInterfaceType(receiver) |
| .substituteType(node.interfaceTarget.getterType); |
| } |
| |
| @override |
| ir.DartType visitSuperPropertySet(ir.SuperPropertySet node) { |
| return visitNode(node.value); |
| } |
| |
| @override |
| ir.DartType visitSuperMethodInvocation(ir.SuperMethodInvocation node) { |
| if (node.interfaceTarget == null) { |
| // TODO(johnniwinther): Resolve and set the target here. |
| return const ir.DynamicType(); |
| } |
| ir.Class superclass = node.interfaceTarget.enclosingClass; |
| ir.InterfaceType receiverType = typeEnvironment.hierarchy |
| .getTypeAsInstanceOf(typeEnvironment.thisType, superclass); |
| ir.DartType returnType = ir.Substitution.fromInterfaceType(receiverType) |
| .substituteType(node.interfaceTarget.function.returnType); |
| return ir.Substitution.fromPairs( |
| node.interfaceTarget.function.typeParameters, node.arguments.types) |
| .substituteType(returnType); |
| } |
| |
| @override |
| ir.DartType visitThrow(ir.Throw node) => const ir.BottomType(); |
| |
| @override |
| ir.DartType visitRethrow(ir.Rethrow node) => const ir.BottomType(); |
| |
| @override |
| ir.DartType visitLogicalExpression(ir.LogicalExpression node) => |
| typeEnvironment.boolType; |
| |
| @override |
| ir.DartType visitNot(ir.Not node) { |
| return typeEnvironment.boolType; |
| } |
| |
| @override |
| ir.DartType visitConditionalExpression(ir.ConditionalExpression node) { |
| return node.staticType; |
| } |
| |
| @override |
| ir.DartType visitIsExpression(ir.IsExpression node) { |
| return typeEnvironment.boolType; |
| } |
| |
| @override |
| ir.DartType visitTypeLiteral(ir.TypeLiteral node) => typeEnvironment.typeType; |
| |
| @override |
| ir.DartType visitFunctionExpression(ir.FunctionExpression node) { |
| return node.function.functionType; |
| } |
| |
| @override |
| ir.DartType visitLet(ir.Let node) { |
| return visitNode(node.body); |
| } |
| |
| ir.DartType computeInstantiationType( |
| ir.Instantiation node, ir.FunctionType expressionType) { |
| return ir.Substitution.fromPairs( |
| expressionType.typeParameters, node.typeArguments) |
| .substituteType(expressionType.withoutTypeParameters); |
| } |
| |
| @override |
| ir.DartType visitInstantiation(ir.Instantiation node) { |
| ir.FunctionType expressionType = visitNode(node.expression); |
| return computeInstantiationType(node, expressionType); |
| } |
| |
| @override |
| ir.DartType visitInvalidExpression(ir.InvalidExpression node) => |
| const ir.BottomType(); |
| |
| @override |
| ir.DartType visitLoadLibrary(ir.LoadLibrary node) { |
| return typeEnvironment.futureType(const ir.DynamicType()); |
| } |
| } |
| |
| /// Visitor that computes the static type of an expression using a cache to |
| /// avoid recomputations. |
| class CachingStaticTypeVisitor extends StaticTypeVisitor { |
| Map<ir.Expression, ir.DartType> _cache = {}; |
| |
| CachingStaticTypeVisitor(ir.TypeEnvironment typeEnvironment) |
| : super(typeEnvironment); |
| |
| @override |
| ir.DartType visitNode(ir.Node node) { |
| ir.DartType result; |
| if (node is ir.Expression) { |
| result = _cache[node]; |
| if (result != null) return result; |
| result = super.visitNode(node); |
| _cache[node] = result; |
| } else { |
| result = super.visitNode(node); |
| } |
| return result; |
| } |
| } |
| |
| /// Visitor that traverse the whole tree while returning the static type of |
| /// expressions. |
| abstract class StaticTypeTraversalVisitor extends StaticTypeVisitor { |
| StaticTypeTraversalVisitor(ir.TypeEnvironment typeEnvironment) |
| : super(typeEnvironment); |
| |
| @override |
| ir.DartType defaultNode(ir.Node node) { |
| node.visitChildren(this); |
| return null; |
| } |
| |
| Null defaultMember(ir.Member node) { |
| typeEnvironment.thisType = |
| node.enclosingClass != null ? node.enclosingClass.thisType : null; |
| node.visitChildren(this); |
| typeEnvironment.thisType = null; |
| return null; |
| } |
| |
| ir.DartType visitExpressionStatement(ir.ExpressionStatement node) { |
| visitNode(node.expression); |
| return null; |
| } |
| |
| @override |
| ir.DartType visitAsExpression(ir.AsExpression node) { |
| visitNode(node.operand); |
| return super.visitAsExpression(node); |
| } |
| |
| @override |
| ir.DartType visitStringConcatenation(ir.StringConcatenation node) { |
| visitNodes(node.expressions); |
| return super.visitStringConcatenation(node); |
| } |
| |
| @override |
| ir.DartType visitListLiteral(ir.ListLiteral node) { |
| visitNodes(node.expressions); |
| return super.visitListLiteral(node); |
| } |
| |
| @override |
| ir.DartType visitMapLiteral(ir.MapLiteral node) { |
| visitNodes(node.entries); |
| return super.visitMapLiteral(node); |
| } |
| |
| @override |
| ir.DartType visitPropertySet(ir.PropertySet node) { |
| visitNode(node.receiver); |
| return super.visitPropertySet(node); |
| } |
| |
| @override |
| ir.DartType visitDirectMethodInvocation(ir.DirectMethodInvocation node) { |
| visitNodes(node.arguments.positional); |
| visitNodes(node.arguments.named); |
| return super.visitDirectMethodInvocation(node); |
| } |
| |
| @override |
| ir.DartType visitDirectPropertySet(ir.DirectPropertySet node) { |
| visitNode(node.receiver); |
| return super.visitDirectPropertySet(node); |
| } |
| |
| @override |
| ir.DartType visitMethodInvocation(ir.MethodInvocation node) { |
| if (isSpecialCasedBinaryOperator(node.interfaceTarget)) { |
| return super.visitMethodInvocation(node); |
| } |
| visitNodes(node.arguments.positional); |
| visitNodes(node.arguments.named); |
| return super.visitMethodInvocation(node); |
| } |
| |
| @override |
| ir.DartType visitStaticInvocation(ir.StaticInvocation node) { |
| visitNodes(node.arguments.positional); |
| visitNodes(node.arguments.named); |
| return super.visitStaticInvocation(node); |
| } |
| |
| @override |
| ir.DartType visitConstructorInvocation(ir.ConstructorInvocation node) { |
| visitNodes(node.arguments.positional); |
| visitNodes(node.arguments.named); |
| return super.visitConstructorInvocation(node); |
| } |
| |
| @override |
| ir.DartType visitSuperMethodInvocation(ir.SuperMethodInvocation node) { |
| visitNodes(node.arguments.positional); |
| visitNodes(node.arguments.named); |
| return super.visitSuperMethodInvocation(node); |
| } |
| |
| @override |
| ir.DartType visitThrow(ir.Throw node) { |
| visitNode(node.expression); |
| return super.visitThrow(node); |
| } |
| |
| @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); |
| } |
| |
| @override |
| ir.DartType visitIsExpression(ir.IsExpression node) { |
| visitNode(node.operand); |
| return super.visitIsExpression(node); |
| } |
| |
| @override |
| ir.DartType visitFunctionExpression(ir.FunctionExpression node) { |
| visitNode(node.function.body); |
| return super.visitFunctionExpression(node); |
| } |
| |
| @override |
| ir.DartType visitLet(ir.Let node) { |
| visitNode(node.variable.initializer); |
| return super.visitLet(node); |
| } |
| } |