| OLD | NEW |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'package:kernel/ast.dart' as ir; | 5 import 'package:kernel/ast.dart' as ir; |
| 6 | 6 |
| 7 import '../closure.dart'; | 7 import '../closure.dart'; |
| 8 import '../common.dart'; | 8 import '../common.dart'; |
| 9 import '../compiler.dart'; | 9 import '../constants/constant_system.dart'; |
| 10 import '../elements/entities.dart'; | 10 import '../elements/entities.dart'; |
| 11 import '../kernel/element_map.dart'; | 11 import '../options.dart'; |
| 12 import '../types/constants.dart'; |
| 12 import '../universe/side_effects.dart' show SideEffects; | 13 import '../universe/side_effects.dart' show SideEffects; |
| 14 import '../world.dart'; |
| 13 import 'inferrer_engine.dart'; | 15 import 'inferrer_engine.dart'; |
| 14 import 'locals_handler.dart'; | 16 import 'locals_handler.dart'; |
| 15 import 'type_graph_nodes.dart'; | 17 import 'type_graph_nodes.dart'; |
| 16 import 'type_system.dart'; | 18 import 'type_system.dart'; |
| 17 | 19 |
| 18 /// [KernelTypeGraphBuilder] constructs a type-inference graph for a particular | 20 /// [KernelTypeGraphBuilder] constructs a type-inference graph for a particular |
| 19 /// element. | 21 /// element. |
| 20 /// | 22 /// |
| 21 /// Calling [run] will start the work of visiting the body of the code to | 23 /// Calling [run] will start the work of visiting the body of the code to |
| 22 /// construct a set of inference-nodes that abstractly represent what the code | 24 /// construct a set of inference-nodes that abstractly represent what the code |
| 23 /// is doing. | 25 /// is doing. |
| 24 class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> { | 26 class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation> { |
| 25 final Compiler compiler; | 27 final CompilerOptions _options; |
| 26 final MemberEntity analyzedMember; | 28 final ClosedWorld _closedWorld; |
| 27 final ir.Node analyzedNode; | 29 final ClosureDataLookup<ir.Node> _closureDataLookup; |
| 28 final TypeSystem<ir.Node> types; | 30 final InferrerEngine<ir.Node> _inferrer; |
| 29 LocalsHandler locals; | 31 final TypeSystem<ir.Node> _types; |
| 30 final InferrerEngine<ir.Node> inferrer; | 32 final MemberEntity _analyzedMember; |
| 31 SideEffects sideEffects = new SideEffects.empty(); | 33 final ir.Node _analyzedNode; |
| 32 int loopLevel = 0; | 34 LocalsHandler _locals; |
| 33 bool get inLoop => loopLevel > 0; | |
| 34 TypeInformation returnType; | |
| 35 | 35 |
| 36 final Set<Local> capturedVariables = new Set<Local>(); | 36 TypeInformation _returnType; |
| 37 final Set<Local> _capturedVariables = new Set<Local>(); |
| 37 | 38 |
| 38 KernelTypeGraphBuilder.internal(this.analyzedMember, this.inferrer, | 39 KernelTypeGraphBuilder( |
| 39 this.compiler, this.locals, this.analyzedNode) | 40 this._options, |
| 40 : this.types = inferrer.types { | 41 this._closedWorld, |
| 41 if (locals != null) return; | 42 this._closureDataLookup, |
| 43 this._inferrer, |
| 44 this._analyzedMember, |
| 45 this._analyzedNode, |
| 46 [this._locals]) |
| 47 : this._types = _inferrer.types { |
| 48 if (_locals != null) return; |
| 42 | 49 |
| 43 FieldInitializationScope<ir.Node> fieldScope = | 50 FieldInitializationScope<ir.Node> fieldScope = |
| 44 analyzedNode is ir.Constructor | 51 _analyzedNode is ir.Constructor |
| 45 ? new FieldInitializationScope(types) | 52 ? new FieldInitializationScope(_types) |
| 46 : null; | 53 : null; |
| 47 locals = new LocalsHandler( | 54 _locals = new LocalsHandler( |
| 48 inferrer, types, compiler.options, analyzedNode, fieldScope); | 55 _inferrer, _types, _options, _analyzedNode, fieldScope); |
| 49 } | |
| 50 | |
| 51 factory KernelTypeGraphBuilder( | |
| 52 MemberEntity element, | |
| 53 Compiler compiler, | |
| 54 KernelToElementMapForBuilding elementMap, | |
| 55 InferrerEngine<ir.Node> inferrer, | |
| 56 ir.TreeNode analyzedNode, | |
| 57 [LocalsHandler<ir.Node> handler]) { | |
| 58 return new KernelTypeGraphBuilder.internal( | |
| 59 element, inferrer, compiler, handler, analyzedNode); | |
| 60 } | 56 } |
| 61 | 57 |
| 62 TypeInformation run() { | 58 TypeInformation run() { |
| 63 if (analyzedMember.isField) { | 59 if (_analyzedMember.isField) { |
| 64 if (analyzedNode == null || analyzedNode is ir.NullLiteral) { | 60 if (_analyzedNode == null || _analyzedNode is ir.NullLiteral) { |
| 65 // Eagerly bailout, because computing the closure data only | 61 // Eagerly bailout, because computing the closure data only |
| 66 // works for functions and field assignments. | 62 // works for functions and field assignments. |
| 67 return types.nullType; | 63 return _types.nullType; |
| 68 } | 64 } |
| 69 } | 65 } |
| 70 | 66 |
| 71 // Update the locals that are boxed in [locals]. These locals will | 67 // Update the locals that are boxed in [locals]. These locals will |
| 72 // be handled specially, in that we are computing their LUB at | 68 // be handled specially, in that we are computing their LUB at |
| 73 // each update, and reading them yields the type that was found in a | 69 // each update, and reading them yields the type that was found in a |
| 74 // previous analysis of [outermostElement]. | 70 // previous analysis of [outermostElement]. |
| 75 ClosureRepresentationInfo closureData = compiler | 71 ClosureRepresentationInfo closureData = |
| 76 .backendStrategy.closureDataLookup | 72 _closureDataLookup.getClosureInfoForMember(_analyzedMember); |
| 77 .getClosureInfoForMember(analyzedMember); | |
| 78 closureData.forEachCapturedVariable((variable, field) { | 73 closureData.forEachCapturedVariable((variable, field) { |
| 79 locals.setCaptured(variable, field); | 74 _locals.setCaptured(variable, field); |
| 80 }); | 75 }); |
| 81 closureData.forEachBoxedVariable((variable, field) { | 76 closureData.forEachBoxedVariable((variable, field) { |
| 82 locals.setCapturedAndBoxed(variable, field); | 77 _locals.setCapturedAndBoxed(variable, field); |
| 83 }); | 78 }); |
| 84 | 79 |
| 85 return analyzedNode.accept(this); | 80 return _analyzedNode.accept(this); |
| 86 } | 81 } |
| 87 | 82 |
| 88 void recordReturnType(TypeInformation type) { | 83 void recordReturnType(TypeInformation type) { |
| 89 FunctionEntity analyzedMethod = analyzedMember; | 84 FunctionEntity analyzedMethod = _analyzedMember; |
| 90 returnType = | 85 _returnType = |
| 91 inferrer.addReturnTypeForMethod(analyzedMethod, returnType, type); | 86 _inferrer.addReturnTypeForMethod(analyzedMethod, _returnType, type); |
| 92 } | 87 } |
| 93 | 88 |
| 94 void initializationIsIndefinite() { | 89 void initializationIsIndefinite() { |
| 95 MemberEntity member = analyzedMember; | 90 MemberEntity member = _analyzedMember; |
| 96 if (member is ConstructorEntity && member.isGenerativeConstructor) { | 91 if (member is ConstructorEntity && member.isGenerativeConstructor) { |
| 97 locals.fieldScope.isIndefinite = true; | 92 _locals.fieldScope.isIndefinite = true; |
| 98 } | 93 } |
| 99 } | 94 } |
| 100 | 95 |
| 101 TypeInformation visit(ir.Node node) { | 96 TypeInformation visit(ir.Node node) { |
| 102 return node == null ? null : node.accept(this); | 97 return node == null ? null : node.accept(this); |
| 103 } | 98 } |
| 104 | 99 |
| 105 @override | 100 @override |
| 106 TypeInformation visitFunctionNode(ir.FunctionNode node) { | 101 TypeInformation visitFunctionNode(ir.FunctionNode node) { |
| 107 // TODO(redemption): Handle constructors. | 102 // TODO(redemption): Handle constructors. |
| 108 // TODO(redemption): Handle native methods. | 103 // TODO(redemption): Handle native methods. |
| 109 // TODO(redemption): Set up parameters. | 104 // TODO(redemption): Set up parameters. |
| 110 visit(node.body); | 105 visit(node.body); |
| 111 switch (node.asyncMarker) { | 106 switch (node.asyncMarker) { |
| 112 case ir.AsyncMarker.Sync: | 107 case ir.AsyncMarker.Sync: |
| 113 if (returnType == null) { | 108 if (_returnType == null) { |
| 114 // No return in the body. | 109 // No return in the body. |
| 115 returnType = locals.seenReturnOrThrow | 110 _returnType = _locals.seenReturnOrThrow |
| 116 ? types.nonNullEmpty() // Body always throws. | 111 ? _types.nonNullEmpty() // Body always throws. |
| 117 : types.nullType; | 112 : _types.nullType; |
| 118 } else if (!locals.seenReturnOrThrow) { | 113 } else if (!_locals.seenReturnOrThrow) { |
| 119 // We haven'TypeInformation seen returns on all branches. So the metho
d may | 114 // We haven'TypeInformation seen returns on all branches. So the metho
d may |
| 120 // also return null. | 115 // also return null. |
| 121 recordReturnType(types.nullType); | 116 recordReturnType(_types.nullType); |
| 122 } | 117 } |
| 123 break; | 118 break; |
| 124 | 119 |
| 125 case ir.AsyncMarker.SyncStar: | 120 case ir.AsyncMarker.SyncStar: |
| 126 // TODO(asgerf): Maybe make a ContainerTypeMask for these? The type | 121 // TODO(asgerf): Maybe make a ContainerTypeMask for these? The type |
| 127 // contained is the method body's return type. | 122 // contained is the method body's return type. |
| 128 recordReturnType(types.syncStarIterableType); | 123 recordReturnType(_types.syncStarIterableType); |
| 129 break; | 124 break; |
| 130 | 125 |
| 131 case ir.AsyncMarker.Async: | 126 case ir.AsyncMarker.Async: |
| 132 recordReturnType(types.asyncFutureType); | 127 recordReturnType(_types.asyncFutureType); |
| 133 break; | 128 break; |
| 134 | 129 |
| 135 case ir.AsyncMarker.AsyncStar: | 130 case ir.AsyncMarker.AsyncStar: |
| 136 recordReturnType(types.asyncStarStreamType); | 131 recordReturnType(_types.asyncStarStreamType); |
| 137 break; | 132 break; |
| 138 case ir.AsyncMarker.SyncYielding: | 133 case ir.AsyncMarker.SyncYielding: |
| 139 failedAt( | 134 failedAt( |
| 140 analyzedMember, "Unexpected async marker: ${node.asyncMarker}"); | 135 _analyzedMember, "Unexpected async marker: ${node.asyncMarker}"); |
| 141 break; | 136 break; |
| 142 } | 137 } |
| 143 return returnType; | 138 return _returnType; |
| 144 } | 139 } |
| 145 | 140 |
| 146 @override | 141 @override |
| 147 TypeInformation defaultExpression(ir.Expression expression) { | 142 TypeInformation defaultExpression(ir.Expression expression) { |
| 148 // TODO(efortuna): Remove when more is implemented. | 143 // TODO(efortuna): Remove when more is implemented. |
| 149 return types.dynamicType; | 144 return _types.dynamicType; |
| 150 } | 145 } |
| 151 | 146 |
| 152 @override | 147 @override |
| 153 TypeInformation visitNullLiteral(ir.NullLiteral literal) { | 148 TypeInformation visitNullLiteral(ir.NullLiteral literal) { |
| 154 return types.nullType; | 149 return _types.nullType; |
| 155 } | 150 } |
| 156 | 151 |
| 157 @override | 152 @override |
| 158 TypeInformation visitBlock(ir.Block block) { | 153 TypeInformation visitBlock(ir.Block block) { |
| 159 for (ir.Statement statement in block.statements) { | 154 for (ir.Statement statement in block.statements) { |
| 160 statement.accept(this); | 155 statement.accept(this); |
| 161 if (locals.aborts) break; | 156 if (_locals.aborts) break; |
| 162 } | 157 } |
| 163 return null; | 158 return null; |
| 164 } | 159 } |
| 165 | 160 |
| 166 @override | 161 @override |
| 167 TypeInformation visitListLiteral(ir.ListLiteral listLiteral) { | 162 TypeInformation visitListLiteral(ir.ListLiteral listLiteral) { |
| 168 // We only set the type once. We don't need to re-visit the children | 163 // We only set the type once. We don't need to re-visit the children |
| 169 // when re-analyzing the node. | 164 // when re-analyzing the node. |
| 170 return inferrer.concreteTypes.putIfAbsent(listLiteral, () { | 165 return _inferrer.concreteTypes.putIfAbsent(listLiteral, () { |
| 171 TypeInformation elementType; | 166 TypeInformation elementType; |
| 172 int length = 0; | 167 int length = 0; |
| 173 for (ir.Expression element in listLiteral.expressions) { | 168 for (ir.Expression element in listLiteral.expressions) { |
| 174 TypeInformation type = element.accept(this); | 169 TypeInformation type = element.accept(this); |
| 175 elementType = elementType == null | 170 elementType = elementType == null |
| 176 ? types.allocatePhi(null, null, type, isTry: false) | 171 ? _types.allocatePhi(null, null, type, isTry: false) |
| 177 : types.addPhiInput(null, elementType, type); | 172 : _types.addPhiInput(null, elementType, type); |
| 178 length++; | 173 length++; |
| 179 } | 174 } |
| 180 elementType = elementType == null | 175 elementType = elementType == null |
| 181 ? types.nonNullEmpty() | 176 ? _types.nonNullEmpty() |
| 182 : types.simplifyPhi(null, null, elementType); | 177 : _types.simplifyPhi(null, null, elementType); |
| 183 TypeInformation containerType = | 178 TypeInformation containerType = |
| 184 listLiteral.isConst ? types.constListType : types.growableListType; | 179 listLiteral.isConst ? _types.constListType : _types.growableListType; |
| 185 return types.allocateList( | 180 return _types.allocateList( |
| 186 containerType, listLiteral, analyzedMember, elementType, length); | 181 containerType, listLiteral, _analyzedMember, elementType, length); |
| 187 }); | 182 }); |
| 188 } | 183 } |
| 189 | 184 |
| 190 @override | 185 @override |
| 191 TypeInformation visitReturnStatement(ir.ReturnStatement node) { | 186 TypeInformation visitReturnStatement(ir.ReturnStatement node) { |
| 192 ir.Node expression = node.expression; | 187 ir.Node expression = node.expression; |
| 193 recordReturnType( | 188 recordReturnType( |
| 194 expression == null ? types.nullType : expression.accept(this)); | 189 expression == null ? _types.nullType : expression.accept(this)); |
| 195 locals.seenReturnOrThrow = true; | 190 _locals.seenReturnOrThrow = true; |
| 196 initializationIsIndefinite(); | 191 initializationIsIndefinite(); |
| 197 return null; | 192 return null; |
| 198 } | 193 } |
| 194 |
| 195 @override |
| 196 TypeInformation visitIntLiteral(ir.IntLiteral node) { |
| 197 ConstantSystem constantSystem = _closedWorld.constantSystem; |
| 198 // The JavaScript backend may turn this literal into a double at |
| 199 // runtime. |
| 200 return _types.getConcreteTypeFor( |
| 201 computeTypeMask(_closedWorld, constantSystem.createInt(node.value))); |
| 202 } |
| 203 |
| 204 @override |
| 205 TypeInformation visitDoubleLiteral(ir.DoubleLiteral node) { |
| 206 ConstantSystem constantSystem = _closedWorld.constantSystem; |
| 207 // The JavaScript backend may turn this literal into an integer at |
| 208 // runtime. |
| 209 return _types.getConcreteTypeFor( |
| 210 computeTypeMask(_closedWorld, constantSystem.createDouble(node.value))); |
| 211 } |
| 199 } | 212 } |
| OLD | NEW |