/* * Copyright 2009 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.asm; import com.google.gwt.dev.javac.asm.CollectAnnotationData.AnnotationData; import com.google.gwt.dev.javac.asm.CollectClassData.ClassType; import com.google.gwt.dev.javac.asm.CollectClassDataTest.Pseudo$Inner.Deepest; import com.google.gwt.dev.javac.asmbridge.EmptyVisitor; import com.google.gwt.dev.javac.typemodel.test.PrimitiveValuesAnnotation; import com.google.gwt.dev.javac.typemodel.test.TestAnnotation; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import java.util.List; /** * Tests for {@link CollectClassData}. */ public class CollectClassDataTest extends AsmTestCase { /** * Test class used in {@link CollectClassData#testLongDoubleArgs}. */ static class LongDoubleArgs { @SuppressWarnings("unused") public LongDoubleArgs(int x, long y, double z, Object o) { } @SuppressWarnings("unused") public void longDoubleMethod(int xx, long yy, double zz, String s) { } } /** * Test class used in {@link CollectClassData#testOne}. */ public static class One extends EmptyVisitor { @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { new EmptyVisitor() { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { } }; return new CollectAnnotationData(desc, visible); } } /** * Test class used in {@link CollectClassData#testPseudoInner}. */ protected static class Pseudo$Inner { static class Deepest { } } /** * Test class used in {@link CollectClassData#testTwo}. */ @PrimitiveValuesAnnotation(b = 42, i = 42) protected static class Two { /** * Test class used in {@link CollectClassData#testTwoInner}. */ public class TwoInner { } private final String field; @TestAnnotation("field") private final String annotatedField; public Two(int a) { this(a, null); } @TestAnnotation("foo") public String foo(int a) throws IllegalStateException { return annotatedField; } public Two(int a, String b) { new TwoInner(); field = b; annotatedField = field; } } /** * Test local classes. */ public static class Three { public int foo; /** * Static method that has a local class in it. */ public static void methodWithLocalStatic() { class Foo { } Foo x = new Foo(); } /** * Method that has a local class in it. */ public void methodWithLocal() { class Foo { Foo() { foo = 1; } } Foo x = new Foo(); } } public void testAnonymous() { CollectClassData cd = collect(One.class.getName() + "$1"); // Don't access on anonymous classes, it varies among compilers // assertEquals(0, cd.getAccess() & ~Opcodes.ACC_SUPER); assertEquals(ClassType.Anonymous, cd.getClassType()); assertEquals(0, cd.getFields().size()); List<CollectMethodData> methods = cd.getMethods(); assertEquals(2, methods.size()); assertEquals(null, cd.getNestedSourceName()); } public void testOne() { CollectClassData cd = collect(One.class); // Don't check for super bit, as it will depend on the JDK used to compile. assertEquals(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, cd.getAccess() & ~Opcodes.ACC_SUPER); assertEquals(ClassType.Nested, cd.getClassType()); assertEquals(0, cd.getFields().size()); assertEquals(0, cd.getInterfaceInternalNames().length); assertEquals(0, cd.getAnnotations().size()); assertEquals("com/google/gwt/dev/javac/asmbridge/EmptyVisitor", cd.getSuperInternalName()); assertEquals("CollectClassDataTest.One", cd.getNestedSourceName()); List<CollectMethodData> methods = cd.getMethods(); assertEquals(2, methods.size()); // TODO(jat): Is it safe to assume the implicit constructor is always first? CollectMethodData method = methods.get(0); Type[] argTypes = method.getArgTypes(); String[] argNames = method.getArgNames(); assertEquals("<init>", method.getName()); assertEquals(0, argTypes.length); assertEquals(0, argNames.length); assertEquals(0, method.getArgAnnotations().length); assertEquals(0, method.getAnnotations().size()); assertEquals(0, method.getExceptions().length); method = methods.get(1); argTypes = method.getArgTypes(); argNames = method.getArgNames(); assertEquals("visitAnnotation", method.getName()); assertEquals(2, argTypes.length); assertEquals("java.lang.String", argTypes[0].getClassName()); assertEquals("boolean", argTypes[1].getClassName()); assertEquals(2, argNames.length); assertEquals("desc", argNames[0]); assertEquals("visible", argNames[1]); assertEquals(2, method.getArgAnnotations().length); assertEquals(0, method.getArgAnnotations()[0].size()); assertEquals(0, method.getArgAnnotations()[1].size()); // Note that @Override is a source-only annotation assertEquals(0, method.getAnnotations().size()); assertEquals(0, method.getExceptions().length); } public void testTopLevel() { CollectClassData cd = collect(CollectClassDataTest.class); // Don't check for super bit, as it will depend on the JDK used to compile. assertEquals(Opcodes.ACC_PUBLIC, cd.getAccess() & ~Opcodes.ACC_SUPER); assertEquals(ClassType.TopLevel, cd.getClassType()); assertEquals("CollectClassDataTest", cd.getNestedSourceName()); } public void testTwo() { CollectClassData cd = collect(Two.class); // Don't check for super bit, as it will depend on the JDK used to compile. assertEquals(Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC, cd.getAccess() & ~Opcodes.ACC_SUPER); assertEquals(ClassType.Nested, cd.getClassType()); List<CollectFieldData> fields = cd.getFields(); assertEquals(2, fields.size()); CollectFieldData field = fields.get(0); assertEquals("field", field.getName()); assertEquals("Ljava/lang/String;", field.getDesc()); List<CollectAnnotationData> annotations = field.getAnnotations(); assertEquals(0, annotations.size()); field = fields.get(1); assertEquals("annotatedField", field.getName()); assertEquals("Ljava/lang/String;", field.getDesc()); annotations = field.getAnnotations(); assertEquals(1, annotations.size()); AnnotationData annotation = annotations.get(0).getAnnotation(); assertEquals("Lcom/google/gwt/dev/javac/typemodel/test/TestAnnotation;", annotation.getDesc()); assertEquals("field", annotation.getValues().get("value")); assertEquals(0, cd.getInterfaceInternalNames().length); annotations = cd.getAnnotations(); assertEquals(1, annotations.size()); annotation = annotations.get(0).getAnnotation(); assertEquals( "Lcom/google/gwt/dev/javac/typemodel/test/PrimitiveValuesAnnotation;", annotation.getDesc()); assertEquals(Byte.valueOf((byte) 42), annotation.getValues().get("b")); assertEquals(42, annotation.getValues().get("i")); assertEquals("java/lang/Object", cd.getSuperInternalName()); assertEquals("CollectClassDataTest.Two", cd.getNestedSourceName()); List<CollectMethodData> methods = cd.getMethods(); assertEquals(3, methods.size()); // TODO(jat): Is it safe to assume the order? CollectMethodData method = methods.get(0); Type[] argTypes = method.getArgTypes(); String[] argNames = method.getArgNames(); assertEquals("<init>", method.getName()); assertEquals(1, argTypes.length); assertEquals(1, argNames.length); assertEquals(1, method.getArgAnnotations().length); assertEquals(0, method.getAnnotations().size()); assertEquals(0, method.getExceptions().length); method = methods.get(1); argTypes = method.getArgTypes(); argNames = method.getArgNames(); assertEquals("foo", method.getName()); assertEquals(1, argTypes.length); assertEquals("int", argTypes[0].getClassName()); assertEquals(1, argNames.length); assertEquals("a", argNames[0]); assertEquals(1, method.getArgAnnotations().length); assertEquals(0, method.getArgAnnotations()[0].size()); assertEquals(1, method.getAnnotations().size()); assertEquals(1, method.getExceptions().length); method = methods.get(2); argTypes = method.getArgTypes(); argNames = method.getArgNames(); assertEquals("<init>", method.getName()); assertEquals(2, argTypes.length); assertEquals("int", argTypes[0].getClassName()); assertEquals("java.lang.String", argTypes[1].getClassName()); assertEquals(2, argNames.length); assertEquals("a", argNames[0]); assertEquals("b", argNames[1]); assertEquals(2, method.getArgAnnotations().length); assertEquals(0, method.getArgAnnotations()[0].size()); assertEquals(0, method.getArgAnnotations()[1].size()); assertEquals(0, method.getAnnotations().size()); assertEquals(0, method.getExceptions().length); } public void testTwoInner() { CollectClassData cd = collect(Two.TwoInner.class); // Don't check for super bit, as it will depend on the JDK used to compile. assertEquals(Opcodes.ACC_PUBLIC, cd.getAccess() & ~Opcodes.ACC_SUPER); assertEquals(ClassType.Inner, cd.getClassType()); assertEquals("CollectClassDataTest.Two.TwoInner", cd.getNestedSourceName()); } public void testPseudoInner() { CollectClassData cd = collect(Pseudo$Inner.class); assertEquals("CollectClassDataTest.Pseudo$Inner", cd.getNestedSourceName()); } public void testPseudoInnerDeepest() { CollectClassData cd = collect(Deepest.class); assertEquals("CollectClassDataTest.Pseudo$Inner.Deepest", cd.getNestedSourceName()); } public void testLocal() { CollectClassData cd = collect(Three.class.getName() + "$2Foo"); // Don't check for super bit, as it will depend on the JDK used to compile. assertEquals(0, cd.getAccess() & ~Opcodes.ACC_SUPER); assertEquals(ClassType.Local, cd.getClassType()); assertEquals("methodWithLocal", cd.getEnclosingMethodName()); } public void testLocalStatic() { CollectClassData cd = collect(Three.class.getName() + "$1Foo"); // Don't check for super bit, as it will depend on the JDK used to compile. assertEquals(0, cd.getAccess() & ~Opcodes.ACC_SUPER); assertEquals(ClassType.Local, cd.getClassType()); assertEquals("methodWithLocalStatic", cd.getEnclosingMethodName()); assertEquals(null, cd.getNestedSourceName()); } // See http://code.google.com/p/google-web-toolkit/issues/detail?id=6591 // Argument names were incorrect in the presence of long/double args public void testLongDoubleArgs() { CollectClassData cd = collect(LongDoubleArgs.class); assertEquals("CollectClassDataTest.LongDoubleArgs", cd.getNestedSourceName()); List<CollectMethodData> methods = cd.getMethods(); assertEquals(2, methods.size()); CollectMethodData method = methods.get(0); Type[] argTypes = method.getArgTypes(); String[] argNames = method.getArgNames(); assertEquals("<init>", method.getName()); assertEquals("x", argNames[0]); assertEquals("y", argNames[1]); assertEquals("z", argNames[2]); assertEquals("o", argNames[3]); assertEquals("I", argTypes[0].toString()); assertEquals("J", argTypes[1].toString()); assertEquals("D", argTypes[2].toString()); assertEquals("Ljava/lang/Object;", argTypes[3].toString()); method = methods.get(1); argTypes = method.getArgTypes(); argNames = method.getArgNames(); assertEquals("longDoubleMethod", method.getName()); assertEquals("xx", argNames[0]); assertEquals("yy", argNames[1]); assertEquals("zz", argNames[2]); assertEquals("s", argNames[3]); assertEquals("I", argTypes[0].toString()); assertEquals("J", argTypes[1].toString()); assertEquals("D", argTypes[2].toString()); assertEquals("Ljava/lang/String;", argTypes[3].toString()); } private CollectClassData collect(Class<?> clazz) { return collect(clazz.getName()); } private CollectClassData collect(String className) { byte[] bytes = getClassBytes(className); assertNotNull("Couldn't load bytes for " + className, bytes); CollectClassData cv = new CollectClassData(); ClassReader reader = new ClassReader(bytes); reader.accept(cv, 0); return cv; } }