Mercurial > hg > icedrobot > daneel
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); }