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