changeset 58:8cc04242b09e

Implemented rewriting of MOVE, MOVE_WIDE and MOVE_OBJECT. Contributed-by: Remi Forax * rewriter/DexRewriter.java (visitInstrUnaryOp): Implemented. * rewriter/Interpreter.java: Switched to immutable registers. * rewriter/Register.java: Made registers immutable.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Mon, 21 Mar 2011 18:24:18 +0100
parents 8c9542073cf4
children 09769b7503f1
files 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/Patchable.java src/main/java/org/icedrobot/daneel/rewriter/Register.java
diffstat 4 files changed, 144 insertions(+), 76 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Sun Mar 20 21:01:31 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Mon Mar 21 18:24:18 2011 +0100
@@ -166,6 +166,7 @@
         public MethodRewriter(MethodVisitor mv, String desc) {
             this.mv = new PatchMethodVisitor(mv);
             this.desc = desc;
+            this.returnRegisterType = NO_TYPE;
         }
 
         /**
@@ -234,6 +235,19 @@
         }
 
         @Override
+        public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
+            // XXX Ignore annotations for now.
+            return null;
+        }
+
+        @Override
+        public DexAnnotationVisitor visitParameterAnnotation(int parameter,
+                int visibility, String type) {
+            // XXX Ignore annotations for now.
+            return null;
+        }
+
+        @Override
         public void visitCode(int registerSize, int insSize, int outSize) {
             mv.visitCode();
             this.locals = registerSize - insSize;
@@ -460,18 +474,60 @@
                 throw new UnsupportedOperationException("NYI " + opcode);
             }
         }
-        
-        @Override
-        public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
-            // XXX Ignore annotations for now.
-            return null;
-        }
 
         @Override
-        public DexAnnotationVisitor visitParameterAnnotation(int parameter,
-                int visibility, String type) {
-            // XXX Ignore annotations for now.
-            return null;
+        public void visitInstrUnaryOp(Opcode opcode, int dest, int src) {
+            final int vdest = registerToSlot(dest);
+            final int vsrc = registerToSlot(src);
+            
+            switch(opcode) {
+            case MOVE:
+            case MOVE_FROM16:
+            case MOVE_16:
+            case MOVE_WIDE:
+            case MOVE_WIDE_FROM16:
+            case MOVE_WIDE_16: 
+            case MOVE_OBJECT:
+            case MOVE_OBJECT_FROM16:
+            case MOVE_OBJECT_16: {
+                final Register register  = interpreter.getRegister(vsrc);
+                int type = register.getType();
+                if (Register.isArbitrary(type)) {
+                    int runtimeType = Register.asRuntimeType(type);
+                    mv.setPatchMode();
+                    mv.visitVarInsn(Register.getJavaOpcode(runtimeType, ILOAD), vsrc);
+                    final AbstractInsnNode load = mv.getLastInsnNode();
+                    mv.visitVarInsn(Register.getJavaOpcode(runtimeType, ISTORE), vdest);
+                    final AbstractInsnNode store = mv.getLastInsnNode();
+                    final Patchable patchable = register.getPatchable();
+                    interpreter.storeArbitraryType(vdest, type, new Patchable() {
+                        @Override
+                        protected void patch(int registerType) {
+                            patchable.doPatch(registerType);
+                            if (registerType == FLOAT_TYPE) {
+                                mv.patch(load, new VarInsnNode(FLOAD, vsrc));
+                                mv.patch(store, new VarInsnNode(FSTORE, vdest));
+                                return;
+                            }
+                            if (registerType == DOUBLE_TYPE) {
+                                mv.patch(load, new VarInsnNode(DLOAD, vsrc));
+                                mv.patch(store, new VarInsnNode(DSTORE, vdest));
+                                return;
+                            }
+                        }
+                    });
+                } else {
+                    mv.visitVarInsn(Register.getJavaOpcode(type, ILOAD), vsrc);
+                    interpreter.load(vsrc, type);
+                    mv.visitVarInsn(Register.getJavaOpcode(type, ISTORE), vdest);
+                    interpreter.store(vdest, type);
+                }
+                return;
+            }
+            default:
+            }
+            
+            throw new UnsupportedOperationException("NYI " + opcode);
         }
 
         @Override
@@ -588,13 +644,6 @@
         }
 
         @Override
-        public void visitInstrUnaryOp(Opcode opcode, int vdest, int vsrc) {
-            vdest = registerToSlot(vdest);
-            vsrc = registerToSlot(vsrc);
-            throw new UnsupportedOperationException("NYI " + opcode);
-        }
-
-        @Override
         public void visitTryCatch(Label start, Label end, Label handler, String type) {
             throw new UnsupportedOperationException("NYI");
         }
--- a/src/main/java/org/icedrobot/daneel/rewriter/Interpreter.java	Sun Mar 20 21:01:31 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/Interpreter.java	Mon Mar 21 18:24:18 2011 +0100
@@ -48,32 +48,38 @@
     public Interpreter(int maxRegisters) {
         Register[] registers = new Register[maxRegisters];
         for (int i = 0; i < maxRegisters; i++) {
-            registers[i] = new Register();
+            registers[i] = Register.UNINITIALIZED;
         }
         this.registers = registers;
     }
 
+    public Register getRegister(int vregister) {
+        return registers[vregister];
+    }
+
     public void load(int vregister, int expectedType) {
-        registers[vregister].load(expectedType);
+        registers[vregister] = registers[vregister].load(expectedType);
     }
 
     public void store(int vregister, int type) {
-        registers[vregister].store(type);
+        if (Register.isArbitrary(type)) {
+            throw new IllegalArgumentException("invalid type");
+        }
+
+        registers[vregister] = new Register(type, null);
     }
 
     public void storeArbitraryType(int vregister, int arbitraryType,
             Patchable patchable) {
-        registers[vregister].storeArbitraryType(arbitraryType, patchable);
+        if (!Register.isArbitrary(arbitraryType)) {
+            throw new IllegalArgumentException("invalid type");
+        }
+
+        registers[vregister] = new Register(arbitraryType, patchable);
     }
 
     public void cloneJoinPoint(Object label) {
-        Register[] registers = this.registers;
-        int length = registers.length;
-        Register[] clone = new Register[length];
-        for (int i = 0; i < length; i++) {
-            clone[i] = registers[i].duplicate();
-        }
-        joinPointMap.put(label, clone);
+        joinPointMap.put(label, registers.clone());
     }
 
     public Register[] getJoinPoint(Object label) {
@@ -99,7 +105,7 @@
         }
 
         for (int i = 0; i < registers.length; i++) {
-            this.registers[i].merge(registers[i]);
+            this.registers[i] = this.registers[i].merge(registers[i]);
         }
     }
 
--- a/src/main/java/org/icedrobot/daneel/rewriter/Patchable.java	Sun Mar 20 21:01:31 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/Patchable.java	Mon Mar 21 18:24:18 2011 +0100
@@ -38,12 +38,16 @@
 package org.icedrobot.daneel.rewriter;
 
 abstract class Patchable {
-    private boolean patchCalled;
+    private int type = Register.NO_TYPE;
 
+    public int getType() {
+        return type;
+    }
+    
     void doPatch(int registerType) {
-        if (patchCalled)
+        if (type != Register.NO_TYPE)
             return;
-        patchCalled = true;
+        type = registerType;
         patch(registerType);
     }
 
--- a/src/main/java/org/icedrobot/daneel/rewriter/Register.java	Sun Mar 20 21:01:31 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/Register.java	Mon Mar 21 18:24:18 2011 +0100
@@ -60,83 +60,92 @@
 
     private static final int ARBITRARY_MASK = Integer.MIN_VALUE;
 
-    private int type;
-
-    private Patchable patchable;
-
-    Register() {
-        type = NO_TYPE;
-        // and patchable = null
-    }
-
-    public void load(int expectedType) {
-        assert (expectedType & ARBITRARY_MASK) == 0;
+    static final Register UNINITIALIZED = new Register(NO_TYPE, null);
+    
+    private final int type;
 
-        if ((this.type & ARBITRARY_MASK) != 0 && patchable != null) {
-            patchable.doPatch(expectedType);
-            patchable = null;
-        }
-        this.type = expectedType;
-    }
+    private final Patchable patchable;
 
-    public void store(int type) {
+    Register(int type, Patchable patchable) {
         this.type = type;
-        this.patchable = null;
-    }
-
-    public void storeArbitraryType(int arbitraryType, Patchable patchable) {
-        this.type = arbitraryType;
         this.patchable = patchable;
     }
 
-    public Register duplicate() {
-        Register register = new Register();
-        register.type = type;
-        register.patchable = patchable;
-        return register;
+    public int getType() {
+        if (patchable != null) {
+            int patchableType = patchable.getType();
+            return (patchableType != NO_TYPE)? patchableType: type;
+        }
+        return type;
+    }
+
+    public Patchable getPatchable() {
+        return patchable;
     }
 
-    public void merge(Register register) {
-        int thisType = this.type;
-        int registerType = register.type;
-        if (thisType == registerType) { // short cut
-            return;
+    public Register load(int expectedType) {
+        if (isArbitrary(expectedType)) {
+            throw new IllegalArgumentException("invalid type");
         }
 
-        boolean thisIsArbitrary = (thisType & ARBITRARY_MASK) != 0;
-        boolean registerIsArbitrary = (registerType & ARBITRARY_MASK) != 0;
+        if (patchable != null) {
+            patchable.doPatch(expectedType);
+        }
+        return new Register(expectedType, null);
+    }
+
+    public Register merge(Register register) {
+        int thisType = getType();
+        int registerType = register.getType();
+
+        if (thisType == NO_TYPE) {
+            return register;
+        }
+        if (registerType == NO_TYPE) {
+            return this;
+        }
+
+        boolean thisIsArbitrary = isArbitrary(thisType);
+        boolean registerIsArbitrary = isArbitrary(registerType);
 
         if (thisIsArbitrary) {
             if (registerIsArbitrary) {
                 // maybe there are not compatible, but this means that
                 // the code is malformed
-                patchable = Patchable.union(patchable, register.patchable);
-                return;
+                return new Register(thisType, Patchable.union(patchable, register.patchable));
             }
             if (patchable != null) { // otherwise, it's an uninitialized value
                 patchable.doPatch(registerType);
-                patchable = null;
             }
-            this.type = registerType;
-            return;
+            return new Register(registerType, null);
         }
 
         // the two register aren't arbitrary, we don't check is they are compatible
         // because they can be incompatible if the register is no more used
         // This should not be the common case because dx remove unused registers
         if (!registerIsArbitrary) {
-            return;
+            return this;
         }
 
         if (register.patchable != null) {
             register.patchable.doPatch(thisType);
-            register.patchable = null;
         }
-        register.type = thisType;
+        return new Register(thisType, null);
+    }
+
+    public static boolean isArbitrary(int type) {
+        return (type & ARBITRARY_MASK) != 0;
+    }
+
+    public static int asRuntimeType(int type) {
+        return (type == LONG_TYPE || type == DOUBLE_TYPE) ? LONG_TYPE
+                : INT_TYPE;
     }
 
     public static int getJavaOpcode(int type, int opcode) {
-        assert (type & ARBITRARY_MASK) == 0;
+        if (isArbitrary(type)) {
+            throw new IllegalArgumentException("invalid type");
+        }
 
         if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
             return opcode + ((type != BOOLEAN_TYPE) ? type : BYTE_TYPE);