1 /*
2  * Copyright (C) 2009 The Android Open Source Project
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 dex.reader;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertTrue;
22 
23 import java.io.FileWriter;
24 import java.io.IOException;
25 import java.lang.reflect.Modifier;
26 import java.util.Arrays;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
30 
31 import org.junit.Test;
32 
33 import dex.reader.util.JavaSource;
34 import dex.structure.DexAnnotation;
35 import dex.structure.DexAnnotationAttribute;
36 import dex.structure.DexClass;
37 import dex.structure.DexEncodedValue;
38 import dex.structure.DexField;
39 import dex.structure.DexFile;
40 import dex.structure.DexMethod;
41 import dex.structure.DexParameter;
42 
43 
44 public class DexFileReaderTests extends DexTestsCommon {
45 
46     private static final String LDALVIK_ANNOTATION_SIGNATURE = "Ldalvik/annotation/Signature;";
47 
48 
49     JavaSource A = new JavaSource("a.b.c.A",
50             "package a.b.c; public class A{ public void get() {}}"
51     );
52 
53     @Test
testA()54     public void testA() throws IOException {
55         DexFile dexFile = javaToDexUtil.getFrom(A);
56         assertEquals(1, dexFile.getDefinedClasses().size());
57         @SuppressWarnings("unused")
58         DexClass class1 = getClass(dexFile, "La/b/c/A;");
59         System.out.println(dexFile);
60     }
61 
62 
63     JavaSource T0 = new JavaSource("T0",
64             "public class T0 {" +
65             "    public int publicIntField;" +
66             "    protected long protectedLongField;" +
67             "    short defaultShortField;" +
68             "    private double privateDoubleField;" +
69             "    " +
70             "    public String publicStringMethodInt(int a){ return \"bla\"; }" +
71             "    protected String protectedStringMethodInt(int a){ return \"bla\"; }" +
72             "    String defaultStringMethodInt(int a){ return \"bla\"; }" +
73             "    private String privateStringMethodInt(int a){ return \"bla\"; }" +
74             "}"
75     );
76 
77     /**
78      * Tests parsing a simple class.
79      */
80     @Test
testT0()81     public void testT0() throws IOException {
82 
83         DexFile dexFile = javaToDexUtil.getFrom(T0);
84         assertEquals(1, dexFile.getDefinedClasses().size());
85         DexClass clazz = dexFile.getDefinedClasses().get(0);
86         assertEquals("LT0;", clazz.getName());
87         assertPublic(clazz);
88         //fields
89         assertEquals(4, clazz.getFields().size());
90         DexField field = getField(clazz, "publicIntField");
91         assertPublic(field);
92         field = getField(clazz, "protectedLongField");
93         assertProtected(field);
94         field = getField(clazz, "defaultShortField");
95         assertDefault(field);
96         field = getField(clazz, "privateDoubleField");
97         assertPrivate(field);
98         //methods
99         DexMethod method = getMethod(clazz, "publicStringMethodInt", "I");
100         assertPublic(method);
101         method = getMethod(clazz, "protectedStringMethodInt", "I");/** a.b.C */
102         assertProtected(method);
103         method = getMethod(clazz, "defaultStringMethodInt", "I");
104         assertDefault(method);
105         method = getMethod(clazz, "privateStringMethodInt", "I");
106         assertPrivate(method);
107     }
108 
109     JavaSource T1 = new JavaSource( "T1","public class T1 extends T0 {}" );
110 
111 
toSet(JavaSource...javaSources)112     private static Set<JavaSource> toSet(JavaSource...javaSources){
113         return new HashSet<JavaSource>(Arrays.asList(javaSources));
114     }
115 
toStringSet(JavaSource... javaSources)116     private static Set<String> toStringSet(JavaSource... javaSources) {
117         Set<String> names = new HashSet<String>();
118         for (JavaSource javaSource : javaSources) {
119             names.add(javaSource.getName());
120         }
121 
122         return names;
123     }
124 
toStringSet(String... javaSourceName)125     private static Set<String> toStringSet(String... javaSourceName) {
126         return new HashSet<String>(Arrays.asList(javaSourceName));
127     }
128 
129     /**
130      * Tests parsing a simple sub class.
131      * @throws IOException
132      */
133     @Test
testT1()134     public void testT1() throws IOException {
135         DexFile dexFile = javaToDexUtil.getFrom(toSet(T1, T0), toStringSet(T1));
136         assertEquals(1, dexFile.getDefinedClasses().size());
137         DexClass clazz = dexFile.getDefinedClasses().get(0);
138         assertEquals("LT1;", clazz.getName());
139         assertPublic(clazz);
140         assertEquals("LT0;", clazz.getSuperClass());
141     }
142 
143     /**
144      * Tests parsing T0 and T1 from same dex file.
145      *
146      * @throws IOException
147      */
148     @Test
testT0_T1()149     public void testT0_T1() throws IOException {
150         DexFile dexFile = javaToDexUtil.getFrom(T1, T0);
151         assertEquals(2, dexFile.getDefinedClasses().size());
152 
153         DexClass T0 = getClass(dexFile, "LT0;");
154         assertPublic(T0);
155 
156         DexClass T1 = getClass(dexFile, "LT1;");
157         assertPublic(T1);
158 
159         assertEquals(T1.getSuperClass(), T0.getName());
160     }
161 
162     static final JavaSource A0 = new JavaSource("A0",
163     "import java.lang.annotation.*;" +
164     "@Retention(RetentionPolicy.RUNTIME)" +
165     "@Target(ElementType.TYPE)" +
166     "public @interface A0 {}"
167      );
168 
169     /**
170      * Tests parsing Annotation Declaration.
171      */
172     @Test
testA0()173     public void testA0() throws IOException {
174         DexFile dexFile = javaToDexUtil.getFrom(A0);
175         assertEquals(1, dexFile.getDefinedClasses().size());
176 
177         DexClass A0 = getClass(dexFile, "LA0;");
178         assertPublic(A0);
179         assertEquals(2, A0.getAnnotations().size());
180     }
181 
182 
183     static final JavaSource T3 = new JavaSource("T3",
184     "import java.io.*;" +
185     "@A0 " +
186     "public final class T3 {}"
187     );
188 
189 
190     /**
191      * Tests parsing Annotated Class.
192      */
193     @Test
testA0_T3()194     public void testA0_T3() throws IOException {
195         DexFile dexFile = javaToDexUtil.getFrom(T3, A0);
196         assertEquals(2, dexFile.getDefinedClasses().size());
197 
198         DexClass T3 = getClass(dexFile, "LT3;");
199         assertPublic(T3);
200         assertEquals(1, T3.getAnnotations().size());
201 
202         DexAnnotation annotation = getAnnotation(T3, "LA0;");
203 
204         DexClass A0 = getClass(dexFile, "LA0;");
205 
206         assertEquals(A0.getName(), annotation.getTypeName());
207     }
208 
209 
210     static final JavaSource G0 = new JavaSource("G0","public class G0<T>{}");
211 
212     /**
213      * Tests parsing Generic Type.
214      */
215     @Test
testG0()216     public void testG0() throws IOException {
217         DexFile dexFile = javaToDexUtil.getFrom(G0);
218         assertEquals(1, dexFile.getDefinedClasses().size());
219         DexClass G0 = getClass(dexFile, "LG0;");
220         assertPublic(G0);
221         DexAnnotation sig = getAnnotation(G0, LDALVIK_ANNOTATION_SIGNATURE);
222         assertEquals(1, sig.getAttributes().size());
223         DexAnnotationAttribute dexAnnotationValue = sig.getAttributes().get(0);
224         assertNotNull(dexAnnotationValue.getEncodedValue());
225         Object value = dexAnnotationValue.getEncodedValue().getValue();
226         assertTrue(value instanceof List);
227         StringBuilder builder = new StringBuilder();
228         for (Object o : (List<?>)value) {
229             builder.append(((DexEncodedValue)o).getValue());
230         }
231         //FIXME verify
232         assertEquals("<T:Ljava/lang/Object;>Ljava/lang/Object;", builder.toString());
233     }
234 
235     static final JavaSource G1 = new JavaSource("G1","public class G1<T extends G1>{}");
236 
237     /**
238      * Tests parsing Generic Type.
239      */
240     @Test
testG1()241     public void testG1() throws IOException {
242         DexFile dexFile = javaToDexUtil.getFrom(G1);
243         assertEquals(1, dexFile.getDefinedClasses().size());
244         DexClass G1 = getClass(dexFile, "LG1;");
245         assertPublic(G1);
246         DexAnnotation sig = getAnnotation(G1, LDALVIK_ANNOTATION_SIGNATURE);
247         assertEquals(1, sig.getAttributes().size());
248         DexAnnotationAttribute dexAnnotationValue = sig.getAttributes().get(0);
249         assertNotNull(dexAnnotationValue.getEncodedValue());
250         Object value = dexAnnotationValue.getEncodedValue().getValue();
251         assertTrue(value instanceof List);
252         StringBuilder builder = new StringBuilder();
253         for (Object o : (List<?>)value) {
254             builder.append(((DexEncodedValue)o).getValue());
255         }
256         //FIXME verify
257         assertEquals("<T:LG1;>Ljava/lang/Object;", builder.toString());
258     }
259 
260 
261 
262     static final JavaSource I0 = new JavaSource("I0",
263     "import java.io.Serializable;" +
264     "public interface I0 extends Serializable {}"
265     );
266 
267     /**
268      * Tests parsing Interface Type.
269      */
270     @Test
testI0()271     public void testI0() throws IOException {
272         DexFile dexFile = javaToDexUtil.getFrom(I0);
273         assertEquals(1, dexFile.getDefinedClasses().size());
274         DexClass I0 = getClass(dexFile, "LI0;");
275         assertPublic(I0);
276         assertTrue(Modifier.isInterface(I0.getModifiers()));
277         assertEquals(1,  I0.getInterfaces().size());
278         assertEquals("Ljava/io/Serializable;",  I0.getInterfaces().get(0));
279     }
280 
281 
282     static final JavaSource Outer0 = new JavaSource("Outer0",
283     "public class Outer0 {" +
284     "    static class StaticInner {}" +
285     "    class Inner{}" +
286     "}"
287     );
288 
289     /**
290      * Tests parsing Interface Type.
291      * @throws IOException
292      */
293     @SuppressWarnings("unchecked")
294     @Test
testOuter0()295     public void testOuter0() throws IOException {
296         DexFile dexFile = javaToDexUtil.getFrom(toSet(Outer0), toStringSet("Outer0", "Outer0$Inner", "Outer0$StaticInner"));
297         assertEquals(3, dexFile.getDefinedClasses().size());
298         DexClass Outer0 = getClass(dexFile, "LOuter0;");
299         DexAnnotation sig = getAnnotation(Outer0, "Ldalvik/annotation/MemberClasses;");
300         assertEquals(1, sig.getAttributes().size());
301         DexAnnotationAttribute dexAnnotationValue = sig.getAttributes().get(0);
302         assertNotNull(dexAnnotationValue.getEncodedValue());
303         List<DexEncodedValue> values = (List<DexEncodedValue>) dexAnnotationValue.getEncodedValue().getValue();
304         Set<String> innerTypeNames = new HashSet<String>();
305         for (DexEncodedValue value : values) {
306             innerTypeNames.add((String) value.getValue());
307         }
308         DexClass inner = getClass(dexFile, "LOuter0$Inner;");
309         DexClass staticInner = getClass(dexFile, "LOuter0$StaticInner;");
310         assertTrue(innerTypeNames.contains(inner.getName()));
311         assertTrue(innerTypeNames.contains(staticInner.getName()));
312     }
313 
314     static final JavaSource parameterAnnotation = new JavaSource("A",
315             "public class A {" +
316             "  void m(@Deprecated int a) {}" +
317             "}");
318 
319     /**
320      * Tests parameter annotation.
321      *
322      * @throws IOException
323      */
324     @Test
testParameterAnnotation()325     public void testParameterAnnotation() throws IOException {
326         DexFile dexFile = javaToDexUtil.getFrom(parameterAnnotation);
327         assertEquals(1, dexFile.getDefinedClasses().size());
328         DexClass A = getClass(dexFile, "LA;");
329 
330         DexMethod method = getMethod(A, "m", "I");
331         assertEquals(1, method.getParameters().size());
332         DexParameter dexParameter = method.getParameters().get(0);
333         assertEquals("I", dexParameter.getTypeName());
334 
335         assertEquals(1, dexParameter.getAnnotations().size());
336         DexAnnotation annotation = dexParameter.getAnnotations().iterator().next();
337         assertEquals("Ljava/lang/Deprecated;", annotation.getTypeName());
338     }
339 
340     @Test
testEnum()341     public void testEnum() throws IOException {
342         JavaSource source = new JavaSource("E", "public enum E { A,B; public static final E C = null; }");
343         DexFile dexFile = javaToDexUtil.getFrom(source);
344         assertEquals(1, dexFile.getDefinedClasses().size());
345         DexClass E = getClass(dexFile, "LE;");
346         System.out.println(E);
347         System.out.println(E.getFields());
348     }
349 
350     /**
351      * Tests parsing of huge dex file.
352      * @throws IOException
353      */
354     @Test
testAllReader()355     public void testAllReader() throws IOException {
356         FileWriter w = new FileWriter("dex/classes.out.dex");
357         DexFileReader dexReader = new DexFileReader();
358         DexFile dexFile = dexReader.read(new DexBuffer("dex/classes.dex"));
359         TypeFormatter formatter = new TypeFormatter();
360         w.append(formatter.formatDexFile(dexFile));
361         w.flush();
362         w.close();
363         assertTrue(true);
364     }
365 
366     /**
367      * Tests parsing of huge dex file.
368      * @throws IOException
369      */
370     @Test
testAllReader0()371     public void testAllReader0() throws IOException {
372         FileWriter w = new FileWriter("dex/classes0.out.dex");
373         DexFileReader dexReader = new DexFileReader();
374         DexFile dexFile = dexReader.read(new DexBuffer("dex/classes0.dex"));
375         TypeFormatter formatter = new TypeFormatter();
376         w.append(formatter.formatDexFile(dexFile));
377         w.flush();
378         w.close();
379         assertTrue(true);
380     }
381 
382 }
383