/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dex.reader; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.Test; import dex.reader.util.JavaSource; import dex.structure.DexAnnotation; import dex.structure.DexAnnotationAttribute; import dex.structure.DexClass; import dex.structure.DexEncodedValue; import dex.structure.DexField; import dex.structure.DexFile; import dex.structure.DexMethod; import dex.structure.DexParameter; public class DexFileReaderTests extends DexTestsCommon { private static final String LDALVIK_ANNOTATION_SIGNATURE = "Ldalvik/annotation/Signature;"; JavaSource A = new JavaSource("a.b.c.A", "package a.b.c; public class A{ public void get() {}}" ); @Test public void testA() throws IOException { DexFile dexFile = javaToDexUtil.getFrom(A); assertEquals(1, dexFile.getDefinedClasses().size()); @SuppressWarnings("unused") DexClass class1 = getClass(dexFile, "La/b/c/A;"); System.out.println(dexFile); } JavaSource T0 = new JavaSource("T0", "public class T0 {" + " public int publicIntField;" + " protected long protectedLongField;" + " short defaultShortField;" + " private double privateDoubleField;" + " " + " public String publicStringMethodInt(int a){ return \"bla\"; }" + " protected String protectedStringMethodInt(int a){ return \"bla\"; }" + " String defaultStringMethodInt(int a){ return \"bla\"; }" + " private String privateStringMethodInt(int a){ return \"bla\"; }" + "}" ); /** * Tests parsing a simple class. */ @Test public void testT0() throws IOException { DexFile dexFile = javaToDexUtil.getFrom(T0); assertEquals(1, dexFile.getDefinedClasses().size()); DexClass clazz = dexFile.getDefinedClasses().get(0); assertEquals("LT0;", clazz.getName()); assertPublic(clazz); //fields assertEquals(4, clazz.getFields().size()); DexField field = getField(clazz, "publicIntField"); assertPublic(field); field = getField(clazz, "protectedLongField"); assertProtected(field); field = getField(clazz, "defaultShortField"); assertDefault(field); field = getField(clazz, "privateDoubleField"); assertPrivate(field); //methods DexMethod method = getMethod(clazz, "publicStringMethodInt", "I"); assertPublic(method); method = getMethod(clazz, "protectedStringMethodInt", "I");/** a.b.C */ assertProtected(method); method = getMethod(clazz, "defaultStringMethodInt", "I"); assertDefault(method); method = getMethod(clazz, "privateStringMethodInt", "I"); assertPrivate(method); } JavaSource T1 = new JavaSource( "T1","public class T1 extends T0 {}" ); private static Set<JavaSource> toSet(JavaSource...javaSources){ return new HashSet<JavaSource>(Arrays.asList(javaSources)); } private static Set<String> toStringSet(JavaSource... javaSources) { Set<String> names = new HashSet<String>(); for (JavaSource javaSource : javaSources) { names.add(javaSource.getName()); } return names; } private static Set<String> toStringSet(String... javaSourceName) { return new HashSet<String>(Arrays.asList(javaSourceName)); } /** * Tests parsing a simple sub class. * @throws IOException */ @Test public void testT1() throws IOException { DexFile dexFile = javaToDexUtil.getFrom(toSet(T1, T0), toStringSet(T1)); assertEquals(1, dexFile.getDefinedClasses().size()); DexClass clazz = dexFile.getDefinedClasses().get(0); assertEquals("LT1;", clazz.getName()); assertPublic(clazz); assertEquals("LT0;", clazz.getSuperClass()); } /** * Tests parsing T0 and T1 from same dex file. * * @throws IOException */ @Test public void testT0_T1() throws IOException { DexFile dexFile = javaToDexUtil.getFrom(T1, T0); assertEquals(2, dexFile.getDefinedClasses().size()); DexClass T0 = getClass(dexFile, "LT0;"); assertPublic(T0); DexClass T1 = getClass(dexFile, "LT1;"); assertPublic(T1); assertEquals(T1.getSuperClass(), T0.getName()); } static final JavaSource A0 = new JavaSource("A0", "import java.lang.annotation.*;" + "@Retention(RetentionPolicy.RUNTIME)" + "@Target(ElementType.TYPE)" + "public @interface A0 {}" ); /** * Tests parsing Annotation Declaration. */ @Test public void testA0() throws IOException { DexFile dexFile = javaToDexUtil.getFrom(A0); assertEquals(1, dexFile.getDefinedClasses().size()); DexClass A0 = getClass(dexFile, "LA0;"); assertPublic(A0); assertEquals(2, A0.getAnnotations().size()); } static final JavaSource T3 = new JavaSource("T3", "import java.io.*;" + "@A0 " + "public final class T3 {}" ); /** * Tests parsing Annotated Class. */ @Test public void testA0_T3() throws IOException { DexFile dexFile = javaToDexUtil.getFrom(T3, A0); assertEquals(2, dexFile.getDefinedClasses().size()); DexClass T3 = getClass(dexFile, "LT3;"); assertPublic(T3); assertEquals(1, T3.getAnnotations().size()); DexAnnotation annotation = getAnnotation(T3, "LA0;"); DexClass A0 = getClass(dexFile, "LA0;"); assertEquals(A0.getName(), annotation.getTypeName()); } static final JavaSource G0 = new JavaSource("G0","public class G0<T>{}"); /** * Tests parsing Generic Type. */ @Test public void testG0() throws IOException { DexFile dexFile = javaToDexUtil.getFrom(G0); assertEquals(1, dexFile.getDefinedClasses().size()); DexClass G0 = getClass(dexFile, "LG0;"); assertPublic(G0); DexAnnotation sig = getAnnotation(G0, LDALVIK_ANNOTATION_SIGNATURE); assertEquals(1, sig.getAttributes().size()); DexAnnotationAttribute dexAnnotationValue = sig.getAttributes().get(0); assertNotNull(dexAnnotationValue.getEncodedValue()); Object value = dexAnnotationValue.getEncodedValue().getValue(); assertTrue(value instanceof List); StringBuilder builder = new StringBuilder(); for (Object o : (List<?>)value) { builder.append(((DexEncodedValue)o).getValue()); } //FIXME verify assertEquals("<T:Ljava/lang/Object;>Ljava/lang/Object;", builder.toString()); } static final JavaSource G1 = new JavaSource("G1","public class G1<T extends G1>{}"); /** * Tests parsing Generic Type. */ @Test public void testG1() throws IOException { DexFile dexFile = javaToDexUtil.getFrom(G1); assertEquals(1, dexFile.getDefinedClasses().size()); DexClass G1 = getClass(dexFile, "LG1;"); assertPublic(G1); DexAnnotation sig = getAnnotation(G1, LDALVIK_ANNOTATION_SIGNATURE); assertEquals(1, sig.getAttributes().size()); DexAnnotationAttribute dexAnnotationValue = sig.getAttributes().get(0); assertNotNull(dexAnnotationValue.getEncodedValue()); Object value = dexAnnotationValue.getEncodedValue().getValue(); assertTrue(value instanceof List); StringBuilder builder = new StringBuilder(); for (Object o : (List<?>)value) { builder.append(((DexEncodedValue)o).getValue()); } //FIXME verify assertEquals("<T:LG1;>Ljava/lang/Object;", builder.toString()); } static final JavaSource I0 = new JavaSource("I0", "import java.io.Serializable;" + "public interface I0 extends Serializable {}" ); /** * Tests parsing Interface Type. */ @Test public void testI0() throws IOException { DexFile dexFile = javaToDexUtil.getFrom(I0); assertEquals(1, dexFile.getDefinedClasses().size()); DexClass I0 = getClass(dexFile, "LI0;"); assertPublic(I0); assertTrue(Modifier.isInterface(I0.getModifiers())); assertEquals(1, I0.getInterfaces().size()); assertEquals("Ljava/io/Serializable;", I0.getInterfaces().get(0)); } static final JavaSource Outer0 = new JavaSource("Outer0", "public class Outer0 {" + " static class StaticInner {}" + " class Inner{}" + "}" ); /** * Tests parsing Interface Type. * @throws IOException */ @SuppressWarnings("unchecked") @Test public void testOuter0() throws IOException { DexFile dexFile = javaToDexUtil.getFrom(toSet(Outer0), toStringSet("Outer0", "Outer0$Inner", "Outer0$StaticInner")); assertEquals(3, dexFile.getDefinedClasses().size()); DexClass Outer0 = getClass(dexFile, "LOuter0;"); DexAnnotation sig = getAnnotation(Outer0, "Ldalvik/annotation/MemberClasses;"); assertEquals(1, sig.getAttributes().size()); DexAnnotationAttribute dexAnnotationValue = sig.getAttributes().get(0); assertNotNull(dexAnnotationValue.getEncodedValue()); List<DexEncodedValue> values = (List<DexEncodedValue>) dexAnnotationValue.getEncodedValue().getValue(); Set<String> innerTypeNames = new HashSet<String>(); for (DexEncodedValue value : values) { innerTypeNames.add((String) value.getValue()); } DexClass inner = getClass(dexFile, "LOuter0$Inner;"); DexClass staticInner = getClass(dexFile, "LOuter0$StaticInner;"); assertTrue(innerTypeNames.contains(inner.getName())); assertTrue(innerTypeNames.contains(staticInner.getName())); } static final JavaSource parameterAnnotation = new JavaSource("A", "public class A {" + " void m(@Deprecated int a) {}" + "}"); /** * Tests parameter annotation. * * @throws IOException */ @Test public void testParameterAnnotation() throws IOException { DexFile dexFile = javaToDexUtil.getFrom(parameterAnnotation); assertEquals(1, dexFile.getDefinedClasses().size()); DexClass A = getClass(dexFile, "LA;"); DexMethod method = getMethod(A, "m", "I"); assertEquals(1, method.getParameters().size()); DexParameter dexParameter = method.getParameters().get(0); assertEquals("I", dexParameter.getTypeName()); assertEquals(1, dexParameter.getAnnotations().size()); DexAnnotation annotation = dexParameter.getAnnotations().iterator().next(); assertEquals("Ljava/lang/Deprecated;", annotation.getTypeName()); } @Test public void testEnum() throws IOException { JavaSource source = new JavaSource("E", "public enum E { A,B; public static final E C = null; }"); DexFile dexFile = javaToDexUtil.getFrom(source); assertEquals(1, dexFile.getDefinedClasses().size()); DexClass E = getClass(dexFile, "LE;"); System.out.println(E); System.out.println(E.getFields()); } /** * Tests parsing of huge dex file. * @throws IOException */ @Test public void testAllReader() throws IOException { FileWriter w = new FileWriter("dex/classes.out.dex"); DexFileReader dexReader = new DexFileReader(); DexFile dexFile = dexReader.read(new DexBuffer("dex/classes.dex")); TypeFormatter formatter = new TypeFormatter(); w.append(formatter.formatDexFile(dexFile)); w.flush(); w.close(); assertTrue(true); } /** * Tests parsing of huge dex file. * @throws IOException */ @Test public void testAllReader0() throws IOException { FileWriter w = new FileWriter("dex/classes0.out.dex"); DexFileReader dexReader = new DexFileReader(); DexFile dexFile = dexReader.read(new DexBuffer("dex/classes0.dex")); TypeFormatter formatter = new TypeFormatter(); w.append(formatter.formatDexFile(dexFile)); w.flush(); w.close(); assertTrue(true); } }