xref: /aosp_15_r20/external/turbine/javatests/com/google/turbine/bytecode/ClassReaderTest.java (revision f7d94438c8bcdfdbf0d5a2a5e40120d0696e7088)
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