Merge "Load in-memory dex into a single DexFile/Element object"
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
index 663a7c8..9cf5f80 100644
--- a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -172,8 +172,9 @@
*/
public BaseDexClassLoader(ByteBuffer[] dexFiles, String librarySearchPath, ClassLoader parent) {
super(parent);
- this.pathList = new DexPathList(this, dexFiles, librarySearchPath);
this.sharedLibraryLoaders = null;
+ this.pathList = new DexPathList(this, librarySearchPath);
+ this.pathList.initByteBufferDexPath(dexFiles);
}
@Override
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index 535257f..8d32931 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -102,15 +102,16 @@
* @param elements
* the temporary dex path list elements from DexPathList.makeElements
*/
- DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
+ DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements)
+ throws IOException {
mCookie = openDexFile(fileName, null, 0, loader, elements);
mInternalCookie = mCookie;
mFileName = fileName;
//System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
}
- DexFile(ByteBuffer buf) throws IOException {
- mCookie = openInMemoryDexFile(buf);
+ DexFile(ByteBuffer[] bufs) throws IOException {
+ mCookie = openInMemoryDexFiles(bufs);
mInternalCookie = mCookie;
mFileName = null;
}
@@ -369,16 +370,23 @@
elements);
}
- private static Object openInMemoryDexFile(ByteBuffer buf) throws IOException {
- if (buf.isDirect()) {
- return createCookieWithDirectBuffer(buf, buf.position(), buf.limit());
- } else {
- return createCookieWithArray(buf.array(), buf.position(), buf.limit());
+ private static Object openInMemoryDexFiles(ByteBuffer[] bufs) throws IOException {
+ // Preprocess the ByteBuffers for openInMemoryDexFilesNative. We extract
+ // the backing array (non-direct buffers only) and start/end positions
+ // so that the native method does not have to call Java methods anymore.
+ byte[][] arrays = new byte[bufs.length][];
+ int[] starts = new int[bufs.length];
+ int[] ends = new int[bufs.length];
+ for (int i = 0; i < bufs.length; ++i) {
+ arrays[i] = bufs[i].isDirect() ? null : bufs[i].array();
+ starts[i] = bufs[i].position();
+ ends[i] = bufs[i].limit();
}
+ return openInMemoryDexFilesNative(bufs, arrays, starts, ends);
}
- private static native Object createCookieWithDirectBuffer(ByteBuffer buf, int start, int end);
- private static native Object createCookieWithArray(byte[] buf, int start, int end);
+ private static native Object openInMemoryDexFilesNative(ByteBuffer[] bufs, byte[][] arrays,
+ int[] starts, int[] ends);
/*
* Returns true if the dex file is backed by a valid oat file.
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index 6340b77..fc57dc0 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -49,8 +49,10 @@
*
* <p>This class also contains methods to use these lists to look up
* classes and resources.</p>
+ *
+ * @hide
*/
-/*package*/ final class DexPathList {
+public final class DexPathList {
private static final String DEX_SUFFIX = ".dex";
private static final String zipSeparator = "!/";
@@ -99,33 +101,16 @@
*
* @param dexFiles the bytebuffers containing the dex files that we should load classes from.
*/
- public DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles,
- String librarySearchPath) {
+ public DexPathList(ClassLoader definingContext, String librarySearchPath) {
if (definingContext == null) {
throw new NullPointerException("definingContext == null");
}
- if (dexFiles == null) {
- throw new NullPointerException("dexFiles == null");
- }
- if (Arrays.stream(dexFiles).anyMatch(v -> v == null)) {
- throw new NullPointerException("dexFiles contains a null Buffer!");
- }
this.definingContext = definingContext;
-
this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
this.systemNativeLibraryDirectories =
splitPaths(System.getProperty("java.library.path"), true);
this.nativeLibraryPathElements = makePathElements(getAllNativeLibraryDirectories());
-
- ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
- this.dexElements = makeInMemoryDexElements(dexFiles, suppressedExceptions);
- if (suppressedExceptions.size() > 0) {
- this.dexElementsSuppressedExceptions =
- suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
- } else {
- dexElementsSuppressedExceptions = null;
- }
}
/**
@@ -261,6 +246,41 @@
}
/**
+ * For InMemoryDexClassLoader. Initializes {@code dexElements} with dex files
+ * loaded from {@code dexFiles} buffers.
+ *
+ * @param dexFiles ByteBuffers containing raw dex data. Apks are not supported.
+ */
+ /* package */ void initByteBufferDexPath(ByteBuffer[] dexFiles) {
+ if (dexFiles == null) {
+ throw new NullPointerException("dexFiles == null");
+ }
+ if (Arrays.stream(dexFiles).anyMatch(v -> v == null)) {
+ throw new NullPointerException("dexFiles contains a null Buffer!");
+ }
+ if (dexElements != null || dexElementsSuppressedExceptions != null) {
+ throw new IllegalStateException("Should only be called once");
+ }
+
+ final List<IOException> suppressedExceptions = new ArrayList<IOException>();
+
+ try {
+ Element[] null_elements = null;
+ DexFile dex = new DexFile(dexFiles);
+ dexElements = new Element[] { new Element(dex) };
+ } catch (IOException suppressed) {
+ System.logE("Unable to load dex files", suppressed);
+ suppressedExceptions.add(suppressed);
+ dexElements = new Element[0];
+ }
+
+ if (suppressedExceptions.size() > 0) {
+ dexElementsSuppressedExceptions = suppressedExceptions.toArray(
+ new IOException[suppressedExceptions.size()]);
+ }
+ }
+
+ /**
* Splits the given dex path string into elements using the path
* separator, pruning out any elements that do not refer to existing
* and readable files.
@@ -301,14 +321,16 @@
return result;
}
+ // This method is not used anymore. Kept around only because there are many legacy users of it.
+ @SuppressWarnings("unused")
@UnsupportedAppUsage
- private static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles,
+ public static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles,
List<IOException> suppressedExceptions) {
Element[] elements = new Element[dexFiles.length];
int elementPos = 0;
for (ByteBuffer buf : dexFiles) {
try {
- DexFile dex = new DexFile(buf);
+ DexFile dex = new DexFile(new ByteBuffer[] { buf });
elements[elementPos++] = new Element(dex);
} catch (IOException suppressed) {
System.logE("Unable to load dex file: " + buf, suppressed);
diff --git a/luni/src/test/java/libcore/dalvik/system/InMemoryDexClassLoaderTest.java b/luni/src/test/java/libcore/dalvik/system/InMemoryDexClassLoaderTest.java
index ef87ee5..1012c9f 100644
--- a/luni/src/test/java/libcore/dalvik/system/InMemoryDexClassLoaderTest.java
+++ b/luni/src/test/java/libcore/dalvik/system/InMemoryDexClassLoaderTest.java
@@ -24,11 +24,13 @@
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.file.Files;
+import java.util.ArrayList;
import libcore.io.Streams;
import junit.framework.TestCase;
import dalvik.system.BaseDexClassLoader;
import dalvik.system.InMemoryDexClassLoader;
+import dalvik.system.DexPathList;
/**
* Tests for the class {@link InMemoryDexClassLoader}.
@@ -424,6 +426,20 @@
classLoader.loadClass("test.TestMethods");
}
+ /**
+ * DexPathList.makeInMemoryDexElements() is a legacy code path not used by
+ * InMemoryDexClassLoader anymore but heavily used by 3p apps. Test that it still works.
+ */
+ public void testMakeInMemoryDexElements() throws Exception {
+ ArrayList<IOException> exceptions = new ArrayList<>();
+ Object[] elements = DexPathList.makeInMemoryDexElements(
+ new ByteBuffer[] { readFileToByteBufferDirect(dex1),
+ readFileToByteBufferIndirect(dex2) },
+ exceptions);
+ assertEquals(2, elements.length);
+ assertTrue(exceptions.isEmpty());
+ }
+
private static File makeEmptyFile(File directory, String name) throws IOException {
assertTrue(directory.mkdirs());
File result = new File(directory, name);