changeset 28:fe791833f7d8

Added preliminary class loader for Daneel. * dex/DexFile.java (parse): New entry point taking file objects. * loader/ApkFile.java: New class for reading the contents of an APK file. * loader/DaneelClassLoader.java: New class loader implementation. * tools/Main.java (main): Adapted to new entry point in DexFile.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Thu, 17 Mar 2011 01:07:27 +0100
parents b0d7f00453ea
children 5c09eb900283
files src/main/java/org/icedrobot/daneel/dex/DexFile.java src/main/java/org/icedrobot/daneel/loader/ApkFile.java src/main/java/org/icedrobot/daneel/loader/DaneelClassLoader.java src/main/java/org/icedrobot/daneel/tools/Main.java
diffstat 4 files changed, 285 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/dex/DexFile.java	Thu Mar 17 00:56:23 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/DexFile.java	Thu Mar 17 01:07:27 2011 +0100
@@ -1,7 +1,11 @@
 package org.icedrobot.daneel.dex;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
@@ -21,6 +25,12 @@
         return new DexFile(buf);
     }
 
+    public static DexFile parse(File file) throws IOException {
+        ByteBuffer buffer = (new RandomAccessFile(file, "r")).getChannel().map(
+                FileChannel.MapMode.READ_ONLY, 0, file.length());
+        return new DexFile(buffer);
+    }
+
     public static DexFile parse(byte[] bytes) {
         return new DexFile(ByteBuffer.wrap(bytes));
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/org/icedrobot/daneel/loader/ApkFile.java	Thu Mar 17 01:07:27 2011 +0100
@@ -0,0 +1,113 @@
+/*
+ * Daneel - Dalvik to Java bytecode compiler
+ * Copyright (C) 2011  IcedRobot team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file is subject to the "Classpath" exception:
+ *
+ * Linking this library statically or dynamically with other modules is
+ * making a combined work based on this library.  Thus, the terms and
+ * conditions of the GNU General Public License cover the whole
+ * combination.
+ *
+ * As a special exception, the copyright holders of this library give you
+ * permission to link this library with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under terms
+ * of your choice, provided that you also meet, for each linked independent
+ * module, the terms and conditions of the license of that module.  An
+ * independent module is a module which is not derived from or based on
+ * this library.  If you modify this library, you may extend this exception
+ * to your version of the library, but you are not obligated to do so.  If
+ * you do not wish to do so, delete this exception statement from your
+ * version.
+ */
+
+package org.icedrobot.daneel.loader;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import org.icedrobot.daneel.DaneelException;
+import org.icedrobot.daneel.dex.DexFile;
+
+/**
+ * This class reads the contents of an APK file. Those APK files are actually
+ * JAR files which also contain resources and classes, but with all classes
+ * packed into one DEX file within the archive.
+ */
+public class ApkFile extends JarFile {
+
+    /** The actual file object for this APK file. */
+    private final File file;
+
+    /**
+     * Creates a new reader for the APK file represented by the given object.
+     * @param file The given file object.
+     * @throws IOException In case of an error while accessing the file.
+     */
+    public ApkFile(File file) throws IOException {
+        super(file);
+        this.file = file;
+    }
+
+    /**
+     * Creates a new reader for the APK file represented by the given name.
+     * @param name The given file name.
+     * @throws IOException In case of an error while accessing the file.
+     */
+    public ApkFile(String name) throws IOException {
+        super(name);
+        this.file = new File(name);
+    }
+
+    /**
+     * Returns the DEX file containing all the classes of this APK file.
+     * @return The contained DEX file object.
+     * @throws IOException In case of an error while accessing the file.
+     */
+    public DexFile getClasses() throws IOException {
+        ZipEntry entry = getEntry("classes.dex");
+        if (entry == null)
+            throw new DaneelException("The APK doesn't contain classes.");
+        if (entry.getSize() > Integer.MAX_VALUE)
+            throw new DaneelException("Oversized DEX file detected.");
+        ByteBuffer buffer = ByteBuffer.allocate((int) entry.getSize());
+        Channels.newChannel(getInputStream(entry)).read(buffer);
+        buffer.rewind();
+        return DexFile.parse(buffer);
+    }
+
+    /**
+     * Constructs an URL representation for the specified entry inside the APK
+     * file. See {@link java.net.JarURLConnection} for details about the syntax
+     * of such an URL.
+     * @param entry The given entry inside this APK file.
+     * @return The URL representation for the entry.
+     */
+    public URL getJarURL(ZipEntry entry) {
+        try {
+            return new URL("jar:" + file.toURI() + "!/" + entry.getName());
+        } catch (MalformedURLException e) {
+            throw new DaneelException("Unable to construct URL.", e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/org/icedrobot/daneel/loader/DaneelClassLoader.java	Thu Mar 17 01:07:27 2011 +0100
@@ -0,0 +1,160 @@
+/*
+ * Daneel - Dalvik to Java bytecode compiler
+ * Copyright (C) 2011  IcedRobot team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file is subject to the "Classpath" exception:
+ *
+ * Linking this library statically or dynamically with other modules is
+ * making a combined work based on this library.  Thus, the terms and
+ * conditions of the GNU General Public License cover the whole
+ * combination.
+ *
+ * As a special exception, the copyright holders of this library give you
+ * permission to link this library with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under terms
+ * of your choice, provided that you also meet, for each linked independent
+ * module, the terms and conditions of the license of that module.  An
+ * independent module is a module which is not derived from or based on
+ * this library.  If you modify this library, you may extend this exception
+ * to your version of the library, but you are not obligated to do so.  If
+ * you do not wish to do so, delete this exception statement from your
+ * version.
+ */
+
+package org.icedrobot.daneel.loader;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+
+import org.icedrobot.daneel.DaneelException;
+import org.icedrobot.daneel.dex.DexFile;
+import org.icedrobot.daneel.rewriter.DexRewriter;
+
+/**
+ * A class loader capable of loading classes compiled for the Dalvik VM. The
+ * classes are loaded by transforming them into the Java VM class file format
+ * and then defining them in the host VM itself.
+ */
+public class DaneelClassLoader extends ClassLoader {
+
+    /** The list of all files containing classes. */
+    private final DexFile[] classFiles;
+
+    /** The list of all files containing resources. */
+    private final ApkFile[] resourceFiles;
+
+    /**
+     * Constructs a new class loader. Keep a constructor with such a signature
+     * around so that this class loader can be used as a system class loader.
+     * For details take a look at the {@link ClassLoader#getSystemClassLoader()}
+     * documentation.
+     * 
+     * @param parent The parent class loader for delegation.
+     */
+    public DaneelClassLoader(ClassLoader parent) {
+        this(parent, defaultFiles());
+    }
+
+    /**
+     * Constructs a new class loader for a given set of files from which to load
+     * classes and resources.
+     * 
+     * @param parent The parent class loader for delegation.
+     * @param files The set of files from which to load.
+     */
+    public DaneelClassLoader(ClassLoader parent, File[] files) {
+        super(parent);
+
+        // Split the given files into APK and DEX files.
+        List<DexFile> dexs = new LinkedList<DexFile>();
+        List<ApkFile> apks = new LinkedList<ApkFile>();
+        for (File file : files) {
+            try {
+                if (file.getName().endsWith(".apk")) {
+                    ApkFile apk = new ApkFile(file);
+                    dexs.add(apk.getClasses());
+                    apks.add(apk);
+                } else {
+                    DexFile dex = DexFile.parse(file);
+                    dexs.add(dex);
+                }
+            } catch (IOException e) {
+                throw new DaneelException("Unable to setup class loader.", e);
+            }
+        }
+
+        classFiles = dexs.toArray(new DexFile[dexs.size()]);
+        resourceFiles = apks.toArray(new ApkFile[apks.size()]);
+    }
+
+    /**
+     * Method which tries to find a class for the given name.
+     */
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        System.out.printf("Trying to find class '%s' ...\n", name);
+
+        // Iterate over all files containing classes.
+        for (DexFile dex : classFiles) {
+            try {
+                byte[] bytecode = DexRewriter.rewrite(name, dex);
+                return defineClass(name, bytecode, 0, bytecode.length);
+            } catch (ClassNotFoundException e) {
+                // Ignore and skip to the next file.
+            }
+        }
+
+        // Unable to find class definition for given class name.
+        throw new ClassNotFoundException();
+    }
+
+    /**
+     * Method which tries to find a resource for the given name.
+     */
+    @Override
+    protected URL findResource(String name) {
+        System.out.printf("Trying to find resource '%s' ...\n", name);
+
+        // Iterate over all files containing resources.
+        for (ApkFile apk : resourceFiles) {
+            ZipEntry entry = apk.getEntry(name);
+            if (entry != null)
+                return apk.getJarURL(entry);
+        }
+
+        // Unable to find resource for given resource name.
+        return null;
+    }
+
+    /**
+     * Helper method to get the default set of files to load from.
+     */
+    private static File[] defaultFiles() {
+        String path = System.getProperty("daneel.class.path");
+        if (path == null || path.isEmpty())
+            return new File[0];
+        String[] paths = path.split(File.pathSeparator);
+        File[] files = new File[paths.length];
+        for (int i = 0; i < paths.length; i++)
+            files[i] = new File(paths[i]);
+        return files;
+    }
+}
--- a/src/main/java/org/icedrobot/daneel/tools/Main.java	Thu Mar 17 00:56:23 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/tools/Main.java	Thu Mar 17 01:07:27 2011 +0100
@@ -1,9 +1,7 @@
 package org.icedrobot.daneel.tools;
 
 import java.io.File;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
+
 import org.icedrobot.daneel.dex.DexFile;
 
 public class Main {
@@ -11,11 +9,7 @@
     public static void main(String[] args) throws Exception {
         String dexFileName = args[0];
         File dexFile = new File(dexFileName);
-        RandomAccessFile dexFileAccess = new RandomAccessFile(dexFile, "r");
-        FileChannel dexFileChannel = dexFileAccess.getChannel();
-        ByteBuffer dexFileBuffer = dexFileChannel.map(
-                FileChannel.MapMode.READ_ONLY, 0, dexFile.length());
-        DexFile parsedDexFile = DexFile.parse(dexFileBuffer);
+        DexFile parsedDexFile = DexFile.parse(dexFile);
         System.out.println(parsedDexFile);
     }