Home | History | Annotate | Download | only in bytecode
      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
     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
    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
    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
    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
    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
    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
    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