changeset 109:cf71c6ab82fc

Add a flag daneel.verify to enable post verification of DEX to bytecode translation
author forax
date Fri, 01 Apr 2011 11:15:49 +0200
parents 19915795c08c
children b4b1ddad8f7b 43109f72e569
files src/main/java/org/icedrobot/daneel/loader/DaneelClassLoader.java src/main/java/org/icedrobot/daneel/loader/Verifier.java src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java src/main/java/org/icedrobot/daneel/rewriter/Interpreter.java src/main/java/org/icedrobot/daneel/rewriter/Main.java src/main/java/org/icedrobot/daneel/rewriter/PatchMethodVisitor.java src/main/java/org/icedrobot/daneel/rewriter/Register.java
diffstat 7 files changed, 516 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/loader/DaneelClassLoader.java	Thu Mar 31 00:54:29 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/loader/DaneelClassLoader.java	Fri Apr 01 11:15:49 2011 +0200
@@ -40,6 +40,7 @@
 import java.io.File;
 import java.io.IOError;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.zip.ZipEntry;
@@ -117,6 +118,9 @@
         for (DexFile dexFile : dexFiles) {
             try {
                 byte[] bytecode = DexRewriter.rewrite(name, dexFile);
+                if (VERIFY) {
+                    Verifier.verify(this, dexFile, name, bytecode, new PrintWriter(System.err));
+                }
                 return defineClass(name, bytecode, 0, bytecode.length);
             } catch (ClassNotFoundException e) {
                 // Ignore and skip to the next file.
@@ -208,4 +212,9 @@
         }
         return apks.toArray(new ApkFile[apks.size()]);
     }
+    
+    private static final boolean VERIFY;
+    static {
+        VERIFY = Boolean.getBoolean("daneel.verify");
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/org/icedrobot/daneel/loader/Verifier.java	Fri Apr 01 11:15:49 2011 +0200
@@ -0,0 +1,478 @@
+/*
+ * 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.
+ */
+
+/*
+ * A non negligible part of this code comes from a code
+ * that can be found in ASM's class file CheckClassAdapter.java.
+ * 
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2007 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.icedrobot.daneel.loader;
+
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+import org.icedrobot.daneel.dex.DexAnnotationVisitor;
+import org.icedrobot.daneel.dex.DexClassVisitor;
+import org.icedrobot.daneel.dex.DexFieldVisitor;
+import org.icedrobot.daneel.dex.DexFile;
+import org.icedrobot.daneel.dex.DexMethodVisitor;
+import org.icedrobot.daneel.dex.DexReader;
+import org.icedrobot.daneel.dex.Label;
+import org.icedrobot.daneel.dex.Opcode;
+import org.icedrobot.daneel.rewriter.DexRewriter.MethodRewriter;
+import org.icedrobot.daneel.rewriter.Interpreter;
+import org.icedrobot.daneel.util.TypeUtil;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.EmptyVisitor;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TryCatchBlockNode;
+import org.objectweb.asm.tree.analysis.Analyzer;
+import org.objectweb.asm.tree.analysis.Frame;
+import org.objectweb.asm.tree.analysis.SimpleVerifier;
+import org.objectweb.asm.util.CheckClassAdapter;
+import org.objectweb.asm.util.TraceMethodVisitor;
+
+class Verifier {
+    public static void verify(ClassLoader classloader, DexFile dexFile, String className, byte[] bytecode, PrintWriter writer) {
+        ClassReader reader = new ClassReader(bytecode);
+        ClassNode node = new ClassNode();
+        reader.accept(new CheckClassAdapter(node, false),
+                ClassReader.SKIP_DEBUG);
+
+        Type superType = (node.superName == null) ? null : Type
+                .getObjectType(node.superName);
+
+        @SuppressWarnings("unchecked")
+        List<String> interfaces = node.interfaces;
+        Type[] interfazes = new Type[interfaces.size()];
+        for (int i = 0; i < interfazes.length; i++) {
+            interfazes[i] = Type.getObjectType(interfaces.get(i));
+        }
+
+        @SuppressWarnings("unchecked")
+        List<MethodNode> methods = node.methods;
+        String internalName = node.name;
+        for (int i = 0; i < methods.size(); i++) {
+            MethodNode method = methods.get(i);
+            SimpleVerifier verifier = new SimpleVerifier(
+                    Type.getObjectType(internalName),
+                    superType,
+                    Arrays.asList(interfazes),
+                    (node.access & ~Opcodes.ACC_INTERFACE) != 0);
+            Analyzer analyzer = new Analyzer(verifier);
+            verifier.setClassLoader(classloader);
+
+            try {
+                analyzer.analyze(internalName, method);
+            } catch (Exception e) {
+                dump(dexFile, className, method, analyzer, writer);
+                e.printStackTrace(writer);
+            }
+        }
+        writer.flush();
+    }
+
+    private static void dump(DexFile dexFile, final String className, final MethodNode method,
+            Analyzer analyzer, final PrintWriter writer) {
+        writer.println("--- DEX bytecode");
+        try {
+            dexFile.accept("L" + className.replace('.', '/') + ";", new DexClassVisitor() {
+                @Override
+                public void visit(int access, String name, String supername,
+                        String[] interfaces) {
+                    // nothing
+                }
+                
+                @Override
+                public void visitSource(String source) {
+                    // nothing
+                }
+                
+                @Override
+                public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
+                    return null;
+                }
+                
+                @Override
+                public DexMethodVisitor visitMethod(int access, String name, String shorty,
+                        String returnType, String[] parameterTypes) {
+                    
+                    String desc = TypeUtil.convertProtoToDesc(returnType, parameterTypes);
+                    if (!name.equals(method.name) || !desc.equals(method.desc)) {
+                        return null;
+                    }
+                    
+                    final MethodRewriter mv = new MethodRewriter(new EmptyVisitor(),
+                            (access & Opcodes.ACC_STATIC) != 0,
+                            desc);
+                    
+                    writer.println(className+'.'+method.name + method.desc);
+                    return new DexMethodVisitor() {
+                        private final HashMap<Label, String> labelMap =
+                            new HashMap<Label, String>();
+                        private final StringBuilder builder = new StringBuilder();
+                        
+                        private String getLabel(Label label) {
+                            String text = labelMap.get(label);
+                            if (text != null) {
+                                return text;
+                            }
+                            text = "L" + labelMap.size();
+                            labelMap.put(label, text);
+                            return text;
+                        }
+                        
+                        private void dumpRegisters() {
+                            StringBuilder registerBuilder = new StringBuilder(); 
+                            Interpreter interpreter = mv.getInterpreter();
+                            for(int i=0; i<interpreter.getRegisterCount(); i++) {
+                                registerBuilder.append(interpreter.getRegister(i));
+                                if (i != interpreter.getRegisterCount() - 1)
+                                    registerBuilder.append(' ');
+                            }
+                            writer.println(String.format("%-50s %s", builder, registerBuilder));
+                            builder.setLength(0);
+                        }
+                        
+                        @Override
+                        public void visitCode(int registers, int ins, int outs) {
+                            builder.append("code registers:"+registers+" ins:"+ins+" outs:"+outs);
+                            mv.visitCode(registers, ins, outs);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
+                            return null;
+                        }
+                        
+                        @Override
+                        public DexAnnotationVisitor visitParameterAnnotation(int parameter,
+                                int visibility, String type) {
+                            return null;
+                        }
+                        
+                        @Override
+                        public void visitLocalVariable(String name, String desc, Label start,
+                                Label end, int reg) {
+                            mv.visitLocalVariable(name, desc, start, end, reg);
+                        }
+                        
+                        @Override
+                        public void visitLineNumber(String source, int line, Label start) {
+                            mv.visitLineNumber(source, line, start);
+                        }
+                        
+                        @Override
+                        public void visitTryCatch(Label start, Label end, Label handler, String type) {
+                            builder.append("try-catch "+type+" "+getLabel(start)+" "+getLabel(end)+" "+getLabel(handler));
+                            mv.visitTryCatch(start, end, handler, type);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitLabel(Label label) {
+                            builder.append(getLabel(label)+":");
+                            mv.visitLabel(label);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstr(Opcode opcode) {
+                            builder.append(opcode.name());
+                            mv.visitInstr(opcode);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrOp(Opcode opcode, int srcOrDst) {
+                            builder.append(opcode.name()+" "+srcOrDst);
+                            mv.visitInstrOp(opcode, srcOrDst);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrUnaryOp(Opcode opcode, int vdest, int vsrc) {
+                            builder.append(opcode.name()+" "+vdest+", "+vsrc);
+                            mv.visitInstrUnaryOp(opcode, vdest, vsrc);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrBinOpAndLiteral(Opcode opcode, int vdest, int vsrc,
+                                int value) {
+                            builder.append(opcode.name()+" "+vdest+", "+vsrc+" #"+value);
+                            mv.visitInstrBinOpAndLiteral(opcode, vdest, vsrc, value);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrBinOp(Opcode opcode, int vdest, int vsrc1, int vsrc2) {
+                            builder.append(opcode.name()+" "+vdest+", "+vsrc1+", "+vsrc2);
+                            mv.visitInstrBinOp(opcode, vdest, vsrc1, vsrc2);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrMethod(Opcode opcode, int num, int va, int vpacked,
+                                String owner, String name, String desc) {
+                            builder.append(opcode.name()+" "+owner+"."+name+desc+" "+num+" "+va+" "+vpacked);
+                            mv.visitInstrMethod(opcode, num, va, vpacked, owner, name, desc);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrField(Opcode opcode, int vsrcOrDest, int vref,
+                                String owner, String name, String desc) {
+                            builder.append(opcode.name()+" "+owner+"."+name+desc+" "+vsrcOrDest+" "+vref);
+                            mv.visitInstrField(opcode, vsrcOrDest, vref, owner, name, desc);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrConstU32(Opcode opcode, int vdest, int value) {
+                            builder.append(opcode.name()+" "+vdest+" #"+value);
+                            mv.visitInstrConstU32(opcode, vdest, value);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrConstU64(Opcode opcode, int vdest, long value) {
+                            builder.append(opcode.name()+" "+vdest+" #"+value);
+                            mv.visitInstrConstU64(opcode, vdest, value);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrConstString(Opcode opcode, int vdest, String value) {
+                            builder.append(opcode.name()+" "+vdest+" \""+value+'"');
+                            mv.visitInstrConstString(opcode, vdest, value);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrInstanceof(Opcode opcode, int vdest, int vsrc,
+                                String type) {
+                            builder.append(opcode.name()+" "+type+" "+vdest+", "+vsrc);
+                            mv.visitInstrInstanceof(opcode, vdest, vsrc, type);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrClass(Opcode opcode, int vsrcOrDest, String type) {
+                            builder.append(opcode.name()+" "+type+" "+vsrcOrDest);
+                            mv.visitInstrClass(opcode, vsrcOrDest, type);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrGoto(Opcode opcode, Label label) {
+                            builder.append(opcode.name()+" "+getLabel(label));
+                            mv.visitInstrGoto(opcode, label);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrIfTestZ(Opcode opcode, int vsrc, Label label) {
+                            builder.append(opcode.name()+" "+vsrc+" "+getLabel(label));
+                            mv.visitInstrIfTestZ(opcode, vsrc, label);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrIfTest(Opcode opcode, int vsrc1, int vsrc2,
+                                Label label) {
+                            builder.append(opcode.name()+" "+vsrc1+", "+vsrc2+" "+getLabel(label));
+                            mv.visitInstrIfTest(opcode, vsrc1, vsrc2, label);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrNewArray(Opcode opcode, int vdest, int vsize,
+                                String type) {
+                            builder.append(opcode.name()+" "+type+" "+vdest+"  "+vsize);
+                            mv.visitInstrNewArray(opcode, vdest, vsize, type);
+                            dumpRegisters();
+                        }
+                        
+                        @Override
+                        public void visitInstrFilledNewArray(Opcode opcode, int num, int va,
+                                int vpacked, String type) {
+                            builder.append(opcode.name()+" "+type+" "+num+" "+va+",  "+vpacked);
+                            mv.visitInstrFilledNewArray(opcode, num, va, vpacked, type);
+                        }
+                        
+                        @Override
+                        public void visitInstrFillArrayData(Opcode opcode, int vsrc,
+                                int elementWidth, int elementNumber, ByteBuffer data) {
+                            builder.append(opcode.name()+" "+vsrc+" "+elementWidth+" "+elementNumber);
+                            mv.visitInstrFillArrayData(opcode, vsrc, elementWidth, elementNumber, data);
+                            dumpRegisters();
+                            while (data.hasRemaining()) {
+                                writer.print((int)data.get());
+                                if (data.hasRemaining()) {
+                                    writer.print(", ");
+                                }
+                            }
+                            writer.println();
+                        }
+                        
+                        @Override
+                        public void visitInstrArray(Opcode opcode, int vsrcOrDest, int varray,
+                                int vindex) {
+                            builder.append(opcode.name()+" "+vsrcOrDest+", "+varray+"  "+vindex);
+                            mv.visitInstrArray(opcode, vsrcOrDest, varray, vindex);
+                            dumpRegisters();
+                        }
+                        
+                        
+                        @Override
+                        public void visitInstrSparseSwitch(Opcode opcode, int vsrc, int[] keys,
+                                Label[] targets) {
+                            builder.append(opcode.name()+" "+vsrc);
+                            mv.visitInstrSparseSwitch(opcode, vsrc, keys, targets);
+                            dumpRegisters();
+                            for(int i=0; i<keys.length; i++) {
+                                writer.println("  "+keys[i]+" "+targets[i]);
+                            }
+                            writer.println();
+                        }
+                        
+                        @Override
+                        public void visitInstrPackedSwitch(Opcode opcode, int vsrc, int firstKey,
+                                Label[] targets) {
+                            builder.append(opcode.name()+" "+vsrc+" "+firstKey);
+                            mv.visitInstrPackedSwitch(opcode, vsrc, firstKey, targets);
+                            dumpRegisters();
+                            for(int i=0; i<targets.length; i++) {
+                                writer.println("  "+targets[i]);
+                            }
+                            writer.println();
+                        }
+                        
+                        @Override
+                        public void visitEnd() {
+                            mv.visitEnd();
+                        }
+                    };
+                }
+                
+                @Override
+                public DexFieldVisitor visitField(int access, String name, String type,
+                        Object value) {
+                    return null;
+                }
+                
+                @Override
+                public void visitEnd() {
+                   // do nothing
+                } 
+            }, DexReader.SKIP_NONE);
+        } catch (ClassNotFoundException e) {
+            throw new AssertionError(e);
+        }
+        
+        writer.println();
+        writer.println("--- JVM bytecode");
+        Frame[] frames = analyzer.getFrames();
+        TraceMethodVisitor mv = new TraceMethodVisitor();
+        
+        for (int j = 0; j < method.instructions.size(); ++j) {
+            method.instructions.get(j).accept(mv);
+
+            StringBuilder builder = new StringBuilder();
+            Frame f = frames[j];
+            if (f == null) {
+                builder.append('?');
+            } else {
+                for (int k = 0; k < f.getLocals(); ++k) {
+                    builder.append(f.getLocal(k).toString())
+                            .append(' ');
+                }
+                builder.append(" : ");
+                for (int k = 0; k < f.getStackSize(); ++k) {
+                    builder.append(f.getStack(k).toString())
+                            .append(' ');
+                }
+            }
+            while (builder.length() < method.maxStack + method.maxLocals + 1) {
+                builder.append(' ');
+            }
+            writer.print(String.format("%05d", j));
+            writer.print(" " + builder + " : " + mv.text.get(j));
+        }
+        for (int j = 0; j < method.tryCatchBlocks.size(); ++j) {
+            ((TryCatchBlockNode)method.tryCatchBlocks.get(j)).accept(mv);
+            writer.print(" " + mv.text.get(j));
+        }
+        writer.println();
+    }
+}
--- a/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Thu Mar 31 00:54:29 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Fri Apr 01 11:15:49 2011 +0200
@@ -157,7 +157,7 @@
         cv.visitEnd();
     }
 
-    static class MethodRewriter implements DexMethodVisitor {
+    public static class MethodRewriter implements DexMethodVisitor {
         final PatchMethodVisitor mv;
         private final boolean isStatic;
         private final String desc;
@@ -183,6 +183,10 @@
             this.returnRegisterType = VOID_TYPE;
         }
 
+        public Interpreter getInterpreter() {
+            return interpreter;
+        }
+        
         /**
          * Translate a register with Dalvik calling convention to a slot with
          * Java calling convention
--- a/src/main/java/org/icedrobot/daneel/rewriter/Interpreter.java	Thu Mar 31 00:54:29 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/rewriter/Interpreter.java	Fri Apr 01 11:15:49 2011 +0200
@@ -40,13 +40,11 @@
 import java.util.Arrays;
 import java.util.HashMap;
 
-import org.objectweb.asm.Type;
-
 /**
  * An abstract interpreter for virtual registers. It is used to infer the
  * register type information by tracking all load and store operations on them.
  */
-class Interpreter {
+public class Interpreter {
     private final Register[] registers;
     private final HashMap<Object, Register[]> joinPointMap = new HashMap<Object, Register[]>();
     private boolean isDead;
@@ -57,7 +55,7 @@
      * 
      * @param registers The array of registers to handle.
      */
-    Interpreter(Register[] registers) {
+    public Interpreter(Register[] registers) {
        this.registers = registers;
     }
 
@@ -70,6 +68,15 @@
     public Register getRegister(int vregister) {
         return registers[vregister];
     }
+    
+    /**
+     * Returns the number of virtual registers.
+     * 
+     * @return the number of virtual registers managed by the current interpreter.
+     */
+    public int getRegisterCount() {
+        return registers.length;
+    }
 
     /**
      * Tracks a load operation on the given register. The expected type has to
--- a/src/main/java/org/icedrobot/daneel/rewriter/Main.java	Thu Mar 31 00:54:29 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/rewriter/Main.java	Fri Apr 01 11:15:49 2011 +0200
@@ -39,16 +39,11 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.io.PrintWriter;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Arrays;
 
-import org.icedrobot.daneel.dex.DexFile;
-import org.icedrobot.daneel.dex.DexReader;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.util.CheckClassAdapter;
+import org.icedrobot.daneel.loader.DaneelClassLoader;
 
 /**
  * Run: java -cp classes:lib/smali-1.2.6.jar:lib/asm-debug-all-4.0.jar
@@ -58,23 +53,11 @@
     public static void main(String[] args) throws IOException,
             ClassNotFoundException, NoSuchMethodException,
             IllegalAccessException {
-        final DexReader dexReader = DexFile.parse(new File(args[0]));
         
-        ClassLoader classLoader = new ClassLoader() {
-            @Override
-            protected Class<?> findClass(String name)
-                    throws ClassNotFoundException {
-                ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
-                dexReader.accept('L'+name.replace('.', '/')+';', new DexRewriter(writer), 0);
-                byte[] array = writer.toByteArray();
-
-                CheckClassAdapter.verify(new ClassReader(array), true,
-                        new PrintWriter(System.err));
-
-                return defineClass(name, array, 0, array.length);
-            }
-        };
-
+        // enable verification by default
+        System.setProperty("daneel.verify", "true");
+        
+        ClassLoader classLoader = new DaneelClassLoader(Main.class.getClassLoader(), new File(args[0]));
         Class<?> clazz = classLoader.loadClass(args[1]);
         Method method = clazz.getMethod("main", String[].class);
         try {
--- a/src/main/java/org/icedrobot/daneel/rewriter/PatchMethodVisitor.java	Thu Mar 31 00:54:29 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/rewriter/PatchMethodVisitor.java	Fri Apr 01 11:15:49 2011 +0200
@@ -118,6 +118,13 @@
         }
         return methodNode.instructions.getLast();
     }
+    
+    public void addNode(AbstractInsnNode node) {
+        if (methodNode == null) {
+            throw new IllegalStateException("patchMode is not activated");
+        }
+        methodNode.instructions.add(node);
+    }
 
     public void patch(AbstractInsnNode node, AbstractInsnNode newNode) {
         if (methodNode == null) {
--- a/src/main/java/org/icedrobot/daneel/rewriter/Register.java	Thu Mar 31 00:54:29 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/rewriter/Register.java	Fri Apr 01 11:15:49 2011 +0200
@@ -326,7 +326,7 @@
         case U64_TYPE:
             return "u64";
         case NO_TYPE:
-            return "uninitialized";
+            return "uninit";
         }
         if (!isArray(type)) {
             throw new AssertionError();