More progress on instruction coding.

* Redefine the target to be an absolute, not relative, offset.
* Add a cursor to the CodeInput API.
* Recognize payload opcodes as valid.
* Add codecs for the payload instructions.

Change-Id: I77f8e5a9e2b6b72f6a1f5ecc2eb4d701edd66837
diff --git a/dx/src/com/android/dx/io/CodeInput.java b/dx/src/com/android/dx/io/CodeInput.java
index a4f7306..c0fea78 100644
--- a/dx/src/com/android/dx/io/CodeInput.java
+++ b/dx/src/com/android/dx/io/CodeInput.java
@@ -23,6 +23,13 @@
  */
 public interface CodeInput {
     /**
+     * Gets the cursor. This is the offset of the next code unit to
+     * be read from the start of the input (which is generally the
+     * start of a method).
+     */
+    public int cursor();
+
+    /**
      * Reads a code unit.
      */
     public int read() throws EOFException;
diff --git a/dx/src/com/android/dx/io/CodeOutput.java b/dx/src/com/android/dx/io/CodeOutput.java
index c87d1b1..76ad3a2 100644
--- a/dx/src/com/android/dx/io/CodeOutput.java
+++ b/dx/src/com/android/dx/io/CodeOutput.java
@@ -44,4 +44,9 @@
      * Writes five code units.
      */
     public void write(short u0, short u1, short u2, short u3, short u4);
+
+    /**
+     * Writes the contents of the given array.
+     */
+    public void write(short[] data);
 }
diff --git a/dx/src/com/android/dx/io/DecodedInstruction.java b/dx/src/com/android/dx/io/DecodedInstruction.java
index 4e5b337..24ea919 100644
--- a/dx/src/com/android/dx/io/DecodedInstruction.java
+++ b/dx/src/com/android/dx/io/DecodedInstruction.java
@@ -44,7 +44,10 @@
     /** null-ok; index type */
     private final IndexType indexType;
 
-    /** target address offset argument */
+    /**
+     * target address argument. This is an absolute address, not just a
+     * signed offset.
+     */
     private final int target;
 
     /**
diff --git a/dx/src/com/android/dx/io/InstructionCodec.java b/dx/src/com/android/dx/io/InstructionCodec.java
index 45d955c..d2cb92e 100644
--- a/dx/src/com/android/dx/io/InstructionCodec.java
+++ b/dx/src/com/android/dx/io/InstructionCodec.java
@@ -105,10 +105,11 @@
     FORMAT_10T() {
         @Override public DecodedInstruction decode(int opcodeUnit,
                 CodeInput in) throws EOFException {
+            int baseOffset = in.cursor() - 1;
             int opcode = byte0(opcodeUnit);
-            int a = (byte) byte1(opcodeUnit); // sign-extend
+            int target = (byte) byte1(opcodeUnit); // sign-extend
             return new DecodedInstruction(this, opcode, 0, null,
-                    a, 0L, null,
+                    baseOffset + target, 0L, null,
                     0, 0, 0, 0, 0, 0);
         }
 
@@ -120,11 +121,12 @@
     FORMAT_20T() {
         @Override public DecodedInstruction decode(int opcodeUnit,
                 CodeInput in) throws EOFException {
+            int baseOffset = in.cursor() - 1;
             int opcode = byte0(opcodeUnit);
             int literal = byte1(opcodeUnit); // should be zero
             int target = (short) in.read(); // sign-extend
             return new DecodedInstruction(this, opcode, 0, null,
-                    target, literal, null,
+                    baseOffset + target, literal, null,
                     0, 0, 0, 0, 0, 0);
         }
 
@@ -174,11 +176,12 @@
     FORMAT_21T() {
         @Override public DecodedInstruction decode(int opcodeUnit,
                 CodeInput in) throws EOFException {
+            int baseOffset = in.cursor() - 1;
             int opcode = byte0(opcodeUnit);
             int a = byte1(opcodeUnit);
             int target = (short) in.read(); // sign-extend
             return new DecodedInstruction(this, opcode, 0, null,
-                    target, 0L, null,
+                    baseOffset + target, 0L, null,
                     1, a, 0, 0, 0, 0);
         }
 
@@ -299,12 +302,13 @@
     FORMAT_22T() {
         @Override public DecodedInstruction decode(int opcodeUnit,
                 CodeInput in) throws EOFException {
+            int baseOffset = in.cursor() - 1;
             int opcode = byte0(opcodeUnit);
             int a = nibble2(opcodeUnit);
             int b = nibble3(opcodeUnit);
             int target = (short) in.read(); // sign-extend
             return new DecodedInstruction(this, opcode, 0, null,
-                    target, 0L, null,
+                    baseOffset + target, 0L, null,
                     2, a, b, 0, 0, 0);
         }
 
@@ -381,11 +385,12 @@
     FORMAT_30T() {
         @Override public DecodedInstruction decode(int opcodeUnit,
                 CodeInput in) throws EOFException {
+            int baseOffset = in.cursor() - 1;
             int opcode = byte0(opcodeUnit);
             int literal = byte1(opcodeUnit); // should be zero
             int target = in.readInt();
             return new DecodedInstruction(this, opcode, 0, null,
-                    target, literal, null,
+                    baseOffset + target, literal, null,
                     0, 0, 0, 0, 0, 0);
         }
 
@@ -435,11 +440,12 @@
     FORMAT_31T() {
         @Override public DecodedInstruction decode(int opcodeUnit,
                 CodeInput in) throws EOFException {
+            int baseOffset = in.cursor() - 1;
             int opcode = byte0(opcodeUnit);
             int a = byte1(opcodeUnit);
             int target = in.readInt();
             return new DecodedInstruction(this, opcode, 0, null,
-                    target, 0L, null,
+                    baseOffset + target, 0L, null,
                     1, a, 0, 0, 0, 0);
         }
 
@@ -688,6 +694,84 @@
                     insn.getRegisterCountUnit(),
                     insn.getAUnit());
         }
+    },
+
+    FORMAT_PACKED_SWITCH_PAYLOAD() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int size = in.read();
+            int unitCount = (size * 2) + 3; // Doesn't count the opcode unit.
+            short[] data = new short[unitCount];
+
+            data[0] = (short) size;
+
+            for (int i = 1; i < unitCount; i++) {
+                data[i] = (short) in.read();
+            }
+
+            return new DecodedInstruction(this, opcodeUnit, 0, null,
+                    0, 0L, data,
+                    0, 0, 0, 0, 0, 0);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(insn.getOpcodeUnit());
+            out.write(insn.getData());
+        }
+    },
+
+    FORMAT_SPARSE_SWITCH_PAYLOAD() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int size = in.read();
+            int unitCount = (size * 4) + 1; // Doesn't count the opcode unit.
+            short[] data = new short[unitCount];
+
+            data[0] = (short) size;
+
+            for (int i = 1; i < unitCount; i++) {
+                data[i] = (short) in.read();
+            }
+
+            return new DecodedInstruction(this, opcodeUnit, 0, null,
+                    0, 0L, data,
+                    0, 0, 0, 0, 0, 0);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(insn.getOpcodeUnit());
+            out.write(insn.getData());
+        }
+    },
+
+    FORMAT_FILL_ARRAY_DATA_PAYLOAD() {
+        @Override public DecodedInstruction decode(int opcodeUnit,
+                CodeInput in) throws EOFException {
+            int elementWidth = in.read();
+            int size = in.readInt();
+
+            // Doesn't count the opcode unit.
+            int unitCount = (size * elementWidth + 1) / 2 + 3;
+
+            short[] data = new short[unitCount];
+
+            data[0] = unit0(size);
+            data[1] = unit1(size);
+            data[2] = (short) elementWidth;
+
+            for (int i = 3; i < unitCount; i++) {
+                data[i] = (short) in.read();
+            }
+
+            return new DecodedInstruction(this, opcodeUnit, 0, null,
+                    0, 0L, data,
+                    0, 0, 0, 0, 0, 0);
+        }
+
+        @Override public void encode(DecodedInstruction insn, CodeOutput out) {
+            out.write(insn.getOpcodeUnit());
+            out.write(insn.getData());
+        }
     };
 
     /**
diff --git a/dx/src/com/android/dx/io/OpcodeInfo.java b/dx/src/com/android/dx/io/OpcodeInfo.java
index 133e943..4507d9b 100644
--- a/dx/src/com/android/dx/io/OpcodeInfo.java
+++ b/dx/src/com/android/dx/io/OpcodeInfo.java
@@ -30,6 +30,20 @@
     /** non-null; array containing all the information */
     private static final Info[] INFO;
 
+    // TODO: These payload opcodes should be generated by opcode-gen.
+
+    public static final Info PACKED_SWITCH_PAYLOAD =
+        new Info(Opcodes.PACKED_SWITCH_PAYLOAD,
+                InstructionCodec.FORMAT_PACKED_SWITCH_PAYLOAD, null);
+
+    public static final Info SPARSE_SWITCH_PAYLOAD =
+        new Info(Opcodes.SPARSE_SWITCH_PAYLOAD,
+                InstructionCodec.FORMAT_SPARSE_SWITCH_PAYLOAD, null);
+
+    public static final Info FILL_ARRAY_DATA_PAYLOAD =
+        new Info(Opcodes.FILL_ARRAY_DATA_PAYLOAD,
+                InstructionCodec.FORMAT_FILL_ARRAY_DATA_PAYLOAD, null);
+
     // BEGIN(opcode-info-defs); GENERATED AUTOMATICALLY BY opcode-gen
     public static final Info NOP =
         new Info(Opcodes.NOP,
@@ -1065,6 +1079,11 @@
     static {
         INFO = new Info[Opcodes.MAX_VALUE - Opcodes.MIN_VALUE + 1];
 
+        // TODO: These payload opcodes should be generated by opcode-gen.
+        set(PACKED_SWITCH_PAYLOAD);
+        set(SPARSE_SWITCH_PAYLOAD);
+        set(FILL_ARRAY_DATA_PAYLOAD);
+
         // BEGIN(opcode-info-init); GENERATED AUTOMATICALLY BY opcode-gen
         set(NOP);
         set(MOVE);
diff --git a/dx/src/com/android/dx/io/Opcodes.java b/dx/src/com/android/dx/io/Opcodes.java
index 51d92c2..bf0aa66 100644
--- a/dx/src/com/android/dx/io/Opcodes.java
+++ b/dx/src/com/android/dx/io/Opcodes.java
@@ -21,7 +21,11 @@
  * document for the meaning and instruction format of each opcode.
  */
 public final class Opcodes {
-    /** pseudo-opcode used for nonstandard format "instructions" */
+    /**
+     * pseudo-opcode used for nonstandard format payload "instructions". TODO:
+     * Retire this concept, and start treating the payload instructions
+     * more like the rest.
+     */
     public static final int SPECIAL_FORMAT = -1;
 
     /**
@@ -296,6 +300,8 @@
     public static final int INVOKE_INTERFACE_JUMBO = 0x26ff;
     // END(opcodes)
 
+    // TODO: Generate these payload opcodes with opcode-gen.
+
     /**
      * special pseudo-opcode value for packed-switch data payload
      * instructions
@@ -336,21 +342,23 @@
     public static boolean isValidShape(int opcode) {
         /*
          * Note: This method bakes in knowledge that all opcodes are
-         * either single-byte or of the form ((byteValue << 8) |
-         * 0xff).
+         * either single-byte or of the forms (byteValue << 8) or
+         * ((byteValue << 8) 0xff).
          */
 
         // Note: SPECIAL_FORMAT == NO_NEXT.
-        if ((opcode >= SPECIAL_FORMAT) && (opcode <= 0xff)) {
+        if (opcode < SPECIAL_FORMAT) {
+            return false;
+        } else if (opcode == SPECIAL_FORMAT) {
             return true;
         }
 
-        if ((opcode >= 0xff) && (opcode <= 0xffff)
-                && ((opcode & 0xff) == 0xff)) {
+        int lowByte = opcode & 0xff;
+        if ((lowByte == 0) || (lowByte == 0xff)) {
             return true;
         }
 
-        return false;
+        return (opcode & 0xff00) == 0;
     }
 
     /**
@@ -360,11 +368,11 @@
     public static int extractOpcodeFromUnit(int opcodeUnit) {
         /*
          * Note: This method bakes in knowledge that all opcodes are
-         * either single-byte or of the form ((byteValue << 8) |
-         * 0xff).
+         * either single-byte or of the forms (byteValue << 8) or
+         * ((byteValue << 8) 0xff).
          */
 
         int lowByte = opcodeUnit & 0xff;
-        return (lowByte == 0xff) ? opcodeUnit : lowByte;
+        return ((lowByte == 0) || (lowByte == 0xff)) ? opcodeUnit : lowByte;
     }
 }
diff --git a/dx/src/com/android/dx/io/ShortArrayCodeInput.java b/dx/src/com/android/dx/io/ShortArrayCodeInput.java
index c0faf71..da80df6 100644
--- a/dx/src/com/android/dx/io/ShortArrayCodeInput.java
+++ b/dx/src/com/android/dx/io/ShortArrayCodeInput.java
@@ -41,6 +41,11 @@
     }
 
     /** @inheritDoc */
+    public int cursor() {
+        return cursor;
+    }
+
+    /** @inheritDoc */
     public int read() throws EOFException {
         try {
             return array[cursor++];