Chromium Code Reviews| Index: lib/compiler/implementation/types/concrete_types_inferrer.dart |
| diff --git a/lib/compiler/implementation/types/concrete_types_inferrer.dart b/lib/compiler/implementation/types/concrete_types_inferrer.dart |
| index 0a835de894b4cdf04df536755be659eb6928b3e4..f324961819e45ccaec06d4a8dbd4be6a384597c7 100644 |
| --- a/lib/compiler/implementation/types/concrete_types_inferrer.dart |
| +++ b/lib/compiler/implementation/types/concrete_types_inferrer.dart |
| @@ -144,6 +144,10 @@ class UnionType implements ConcreteType { |
| return result; |
| } |
| + // TODO(polux): Collapse {num, int, ...}, {num, double, ...} and |
| + // {int, double,...} into {num, ...} as an optimization. It will require |
| + // UnionType to know about these class elements, which is cumbersome because |
| + // there are no nested classes. We need factory methods instead. |
| ConcreteType union(ConcreteType other) { |
| if (other.isUnkown()) { |
| return const UnknownConcreteType(); |
| @@ -249,6 +253,7 @@ class ConcreteTypeCartesianProductIterator implements Iterator { |
| class BaseTypes { |
| final BaseType intBaseType; |
| final BaseType doubleBaseType; |
| + final BaseType numBaseType; |
| final BaseType boolBaseType; |
| final BaseType stringBaseType; |
| final BaseType listBaseType; |
| @@ -258,6 +263,7 @@ class BaseTypes { |
| BaseTypes(Compiler compiler) : |
| intBaseType = new ClassBaseType(compiler.intClass), |
| doubleBaseType = new ClassBaseType(compiler.doubleClass), |
| + numBaseType = new ClassBaseType(compiler.numClass), |
| boolBaseType = new ClassBaseType(compiler.boolClass), |
| stringBaseType = new ClassBaseType(compiler.stringClass), |
| listBaseType = new ClassBaseType(compiler.listClass), |
| @@ -403,6 +409,73 @@ class ConcreteTypesInferrer { |
| callers = new Map<FunctionElement, Set<FunctionElement>>(), |
| readers = new Map<Element, Set<FunctionElement>>(); |
| + /** |
| + * Populates [cache] with ad hoc rules like: |
| + * |
| + * {int} + {int} -> {int} |
| + * {int} + {double} -> {num} |
| + * {int} + {num} -> {double} |
| + * ... |
| + */ |
| + populateCacheWithBuiltinRules() { |
| + // Builds the environment that would be looked up if we were to analyze |
| + // o.method(arg) where o has concrete type {receiverType} and arg has |
| + // concrete type {argumentType}. |
| + ConcreteTypesEnvironment makeEnvironment(BaseType receiverType, |
| + FunctionElement method, |
| + BaseType argumentType) { |
| + ArgumentsTypes argumentsTypes = new ArgumentsTypes( |
| + [new ConcreteType.singleton(argumentType)], |
| + new Map()); |
| + Map<Element, ConcreteType> argumentMap = |
| + associateArguments(method, argumentsTypes); |
| + ConcreteTypeCartesianProduct product = |
| + new ConcreteTypeCartesianProduct(receiverType, argumentMap); |
| + return product.iterator().next(); |
|
karlklose
2012/10/23 10:58:15
Can we construct this object more directly instead
polux
2012/10/25 12:02:01
Absolutely! Done.
On 2012/10/23 10:58:15, karlklo
|
| + } |
| + |
| + // Adds the rule {receiverType}.method({argumentType}) -> {returnType} |
| + // to cache. |
| + void rule(ClassBaseType receiverType, String method, |
| + BaseType argumentType, BaseType returnType) { |
| + // The following line shouldn't be needed but the mock compiler doesn't |
| + // resolve num for some reason. |
| + if (receiverType.element.resolutionState == STATE_NOT_STARTED) { |
|
karlklose
2012/10/23 10:58:15
use ensureResolved?
polux
2012/10/25 12:02:01
Done.
|
| + compiler.resolver.resolveClass(receiverType.element); |
| + } |
| + FunctionElement methodElement = |
| + receiverType.element.lookupMember(new SourceString(method)); |
| + ConcreteTypesEnvironment environment = |
| + makeEnvironment(receiverType, methodElement, argumentType); |
| + Map<ConcreteTypesEnvironment, ConcreteType> map = |
| + cache.containsKey(methodElement) |
| + ? cache[methodElement] |
| + : new Map<ConcreteTypesEnvironment, ConcreteType>(); |
| + map[environment] = new ConcreteType.singleton(returnType); |
| + cache[methodElement] = map; |
| + } |
| + |
| + // The hardcoded typing rules. |
| + final ClassBaseType int = baseTypes.intBaseType; |
| + final ClassBaseType double = baseTypes.doubleBaseType; |
| + final ClassBaseType num = baseTypes.numBaseType; |
| + for (String operator in ['add', 'mul', 'sub']) { |
| + final String method = r"operator$".concat(operator); |
| + |
| + rule(int, method, int, int); |
| + rule(int, method, double, num); |
| + rule(int, method, num, num); |
| + |
| + rule(double, method, double, double); |
| + rule(double, method, int, num); |
| + rule(double, method, num, num); |
| + |
| + rule(num, method, int, num); |
| + rule(num, method, double, num); |
| + rule(num, method, num, num); |
| + } |
| + } |
| + |
| // --- utility methods --- |
| /** |
| @@ -554,7 +627,7 @@ class ConcreteTypesInferrer { |
| Map<Element, ConcreteType> associateArguments(FunctionElement function, |
| ArgumentsTypes argumentsTypes) { |
| final Map<Element, ConcreteType> result = new Map<Element, ConcreteType>(); |
| - final FunctionSignature signature = function.functionSignature; |
| + final FunctionSignature signature = function.computeSignature(compiler); |
| // too many arguments |
| if (argumentsTypes.length > signature.parameterCount) { |
| return null; |
| @@ -644,6 +717,11 @@ class ConcreteTypesInferrer { |
| ConcreteType analyzeMethod(FunctionElement element, |
| ConcreteTypesEnvironment environment) { |
| FunctionExpression tree = element.parseNode(compiler); |
| + // This should never happen since we only deal with concrete types, except |
| + // for external methods whose typing rules have not been hardcoded yet. |
| + if (!tree.hasBody()) { |
| + return new ConcreteType.unknown(); |
| + } |
| TreeElements elements = |
| compiler.enqueuer.resolution.resolvedElements[element]; |
| Visitor visitor = |
| @@ -702,6 +780,7 @@ class ConcreteTypesInferrer { |
| void analyzeMain(Element element) { |
| baseTypes = new BaseTypes(compiler); |
| cache[element] = new Map<ConcreteTypesEnvironment, ConcreteType>(); |
| + populateCacheWithBuiltinRules(); |
| try { |
| workQueue.addLast( |
| new InferenceWorkItem(element, new ConcreteTypesEnvironment())); |
| @@ -1099,6 +1178,7 @@ class TypeInferrerVisitor extends ResolvedVisitor<ConcreteType> { |
| } |
| ConcreteType visitLiteralMapEntry(LiteralMapEntry node) { |
| + // We don't need to visit the key, it's always a string. |
| return analyze(node.value); |
| } |
| @@ -1139,7 +1219,7 @@ class TypeInferrerVisitor extends ResolvedVisitor<ConcreteType> { |
| } |
| ConcreteType visitOperatorSend(Send node) { |
| - inferrer.fail(node, 'not implemented'); |
| + return visitDynamicSend(node); |
| } |
| ConcreteType visitGetterSend(Send node) { |
| @@ -1206,14 +1286,54 @@ class TypeInferrerVisitor extends ResolvedVisitor<ConcreteType> { |
| inferrer.fail(node, 'not implemented'); |
| } |
| + // TODO(polux): handle unary operators |
| + final Set<SourceString> operators = new Set<SourceString>() |
|
karlklose
2012/10/23 10:58:15
When all operators are supported, we should be abl
polux
2012/10/25 12:02:01
Done.
|
| + ..add(const SourceString('==')) |
| + ..add(const SourceString('!=')) |
| + ..add(const SourceString('~')) |
| + ..add(const SourceString('[]')) |
| + ..add(const SourceString('[]=')) |
| + ..add(const SourceString('*')) |
| + ..add(const SourceString('*=')) |
| + ..add(const SourceString('/')) |
| + ..add(const SourceString('/=')) |
| + ..add(const SourceString('%')) |
| + ..add(const SourceString('%=')) |
| + ..add(const SourceString('~/')) |
| + ..add(const SourceString('~/=')) |
| + ..add(const SourceString('+')) |
| + ..add(const SourceString('+=')) |
| + ..add(const SourceString('-')) |
| + ..add(const SourceString('-=')) |
| + ..add(const SourceString('<<')) |
| + ..add(const SourceString('<<=')) |
| + ..add(const SourceString('>>')) |
| + ..add(const SourceString('>>=')) |
| + ..add(const SourceString('>=')) |
| + ..add(const SourceString('>')) |
| + ..add(const SourceString('<=')) |
| + ..add(const SourceString('<')) |
| + ..add(const SourceString('&')) |
| + ..add(const SourceString('&=')) |
| + ..add(const SourceString('^')) |
| + ..add(const SourceString('^=')) |
| + ..add(const SourceString('|')) |
| + ..add(const SourceString('|=')); |
| + |
| + SourceString canonicalize(SourceString s) { |
|
karlklose
2012/10/23 10:58:15
Perhaps 'canonicalMethodName'?
polux
2012/10/25 12:02:01
Done.
|
| + return operators.contains(s) |
| + ? Elements.constructOperatorName(s, false) |
| + : s; |
| + } |
| + |
| ConcreteType visitDynamicSend(Send node) { |
| ConcreteType receiverType = analyze(node.receiver); |
| ConcreteType result = new ConcreteType.empty(); |
| final argumentsTypes = analyzeArguments(node.arguments); |
| if (receiverType.isUnkown()) { |
| - List<FunctionElement> methods = |
| - inferrer.getMembersByName(node.selector.asIdentifier().source); |
| + List<FunctionElement> methods = inferrer.getMembersByName( |
| + canonicalize(node.selector.asIdentifier().source)); |
| for (final method in methods) { |
| inferrer.addCaller(method, currentMethod); |
| Element classElem = method.enclosingElement; |
| @@ -1225,9 +1345,10 @@ class TypeInferrerVisitor extends ResolvedVisitor<ConcreteType> { |
| } else { |
| for (BaseType baseReceiverType in receiverType.baseTypes) { |
| if (!baseReceiverType.isNull()) { |
| - FunctionElement method = (baseReceiverType as ClassBaseType).element |
| - .lookupMember(node.selector.asIdentifier().source); |
| - if (method != null) { |
| + ClassBaseType classBaseReceiverType = baseReceiverType; |
| + FunctionElement method = classBaseReceiverType.element |
| + .lookupMember(canonicalize(node.selector.asIdentifier().source)); |
| + if (method !== null) { |
|
karlklose
2012/10/23 10:58:15
Use != instead of !==.
polux
2012/10/25 12:02:01
Done.
|
| inferrer.addCaller(method, currentMethod); |
| result = result.union(inferrer.getSendReturnType(method, |
| baseReceiverType, argumentsTypes)); |