1 /* 2 * Copyright 2016 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.turbine.bytecode; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import com.google.common.base.Strings; 22 import com.google.common.collect.Iterables; 23 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue; 24 import com.google.turbine.model.Const; 25 import com.google.turbine.model.TurbineConstantTypeKind; 26 import com.google.turbine.model.TurbineFlag; 27 import org.junit.Test; 28 import org.junit.runner.RunWith; 29 import org.junit.runners.JUnit4; 30 import org.objectweb.asm.AnnotationVisitor; 31 import org.objectweb.asm.ClassWriter; 32 import org.objectweb.asm.Opcodes; 33 34 @RunWith(JUnit4.class) 35 public class ClassReaderTest { 36 37 @Test 38 public void methods() { 39 ClassWriter cw = new ClassWriter(0); 40 cw.visitAnnotation("Ljava/lang/Deprecated;", true); 41 cw.visit( 42 52, 43 Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER, 44 "test/Hello", 45 null, 46 "java/lang/Object", 47 null); 48 cw.visitMethod( 49 Opcodes.ACC_PUBLIC, 50 "f", 51 "(Ljava/lang/String;)Ljava/lang/String;", 52 "<T:Ljava/lang/String;>(TT;)TT;", 53 null); 54 cw.visitMethod( 55 Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, 56 "g", 57 "(Z)V", 58 "<T:Ljava/lang/Error;>(Z)V^TT;", 59 new String[] {"java/lang/Error"}); 60 cw.visitMethod(0, "h", "(I)V", null, null); 61 byte[] bytes = cw.toByteArray(); 62 63 ClassFile classFile = com.google.turbine.bytecode.ClassReader.read(null, bytes); 64 65 assertThat(classFile.access()) 66 .isEqualTo(TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL | TurbineFlag.ACC_SUPER); 67 assertThat(classFile.name()).isEqualTo("test/Hello"); 68 assertThat(classFile.signature()).isNull(); 69 assertThat(classFile.superName()).isEqualTo("java/lang/Object"); 70 assertThat(classFile.interfaces()).isEmpty(); 71 72 assertThat(classFile.methods()).hasSize(3); 73 74 ClassFile.MethodInfo f = classFile.methods().get(0); 75 assertThat(f.access()).isEqualTo(TurbineFlag.ACC_PUBLIC); 76 assertThat(f.name()).isEqualTo("f"); 77 assertThat(f.descriptor()).isEqualTo("(Ljava/lang/String;)Ljava/lang/String;"); 78 assertThat(f.signature()).isEqualTo("<T:Ljava/lang/String;>(TT;)TT;"); 79 assertThat(f.exceptions()).isEmpty(); 80 assertThat(f.annotations()).isEmpty(); 81 assertThat(f.parameterAnnotations()).isEmpty(); 82 assertThat(f.defaultValue()).isNull(); 83 84 ClassFile.MethodInfo g = classFile.methods().get(1); 85 assertThat(g.access()).isEqualTo(TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_STATIC); 86 assertThat(g.name()).isEqualTo("g"); 87 assertThat(g.descriptor()).isEqualTo("(Z)V"); 88 assertThat(g.signature()).isEqualTo("<T:Ljava/lang/Error;>(Z)V^TT;"); 89 90 ClassFile.MethodInfo h = classFile.methods().get(2); 91 assertThat(h.access()).isEqualTo(0); 92 assertThat(h.name()).isEqualTo("h"); 93 assertThat(h.descriptor()).isEqualTo("(I)V"); 94 assertThat(h.signature()).isNull(); 95 } 96 97 @Test 98 public void annotationDeclaration() { 99 ClassWriter cw = new ClassWriter(0); 100 cw.visit( 101 52, 102 Opcodes.ACC_PUBLIC + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ABSTRACT + Opcodes.ACC_INTERFACE, 103 "test/Hello", 104 null, 105 "java/lang/Object", 106 new String[] {"java/lang/annotation/Annotation"}); 107 AnnotationVisitor av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); 108 av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME"); 109 av.visitEnd(); 110 cw.visitEnd(); 111 byte[] bytes = cw.toByteArray(); 112 113 ClassFile classFile = com.google.turbine.bytecode.ClassReader.read(null, bytes); 114 115 assertThat(classFile.access()) 116 .isEqualTo( 117 TurbineFlag.ACC_PUBLIC 118 | TurbineFlag.ACC_ANNOTATION 119 | TurbineFlag.ACC_ABSTRACT 120 | TurbineFlag.ACC_INTERFACE); 121 assertThat(classFile.name()).isEqualTo("test/Hello"); 122 assertThat(classFile.signature()).isNull(); 123 assertThat(classFile.superName()).isEqualTo("java/lang/Object"); 124 assertThat(classFile.interfaces()).containsExactly("java/lang/annotation/Annotation"); 125 126 assertThat(classFile.annotations()).hasSize(1); 127 ClassFile.AnnotationInfo annotation = Iterables.getOnlyElement(classFile.annotations()); 128 assertThat(annotation.typeName()).isEqualTo("Ljava/lang/annotation/Retention;"); 129 assertThat(annotation.isRuntimeVisible()).isTrue(); 130 assertThat(annotation.elementValuePairs()).hasSize(1); 131 assertThat(annotation.elementValuePairs()).containsKey("value"); 132 ElementValue value = annotation.elementValuePairs().get("value"); 133 assertThat(value.kind()).isEqualTo(ElementValue.Kind.ENUM); 134 ElementValue.EnumConstValue enumValue = (ElementValue.EnumConstValue) value; 135 assertThat(enumValue.typeName()).isEqualTo("Ljava/lang/annotation/RetentionPolicy;"); 136 assertThat(enumValue.constName()).isEqualTo("RUNTIME"); 137 } 138 139 @Test 140 public void fields() { 141 ClassWriter cw = new ClassWriter(0); 142 cw.visit( 143 52, 144 Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, 145 "test/Hello", 146 "<X:Ljava/lang/Object;>Ljava/lang/Object;", 147 "java/lang/Object", 148 null); 149 cw.visitField(Opcodes.ACC_PUBLIC, "x", "I", null, null); 150 cw.visitField( 151 Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, 152 "y", 153 "I", 154 null, 155 Integer.valueOf(42)); 156 cw.visitField(Opcodes.ACC_PUBLIC, "z", "Ljava/util/List;", "Ljava/util/List<TX;>;", null); 157 cw.visitEnd(); 158 byte[] bytes = cw.toByteArray(); 159 160 ClassFile classFile = com.google.turbine.bytecode.ClassReader.read(null, bytes); 161 162 assertThat(classFile.fields()).hasSize(3); 163 164 ClassFile.FieldInfo x = classFile.fields().get(0); 165 assertThat(x.access()).isEqualTo(TurbineFlag.ACC_PUBLIC); 166 assertThat(x.name()).isEqualTo("x"); 167 assertThat(x.descriptor()).isEqualTo("I"); 168 assertThat(x.signature()).isNull(); 169 assertThat(x.value()).isNull(); 170 assertThat(x.annotations()).isEmpty(); 171 172 ClassFile.FieldInfo y = classFile.fields().get(1); 173 assertThat(y.access()) 174 .isEqualTo(TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_STATIC | TurbineFlag.ACC_FINAL); 175 assertThat(y.name()).isEqualTo("y"); 176 assertThat(y.descriptor()).isEqualTo("I"); 177 assertThat(y.value().constantTypeKind()).isEqualTo(TurbineConstantTypeKind.INT); 178 assertThat(((Const.IntValue) y.value()).value()).isEqualTo(42); 179 180 ClassFile.FieldInfo z = classFile.fields().get(2); 181 assertThat(z.name()).isEqualTo("z"); 182 assertThat(z.descriptor()).isEqualTo("Ljava/util/List;"); 183 // don't bother reading signatures for fields; we only care about constants 184 assertThat(z.signature()).isNull(); 185 } 186 187 @Test 188 public void innerClass() { 189 ClassWriter cw = new ClassWriter(0); 190 cw.visit(52, Opcodes.ACC_SUPER, "test/Hello$Inner", null, "java/lang/Object", null); 191 cw.visitInnerClass( 192 "test/Hello$Inner", "test/Hello", "Inner", Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE); 193 cw.visitInnerClass("test/Hello$Inner$InnerMost", "test/Hello$Inner", "InnerMost", 0); 194 byte[] bytes = cw.toByteArray(); 195 196 ClassFile classFile = com.google.turbine.bytecode.ClassReader.read(null, bytes); 197 198 assertThat(classFile.innerClasses()).hasSize(2); 199 200 ClassFile.InnerClass a = classFile.innerClasses().get(0); 201 assertThat(a.access()).isEqualTo(TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PRIVATE); 202 assertThat(a.innerName()).isEqualTo("Inner"); 203 assertThat(a.innerClass()).isEqualTo("test/Hello$Inner"); 204 assertThat(a.outerClass()).isEqualTo("test/Hello"); 205 206 ClassFile.InnerClass b = classFile.innerClasses().get(1); 207 assertThat(b.innerName()).isEqualTo("InnerMost"); 208 assertThat(b.innerClass()).isEqualTo("test/Hello$Inner$InnerMost"); 209 assertThat(b.outerClass()).isEqualTo("test/Hello$Inner"); 210 } 211 212 @Test 213 public void largeConstant() { 214 String jumbo = Strings.repeat("a", Short.MAX_VALUE + 1); 215 216 ClassWriter cw = new ClassWriter(0); 217 cw.visit(52, Opcodes.ACC_SUPER, jumbo, null, "java/lang/Object", null); 218 byte[] bytes = cw.toByteArray(); 219 220 ClassFile cf = ClassReader.read(null, bytes); 221 assertThat(cf.name()).isEqualTo(jumbo); 222 } 223 224 @Test 225 public void v53() { 226 ClassWriter cw = new ClassWriter(0); 227 cw.visitAnnotation("Ljava/lang/Deprecated;", true); 228 cw.visit( 229 53, 230 Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER, 231 "Hello", 232 null, 233 "java/lang/Object", 234 null); 235 ClassFile cf = ClassReader.read(null, cw.toByteArray()); 236 assertThat(cf.name()).isEqualTo("Hello"); 237 } 238 } 239