| // Copyright (c) 2012, 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. |
| |
| package com.google.dart.compiler.type; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.Maps; |
| import com.google.common.collect.Sets; |
| import com.google.common.io.CharStreams; |
| import com.google.dart.compiler.ErrorCode; |
| import com.google.dart.compiler.ast.DartClass; |
| import com.google.dart.compiler.ast.DartExprStmt; |
| import com.google.dart.compiler.ast.DartExpression; |
| import com.google.dart.compiler.ast.DartField; |
| import com.google.dart.compiler.ast.DartFieldDefinition; |
| import com.google.dart.compiler.ast.DartFunctionExpression; |
| import com.google.dart.compiler.ast.DartFunctionTypeAlias; |
| import com.google.dart.compiler.ast.DartNode; |
| import com.google.dart.compiler.ast.DartStatement; |
| import com.google.dart.compiler.ast.DartUnit; |
| import com.google.dart.compiler.ast.LibraryUnit; |
| import com.google.dart.compiler.parser.DartParser; |
| import com.google.dart.compiler.resolver.ClassElement; |
| import com.google.dart.compiler.resolver.ClassNodeElement; |
| import com.google.dart.compiler.resolver.CoreTypeProvider; |
| import com.google.dart.compiler.resolver.Element; |
| import com.google.dart.compiler.resolver.Elements; |
| import com.google.dart.compiler.resolver.FunctionAliasElement; |
| import com.google.dart.compiler.resolver.LibraryElement; |
| import com.google.dart.compiler.resolver.MemberBuilder; |
| import com.google.dart.compiler.resolver.MockLibraryUnit; |
| import com.google.dart.compiler.resolver.ResolutionContext; |
| import com.google.dart.compiler.resolver.Resolver; |
| import com.google.dart.compiler.resolver.Resolver.ResolveElementsVisitor; |
| import com.google.dart.compiler.resolver.Scope; |
| import com.google.dart.compiler.resolver.SupertypeResolver; |
| import com.google.dart.compiler.resolver.TopLevelElementBuilder; |
| import com.google.dart.compiler.util.DartSourceString; |
| |
| import java.io.IOError; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Base class for static type analysis tests. |
| */ |
| public abstract class TypeAnalyzerTestCase extends TypeTestCase { |
| private class MockCoreTypeProvider implements CoreTypeProvider { |
| private final Type voidType = Types.newVoidType(); |
| private final DynamicType dynamicType = Types.newDynamicType(); |
| |
| @Override |
| public InterfaceType getArrayType(Type elementType) { |
| return list.getType().subst(Arrays.asList(elementType), list.getTypeParameters()); |
| } |
| |
| @Override |
| public InterfaceType getBoolType() { |
| return bool.getType(); |
| } |
| |
| @Override |
| public InterfaceType getDoubleType() { |
| return doubleElement.getType(); |
| } |
| |
| @Override |
| public DynamicType getDynamicType() { |
| return dynamicType; |
| } |
| |
| @Override |
| public InterfaceType getFunctionType() { |
| return function.getType(); |
| } |
| |
| @Override |
| public InterfaceType getIntType() { |
| return intElement.getType(); |
| } |
| |
| @Override |
| public InterfaceType getIteratorType(Type elementType) { |
| InterfaceType iteratorType = iterElement.getType(); |
| return iteratorType.subst(Arrays.asList(elementType), iterElement.getTypeParameters()); |
| } |
| |
| @Override |
| public InterfaceType getMapType(Type key, Type value) { |
| InterfaceType mapType = map.getType(); |
| return mapType.subst(Arrays.asList(key, value), |
| mapType.getElement().getTypeParameters()); |
| } |
| |
| @Override |
| public Type getNullType() { |
| return getDynamicType(); |
| } |
| |
| @Override |
| public InterfaceType getNumType() { |
| return number.getType(); |
| } |
| |
| @Override |
| public InterfaceType getObjectType() { |
| return object.getType(); |
| } |
| |
| @Override |
| public InterfaceType getStringType() { |
| return string.getType(); |
| } |
| |
| @Override |
| public Type getVoidType() { |
| return voidType; |
| } |
| |
| @Override |
| public InterfaceType getTypeType() { |
| return type.getType(); |
| } |
| } |
| private class MockScope extends Scope { |
| private MockScope() { |
| super("test mock scope", null); |
| } |
| |
| @Override |
| public Element findLocalElement(String name) { |
| return coreElements.get(name); |
| } |
| |
| } |
| protected final CoreTypeProvider typeProvider = new MockCoreTypeProvider(); |
| private Resolver resolver = new Resolver(context, getMockScope("<test toplevel>"), typeProvider); |
| |
| private final Types types = Types.getInstance(typeProvider); |
| |
| private HashSet<ClassElement> diagnosedAbstractClasses = new HashSet<ClassElement>(); |
| |
| protected DartStatement analyze(String statement) { |
| DartStatement node = parseStatement(statement); |
| analyzeNode(node); |
| return node; |
| } |
| |
| protected ClassElement analyzeClass(ClassNodeElement cls, int expectedErrorCount) { |
| setExpectedTypeErrorCount(expectedErrorCount); |
| analyzeToplevel(cls.getNode()); |
| checkExpectedTypeErrorCount(cls.getName()); |
| return cls; |
| } |
| |
| protected Map<String, ClassNodeElement> analyzeClasses(Map<String, ClassNodeElement> classes, |
| ErrorCode... codes) { |
| setExpectedTypeErrorCount(codes.length); |
| for (ClassNodeElement cls : classes.values()) { |
| analyzeToplevel(cls.getNode()); |
| } |
| List<ErrorCode> errorCodes = context.getErrorCodes(); |
| assertEquals(Arrays.toString(codes), errorCodes.toString()); |
| errorCodes.clear(); |
| checkExpectedTypeErrorCount(); |
| return classes; |
| } |
| |
| protected void analyzeFail(String statement, ErrorCode errorCode) { |
| try { |
| analyze(statement); |
| fail("Test unexpectedly passed. Expected ErrorCode: " + errorCode); |
| } catch (TestTypeError error) { |
| assertEquals(errorCode, error.getErrorCode()); |
| } |
| } |
| |
| protected Type analyzeIn(ClassElement element, String expression, int expectedErrorCount) { |
| DartExpression node = parseExpression(expression); |
| ResolutionContext resolutionContext = |
| new ResolutionContext(getMockScope("<test expression>"), context, |
| typeProvider).extend(element); |
| ResolveElementsVisitor visitor = |
| resolver.new ResolveElementsVisitor(resolutionContext, element, |
| Elements.methodElement(null, null)); |
| setExpectedTypeErrorCount(expectedErrorCount); |
| node.accept(visitor); |
| Type type = node.accept(makeTypeAnalyzer(element)); |
| checkExpectedTypeErrorCount(expression); |
| return type; |
| } |
| |
| private Type analyzeNode(DartNode node) { |
| ResolutionContext resolutionContext = |
| new ResolutionContext(getMockScope("<test node>"), context, typeProvider); |
| ResolveElementsVisitor visitor = |
| resolver.new ResolveElementsVisitor(resolutionContext, null, |
| Elements.methodElement(null, null)); |
| node.accept(visitor); |
| return node.accept(makeTypeAnalyzer(Elements.dynamicElement())); |
| } |
| |
| private Type analyzeToplevel(DartNode node) { |
| return node.accept(makeTypeAnalyzer(Elements.dynamicElement())); |
| } |
| |
| private String assign(String type, String expression) { |
| return String.format("() { %s x = %s; }", type, expression); |
| } |
| |
| protected Type checkAssignIn(ClassElement element, String type, String expression, int errorCount) { |
| return analyzeIn(element, assign(type, expression), errorCount); |
| } |
| |
| protected void checkFunctionStatement(String statement, String printString) { |
| DartExprStmt node = (DartExprStmt) analyze(statement); |
| DartFunctionExpression expression = (DartFunctionExpression) node.getExpression(); |
| Element element = expression.getElement(); |
| FunctionType type = (FunctionType) element.getType(); |
| assertEquals(printString, type.toString()); |
| } |
| |
| protected void checkSimpleType(Type type, String expression) { |
| assertSame(type, typeOf(expression)); |
| setExpectedTypeErrorCount(1); // x is unresolved. |
| assertSame(type, typeOf("x = " + expression)); |
| checkExpectedTypeErrorCount(); |
| } |
| |
| protected void checkType(Type type, String expression) { |
| assertEquals(type, typeOf(expression)); |
| assertEquals(type, typeOf("x = " + expression)); |
| } |
| |
| private Scope getMockScope(String name) { |
| LibraryUnit libraryUnit = MockLibraryUnit.create(); |
| return new Scope(name, libraryUnit.getElement(), new MockScope()); |
| } |
| |
| private DartParser getParser(String string) { |
| DartSourceString source = new DartSourceString("<source string>", string); |
| return new DartParser(source, string, false, Sets.<String>newHashSet(), listener, null); |
| } |
| |
| private String getResource(String name) { |
| String packageName = getClass().getPackage().getName().replace('.', '/'); |
| String resouceName = packageName + "/" + name; |
| InputStream stream = getClass().getClassLoader().getResourceAsStream(resouceName); |
| if (stream == null) { |
| throw new AssertionError("Missing resource: " + resouceName); |
| } |
| InputStreamReader reader = new InputStreamReader(stream); |
| try { |
| return CharStreams.toString(reader); // Also closes the reader. |
| } catch (IOException e) { |
| throw new IOError(e); |
| } |
| } |
| |
| @Override |
| Types getTypes() { |
| return types; |
| } |
| |
| protected ClassElement loadClass(String file, String name) { |
| ClassElement cls = loadFile(file).get(name); |
| assertNotNull("unable to locate " + name, cls); |
| return cls; |
| } |
| |
| protected Map<String, ClassNodeElement> loadFile(final String name) { |
| String source = getResource(name); |
| return loadSource(source); |
| } |
| |
| protected Map<String, ClassNodeElement> loadSource(String source) { |
| Map<String, ClassNodeElement> classes = Maps.newLinkedHashMap(); |
| DartUnit unit = parseUnit(source); |
| Scope scope = getMockScope("<test toplevel>"); |
| LibraryElement libraryElement = scope.getLibrary(); |
| libraryElement.getScope().declareElement("Object", object); |
| unit.setLibrary(libraryElement.getLibraryUnit()); |
| TopLevelElementBuilder elementBuilder = new TopLevelElementBuilder(); |
| elementBuilder.exec(unit.getLibrary(), unit, context); |
| for (DartNode node : unit.getTopLevelNodes()) { |
| if (node instanceof DartClass) { |
| DartClass classNode = (DartClass) node; |
| ClassNodeElement classElement = classNode.getElement(); |
| String className = classElement.getName(); |
| coreElements.put(className, classElement); |
| classes.put(className, classElement); |
| } else if (node instanceof DartFieldDefinition) { |
| DartFieldDefinition fieldNode = (DartFieldDefinition) node; |
| for (DartField field : fieldNode.getFields()) { |
| Element fieldElement = field.getElement(); |
| coreElements.put(fieldElement.getName(), fieldElement); |
| } |
| } else { |
| DartFunctionTypeAlias alias = (DartFunctionTypeAlias) node; |
| FunctionAliasElement element = alias.getElement(); |
| coreElements.put(element.getName(), element); |
| } |
| } |
| SupertypeResolver supertypeResolver = new SupertypeResolver(); |
| supertypeResolver.exec(unit, context, scope, typeProvider); |
| MemberBuilder memberBuilder = new MemberBuilder(); |
| memberBuilder.exec(unit, context, scope, typeProvider); |
| resolver.exec(unit); |
| return classes; |
| } |
| |
| protected Map<String, ClassNodeElement> loadSource(String firstLine, String secondLine, |
| String... rest) { |
| return loadSource(Joiner.on('\n').join(firstLine, secondLine, (Object[]) rest).toString()); |
| } |
| |
| private TypeAnalyzer.Analyzer makeTypeAnalyzer(ClassElement element) { |
| TypeAnalyzer.Analyzer analyzer = |
| new TypeAnalyzer.Analyzer(context, typeProvider, diagnosedAbstractClasses); |
| analyzer.setCurrentClass(element.getType()); |
| analyzer.pushBasicBlockContext(); |
| return analyzer; |
| } |
| |
| private DartExpression parseExpression(String source) { |
| return getParser(source).parseExpression(); |
| } |
| |
| private DartStatement parseStatement(String source) { |
| return getParser(source).parseStatement(); |
| } |
| |
| private DartUnit parseUnit(String string) { |
| // DartSourceString source = new DartSourceString("<source string>", string); |
| return getParser(string).parseUnit(); |
| } |
| |
| protected String returnWithType(String type, Object expression) { |
| return String.format("%s foo() { return %s; }", type, String.valueOf(expression)); |
| } |
| |
| @Override |
| protected void tearDown() { |
| resolver = null; |
| diagnosedAbstractClasses = null; |
| } |
| |
| private Type typeOf(String expression) { |
| return analyzeNode(parseExpression(expression)); |
| } |
| } |