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