| /* |
| * 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 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 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(); |
| } |
| } |