blob: 8a988cac62638cca1cb1c6417157b175bba04787 [file] [log] [blame]
/*
* Copyright 2008 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.javac;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.dev.javac.CompilationUnit.GeneratedClassnameFinder;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import junit.framework.TestCase;
import java.util.List;
/**
* Test-cases to check that we indeed obtain the correct list of nested types
* with generated classNames by examining bytecodes using ASM.
*
*/
public class GeneratedClassnameFinderTest extends TestCase {
enum EnumClass {
A, B, C,
}
static class MainClass {
static class NestedClass {
void foo() {
TestInterface c = new TestInterface() {
public void foo() {
}
};
EnumClass et = EnumClass.A;
switch (et) {
case A:
break;
}
TestInterface d = new TestInterface() {
public void foo() {
}
};
}
}
void foo() {
TestInterface a = new TestInterface() {
public void foo() {
}
};
EnumClass et = EnumClass.A;
switch (et) {
case A:
break;
}
TestInterface b = new TestInterface() {
public void foo() {
}
};
}
}
interface TestInterface {
void foo();
}
static final TreeLogger logger = new PrintWriterTreeLogger();
public void test() {
String mainClassName = this.getClass().getName().replace('.', '/');
assertEquals(
4,
new GeneratedClassnameFinder(logger, mainClassName).getClassNames().size());
assertEquals(0, new GeneratedClassnameFinder(logger, mainClassName
+ "$EnumClass").getClassNames().size());
assertEquals(0, new GeneratedClassnameFinder(logger, mainClassName
+ "$TestInterface").getClassNames().size());
assertEquals(4, new GeneratedClassnameFinder(logger, mainClassName
+ "$MainClass").getClassNames().size());
assertEquals(2, new GeneratedClassnameFinder(logger, mainClassName
+ "$MainClass$NestedClass").getClassNames().size());
}
public void testAbstractNative() {
assertEquals(2, new AbstractNativeTester().getGeneratedClasses().size());
}
public void testAnonymous() {
assertEquals(1, new AnonymousTester().getGeneratedClasses().size());
}
public void testEnum() {
assertEquals(0, new EnumTester().getGeneratedClasses().size());
}
public void testJavacWeirdness() {
List<String> classNames = new JavacWeirdnessTester().getGeneratedClasses();
assertEquals(3, classNames.size());
assertTrue(classNames.get(0) + " should not contain Foo",
classNames.get(0).indexOf("Foo") == -1);
assertTrue(classNames.get(1) + " should contain Foo",
classNames.get(1).indexOf("Foo") != -1);
assertTrue(classNames.get(2) + " should contain Foo",
classNames.get(2).indexOf("Foo") != -1);
}
public void testNamedLocal() {
assertEquals(2, new NamedLocalTester().getGeneratedClasses().size());
}
public void testNested() {
assertEquals(2, new NestedTester().getGeneratedClasses().size());
}
public void testStatic() {
assertEquals(0, new StaticTester().getGeneratedClasses().size());
}
public void testTopLevel() {
assertEquals(1, new TopLevelTester().getGeneratedClasses().size());
}
}
/**
* For testing a class containing anonymous inner classes with abstract and
* native methods.
*/
class AbstractNativeTester {
void foo() {
abstract class Fooer {
abstract void foo();
}
Fooer a = new Fooer() {
native void foo();
};
a.foo();
}
List<String> getGeneratedClasses() {
return (new GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
this.getClass().getName().replace('.', '/'))).getClassNames();
}
}
/**
* For testing a class containing an anonymous inner class.
*/
class AnonymousTester {
interface TestInterface {
void foo();
}
void foo() {
TestInterface a = new TestInterface() {
public void foo() {
}
};
a.foo();
}
List<String> getGeneratedClasses() {
return (new GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
this.getClass().getName().replace('.', '/'))).getClassNames();
}
}
/**
* For testing a class with an Enum (for which javac generates a synthetic
* class).
*/
class EnumTester {
enum EnumClass {
A, B, C,
}
void foo() {
EnumClass et = EnumClass.A;
switch (et) {
case A:
break;
}
}
List<String> getGeneratedClasses() {
return (new GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
this.getClass().getName().replace('.', '/'))).getClassNames();
}
}
/**
* Javac generates weird code for the following class. It passes a synthetic
* class ...Tester$1 as a first parameter to constructors of Fuji and Granny.
* Normally, it generates the synthetic class, but in this case, it decides not
* to generate the class. However, the bytecode still has reference to the
* synthetic class -- it just passes null for the synthetic class.
*
* This code also tests for an anonymous class extending a named local class.
*/
class JavacWeirdnessTester {
private abstract static class Apple implements Fruit {
}
private static interface Fruit {
}
private static class Fuji extends Apple {
}
private static class Granny extends Apple {
}
private static volatile boolean TRUE = true;
List<String> getGeneratedClasses() {
return (new GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
this.getClass().getName().replace('.', '/'))).getClassNames();
}
private void assertEquals(Object a, Object b) {
}
private void testArrayStore() {
Apple[] apple = TRUE ? new Granny[3] : new Apple[3];
Apple g = TRUE ? (Apple) new Granny() : (Apple) new Fuji();
Apple a = apple[0] = g;
assertEquals(g, a);
}
private void testDeadTypes() {
if (false) {
new Object() {
}.toString();
class Foo {
void a() {
}
}
new Foo().a();
}
}
private void testLocalClasses() {
class Foo {
public Foo(int j) {
assertEquals(1, j);
};
}
final int i;
new Foo(i = 1) {
{
assertEquals(1, i);
}
};
assertEquals(1, i);
}
private void testReturnStatementInCtor() {
class Foo {
int i;
Foo(int i) {
this.i = i;
if (i == 0) {
return;
} else if (i == 1) {
return;
}
return;
}
}
assertEquals(new Foo(0).i, 0);
}
}
/**
* For testing a class with a generated name like $1Foo.
*/
class NamedLocalTester {
void foo1() {
if (false) {
class Foo {
void foo() {
}
}
new Foo().foo();
}
}
void foo2() {
class Foo {
void foo() {
}
}
new Foo().foo();
}
void foo3() {
class Foo {
void foo() {
}
}
new Foo().foo();
}
List<String> getGeneratedClasses() {
return (new GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
this.getClass().getName().replace('.', '/'))).getClassNames();
}
}
/**
* For testing that nested classes are examined recursively for classes with
* generated names.
*/
class NestedTester {
class MainClass {
class NestedClass {
void foo() {
class Foo {
void bar() {
}
}
new Foo().bar();
}
}
void foo() {
class Foo {
void bar() {
}
}
new Foo().bar();
}
}
List<String> getGeneratedClasses() {
return (new GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
this.getClass().getName().replace('.', '/'))).getClassNames();
}
}
/**
* For testing classes with private static members (javac generates a synthetic
* class here but the jdt does not).
*/
class StaticTester {
private abstract static class Apple implements Fruit {
}
private static interface Fruit {
void bar();
}
private static class Fuji extends Apple {
public void bar() {
}
}
private static class Granny extends Apple {
public void bar() {
}
}
List<String> getGeneratedClasses() {
return (new GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
this.getClass().getName().replace('.', '/'))).getClassNames();
}
}
/**
* For testing that a class with a generated name inside another top-level class
* is found.
*/
class TopLevelTester {
public void foo() {
GeneratedClassnameFinderTest.TestInterface a = new GeneratedClassnameFinderTest.TestInterface() {
public void foo() {
}
};
}
List<String> getGeneratedClasses() {
return (new GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
this.getClass().getName().replace('.', '/'))).getClassNames();
}
}