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.collect.ImmutableList.toImmutableList; 20 import static com.google.common.truth.Truth.assertThat; 21 22 import com.google.common.base.Strings; 23 import com.google.common.collect.Iterables; 24 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue; 25 import com.google.turbine.bytecode.ClassFile.ModuleInfo; 26 import com.google.turbine.bytecode.ClassFile.ModuleInfo.ExportInfo; 27 import com.google.turbine.bytecode.ClassFile.ModuleInfo.OpenInfo; 28 import com.google.turbine.bytecode.ClassFile.ModuleInfo.ProvideInfo; 29 import com.google.turbine.bytecode.ClassFile.ModuleInfo.RequireInfo; 30 import com.google.turbine.model.Const; 31 import com.google.turbine.model.TurbineConstantTypeKind; 32 import com.google.turbine.model.TurbineFlag; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.junit.runners.JUnit4; 36 import org.objectweb.asm.AnnotationVisitor; 37 import org.objectweb.asm.ClassWriter; 38 import org.objectweb.asm.ModuleVisitor; 39 import org.objectweb.asm.Opcodes; 40 41 @RunWith(JUnit4.class) 42 public class ClassReaderTest { 43 44 @Test methods()45 public void methods() { 46 ClassWriter cw = new ClassWriter(0); 47 cw.visitAnnotation("Ljava/lang/Deprecated;", true); 48 cw.visit( 49 52, 50 Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER, 51 "test/Hello", 52 null, 53 "java/lang/Object", 54 null); 55 cw.visitMethod( 56 Opcodes.ACC_PUBLIC, 57 "f", 58 "(Ljava/lang/String;)Ljava/lang/String;", 59 "<T:Ljava/lang/String;>(TT;)TT;", 60 null); 61 cw.visitMethod( 62 Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, 63 "g", 64 "(Z)V", 65 "<T:Ljava/lang/Error;>(Z)V^TT;", 66 new String[] {"java/lang/Error"}); 67 cw.visitMethod(0, "h", "(I)V", null, null); 68 byte[] bytes = cw.toByteArray(); 69 70 ClassFile classFile = com.google.turbine.bytecode.ClassReader.read(null, bytes); 71 72 assertThat(classFile.access()) 73 .isEqualTo(TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL | TurbineFlag.ACC_SUPER); 74 assertThat(classFile.name()).isEqualTo("test/Hello"); 75 assertThat(classFile.signature()).isNull(); 76 assertThat(classFile.superName()).isEqualTo("java/lang/Object"); 77 assertThat(classFile.interfaces()).isEmpty(); 78 79 assertThat(classFile.methods()).hasSize(3); 80 81 ClassFile.MethodInfo f = classFile.methods().get(0); 82 assertThat(f.access()).isEqualTo(TurbineFlag.ACC_PUBLIC); 83 assertThat(f.name()).isEqualTo("f"); 84 assertThat(f.descriptor()).isEqualTo("(Ljava/lang/String;)Ljava/lang/String;"); 85 assertThat(f.signature()).isEqualTo("<T:Ljava/lang/String;>(TT;)TT;"); 86 assertThat(f.exceptions()).isEmpty(); 87 assertThat(f.annotations()).isEmpty(); 88 assertThat(f.parameterAnnotations()).isEmpty(); 89 assertThat(f.defaultValue()).isNull(); 90 91 ClassFile.MethodInfo g = classFile.methods().get(1); 92 assertThat(g.access()).isEqualTo(TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_STATIC); 93 assertThat(g.name()).isEqualTo("g"); 94 assertThat(g.descriptor()).isEqualTo("(Z)V"); 95 assertThat(g.signature()).isEqualTo("<T:Ljava/lang/Error;>(Z)V^TT;"); 96 97 ClassFile.MethodInfo h = classFile.methods().get(2); 98 assertThat(h.access()).isEqualTo(0); 99 assertThat(h.name()).isEqualTo("h"); 100 assertThat(h.descriptor()).isEqualTo("(I)V"); 101 assertThat(h.signature()).isNull(); 102 } 103 104 @Test annotationDeclaration()105 public void annotationDeclaration() { 106 ClassWriter cw = new ClassWriter(0); 107 cw.visit( 108 52, 109 Opcodes.ACC_PUBLIC + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ABSTRACT + Opcodes.ACC_INTERFACE, 110 "test/Hello", 111 null, 112 "java/lang/Object", 113 new String[] {"java/lang/annotation/Annotation"}); 114 AnnotationVisitor av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); 115 av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME"); 116 av.visitEnd(); 117 cw.visitEnd(); 118 byte[] bytes = cw.toByteArray(); 119 120 ClassFile classFile = com.google.turbine.bytecode.ClassReader.read(null, bytes); 121 122 assertThat(classFile.access()) 123 .isEqualTo( 124 TurbineFlag.ACC_PUBLIC 125 | TurbineFlag.ACC_ANNOTATION 126 | TurbineFlag.ACC_ABSTRACT 127 | TurbineFlag.ACC_INTERFACE); 128 assertThat(classFile.name()).isEqualTo("test/Hello"); 129 assertThat(classFile.signature()).isNull(); 130 assertThat(classFile.superName()).isEqualTo("java/lang/Object"); 131 assertThat(classFile.interfaces()).containsExactly("java/lang/annotation/Annotation"); 132 133 assertThat(classFile.annotations()).hasSize(1); 134 ClassFile.AnnotationInfo annotation = Iterables.getOnlyElement(classFile.annotations()); 135 assertThat(annotation.typeName()).isEqualTo("Ljava/lang/annotation/Retention;"); 136 assertThat(annotation.elementValuePairs()).hasSize(1); 137 assertThat(annotation.elementValuePairs()).containsKey("value"); 138 ElementValue value = annotation.elementValuePairs().get("value"); 139 assertThat(value.kind()).isEqualTo(ElementValue.Kind.ENUM); 140 ElementValue.EnumConstValue enumValue = (ElementValue.EnumConstValue) value; 141 assertThat(enumValue.typeName()).isEqualTo("Ljava/lang/annotation/RetentionPolicy;"); 142 assertThat(enumValue.constName()).isEqualTo("RUNTIME"); 143 } 144 145 @Test fields()146 public void fields() { 147 ClassWriter cw = new ClassWriter(0); 148 cw.visit( 149 52, 150 Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, 151 "test/Hello", 152 "<X:Ljava/lang/Object;>Ljava/lang/Object;", 153 "java/lang/Object", 154 null); 155 cw.visitField(Opcodes.ACC_PUBLIC, "x", "I", null, null); 156 cw.visitField( 157 Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, 158 "y", 159 "I", 160 null, 161 Integer.valueOf(42)); 162 cw.visitField(Opcodes.ACC_PUBLIC, "z", "Ljava/util/List;", "Ljava/util/List<TX;>;", null); 163 cw.visitEnd(); 164 byte[] bytes = cw.toByteArray(); 165 166 ClassFile classFile = com.google.turbine.bytecode.ClassReader.read(null, bytes); 167 168 assertThat(classFile.fields()).hasSize(3); 169 170 ClassFile.FieldInfo x = classFile.fields().get(0); 171 assertThat(x.access()).isEqualTo(TurbineFlag.ACC_PUBLIC); 172 assertThat(x.name()).isEqualTo("x"); 173 assertThat(x.descriptor()).isEqualTo("I"); 174 assertThat(x.signature()).isNull(); 175 assertThat(x.value()).isNull(); 176 assertThat(x.annotations()).isEmpty(); 177 178 ClassFile.FieldInfo y = classFile.fields().get(1); 179 assertThat(y.access()) 180 .isEqualTo(TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_STATIC | TurbineFlag.ACC_FINAL); 181 assertThat(y.name()).isEqualTo("y"); 182 assertThat(y.descriptor()).isEqualTo("I"); 183 assertThat(y.value().constantTypeKind()).isEqualTo(TurbineConstantTypeKind.INT); 184 assertThat(((Const.IntValue) y.value()).value()).isEqualTo(42); 185 186 ClassFile.FieldInfo z = classFile.fields().get(2); 187 assertThat(z.name()).isEqualTo("z"); 188 assertThat(z.descriptor()).isEqualTo("Ljava/util/List;"); 189 // don't bother reading signatures for fields; we only care about constants 190 assertThat(z.signature()).isNull(); 191 } 192 193 @Test innerClass()194 public void innerClass() { 195 ClassWriter cw = new ClassWriter(0); 196 cw.visit(52, Opcodes.ACC_SUPER, "test/Hello$Inner", null, "java/lang/Object", null); 197 cw.visitInnerClass( 198 "test/Hello$Inner", "test/Hello", "Inner", Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE); 199 cw.visitInnerClass("test/Hello$Inner$InnerMost", "test/Hello$Inner", "InnerMost", 0); 200 byte[] bytes = cw.toByteArray(); 201 202 ClassFile classFile = com.google.turbine.bytecode.ClassReader.read(null, bytes); 203 204 assertThat(classFile.innerClasses()).hasSize(2); 205 206 ClassFile.InnerClass a = classFile.innerClasses().get(0); 207 assertThat(a.access()).isEqualTo(TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PRIVATE); 208 assertThat(a.innerName()).isEqualTo("Inner"); 209 assertThat(a.innerClass()).isEqualTo("test/Hello$Inner"); 210 assertThat(a.outerClass()).isEqualTo("test/Hello"); 211 212 ClassFile.InnerClass b = classFile.innerClasses().get(1); 213 assertThat(b.innerName()).isEqualTo("InnerMost"); 214 assertThat(b.innerClass()).isEqualTo("test/Hello$Inner$InnerMost"); 215 assertThat(b.outerClass()).isEqualTo("test/Hello$Inner"); 216 } 217 218 @Test largeConstant()219 public void largeConstant() { 220 String jumbo = Strings.repeat("a", Short.MAX_VALUE + 1); 221 222 ClassWriter cw = new ClassWriter(0); 223 cw.visit(52, Opcodes.ACC_SUPER, jumbo, null, "java/lang/Object", null); 224 byte[] bytes = cw.toByteArray(); 225 226 ClassFile cf = ClassReader.read(null, bytes); 227 assertThat(cf.name()).isEqualTo(jumbo); 228 } 229 230 @Test v53()231 public void v53() { 232 ClassWriter cw = new ClassWriter(0); 233 cw.visitAnnotation("Ljava/lang/Deprecated;", true); 234 cw.visit( 235 53, 236 Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER, 237 "Hello", 238 null, 239 "java/lang/Object", 240 null); 241 ClassFile cf = ClassReader.read(null, cw.toByteArray()); 242 assertThat(cf.name()).isEqualTo("Hello"); 243 } 244 245 @Test module()246 public void module() { 247 ClassWriter cw = new ClassWriter(0); 248 249 cw.visit(53, /* access= */ 53, "module-info", null, null, null); 250 251 ModuleVisitor mv = cw.visitModule("mod", Opcodes.ACC_OPEN, "mod-ver"); 252 253 mv.visitRequire("r1", Opcodes.ACC_TRANSITIVE, "r1-ver"); 254 mv.visitRequire("r2", Opcodes.ACC_STATIC_PHASE, "r2-ver"); 255 mv.visitRequire("r3", Opcodes.ACC_STATIC_PHASE | Opcodes.ACC_TRANSITIVE, "r3-ver"); 256 257 mv.visitExport("e1", Opcodes.ACC_SYNTHETIC, "e1m1", "e1m2", "e1m3"); 258 mv.visitExport("e2", Opcodes.ACC_MANDATED, "e2m1", "e2m2"); 259 mv.visitExport("e3", /* access= */ 0, "e3m1"); 260 261 mv.visitOpen("o1", Opcodes.ACC_SYNTHETIC, "o1m1", "o1m2", "o1m3"); 262 mv.visitOpen("o2", Opcodes.ACC_MANDATED, "o2m1", "o2m2"); 263 mv.visitOpen("o3", /* access= */ 0, "o3m1"); 264 265 mv.visitUse("u1"); 266 mv.visitUse("u2"); 267 mv.visitUse("u3"); 268 mv.visitUse("u4"); 269 270 mv.visitProvide("p1", "p1i1", "p1i2"); 271 mv.visitProvide("p2", "p2i1", "p2i2", "p2i3"); 272 273 ClassFile cf = ClassReader.read(null, cw.toByteArray()); 274 ModuleInfo module = cf.module(); 275 assertThat(module.name()).isEqualTo("mod"); 276 assertThat(module.flags()).isEqualTo(Opcodes.ACC_OPEN); 277 assertThat(module.version()).isEqualTo("mod-ver"); 278 279 assertThat(module.requires()).hasSize(3); 280 RequireInfo r1 = module.requires().get(0); 281 assertThat(r1.moduleName()).isEqualTo("r1"); 282 assertThat(r1.flags()).isEqualTo(Opcodes.ACC_TRANSITIVE); 283 assertThat(r1.version()).isEqualTo("r1-ver"); 284 RequireInfo r2 = module.requires().get(1); 285 assertThat(r2.moduleName()).isEqualTo("r2"); 286 assertThat(r2.flags()).isEqualTo(Opcodes.ACC_STATIC_PHASE); 287 assertThat(r2.version()).isEqualTo("r2-ver"); 288 RequireInfo r3 = module.requires().get(2); 289 assertThat(r3.moduleName()).isEqualTo("r3"); 290 assertThat(r3.flags()).isEqualTo(Opcodes.ACC_STATIC_PHASE | Opcodes.ACC_TRANSITIVE); 291 assertThat(r3.version()).isEqualTo("r3-ver"); 292 293 assertThat(module.exports()).hasSize(3); 294 ExportInfo e1 = module.exports().get(0); 295 assertThat(e1.moduleName()).isEqualTo("e1"); 296 assertThat(e1.flags()).isEqualTo(Opcodes.ACC_SYNTHETIC); 297 assertThat(e1.modules()).containsExactly("e1m1", "e1m2", "e1m3").inOrder(); 298 ExportInfo e2 = module.exports().get(1); 299 assertThat(e2.moduleName()).isEqualTo("e2"); 300 assertThat(e2.flags()).isEqualTo(Opcodes.ACC_MANDATED); 301 assertThat(e2.modules()).containsExactly("e2m1", "e2m2").inOrder(); 302 ExportInfo e3 = module.exports().get(2); 303 assertThat(e3.moduleName()).isEqualTo("e3"); 304 assertThat(e3.flags()).isEqualTo(0); 305 assertThat(e3.modules()).containsExactly("e3m1").inOrder(); 306 307 assertThat(module.opens()).hasSize(3); 308 OpenInfo o1 = module.opens().get(0); 309 assertThat(o1.moduleName()).isEqualTo("o1"); 310 assertThat(o1.flags()).isEqualTo(Opcodes.ACC_SYNTHETIC); 311 assertThat(o1.modules()).containsExactly("o1m1", "o1m2", "o1m3").inOrder(); 312 OpenInfo o2 = module.opens().get(1); 313 assertThat(o2.moduleName()).isEqualTo("o2"); 314 assertThat(o2.flags()).isEqualTo(Opcodes.ACC_MANDATED); 315 assertThat(o2.modules()).containsExactly("o2m1", "o2m2").inOrder(); 316 OpenInfo o3 = module.opens().get(2); 317 assertThat(o3.moduleName()).isEqualTo("o3"); 318 assertThat(o3.flags()).isEqualTo(0); 319 assertThat(o3.modules()).containsExactly("o3m1").inOrder(); 320 321 assertThat(module.uses().stream().map(u -> u.descriptor()).collect(toImmutableList())) 322 .containsExactly("u1", "u2", "u3", "u4") 323 .inOrder(); 324 325 assertThat(module.provides()).hasSize(2); 326 ProvideInfo p1 = module.provides().get(0); 327 assertThat(p1.descriptor()).isEqualTo("p1"); 328 assertThat(p1.implDescriptors()).containsExactly("p1i1", "p1i2"); 329 ProvideInfo p2 = module.provides().get(1); 330 assertThat(p2.descriptor()).isEqualTo("p2"); 331 assertThat(p2.implDescriptors()).containsExactly("p2i1", "p2i2", "p2i3"); 332 } 333 } 334