DexMergeTestpublic final class DexMergeTest extends TestCase Test that DexMerge works by merging dex files, and then loading them into
the current VM. |
Methods Summary |
---|
private void | copy(java.io.InputStream in, java.io.OutputStream out)
byte[] buffer = new byte[1024];
int count;
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
in.close();
| private java.io.File | dexToJar(java.io.File dex)
File result = File.createTempFile("DexMergeTest", ".jar");
result.deleteOnExit();
JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(result));
jarOut.putNextEntry(new JarEntry("classes.dex"));
copy(new FileInputStream(dex), jarOut);
jarOut.closeEntry();
jarOut.close();
return result;
| public java.lang.ClassLoader | mergeAndLoad(java.lang.String dexAResource, java.lang.String dexBResource)
Dex dexA = resourceToDexBuffer(dexAResource);
Dex dexB = resourceToDexBuffer(dexBResource);
Dex merged = new DexMerger(dexA, dexB, CollisionPolicy.KEEP_FIRST).merge();
File mergedDex = File.createTempFile("DexMergeTest", ".classes.dex");
merged.writeTo(mergedDex);
File mergedJar = dexToJar(mergedDex);
// simplify the javac classpath by not depending directly on 'dalvik.system' classes
return (ClassLoader) Class.forName("dalvik.system.PathClassLoader")
.getConstructor(String.class, ClassLoader.class)
.newInstance(mergedJar.getPath(), getClass().getClassLoader());
| private com.android.dex.Dex | resourceToDexBuffer(java.lang.String resource)
return new Dex(getClass().getResourceAsStream(resource));
| public void | testAnnotations()
ClassLoader loader = mergeAndLoad(
"/testdata/Basic.dex",
"/testdata/Annotated.dex");
Class<?> basic = loader.loadClass("testdata.Basic");
assertEquals(1, basic.getDeclaredMethods().length);
Class<?> annotated = loader.loadClass("testdata.Annotated");
Method method = annotated.getMethod("method", String.class, String.class);
Field field = annotated.getField("field");
@SuppressWarnings("unchecked")
Class<? extends Annotation> marker
= (Class<? extends Annotation>) loader.loadClass("testdata.Annotated$Marker");
assertEquals("@testdata.Annotated$Marker(a=on class, b=[A, B, C], "
+ "c=@testdata.Annotated$Nested(e=E1, f=1695938256, g=7264081114510713000), "
+ "d=[@testdata.Annotated$Nested(e=E2, f=1695938256, g=7264081114510713000)])",
annotated.getAnnotation(marker).toString());
assertEquals("@testdata.Annotated$Marker(a=on method, b=[], "
+ "c=@testdata.Annotated$Nested(e=, f=0, g=0), d=[])",
method.getAnnotation(marker).toString());
assertEquals("@testdata.Annotated$Marker(a=on field, b=[], "
+ "c=@testdata.Annotated$Nested(e=, f=0, g=0), d=[])",
field.getAnnotation(marker).toString());
assertEquals("@testdata.Annotated$Marker(a=on parameter, b=[], "
+ "c=@testdata.Annotated$Nested(e=, f=0, g=0), d=[])",
method.getParameterAnnotations()[1][0].toString());
| public void | testFillArrayData()
ClassLoader loader = mergeAndLoad(
"/testdata/Basic.dex",
"/testdata/FillArrayData.dex");
Class<?> basic = loader.loadClass("testdata.Basic");
assertEquals(1, basic.getDeclaredMethods().length);
Class<?> fillArrayData = loader.loadClass("testdata.FillArrayData");
assertTrue(Arrays.equals(
new byte[] { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, -112, -23, 121 },
(byte[]) fillArrayData.getMethod("newByteArray").invoke(null)));
assertTrue(Arrays.equals(
new char[] { 0xFFFF, 0x4321, 0xABCD, 0, 'a", 'b", 'c" },
(char[]) fillArrayData.getMethod("newCharArray").invoke(null)));
assertTrue(Arrays.equals(
new long[] { 4660046610375530309L, 7540113804746346429L, -6246583658587674878L },
(long[]) fillArrayData.getMethod("newLongArray").invoke(null)));
| public void | testMergedOutputSizeIsBounded()Merging dex files uses pessimistic sizes that naturally leave gaps in the
output files. If those gaps grow too large, the merger is supposed to
compact the result. This exercises that by repeatedly merging a dex with
itself.
/*
* At the time this test was written, the output would grow ~25% with
* each merge. Setting a low 1KiB ceiling on the maximum size caused
* the file to be compacted every four merges.
*/
int steps = 100;
int compactWasteThreshold = 1024;
Dex dexA = resourceToDexBuffer("/testdata/Basic.dex");
Dex dexB = resourceToDexBuffer("/testdata/TryCatchFinally.dex");
Dex merged = new DexMerger(dexA, dexB, CollisionPolicy.KEEP_FIRST).merge();
int maxLength = 0;
for (int i = 0; i < steps; i++) {
DexMerger dexMerger = new DexMerger(dexA, merged, CollisionPolicy.KEEP_FIRST);
dexMerger.setCompactWasteThreshold(compactWasteThreshold);
merged = dexMerger.merge();
maxLength = Math.max(maxLength, merged.getLength());
}
int maxExpectedLength = dexA.getLength() + dexB.getLength() + compactWasteThreshold;
assertTrue(maxLength + " < " + maxExpectedLength, maxLength < maxExpectedLength);
| public void | testStaticValues()
ClassLoader loader = mergeAndLoad(
"/testdata/Basic.dex",
"/testdata/StaticValues.dex");
Class<?> basic = loader.loadClass("testdata.Basic");
assertEquals(1, basic.getDeclaredMethods().length);
Class<?> staticValues = loader.loadClass("testdata.StaticValues");
assertEquals((byte) 1, staticValues.getField("a").get(null));
assertEquals((short) 2, staticValues.getField("b").get(null));
assertEquals('C", staticValues.getField("c").get(null));
assertEquals(0xabcd1234, staticValues.getField("d").get(null));
assertEquals(4660046610375530309L,staticValues.getField("e").get(null));
assertEquals(0.5f, staticValues.getField("f").get(null));
assertEquals(-0.25, staticValues.getField("g").get(null));
assertEquals("this is a String", staticValues.getField("h").get(null));
assertEquals(String.class, staticValues.getField("i").get(null));
assertEquals("[0, 1]", Arrays.toString((int[]) staticValues.getField("j").get(null)));
assertEquals(null, staticValues.getField("k").get(null));
assertEquals(true, staticValues.getField("l").get(null));
assertEquals(false, staticValues.getField("m").get(null));
| public void | testTryCatchFinally()
ClassLoader loader = mergeAndLoad(
"/testdata/Basic.dex",
"/testdata/TryCatchFinally.dex");
Class<?> basic = loader.loadClass("testdata.Basic");
assertEquals(1, basic.getDeclaredMethods().length);
Class<?> tryCatchFinally = loader.loadClass("testdata.TryCatchFinally");
tryCatchFinally.getDeclaredMethod("method").invoke(null);
|
|