blob: c4837a251ce93230313f267999da29a7c047afe3 [file] [log] [blame]
/*
* Copyright 2015 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.CompilerContext;
import com.google.gwt.dev.PrecompileTaskOptionsImpl;
import com.google.gwt.dev.javac.CompilationState;
import com.google.gwt.dev.javac.CompilationStateBuilder;
import com.google.gwt.dev.javac.CompilationUnit;
import com.google.gwt.dev.javac.testing.impl.JavaResourceBase;
import com.google.gwt.dev.jjs.JavaAstConstructor;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JArrayType;
import com.google.gwt.dev.jjs.ast.JCastOperation;
import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JExpressionStatement;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JStatement;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JVariable;
import com.google.gwt.dev.jjs.ast.JVisitor;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.arg.SourceLevel;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* Test {@link com.google.gwt.dev.jjs.impl.GwtAstBuilder} correctly builds the AST.
*
* TODO(leafwang): Write tests for all other features.
*/
public class GwtAstBuilderTest extends JJSTestBase {
Set<Resource> sources = Sets.newLinkedHashSet();
/**
* A Gwt AST verifier, which is used to verify that in Gwt AST, all JReferenceType instances that
* are not in current compilation unit are external.
*/
static class CompilationUnitJavaAstVerifier extends JVisitor {
/**
* Throws an assertion error if a ReferenceType that is not in current compilation unit is not
* external.
*/
public static void assertNonExternalOnlyInCurrentCU(CompilationUnit compilationUnit) {
CompilationUnitJavaAstVerifier verifier =
new CompilationUnitJavaAstVerifier(compilationUnit.getTypes());
for (JDeclaredType type : compilationUnit.getTypes()) {
verifier.accept(type);
}
}
final List<JDeclaredType> typesInCurrentCud;
final List<String> typeNames;
public CompilationUnitJavaAstVerifier(List<JDeclaredType> typesInCurrentCud) {
super();
this.typesInCurrentCud = typesInCurrentCud;
this.typeNames = Lists.newArrayList();
for (JDeclaredType type : typesInCurrentCud) {
typeNames.add(type.getName());
}
}
@Override
public void endVisit(JClassLiteral x, Context ctx) {
/**
* class literals only return a meaningful type after ImplementClassLiteralsAsFields has been
* run.
*/
if (x.getField() != null) {
endVisit((JExpression) x, ctx);
}
}
@Override
public void endVisit(JExpression x, Context ctx) {
if (x.getType() == null) {
return;
}
assertExternal(x.getType().getUnderlyingType());
}
@Override
public void endVisit(JMethod x, Context ctx) {
assertExternal(x.getType());
}
@Override
public void endVisit(JVariable x, Context ctx) {
assertExternal(x.getType());
}
private void assertExternal(JType type) {
JType typeToCheck = type;
if (type instanceof JArrayType) {
typeToCheck = (((JArrayType) type).getLeafType());
}
if (typeToCheck == null || !(typeToCheck instanceof JReferenceType)
|| typeToCheck.equals(JReferenceType.NULL_TYPE)) {
return;
}
if (!typeNames.contains(typeToCheck.getName())) {
assert (typeToCheck.isExternal());
}
}
}
@Override
public void setUp() {
sources.addAll(sourceOracle.getResources());
sources.add(JavaResourceBase.createMockJavaResource("test.DalNavigationTile",
"package test;",
"public class DalNavigationTile extends DalTile {",
"}"
));
sources.add(JavaResourceBase.createMockJavaResource("test.DalTile",
"package test;",
"public class DalTile {"
+ "{ new DalRow().getTiles();"
+ "}",
"}"
));
sources.add(JavaResourceBase.createMockJavaResource("test.DalGrid",
"package test;",
"public class DalGrid {",
" public DalNavigationTile getNavigationTile() {"
+ " DalRow row = new DalRow();"
+ " DalNavigationTile found = null;"
+ " for (DalTile dalTile : row.getTiles()) {"
+ " if (dalTile instanceof DalNavigationTile) {"
+ " found = (DalNavigationTile) dalTile;"
+ " break;"
+ " }"
+ " }"
+ " return found;"
+ "}",
"}"
));
sources.add(JavaResourceBase.createMockJavaResource("test.DalRow",
"package test;",
"public class DalRow {"
+ " public DalTile[] getTiles() {"
+ " int length = 5;"
+ " DalTile[] result = new DalTile[length];"
+ " for (int i = 0; i < length; i++) {"
+ " result[i] = new DalTile();"
+ " }"
+ " return result;"
+ " }",
"}"
));
sourceLevel = SourceLevel.DEFAULT_SOURCE_LEVEL;
}
public void testNestedClassDisposition() throws UnableToCompleteException {
sourceLevel = SourceLevel.JAVA9;
sources.add(JavaResourceBase.createMockJavaResource("test.NestedClasses",
"package test;",
"public class NestedClasses {",
" static class StaticNestedClass {}",
" class InnerNestedClass {}",
" interface Lambda { void run(); }",
" public static void referencedMethod() {}",
" public void m() {",
" class LocalClass {}",
" Object anonymousInner = new Object(){};",
" Lambda lambda = () -> {};",
" Lambda methodRef = NestedClasses::referencedMethod;",
" new Lambda(){",
" public void run(){",
" Lambda lambda = () -> {};",
" }",
" };",
" }",
"}"
));
JProgram program = compileProgram("test.NestedClasses");
JDeclaredType staticNested = program.getFromTypeMap("test.NestedClasses$StaticNestedClass");
assertEquals(JDeclaredType.NestedClassDisposition.STATIC, staticNested.getClassDisposition());
JDeclaredType innerNested = program.getFromTypeMap("test.NestedClasses$InnerNestedClass");
assertEquals(JDeclaredType.NestedClassDisposition.INNER, innerNested.getClassDisposition());
JDeclaredType localNested = program.getFromTypeMap("test.NestedClasses$1LocalClass");
assertEquals(JDeclaredType.NestedClassDisposition.LOCAL, localNested.getClassDisposition());
JDeclaredType anonymousNested = program.getFromTypeMap("test.NestedClasses$1");
assertEquals(JDeclaredType.NestedClassDisposition.ANONYMOUS,
anonymousNested.getClassDisposition());
JDeclaredType lambdaNested = program.getFromTypeMap("test.NestedClasses$lambda$0$Type");
assertEquals(JDeclaredType.NestedClassDisposition.LAMBDA, lambdaNested.getClassDisposition());
JDeclaredType referenceNested =
program.getFromTypeMap("test.NestedClasses$0methodref$referencedMethod$Type");
assertEquals(JDeclaredType.NestedClassDisposition.LAMBDA,
referenceNested.getClassDisposition());
JDeclaredType topLevel = program.getFromTypeMap("test.NestedClasses");
assertEquals(JDeclaredType.NestedClassDisposition.TOP_LEVEL, topLevel.getClassDisposition());
JDeclaredType lambdaNestedInner = program.getFromTypeMap("test.NestedClasses$2$lambda$0$Type");
assertEquals(JDeclaredType.NestedClassDisposition.LAMBDA, lambdaNestedInner
.getClassDisposition());
}
public void testIntersectionBound() throws UnableToCompleteException {
sourceLevel = SourceLevel.JAVA9;
sources.add(JavaResourceBase.createMockJavaResource("test.IntersectionBound",
"package test;",
"public class IntersectionBound {",
" public void main() {",
" get().f();",
" get().g();",
" get().h();",
" }",
" public interface A<T> { void f(); }",
" public interface B { void g(); }",
" public interface C { void h(); }",
" <T extends B & A<String> & C> T get() { return null;} ",
"}"
));
JProgram program = compileProgram("test.IntersectionBound");
JMethod mainMethod = findQualifiedMethod(program, "test.IntersectionBound.main");
for (JStatement statement : ((JMethodBody) mainMethod.getBody()).getStatements()) {
// TODO: should have inserted only a cast to the type needed in the specific context context,
// but that would require some redesign. For now make sure all the casts from the intersection
// type are emitted.
JExpression maybeCastOperation =
((JMethodCall) ((JExpressionStatement) statement).getExpr()).getInstance();
Set<String> castToTypeNames = Sets.newHashSet();
while (maybeCastOperation instanceof JCastOperation) {
JCastOperation castOperation = (JCastOperation) maybeCastOperation;
castToTypeNames.add(castOperation.getCastType().getName());
maybeCastOperation = castOperation.getExpr();
}
assertEquals(
Sets.newHashSet(Arrays.asList(
"test.IntersectionBound$A", "test.IntersectionBound$B", "test.IntersectionBound$C")),
castToTypeNames);
}
}
public void testUniqueArrayTypeInstance() throws UnableToCompleteException {
JProgram program = compileProgram("test.DalGrid");
Set<String> arrayTypeNames = Sets.newHashSet();
for (JArrayType type : program.getAllArrayTypes()) {
arrayTypeNames.add(type.getName());
}
assertEquals(arrayTypeNames.size(), program.getAllArrayTypes().size());
}
public void testNonExternalOnlyInCurrentCud() throws UnableToCompleteException {
CompilationState state = buildCompilationState();
for (CompilationUnit compilationUnit : state.getCompilationUnits()) {
CompilationUnitJavaAstVerifier.assertNonExternalOnlyInCurrentCU(compilationUnit);
}
}
private CompilationState buildCompilationState() throws UnableToCompleteException {
CompilerContext compilerContext =
new CompilerContext.Builder().options(new PrecompileTaskOptionsImpl() {
@Override
public boolean shouldJDTInlineCompileTimeConstants() {
return false;
}
}).build();
compilerContext.getOptions().setSourceLevel(sourceLevel);
compilerContext.getOptions().setStrict(true);
CompilationState state = CompilationStateBuilder.buildFrom(logger, compilerContext, sources);
return state;
}
private JProgram compileProgram(String entryType) throws UnableToCompleteException {
CompilerContext compilerContext = provideCompilerContext();
;
CompilationState state = CompilationStateBuilder.buildFrom(logger, compilerContext, sources);
JProgram program = JavaAstConstructor.construct(logger, state, compilerContext,
null, entryType, "com.google.gwt.lang.Exceptions");
return program;
}
}