# HG changeset patch # User Michael Starzinger # Date 1303943219 -7200 # Node ID 2b2a1c413c0fbd78985a833c1126e33235c7af7e # Parent cc0033e43d3351d49e67d32182a6da54a114bde3 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. diff -r cc0033e43d33 -r 2b2a1c413c0f src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java --- 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); diff -r cc0033e43d33 -r 2b2a1c413c0f src/test/java/org/icedrobot/daneel/DexifyingRunner.java --- 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."); diff -r cc0033e43d33 -r 2b2a1c413c0f src/test/java/org/icedrobot/daneel/rewriter/FillArrayDataTest.java --- 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 }; } }; }