changeset 73:15cfcc7a8756

This patch implements the opcode corresponding to: - instanceof - test condition ==, !=, <=, >= for ints - conversions (int -> double etc.) And fix 2 bugs: - null as a constant (bug found by Volker). - Register.load must not change its type if not untyped Reviewed by: Michael Starzinger src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- src/main/java/org/icedrobot/daneel/rewriter/Register.java | 13 ++++-- 2 files changed, 132 insertions(+), 32 deletions(-)
author forax
date Thu, 24 Mar 2011 18:15:14 +0100
parents 57fe21b3757e
children 4ccee27bea05
files src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java src/main/java/org/icedrobot/daneel/rewriter/Register.java
diffstat 2 files changed, 132 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Wed Mar 23 23:18:49 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Thu Mar 24 18:15:14 2011 +0100
@@ -59,6 +59,7 @@
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Type;
 import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.InsnNode;
 import org.objectweb.asm.tree.LdcInsnNode;
 import org.objectweb.asm.tree.VarInsnNode;
 
@@ -270,6 +271,17 @@
             }
         }
         
+        private void visitNotInstr(int type) {
+            org.objectweb.asm.Label falsePart = new org.objectweb.asm.Label();
+            org.objectweb.asm.Label endPart = new org.objectweb.asm.Label();
+            mv.visitJumpInsn(IFNE, falsePart);
+            mv.visitInsn((type == INT_TYPE)? ICONST_1: LCONST_1);
+            mv.visitJumpInsn(GOTO, endPart);
+            mv.visitLabel(falsePart);
+            mv.visitInsn((type == INT_TYPE)? ICONST_0: LCONST_0);
+            mv.visitLabel(endPart);
+        }
+        
         private static AssertionError newAssertionError(Opcode opcode) {
             return new AssertionError("Invalid opcode: " + opcode);
         }
@@ -487,6 +499,12 @@
                         mv.patch(ldc, new LdcInsnNode(Float
                                 .intBitsToFloat(value)));
                         mv.patch(istore, new VarInsnNode(FSTORE, sdest));
+                        return;
+                    }
+                    if (registerType == OBJECT_TYPE || Register.isArray(registerType)) {
+                        mv.patch(ldc, new InsnNode(ACONST_NULL));
+                        mv.patch(istore, new VarInsnNode(ASTORE, sdest));
+                        return;
                     }
                 }
             });
@@ -509,6 +527,7 @@
                         mv.patch(ldc,
                                 new LdcInsnNode(Double.longBitsToDouble(value)));
                         mv.patch(lstore, new VarInsnNode(DSTORE, sdest));
+                        return;
                     }
                 }
             });
@@ -540,8 +559,12 @@
                 }
                 break;
             }
-            default:
+            case MOVE_EXCEPTION:
                 throw new UnsupportedOperationException("NYI " + opcode);
+            default: //MONITOR_ENTER, MONITOR_EXIT, THROW.
+                mv.visitVarInsn(ALOAD, srcOrDst);
+                interpreter.load(srcOrDst, OBJECT_TYPE);
+                mv.visitInsn(toJavaOpcode[opcode.ordinal()]);
             }
         }
         
@@ -550,6 +573,7 @@
             fixStackAfterAMethodCall();
             final int vdest = registerToSlot(dest);
             final int vsrc = registerToSlot(src);
+            int srcType = INT_TYPE, dstType;
             switch(opcode) {
             case MOVE:
             case MOVE_FROM16:
@@ -584,6 +608,11 @@
                                 mv.patch(store, new VarInsnNode(DSTORE, vdest));
                                 return;
                             }
+                            if (registerType == OBJECT_TYPE || Register.isArray(registerType)) {
+                                mv.patch(load, new VarInsnNode(ALOAD, vsrc));
+                                mv.patch(store, new VarInsnNode(ASTORE, vdest));
+                                return;
+                            }
                         }
                     });
                 } else {
@@ -594,10 +623,44 @@
                 }
                 return;
             }
-            default:
+            
+            case NEG_INT: case NOT_INT:
+                dstType = INT_TYPE;
+                break;
+            case NEG_LONG: case NOT_LONG:
+                srcType = dstType = LONG_TYPE;
+                break;
+            case NEG_FLOAT:
+                srcType = dstType = FLOAT_TYPE;
+                break;
+            case NEG_DOUBLE:
+                srcType = dstType = DOUBLE_TYPE;
+                break;
+            
+            case INT_TO_BYTE:
+            case INT_TO_CHAR:
+            case INT_TO_SHORT:
+                dstType = BYTE_TYPE + opcode.ordinal() - Opcode.INT_TO_BYTE.ordinal();
+                break;
+                
+            default: {
+                int conversion = opcode.ordinal() - Opcode.INT_TO_LONG.ordinal();
+                int div = conversion / 3;
+                int modulo = conversion % 3;
+                srcType = INT_TYPE + div;
+                dstType = INT_TYPE + ((modulo < div)? modulo: modulo + 1);
+            }
             }
             
-            throw new UnsupportedOperationException("NYI " + opcode);
+            mv.visitVarInsn(Register.getJavaOpcode(srcType, ILOAD), vsrc);
+            interpreter.load(vsrc, srcType);
+            if (opcode == Opcode.NOT_INT || opcode == Opcode.NOT_LONG) {
+                visitNotInstr(srcType);
+            } else {
+                mv.visitInsn(toJavaOpcode[opcode.ordinal()]);   
+            }
+            mv.visitVarInsn(Register.getJavaOpcode(dstType, ISTORE), vdest);
+            interpreter.store(vdest, dstType);
         }
 
         @Override
@@ -614,37 +677,42 @@
                 opcode = Opcode.getOpcode(opcode.ordinal() + OP_2ADDR_SHIFT);
             }
             
-            int type;
+            int srcType, dstType;
             switch(opcode) {
             case CMPL_FLOAT: case CMPG_FLOAT: case ADD_FLOAT: case SUB_FLOAT:
             case MUL_FLOAT: case DIV_FLOAT: case REM_FLOAT:
-                type = FLOAT_TYPE;
+                srcType = FLOAT_TYPE;
+                dstType = (opcode == Opcode.CMPL_FLOAT ||
+                           opcode == Opcode.CMPG_FLOAT) ? INT_TYPE : FLOAT_TYPE;
                 break;
             
             case CMPL_DOUBLE: case CMPG_DOUBLE: case ADD_DOUBLE: case SUB_DOUBLE:
             case MUL_DOUBLE:  case DIV_DOUBLE: case REM_DOUBLE:
-                type = DOUBLE_TYPE;
+                srcType = DOUBLE_TYPE;
+                dstType = (opcode == Opcode.CMPL_DOUBLE ||
+                           opcode == Opcode.CMPG_DOUBLE) ? INT_TYPE : DOUBLE_TYPE;
                 break;
                 
             case CMP_LONG: case ADD_LONG: case SUB_LONG: case MUL_LONG:
             case DIV_LONG: case REM_LONG: case AND_LONG: case OR_LONG:
             case XOR_LONG: case SHL_LONG: case SHR_LONG: case USHR_LONG:
-                type = LONG_TYPE;
+                srcType = LONG_TYPE;
+                dstType = (opcode == Opcode.CMP_LONG) ? INT_TYPE : LONG_TYPE;
                 break;
               
             // ADD_INT, SUB_INT, MUL_INT, DIV_INT, REM_INT,
             // AND_INT, OR_INT, XOR_INT, SHL_INT, SHR_INT, USHR_INT,
             default: 
-                type = INT_TYPE;
+                srcType = dstType = INT_TYPE;
             }
             
-            mv.visitVarInsn(Register.getJavaOpcode(type, ILOAD), vsrc1);
-            interpreter.load(vsrc1, type);
-            mv.visitVarInsn(Register.getJavaOpcode(type, ILOAD), vsrc2);
-            interpreter.load(vsrc2, type);
+            mv.visitVarInsn(Register.getJavaOpcode(srcType, ILOAD), vsrc1);
+            interpreter.load(vsrc1, srcType);
+            mv.visitVarInsn(Register.getJavaOpcode(srcType, ILOAD), vsrc2);
+            interpreter.load(vsrc2, srcType);
             mv.visitInsn(toJavaOpcode[opcode.ordinal()]);
-            mv.visitVarInsn(Register.getJavaOpcode(type, ISTORE), vdest);
-            interpreter.store(vdest, type);
+            mv.visitVarInsn(Register.getJavaOpcode(dstType, ISTORE), vdest);
+            interpreter.store(vdest, dstType);
         }
         
         private static final int OP_2ADDR_SHIFT =
@@ -758,8 +826,19 @@
             }
         }
         
-        
-        // unimplemented
+        @Override
+        public void visitInstrInstanceof(Opcode opcode, int vdest, int vsrc,
+                String type) {
+            fixStackAfterAMethodCall();
+            vdest = registerToSlot(vdest);
+            vsrc = registerToSlot(vsrc);
+            mv.visitVarInsn(ALOAD, vsrc);
+            Type asmType = Type.getType(type);
+            interpreter.load(vsrc, getTypeFromASMType(asmType));
+            mv.visitTypeInsn(INSTANCEOF, asmType.getInternalName());
+            mv.visitVarInsn(ISTORE, vdest);
+            interpreter.store(vdest, BOOLEAN_TYPE);
+        }
         
         @Override
         public void visitInstrIfTest(Opcode opcode, int vsrc1, int vsrc2,
@@ -767,9 +846,36 @@
             fixStackAfterAMethodCall();
             vsrc1 = registerToSlot(vsrc1);
             vsrc2 = registerToSlot(vsrc2);
-            throw new UnsupportedOperationException("NYI " + opcode);
+            org.objectweb.asm.Label asmLabel = getASMLabel(label);
+            if (opcode == Opcode.IF_EQ || opcode == Opcode.IF_NE) {
+                int src1Type = interpreter.getRegister(vsrc1).getType();
+                int src2Type = interpreter.getRegister(vsrc2).getType();
+                int javaOpcode;
+                int type;
+                if (src1Type == INT_TYPE || src2Type == INT_TYPE) {
+                    type = INT_TYPE;
+                    javaOpcode = (opcode == Opcode.IF_EQ)? IF_ICMPEQ: IF_ICMPNE;
+                } else { // OBJECT_TYPE or array
+                    type = OBJECT_TYPE;
+                    javaOpcode = (opcode == Opcode.IF_EQ)? IF_ACMPEQ: IF_ACMPNE;
+                }
+                mv.visitVarInsn(Register.getJavaOpcode(type, ILOAD), vsrc1);
+                interpreter.load(vsrc1, OBJECT_TYPE);
+                mv.visitVarInsn(Register.getJavaOpcode(type, ILOAD), vsrc2);
+                interpreter.load(vsrc2, OBJECT_TYPE);
+                mv.visitJumpInsn(javaOpcode, asmLabel);
+            } else {   // IF_LT, IF_GE, IF_GT, IF_LE
+                mv.visitVarInsn(ILOAD, vsrc1);
+                interpreter.load(vsrc1, INT_TYPE);
+                mv.visitVarInsn(ILOAD, vsrc2);
+                interpreter.load(vsrc2, INT_TYPE);
+                mv.visitJumpInsn(toJavaOpcode[opcode.ordinal()], asmLabel);
+            }
         }
-
+        
+        
+        // unimplemented
+        
         @Override
         public void visitInstrFillArrayData(Opcode opcode, int vsrc,
                 Object arrayData) {
@@ -787,15 +893,6 @@
         }
 
         @Override
-        public void visitInstrInstanceof(Opcode opcode, int vdest, int vsrc,
-                String type) {
-            fixStackAfterAMethodCall();
-            vdest = registerToSlot(vdest);
-            vsrc = registerToSlot(vsrc);
-            throw new UnsupportedOperationException("NYI " + opcode);
-        }
-
-        @Override
         public void visitInstrSwitch(Opcode opcode, int vsrc,
                 Map<Integer, Label> labels) {
             fixStackAfterAMethodCall();
--- a/src/main/java/org/icedrobot/daneel/rewriter/Register.java	Wed Mar 23 23:18:49 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/Register.java	Thu Mar 24 18:15:14 2011 +0100
@@ -124,12 +124,14 @@
         return patchable;
     }
     
-    /** Simulate a load from the current register.
-     * If the type of the current register is {@link #isUntyped(int) untyped}
-     * the corresponding {@link Patchable patchable} will be called.
+    /**
+     * Simulate a load from the current register. If the type of the current
+     * register is {@link #isUntyped(int) untyped} the corresponding
+     * {@link Patchable patchable} will be called.
      * 
      * @param expectedType the expected type of the register
-     * @return a new register with the expected type
+     * @return if the current register is untyped, a new register with the
+     *         expected type otherwise the current register.
      */
     public Register load(int expectedType) {
         if (isUntyped(expectedType)) {
@@ -138,8 +140,9 @@
 
         if (patchable != null) {
             patchable.doPatch(expectedType);
+            return new Register(expectedType, null);
         }
-        return new Register(expectedType, null);
+        return this;
     }
 
     /** Merge two registers and compute the type of the resulting register.