OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library simple_types_inferrer; | 5 library simple_types_inferrer; |
6 | 6 |
7 import 'dart:collection' show Queue, LinkedHashSet; | 7 import 'dart:collection' show Queue, LinkedHashSet; |
8 | 8 |
9 import '../closure.dart' show ClosureClassMap, ClosureScope; | 9 import '../closure.dart' show ClosureClassMap, ClosureScope; |
10 import '../dart_types.dart' show DartType, FunctionType, TypeKind; | 10 import '../dart_types.dart' show DartType, FunctionType, TypeKind; |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
106 SentinelTypeMask(this.name) : super(null, 0, false); | 106 SentinelTypeMask(this.name) : super(null, 0, false); |
107 | 107 |
108 bool operator==(other) { | 108 bool operator==(other) { |
109 return identical(this, other); | 109 return identical(this, other); |
110 } | 110 } |
111 | 111 |
112 TypeMask nullable() { | 112 TypeMask nullable() { |
113 throw 'Unsupported operation'; | 113 throw 'Unsupported operation'; |
114 } | 114 } |
115 | 115 |
116 TypeMask intersection(TypeMask other, Compiler compiler) { | |
117 return other; | |
118 } | |
119 | |
116 bool get isNullable => true; | 120 bool get isNullable => true; |
117 | 121 |
118 String toString() => '$name sentinel type mask'; | 122 String toString() => '$name sentinel type mask'; |
119 } | 123 } |
120 | 124 |
121 final OPTIMISTIC = 0; | 125 final OPTIMISTIC = 0; |
122 final RETRY = 1; | 126 final RETRY = 1; |
123 final PESSIMISTIC = 2; | 127 final PESSIMISTIC = 2; |
124 | 128 |
125 class SimpleTypesInferrer extends TypesInferrer { | 129 class SimpleTypesInferrer extends TypesInferrer { |
(...skipping 516 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
642 bool isNativeElement(Element element) { | 646 bool isNativeElement(Element element) { |
643 if (element.isNative()) return true; | 647 if (element.isNative()) return true; |
644 return element.isMember() | 648 return element.isMember() |
645 && element.getEnclosingClass().isNative() | 649 && element.getEnclosingClass().isNative() |
646 && element.isField(); | 650 && element.isField(); |
647 } | 651 } |
648 | 652 |
649 bool internalRecordType(Element analyzedElement, | 653 bool internalRecordType(Element analyzedElement, |
650 TypeMask newType, | 654 TypeMask newType, |
651 Map<Element, TypeMask> types) { | 655 Map<Element, TypeMask> types) { |
656 if (compiler.trustTypeAnnotations) { | |
657 var annotation = analyzedElement.computeType(compiler); | |
658 if (types == returnTypeOf) { | |
659 assert(annotation is FunctionType); | |
660 annotation = annotation.returnType; | |
661 } | |
662 newType = narrowType(newType, annotation); | |
663 } | |
664 | |
652 // Fields and native methods of native classes are handled | 665 // Fields and native methods of native classes are handled |
653 // specially when querying for their type or return type. | 666 // specially when querying for their type or return type. |
654 if (isNativeElement(analyzedElement)) return false; | 667 if (isNativeElement(analyzedElement)) return false; |
655 assert(newType != null); | 668 assert(newType != null); |
656 TypeMask existing = types[analyzedElement]; | 669 TypeMask existing = types[analyzedElement]; |
657 types[analyzedElement] = newType; | 670 types[analyzedElement] = newType; |
658 // If the return type is useful, say it has changed. | 671 // If the return type is useful, say it has changed. |
659 return existing != newType | 672 return existing != newType |
660 && !isDynamicType(newType) | 673 && !isDynamicType(newType) |
661 && newType != nullType; | 674 && newType != nullType; |
(...skipping 15 matching lines...) Expand all Loading... | |
677 var elementType = element.computeType(compiler); | 690 var elementType = element.computeType(compiler); |
678 if (elementType.kind != TypeKind.FUNCTION) { | 691 if (elementType.kind != TypeKind.FUNCTION) { |
679 return dynamicType; | 692 return dynamicType; |
680 } | 693 } |
681 return typeOfNativeBehavior( | 694 return typeOfNativeBehavior( |
682 native.NativeBehavior.ofMethod(element, compiler)); | 695 native.NativeBehavior.ofMethod(element, compiler)); |
683 }); | 696 }); |
684 } | 697 } |
685 TypeMask returnType = returnTypeOf[element]; | 698 TypeMask returnType = returnTypeOf[element]; |
686 if (returnType == null) { | 699 if (returnType == null) { |
687 return dynamicType; | 700 if (compiler.trustTypeAnnotations |
701 && (element.isFunction() | |
702 || element.isGetter() | |
703 || element.isFactoryConstructor())) { | |
704 returnType = narrowType( | |
kasperl
2013/05/16 13:17:20
Couldn't you do the narrowing when you record? (li
ngeoffray
2013/05/17 14:34:25
This is in case the element has not been analyzed
| |
705 dynamicType, element.computeType(compiler).returnType); | |
706 } else { | |
707 returnType = dynamicType; | |
708 } | |
688 } | 709 } |
689 assert(returnType != null); | 710 assert(returnType != null); |
690 return returnType; | 711 return returnType; |
691 } | 712 } |
692 | 713 |
693 TypeMask typeOfNativeBehavior(native.NativeBehavior nativeBehavior) { | 714 TypeMask typeOfNativeBehavior(native.NativeBehavior nativeBehavior) { |
694 if (nativeBehavior == null) return dynamicType; | 715 if (nativeBehavior == null) return dynamicType; |
695 List typesReturned = nativeBehavior.typesReturned; | 716 List typesReturned = nativeBehavior.typesReturned; |
696 if (typesReturned.isEmpty) return dynamicType; | 717 if (typesReturned.isEmpty) return dynamicType; |
697 TypeMask returnType; | 718 TypeMask returnType; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
742 element = element.implementation; | 763 element = element.implementation; |
743 if (isNativeElement(element) && element.isField()) { | 764 if (isNativeElement(element) && element.isField()) { |
744 var type = typeOf.putIfAbsent(element, () { | 765 var type = typeOf.putIfAbsent(element, () { |
745 return new TypeMask.subtype(element.computeType(compiler).asRaw()); | 766 return new TypeMask.subtype(element.computeType(compiler).asRaw()); |
746 }); | 767 }); |
747 assert(type != null); | 768 assert(type != null); |
748 return type; | 769 return type; |
749 } | 770 } |
750 TypeMask type = typeOf[element]; | 771 TypeMask type = typeOf[element]; |
751 if (type == null) { | 772 if (type == null) { |
752 return dynamicType; | 773 if (compiler.trustTypeAnnotations |
774 && (element.isField() | |
775 || element.isParameter())) { | |
776 type = narrowType(dynamicType, element.computeType(compiler)); | |
777 } else { | |
778 type = dynamicType; | |
779 } | |
753 } | 780 } |
754 assert(type != null); | 781 assert(type != null); |
755 return type; | 782 return type; |
756 } | 783 } |
757 | 784 |
758 /** | 785 /** |
759 * Returns the union of the types of all elements that match | 786 * Returns the union of the types of all elements that match |
760 * the called [selector]. | 787 * the called [selector]. |
761 */ | 788 */ |
762 TypeMask typeOfSelector(Selector selector) { | 789 TypeMask typeOfSelector(Selector selector) { |
(...skipping 476 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1239 return secondType; | 1266 return secondType; |
1240 } else if (isDynamicType(firstType)) { | 1267 } else if (isDynamicType(firstType)) { |
1241 return firstType; | 1268 return firstType; |
1242 } else { | 1269 } else { |
1243 TypeMask union = firstType.union(secondType, compiler); | 1270 TypeMask union = firstType.union(secondType, compiler); |
1244 // TODO(kasperl): If the union isn't nullable it seems wasteful | 1271 // TODO(kasperl): If the union isn't nullable it seems wasteful |
1245 // to use dynamic. Fix that. | 1272 // to use dynamic. Fix that. |
1246 return union.containsAll(compiler) ? dynamicType : union; | 1273 return union.containsAll(compiler) ? dynamicType : union; |
1247 } | 1274 } |
1248 } | 1275 } |
1276 | |
1277 TypeMask narrowType(TypeMask type, DartType annotation) { | |
1278 if (annotation.isDynamic) return type; | |
1279 TypeMask otherType; | |
1280 if (annotation.kind == TypeKind.TYPEDEF | |
1281 || annotation.kind == TypeKind.FUNCTION) { | |
1282 otherType = functionType.nullable(); | |
1283 } else if (annotation.kind != TypeKind.INTERFACE) { | |
1284 return type; | |
1285 } else { | |
1286 otherType = new TypeMask.subtype(annotation); | |
1287 } | |
1288 return type.intersection(otherType, compiler); | |
1289 } | |
1249 } | 1290 } |
1250 | 1291 |
1251 /** | 1292 /** |
1252 * Placeholder for inferred arguments types on sends. | 1293 * Placeholder for inferred arguments types on sends. |
1253 */ | 1294 */ |
1254 class ArgumentsTypes { | 1295 class ArgumentsTypes { |
1255 final List<TypeMask> positional; | 1296 final List<TypeMask> positional; |
1256 final Map<SourceString, TypeMask> named; | 1297 final Map<SourceString, TypeMask> named; |
1257 ArgumentsTypes(this.positional, named) | 1298 ArgumentsTypes(this.positional, named) |
1258 : this.named = (named == null) ? new Map<SourceString, TypeMask>() : named; | 1299 : this.named = (named == null) ? new Map<SourceString, TypeMask>() : named; |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1318 assert(type != null); | 1359 assert(type != null); |
1319 if (capturedAndBoxed.contains(local) || inTryBlock) { | 1360 if (capturedAndBoxed.contains(local) || inTryBlock) { |
1320 // If a local is captured and boxed, or is set in a try block, | 1361 // If a local is captured and boxed, or is set in a try block, |
1321 // we compute the LUB of its assignments. | 1362 // we compute the LUB of its assignments. |
1322 // | 1363 // |
1323 // We don't know if an assignment in a try block | 1364 // We don't know if an assignment in a try block |
1324 // will be executed, so all assigments in that block are | 1365 // will be executed, so all assigments in that block are |
1325 // potential types after we have left it. | 1366 // potential types after we have left it. |
1326 type = inferrer.computeLUB(locals[local], type); | 1367 type = inferrer.computeLUB(locals[local], type); |
1327 } | 1368 } |
1328 locals[local] = type; | 1369 locals[local] = type; |
kasperl
2013/05/16 13:17:20
Shouldn't this use narrowing as well?
ngeoffray
2013/05/17 14:34:25
Done.
| |
1329 } | 1370 } |
1330 | 1371 |
1331 void setCapturedAndBoxed(Element local) { | 1372 void setCapturedAndBoxed(Element local) { |
1332 capturedAndBoxed.add(local); | 1373 capturedAndBoxed.add(local); |
1333 } | 1374 } |
1334 | 1375 |
1335 /** | 1376 /** |
1336 * Merge handlers [first] and [second] into [:this:] and returns | 1377 * Merge handlers [first] and [second] into [:this:] and returns |
1337 * whether the merge changed one of the variables types in [first]. | 1378 * whether the merge changed one of the variables types in [first]. |
1338 */ | 1379 */ |
(...skipping 682 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2021 return inferrer.boolType; | 2062 return inferrer.boolType; |
2022 } else if (const SourceString("!") == op.source) { | 2063 } else if (const SourceString("!") == op.source) { |
2023 node.visitChildren(this); | 2064 node.visitChildren(this); |
2024 return inferrer.boolType; | 2065 return inferrer.boolType; |
2025 } else if (const SourceString("is") == op.source) { | 2066 } else if (const SourceString("is") == op.source) { |
2026 node.visitChildren(this); | 2067 node.visitChildren(this); |
2027 return inferrer.boolType; | 2068 return inferrer.boolType; |
2028 } else if (const SourceString("as") == op.source) { | 2069 } else if (const SourceString("as") == op.source) { |
2029 TypeMask receiverType = visit(node.receiver); | 2070 TypeMask receiverType = visit(node.receiver); |
2030 DartType type = elements.getType(node.arguments.head); | 2071 DartType type = elements.getType(node.arguments.head); |
2031 if (type.isDynamic) return receiverType; | 2072 return inferrer.narrowType(receiverType, type); |
2032 TypeMask asType = type.kind == TypeKind.TYPEDEF | |
2033 ? inferrer.functionType.nullable() | |
2034 : new TypeMask.subtype(type); | |
2035 // TODO(ngeoffray): Remove when inferrer.dynamicType is a proper | |
2036 // TypeMask. | |
2037 if (inferrer.isDynamicType(receiverType)) return asType; | |
2038 return receiverType.intersection(asType, compiler); | |
2039 } else if (node.isParameterCheck) { | 2073 } else if (node.isParameterCheck) { |
2040 node.visitChildren(this); | 2074 node.visitChildren(this); |
2041 return inferrer.boolType; | 2075 return inferrer.boolType; |
2042 } else if (node.argumentsNode is Prefix) { | 2076 } else if (node.argumentsNode is Prefix) { |
2043 // Unary operator. | 2077 // Unary operator. |
2044 return visitDynamicSend(node); | 2078 return visitDynamicSend(node); |
2045 } else if (const SourceString('===') == op.source | 2079 } else if (const SourceString('===') == op.source |
2046 || const SourceString('!==') == op.source) { | 2080 || const SourceString('!==') == op.source) { |
2047 node.visitChildren(this); | 2081 node.visitChildren(this); |
2048 return inferrer.boolType; | 2082 return inferrer.boolType; |
(...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2355 } | 2389 } |
2356 | 2390 |
2357 TypeMask visitParenthesizedExpression(ParenthesizedExpression node) { | 2391 TypeMask visitParenthesizedExpression(ParenthesizedExpression node) { |
2358 return visit(node.expression); | 2392 return visit(node.expression); |
2359 } | 2393 } |
2360 | 2394 |
2361 void internalError(String reason, {Node node}) { | 2395 void internalError(String reason, {Node node}) { |
2362 compiler.internalError(reason, node: node); | 2396 compiler.internalError(reason, node: node); |
2363 } | 2397 } |
2364 } | 2398 } |
OLD | NEW |