| 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 '../common.dart'; | 5 import '../common.dart'; |
| 6 import '../common/names.dart'; | 6 import '../common/names.dart'; |
| 7 import '../compiler.dart'; | 7 import '../compiler.dart'; |
| 8 import '../constants/expressions.dart'; | 8 import '../constants/expressions.dart'; |
| 9 import '../constants/values.dart'; | 9 import '../constants/values.dart'; |
| 10 import '../elements/elements.dart'; | 10 import '../elements/elements.dart'; |
| 11 import '../elements/entities.dart'; | 11 import '../elements/entities.dart'; |
| 12 import '../resolution/tree_elements.dart'; | 12 import '../resolution/tree_elements.dart'; |
| 13 import '../tree/nodes.dart' as ast; | 13 import '../tree/nodes.dart' as ast; |
| 14 import '../types/constants.dart'; | |
| 15 import '../types/types.dart'; | 14 import '../types/types.dart'; |
| 16 import '../universe/selector.dart'; | |
| 17 import '../util/util.dart'; | 15 import '../util/util.dart'; |
| 18 import '../world.dart'; | 16 import '../world.dart'; |
| 19 import 'closure_tracer.dart'; | |
| 20 import 'debug.dart' as debug; | |
| 21 import 'locals_handler.dart'; | |
| 22 import 'builder.dart'; | 17 import 'builder.dart'; |
| 23 import 'builder_kernel.dart'; | 18 import 'builder_kernel.dart'; |
| 24 import 'inferrer_engine.dart'; | 19 import 'inferrer_engine.dart'; |
| 25 import 'type_graph_dump.dart'; | |
| 26 import 'type_graph_nodes.dart'; | 20 import 'type_graph_nodes.dart'; |
| 27 import 'type_system.dart'; | 21 import 'type_system.dart'; |
| 28 | 22 |
| 29 class AstInferrerEngine extends InferrerEngineImpl<ast.Node> { | 23 class AstInferrerEngine extends InferrerEngineImpl<ast.Node> { |
| 30 AstInferrerEngine(Compiler compiler, ClosedWorld closedWorld, | 24 AstInferrerEngine(Compiler compiler, ClosedWorld closedWorld, |
| 31 ClosedWorldRefiner closedWorldRefiner, FunctionEntity mainElement) | 25 ClosedWorldRefiner closedWorldRefiner, FunctionEntity mainElement) |
| 32 : super(compiler, closedWorld, closedWorldRefiner, mainElement, | 26 : super(compiler, closedWorld, closedWorldRefiner, mainElement, |
| 33 const TypeSystemStrategyImpl()); | 27 const TypeSystemStrategyImpl()); |
| 34 | 28 |
| 35 GlobalTypeInferenceElementData<ast.Node> createElementData() => | 29 GlobalTypeInferenceElementData<ast.Node> createElementData() => |
| 36 new AstGlobalTypeInferenceElementData(); | 30 new AstGlobalTypeInferenceElementData(); |
| 37 | 31 |
| 38 void runOverAllElements() { | 32 void analyzeAllElements() { |
| 39 if (compiler.disableTypeInference) return; | |
| 40 if (compiler.options.verbose) { | |
| 41 compiler.progress.reset(); | |
| 42 } | |
| 43 sortResolvedAsts().forEach((ResolvedAst resolvedAst) { | 33 sortResolvedAsts().forEach((ResolvedAst resolvedAst) { |
| 44 if (compiler.shouldPrintProgress) { | 34 if (compiler.shouldPrintProgress) { |
| 45 reporter.log('Added $addedInGraph elements in inferencing graph.'); | 35 reporter.log('Added $addedInGraph elements in inferencing graph.'); |
| 46 compiler.progress.reset(); | 36 compiler.progress.reset(); |
| 47 } | 37 } |
| 48 // This also forces the creation of the [ElementTypeInformation] to ensure | 38 // This also forces the creation of the [ElementTypeInformation] to ensure |
| 49 // it is in the graph. | 39 // it is in the graph. |
| 50 MemberElement member = resolvedAst.element; | 40 MemberElement member = resolvedAst.element; |
| 51 ast.Node body; | 41 ast.Node body; |
| 52 if (resolvedAst.kind == ResolvedAstKind.PARSED) { | 42 if (resolvedAst.kind == ResolvedAstKind.PARSED) { |
| 53 body = resolvedAst.body; | 43 body = resolvedAst.body; |
| 54 } | 44 } |
| 55 types.withMember(member, () => analyze(member, body, null)); | 45 types.withMember(member, () => analyze(member, body, null)); |
| 56 }); | 46 }); |
| 57 reporter.log('Added $addedInGraph elements in inferencing graph.'); | 47 reporter.log('Added $addedInGraph elements in inferencing graph.'); |
| 58 | |
| 59 TypeGraphDump dump = debug.PRINT_GRAPH ? new TypeGraphDump(this) : null; | |
| 60 | |
| 61 dump?.beforeAnalysis(); | |
| 62 buildWorkQueue(); | |
| 63 refine(); | |
| 64 | |
| 65 // Try to infer element types of lists and compute their escape information. | |
| 66 types.allocatedLists.values.forEach((TypeInformation info) { | |
| 67 analyzeListAndEnqueue(info); | |
| 68 }); | |
| 69 | |
| 70 // Try to infer the key and value types for maps and compute the values' | |
| 71 // escape information. | |
| 72 types.allocatedMaps.values.forEach((TypeInformation info) { | |
| 73 analyzeMapAndEnqueue(info); | |
| 74 }); | |
| 75 | |
| 76 Set<FunctionEntity> bailedOutOn = new Set<FunctionEntity>(); | |
| 77 | |
| 78 // Trace closures to potentially infer argument types. | |
| 79 types.allocatedClosures.forEach((dynamic info) { | |
| 80 void trace( | |
| 81 Iterable<FunctionEntity> elements, ClosureTracerVisitor tracer) { | |
| 82 tracer.run(); | |
| 83 if (!tracer.continueAnalyzing) { | |
| 84 elements.forEach((FunctionEntity _element) { | |
| 85 MethodElement element = _element; | |
| 86 MethodElement implementation = element.implementation; | |
| 87 closedWorldRefiner.registerMightBePassedToApply(element); | |
| 88 if (debug.VERBOSE) { | |
| 89 print("traced closure $element as ${true} (bail)"); | |
| 90 } | |
| 91 implementation.functionSignature | |
| 92 .forEachParameter((FormalElement _parameter) { | |
| 93 ParameterElement parameter = _parameter; | |
| 94 types | |
| 95 .getInferredTypeOfParameter(parameter) | |
| 96 .giveUp(this, clearAssignments: false); | |
| 97 }); | |
| 98 }); | |
| 99 bailedOutOn.addAll(elements); | |
| 100 return; | |
| 101 } | |
| 102 elements | |
| 103 .where((e) => !bailedOutOn.contains(e)) | |
| 104 .forEach((FunctionEntity _element) { | |
| 105 MethodElement element = _element; | |
| 106 MethodElement implementation = element.implementation; | |
| 107 implementation.functionSignature | |
| 108 .forEachParameter((FormalElement _parameter) { | |
| 109 ParameterElement parameter = _parameter; | |
| 110 ParameterTypeInformation info = | |
| 111 types.getInferredTypeOfParameter(parameter); | |
| 112 info.maybeResume(); | |
| 113 workQueue.add(info); | |
| 114 }); | |
| 115 if (tracer.tracedType.mightBePassedToFunctionApply) { | |
| 116 closedWorldRefiner.registerMightBePassedToApply(element); | |
| 117 } | |
| 118 if (debug.VERBOSE) { | |
| 119 print("traced closure $element as " | |
| 120 "${closedWorldRefiner | |
| 121 .getCurrentlyKnownMightBePassedToApply(element)}"); | |
| 122 } | |
| 123 }); | |
| 124 } | |
| 125 | |
| 126 if (info is ClosureTypeInformation) { | |
| 127 Iterable<FunctionEntity> elements = [info.closure]; | |
| 128 trace(elements, new ClosureTracerVisitor(elements, info, this)); | |
| 129 } else if (info is CallSiteTypeInformation) { | |
| 130 if (info is StaticCallSiteTypeInformation && | |
| 131 info.selector != null && | |
| 132 info.selector.isCall) { | |
| 133 // This is a constructor call to a class with a call method. So we | |
| 134 // need to trace the call method here. | |
| 135 MethodElement calledElement = info.calledElement; | |
| 136 assert(calledElement.isGenerativeConstructor); | |
| 137 ClassElement cls = calledElement.enclosingClass; | |
| 138 MethodElement callMethod = cls.lookupMember(Identifiers.call); | |
| 139 if (callMethod == null) { | |
| 140 callMethod = cls.lookupMember(Identifiers.noSuchMethod_); | |
| 141 } | |
| 142 assert(callMethod != null, failedAt(cls)); | |
| 143 Iterable<FunctionEntity> elements = [callMethod]; | |
| 144 trace(elements, new ClosureTracerVisitor(elements, info, this)); | |
| 145 } else { | |
| 146 // We only are interested in functions here, as other targets | |
| 147 // of this closure call are not a root to trace but an intermediate | |
| 148 // for some other function. | |
| 149 Iterable<FunctionEntity> elements = new List<FunctionEntity>.from( | |
| 150 info.callees.where((e) => e.isFunction)); | |
| 151 trace(elements, new ClosureTracerVisitor(elements, info, this)); | |
| 152 } | |
| 153 } else if (info is MemberTypeInformation) { | |
| 154 trace(<FunctionEntity>[info.member], | |
| 155 new StaticTearOffClosureTracerVisitor(info.member, info, this)); | |
| 156 } else if (info is ParameterTypeInformation) { | |
| 157 failedAt( | |
| 158 NO_LOCATION_SPANNABLE, 'Unexpected closure allocation info $info'); | |
| 159 } | |
| 160 }); | |
| 161 | |
| 162 dump?.beforeTracing(); | |
| 163 | |
| 164 // Reset all nodes that use lists/maps that have been inferred, as well | |
| 165 // as nodes that use elements fetched from these lists/maps. The | |
| 166 // workset for a new run of the analysis will be these nodes. | |
| 167 Set<TypeInformation> seenTypes = new Set<TypeInformation>(); | |
| 168 while (!workQueue.isEmpty) { | |
| 169 TypeInformation info = workQueue.remove(); | |
| 170 if (seenTypes.contains(info)) continue; | |
| 171 // If the node cannot be reset, we do not need to update its users either. | |
| 172 if (!info.reset(this)) continue; | |
| 173 seenTypes.add(info); | |
| 174 workQueue.addAll(info.users); | |
| 175 } | |
| 176 | |
| 177 workQueue.addAll(seenTypes); | |
| 178 refine(); | |
| 179 | |
| 180 if (debug.PRINT_SUMMARY) { | |
| 181 types.allocatedLists.values.forEach((_info) { | |
| 182 ListTypeInformation info = _info; | |
| 183 print('${info.type} ' | |
| 184 'for ${info.originalType.allocationNode} ' | |
| 185 'at ${info.originalType.allocationElement} ' | |
| 186 'after ${info.refineCount}'); | |
| 187 }); | |
| 188 types.allocatedMaps.values.forEach((_info) { | |
| 189 MapTypeInformation info = _info; | |
| 190 print('${info.type} ' | |
| 191 'for ${info.originalType.allocationNode} ' | |
| 192 'at ${info.originalType.allocationElement} ' | |
| 193 'after ${info.refineCount}'); | |
| 194 }); | |
| 195 types.allocatedClosures.forEach((TypeInformation info) { | |
| 196 if (info is ElementTypeInformation) { | |
| 197 print('${info.getInferredSignature(types)} for ' | |
| 198 '${info.debugName}'); | |
| 199 } else if (info is ClosureTypeInformation) { | |
| 200 print('${info.getInferredSignature(types)} for ' | |
| 201 '${info.debugName}'); | |
| 202 } else if (info is DynamicCallSiteTypeInformation) { | |
| 203 for (MemberEntity target in info.targets) { | |
| 204 if (target is FunctionEntity) { | |
| 205 print( | |
| 206 '${types.getInferredSignatureOfMethod(target)} for ${target}')
; | |
| 207 } else { | |
| 208 print( | |
| 209 '${types.getInferredTypeOfMember(target).type} for ${target}')
; | |
| 210 } | |
| 211 } | |
| 212 } else if (info is StaticCallSiteTypeInformation) { | |
| 213 ClassElement cls = info.calledElement.enclosingClass; | |
| 214 MethodElement callMethod = cls.lookupMember(Identifiers.call); | |
| 215 print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}'); | |
| 216 } else { | |
| 217 print('${info.type} for some unknown kind of closure'); | |
| 218 } | |
| 219 }); | |
| 220 analyzedElements.forEach((MemberEntity elem) { | |
| 221 TypeInformation type = types.getInferredTypeOfMember(elem); | |
| 222 print('${elem} :: ${type} from ${type.assignments} '); | |
| 223 }); | |
| 224 } | |
| 225 dump?.afterAnalysis(); | |
| 226 | |
| 227 reporter.log('Inferred $overallRefineCount types.'); | |
| 228 | |
| 229 processLoopInformation(); | |
| 230 } | 48 } |
| 231 | 49 |
| 232 void analyze(MemberEntity element, ast.Node body, ArgumentsTypes arguments) { | 50 void forEachParameter( |
| 233 assert(!(element is MemberElement && !element.isDeclaration)); | 51 covariant MethodElement method, void f(Local parameter)) { |
| 234 if (analyzedElements.contains(element)) return; | 52 MethodElement implementation = method.implementation; |
| 235 analyzedElements.add(element); | 53 implementation.functionSignature |
| 236 | 54 .forEachParameter((FormalElement _parameter) { |
| 237 dynamic visitor = compiler.options.kernelGlobalInference | 55 ParameterElement parameter = _parameter; |
| 238 ? new KernelTypeGraphBuilder(element, compiler, this) | 56 f(parameter); |
| 239 : new ElementGraphBuilder(element, compiler, this); | |
| 240 TypeInformation type; | |
| 241 reporter.withCurrentElement(element, () { | |
| 242 // ignore: UNDEFINED_METHOD | |
| 243 type = visitor.run(); | |
| 244 }); | 57 }); |
| 245 addedInGraph++; | |
| 246 | |
| 247 if (element.isField) { | |
| 248 FieldElement field = element; | |
| 249 if (field.isFinal || field.isConst) { | |
| 250 // If [element] is final and has an initializer, we record | |
| 251 // the inferred type. | |
| 252 if (body != null) { | |
| 253 if (type is! ListTypeInformation && type is! MapTypeInformation) { | |
| 254 // For non-container types, the constant handler does | |
| 255 // constant folding that could give more precise results. | |
| 256 ConstantExpression constant = field.constant; | |
| 257 if (constant != null) { | |
| 258 ConstantValue value = | |
| 259 compiler.backend.constants.getConstantValue(constant); | |
| 260 if (value != null) { | |
| 261 if (value.isFunction) { | |
| 262 FunctionConstantValue functionConstant = value; | |
| 263 MethodElement function = functionConstant.element; | |
| 264 type = types.allocateClosure(function); | |
| 265 } else { | |
| 266 // Although we might find a better type, we have to keep | |
| 267 // the old type around to ensure that we get a complete view | |
| 268 // of the type graph and do not drop any flow edges. | |
| 269 TypeMask refinedType = computeTypeMask(closedWorld, value); | |
| 270 assert(TypeMask.assertIsNormalized(refinedType, closedWorld)); | |
| 271 type = new NarrowTypeInformation(type, refinedType); | |
| 272 types.allocatedTypes.add(type); | |
| 273 } | |
| 274 } else { | |
| 275 assert( | |
| 276 field.isInstanceMember || | |
| 277 constant.isImplicit || | |
| 278 constant.isPotential, | |
| 279 failedAt( | |
| 280 field, | |
| 281 "Constant expression without value: " | |
| 282 "${constant.toStructuredText()}.")); | |
| 283 } | |
| 284 } | |
| 285 } | |
| 286 recordTypeOfField(field, type); | |
| 287 } else if (!element.isInstanceMember) { | |
| 288 recordTypeOfField(field, types.nullType); | |
| 289 } | |
| 290 } else if (body == null) { | |
| 291 // Only update types of static fields if there is no | |
| 292 // assignment. Instance fields are dealt with in the constructor. | |
| 293 if (element.isStatic || element.isTopLevel) { | |
| 294 recordTypeOfField(field, type); | |
| 295 } | |
| 296 } else { | |
| 297 recordTypeOfField(field, type); | |
| 298 } | |
| 299 if ((element.isStatic || element.isTopLevel) && | |
| 300 body != null && | |
| 301 !element.isConst) { | |
| 302 dynamic argument = body; | |
| 303 // TODO(13429): We could do better here by using the | |
| 304 // constant handler to figure out if it's a lazy field or not. | |
| 305 if (argument.asSend() != null || | |
| 306 (argument.asNewExpression() != null && !argument.isConst)) { | |
| 307 recordTypeOfField(field, types.nullType); | |
| 308 } | |
| 309 } | |
| 310 } else { | |
| 311 FunctionEntity method = element; | |
| 312 recordReturnType(method, type); | |
| 313 } | |
| 314 } | 58 } |
| 315 | 59 |
| 316 void updateParameterAssignments(TypeInformation caller, MemberEntity callee, | 60 FunctionEntity lookupCallMethod(covariant ClassElement cls) { |
| 317 ArgumentsTypes arguments, Selector selector, TypeMask mask, | 61 MethodElement callMethod = cls.lookupMember(Identifiers.call); |
| 318 {bool remove, bool addToQueue: true}) { | 62 if (callMethod == null) { |
| 319 if (callee.name == Identifiers.noSuchMethod_) return; | 63 callMethod = cls.lookupMember(Identifiers.noSuchMethod_); |
| 320 if (callee.isField) { | 64 } |
| 321 if (selector.isSetter) { | 65 return callMethod; |
| 322 ElementTypeInformation info = types.getInferredTypeOfMember(callee); | 66 } |
| 323 if (remove) { | 67 |
| 324 info.removeAssignment(arguments.positional[0]); | 68 TypeInformation computeMemberTypeInformation( |
| 325 } else { | 69 MemberEntity member, ast.Node body) { |
| 326 info.addAssignment(arguments.positional[0]); | 70 dynamic visitor = compiler.options.kernelGlobalInference |
| 327 } | 71 ? new KernelTypeGraphBuilder(member, compiler, this) |
| 328 if (addToQueue) workQueue.add(info); | 72 : new ElementGraphBuilder(member, compiler, this); |
| 73 // ignore: UNDEFINED_METHOD |
| 74 return visitor.run(); |
| 75 } |
| 76 |
| 77 bool isFieldInitializerPotentiallyNull( |
| 78 FieldEntity field, ast.Node initializer) { |
| 79 dynamic argument = initializer; |
| 80 // TODO(13429): We could do better here by using the |
| 81 // constant handler to figure out if it's a lazy field or not. |
| 82 return argument.asSend() != null || |
| 83 (argument.asNewExpression() != null && !argument.isConst); |
| 84 } |
| 85 |
| 86 ConstantValue getFieldConstant(covariant FieldElement field) { |
| 87 ConstantExpression constant = field.constant; |
| 88 if (constant != null) { |
| 89 ConstantValue value = |
| 90 compiler.backend.constants.getConstantValue(constant); |
| 91 if (value == null) { |
| 92 assert( |
| 93 field.isInstanceMember || |
| 94 constant.isImplicit || |
| 95 constant.isPotential, |
| 96 failedAt( |
| 97 field, |
| 98 "Constant expression without value: " |
| 99 "${constant.toStructuredText()}.")); |
| 329 } | 100 } |
| 330 } else if (callee.isGetter) { | 101 return value; |
| 331 return; | |
| 332 } else if (selector != null && selector.isGetter) { | |
| 333 // We are tearing a function off and thus create a closure. | |
| 334 assert(callee.isFunction); | |
| 335 MethodElement method = callee; | |
| 336 MemberTypeInformation info = types.getInferredTypeOfMember(method); | |
| 337 if (remove) { | |
| 338 info.closurizedCount--; | |
| 339 } else { | |
| 340 info.closurizedCount++; | |
| 341 if (Elements.isStaticOrTopLevel(method)) { | |
| 342 types.allocatedClosures.add(info); | |
| 343 } else { | |
| 344 // We add the call-site type information here so that we | |
| 345 // can benefit from further refinement of the selector. | |
| 346 types.allocatedClosures.add(caller); | |
| 347 } | |
| 348 FunctionElement function = method.implementation; | |
| 349 FunctionSignature signature = function.functionSignature; | |
| 350 signature.forEachParameter((FormalElement _parameter) { | |
| 351 ParameterElement parameter = _parameter; | |
| 352 ParameterTypeInformation info = | |
| 353 types.getInferredTypeOfParameter(parameter); | |
| 354 info.tagAsTearOffClosureParameter(this); | |
| 355 if (addToQueue) workQueue.add(info); | |
| 356 }); | |
| 357 } | |
| 358 } else { | |
| 359 MethodElement method = callee; | |
| 360 FunctionElement function = method.implementation; | |
| 361 FunctionSignature signature = function.functionSignature; | |
| 362 int parameterIndex = 0; | |
| 363 bool visitingRequiredParameter = true; | |
| 364 signature.forEachParameter((FormalElement _parameter) { | |
| 365 ParameterElement parameter = _parameter; | |
| 366 if (signature.hasOptionalParameters && | |
| 367 parameter == signature.optionalParameters.first) { | |
| 368 visitingRequiredParameter = false; | |
| 369 } | |
| 370 TypeInformation type = visitingRequiredParameter | |
| 371 ? arguments.positional[parameterIndex] | |
| 372 : signature.optionalParametersAreNamed | |
| 373 ? arguments.named[parameter.name] | |
| 374 : parameterIndex < arguments.positional.length | |
| 375 ? arguments.positional[parameterIndex] | |
| 376 : null; | |
| 377 if (type == null) type = getDefaultTypeOfParameter(parameter); | |
| 378 TypeInformation info = types.getInferredTypeOfParameter(parameter); | |
| 379 if (remove) { | |
| 380 info.removeAssignment(type); | |
| 381 } else { | |
| 382 info.addAssignment(type); | |
| 383 } | |
| 384 parameterIndex++; | |
| 385 if (addToQueue) workQueue.add(info); | |
| 386 }); | |
| 387 } | 102 } |
| 103 return null; |
| 388 } | 104 } |
| 389 | 105 |
| 390 // Sorts the resolved elements by size. We do this for this inferrer | 106 // Sorts the resolved elements by size. We do this for this inferrer |
| 391 // to get the same results for [ListTracer] compared to the | 107 // to get the same results for [ListTracer] compared to the |
| 392 // [SimpleTypesInferrer]. | 108 // [SimpleTypesInferrer]. |
| 393 Iterable<ResolvedAst> sortResolvedAsts() { | 109 Iterable<ResolvedAst> sortResolvedAsts() { |
| 394 int max = 0; | 110 int max = 0; |
| 395 Map<int, Setlet<ResolvedAst>> methodSizes = <int, Setlet<ResolvedAst>>{}; | 111 Map<int, Setlet<ResolvedAst>> methodSizes = <int, Setlet<ResolvedAst>>{}; |
| 396 compiler.enqueuer.resolution.processedEntities.forEach((_element) { | 112 compiler.enqueuer.resolution.processedEntities.forEach((_element) { |
| 397 MemberElement element = _element; | 113 MemberElement element = _element; |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 521 @override | 237 @override |
| 522 bool checkPhiNode(ast.Node node) { | 238 bool checkPhiNode(ast.Node node) { |
| 523 return true; | 239 return true; |
| 524 } | 240 } |
| 525 | 241 |
| 526 @override | 242 @override |
| 527 bool checkClassEntity(covariant ClassElement cls) { | 243 bool checkClassEntity(covariant ClassElement cls) { |
| 528 return cls.isDeclaration; | 244 return cls.isDeclaration; |
| 529 } | 245 } |
| 530 } | 246 } |
| OLD | NEW |