/*
* 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);
}
}