changeset 133:2b2a1c413c0f

Implemented rewriting of fill-array-data. * rewriter/DexRewriter.java (visitInstrFillArrayData): Implemented. * DexifyingRunner.java: Now also verifies generated code to ease debugging. * rewriter/FillArrayDataTest.java: Enabled and expanded test case.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Thu, 28 Apr 2011 00:26:59 +0200
parents cc0033e43d33
children 6add930f34ce
files src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java src/test/java/org/icedrobot/daneel/DexifyingRunner.java src/test/java/org/icedrobot/daneel/rewriter/FillArrayDataTest.java
diffstat 3 files changed, 108 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Tue Apr 19 21:40:04 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Thu Apr 28 00:26:59 2011 +0200
@@ -288,11 +288,12 @@
             labelMap.put(label, asmLabel);
             return asmLabel;
         }
-        
-        /** This method must be called before each instruction unless the instruction
-         *  is a MOVE_RESULT* or a MOVE_EXCEPTION.
-         *  It checks if the last instruction was an invoke or the start of an exception handler
-         *  and corrects the stack.
+
+        /**
+         * This method must be called before each instruction unless it is a
+         * {@link Opcode#MOVE_RESULT}* or a {@link Opcode#MOVE_EXCEPTION}
+         * instruction. It checks if the last instruction was an invoke or the
+         * start of an exception handler and corrects the stack.
          */
         private void fixStackAfterAMethodCallOrAnExceptionHandler() {
             int returnRegisterType = this.returnRegisterType;
@@ -325,11 +326,47 @@
             mv.visitInsn((type == INT_TYPE)? ICONST_0: LCONST_0);
             mv.visitLabel(endPart);
         }
-        
+
+        /**
+         * Instantiates a new assertion error used to indicate an invalid
+         * opcode.
+         * 
+         * @param opcode The invalid opcode.
+         * @return The newly created assertion error.
+         */
         private static AssertionError newAssertionError(Opcode opcode) {
             return new AssertionError("Invalid opcode: " + opcode);
         }
 
+        /**
+         * Reads a typed value from the given data buffer. Mainly used to read
+         * {@link Opcode#FILL_ARRAY_DATA} values.
+         * 
+         * @param data The given data buffer positioned at the next element.
+         * @param type The type constant for the expected value type.
+         * @return The boxed primitive value as read from the buffer.
+         */
+        private static Object readTypedValue(ByteBuffer data, int type) {
+            switch (type) {
+            case INT_TYPE:
+                return data.getInt();
+            case LONG_TYPE:
+                return data.getLong();
+            case FLOAT_TYPE:
+                return data.getFloat();
+            case DOUBLE_TYPE:
+                return data.getDouble();
+            case BYTE_TYPE:
+                return data.get();
+            case CHAR_TYPE:
+                return data.getChar();
+            case SHORT_TYPE:
+                return data.getShort();
+            default:
+                throw new AssertionError("Unknown data type: " + type);
+            }
+        }
+
         @Override
         public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
             if (visibility == DexAnnotationVisitor.VISIBILITY_RUNTIME) {
@@ -1151,25 +1188,26 @@
             mv.visitLookupSwitchInsn(defaultLabel, keys, asmLabels);
             mv.visitLabel(defaultLabel);
         }
-        
-        
-        
-        
-        
-        // unimplemented
-        
+
         @Override
         public void visitInstrFillArrayData(Opcode opcode, int vsrc,
                 int elementWidth, int elementNumber, ByteBuffer data) {
             fixStackAfterAMethodCallOrAnExceptionHandler();
             vsrc = registerToSlot(vsrc);
-            throw new UnsupportedOperationException("NYI " + opcode);
+
+            int type = interpreter.getRegister(vsrc).getType();
+            int componentType = Register.getComponentType(type);
+            mv.visitVarInsn(ALOAD, vsrc);
+            for (int i = 0; i < elementNumber; i++) {
+                Object value = readTypedValue(data, componentType);
+                if (i < elementNumber - 1)
+                    mv.visitInsn(DUP);   // array
+                mv.visitLdcInsn(i);      // index
+                mv.visitLdcInsn(value);  // value
+                mv.visitInsn(Register.getJavaOpcode(componentType, IASTORE));
+            }
         }
 
-        
-
-        
-
         @Override
         public void visitEnd() {
             mv.visitMaxs(-1, -1);
--- a/src/test/java/org/icedrobot/daneel/DexifyingRunner.java	Tue Apr 19 21:40:04 2011 +0200
+++ b/src/test/java/org/icedrobot/daneel/DexifyingRunner.java	Thu Apr 28 00:26:59 2011 +0200
@@ -40,10 +40,12 @@
 import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.PrintWriter;
 import java.lang.reflect.Method;
 import java.net.URL;
 
 import org.icedrobot.daneel.dex.DexFile;
+import org.icedrobot.daneel.loader.Verifier;
 import org.icedrobot.daneel.rewriter.DexRewriter;
 import org.junit.runner.notification.RunNotifier;
 import org.junit.runners.BlockJUnit4ClassRunner;
@@ -127,6 +129,10 @@
         }
         byte[] testCode = DexRewriter.rewrite(testClass, dex);
 
+        // Verify the rewritten test code.
+        PrintWriter err = new PrintWriter(System.err);
+        Verifier.verify(null, dex, testClass, testCode, err);
+
         // From this point on we no longer need the DEX file
         if (!tmp.delete())
             throw new DaneelException("Unable to delete DEX file.");
--- a/src/test/java/org/icedrobot/daneel/rewriter/FillArrayDataTest.java	Tue Apr 19 21:40:04 2011 +0200
+++ b/src/test/java/org/icedrobot/daneel/rewriter/FillArrayDataTest.java	Thu Apr 28 00:26:59 2011 +0200
@@ -43,20 +43,60 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@org.junit.Ignore("FILL_ARRAY_DATA not yet implemented")
 @RunWith(DexifyingRunner.class)
 public class FillArrayDataTest {
 
     @Test
-    public void testNewFilled() {
-        int[] array = DEXCode.newFilled();
-        assertArrayEquals(new int[] { 7, 23, 42, 127, 128, 129 }, array);
+    public void testNewFilledChar() {
+        char[] array = DEXCode.newFilledChar();
+        assertArrayEquals(new char[] { 7, 23, 42, 127, 128, 0xffff }, array);
+    }
+
+    @Test
+    public void testNewFilledShort() {
+        short[] array = DEXCode.newFilledShort();
+        assertArrayEquals(new short[] { 7, 23, 42, 127, 128, -1 }, array);
+    }
+
+    @Test
+    public void testNewFilledInt() {
+        int[] array = DEXCode.newFilledInt();
+        assertArrayEquals(new int[] { 7, 23, 42, 127, 128, -1 }, array);
+    }
+
+    @Test
+    public void testNewFilledLong() {
+        long[] array = DEXCode.newFilledLong();
+        assertArrayEquals(new long[] { 7, 23, 42, 127, 128, -1L }, array);
+    }
+
+    @Test
+    public void testNewFilledFloat() {
+        float[] array = DEXCode.newFilledFloat();
+        assertArrayEquals(new float[] { 0.7f, 2.3f, 4.2f, 1.27f }, array, 0);
     }
 
     // Keep this class named "DEXCode" to push it through Daneel.
     private static class DEXCode {
-        public static int[] newFilled() {
-            return new int[] { 7, 23, 42, 127, 128, 129 };
+
+        public static char[] newFilledChar() {
+            return new char[] { 7, 23, 42, 127, 128, 0xffff };
+        }
+
+        public static short[] newFilledShort() {
+            return new short[] { 7, 23, 42, 127, 128, -1 };
+        }
+
+        public static int[] newFilledInt() {
+            return new int[] { 7, 23, 42, 127, 128, -1 };
+        }
+
+        public static long[] newFilledLong() {
+            return new long[] { 7, 23, 42, 127, 128, -1L };
+        }
+
+        public static float[] newFilledFloat() {
+            return new float[] { 0.7f, 2.3f, 4.2f, 1.27f };
         }
     };
 }