Home | History | Annotate | Download | only in instructions
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.dx.io.instructions;
     18 
     19 import com.android.dex.DexException;
     20 import com.android.dx.io.IndexType;
     21 import com.android.dx.io.OpcodeInfo;
     22 import com.android.dx.io.Opcodes;
     23 import com.android.dx.util.Hex;
     24 import java.io.EOFException;
     25 import java.util.Arrays;
     26 
     27 /**
     28  * Representation of an instruction format, which knows how to decode into
     29  * and encode from instances of {@link DecodedInstruction}.
     30  */
     31 public enum InstructionCodec {
     32     FORMAT_00X() {
     33         @Override
     34         public DecodedInstruction decode(int opcodeUnit,
     35                 CodeInput in) throws EOFException {
     36             return new ZeroRegisterDecodedInstruction(
     37                     this, opcodeUnit, 0, null,
     38                     0, 0L);
     39         }
     40 
     41         @Override
     42         public void encode(DecodedInstruction insn, CodeOutput out) {
     43             out.write(insn.getOpcodeUnit());
     44         }
     45     },
     46 
     47     FORMAT_10X() {
     48         @Override
     49         public DecodedInstruction decode(int opcodeUnit,
     50                 CodeInput in) throws EOFException {
     51             int opcode = byte0(opcodeUnit);
     52             int literal = byte1(opcodeUnit); // should be zero
     53             return new ZeroRegisterDecodedInstruction(
     54                     this, opcode, 0, null,
     55                     0, literal);
     56         }
     57 
     58         @Override
     59         public void encode(DecodedInstruction insn, CodeOutput out) {
     60             out.write(insn.getOpcodeUnit());
     61         }
     62     },
     63 
     64     FORMAT_12X() {
     65         @Override
     66         public DecodedInstruction decode(int opcodeUnit,
     67                 CodeInput in) throws EOFException {
     68             int opcode = byte0(opcodeUnit);
     69             int a = nibble2(opcodeUnit);
     70             int b = nibble3(opcodeUnit);
     71             return new TwoRegisterDecodedInstruction(
     72                     this, opcode, 0, null,
     73                     0, 0L,
     74                     a, b);
     75         }
     76 
     77         @Override
     78         public void encode(DecodedInstruction insn, CodeOutput out) {
     79             out.write(
     80                     codeUnit(insn.getOpcodeUnit(),
     81                              makeByte(insn.getA(), insn.getB())));
     82         }
     83     },
     84 
     85     FORMAT_11N() {
     86         @Override
     87         public DecodedInstruction decode(int opcodeUnit,
     88                 CodeInput in) throws EOFException {
     89             int opcode = byte0(opcodeUnit);
     90             int a = nibble2(opcodeUnit);
     91             int literal = (nibble3(opcodeUnit) << 28) >> 28; // sign-extend
     92             return new OneRegisterDecodedInstruction(
     93                     this, opcode, 0, null,
     94                     0, literal,
     95                     a);
     96         }
     97 
     98         @Override
     99         public void encode(DecodedInstruction insn, CodeOutput out) {
    100             out.write(
    101                     codeUnit(insn.getOpcodeUnit(),
    102                              makeByte(insn.getA(), insn.getLiteralNibble())));
    103         }
    104     },
    105 
    106     FORMAT_11X() {
    107         @Override
    108         public DecodedInstruction decode(int opcodeUnit,
    109                 CodeInput in) throws EOFException {
    110             int opcode = byte0(opcodeUnit);
    111             int a = byte1(opcodeUnit);
    112             return new OneRegisterDecodedInstruction(
    113                     this, opcode, 0, null,
    114                     0, 0L,
    115                     a);
    116         }
    117 
    118         @Override
    119         public void encode(DecodedInstruction insn, CodeOutput out) {
    120             out.write(codeUnit(insn.getOpcode(), insn.getA()));
    121         }
    122     },
    123 
    124     FORMAT_10T() {
    125         @Override
    126         public DecodedInstruction decode(int opcodeUnit,
    127                 CodeInput in) throws EOFException {
    128             int baseAddress = in.cursor() - 1;
    129             int opcode = byte0(opcodeUnit);
    130             int target = (byte) byte1(opcodeUnit); // sign-extend
    131             return new ZeroRegisterDecodedInstruction(
    132                     this, opcode, 0, null,
    133                     baseAddress + target, 0L);
    134         }
    135 
    136         @Override
    137         public void encode(DecodedInstruction insn, CodeOutput out) {
    138             int relativeTarget = insn.getTargetByte(out.cursor());
    139             out.write(codeUnit(insn.getOpcode(), relativeTarget));
    140         }
    141     },
    142 
    143     FORMAT_20T() {
    144         @Override
    145         public DecodedInstruction decode(int opcodeUnit,
    146                 CodeInput in) throws EOFException {
    147             int baseAddress = in.cursor() - 1;
    148             int opcode = byte0(opcodeUnit);
    149             int literal = byte1(opcodeUnit); // should be zero
    150             int target = (short) in.read(); // sign-extend
    151             return new ZeroRegisterDecodedInstruction(
    152                     this, opcode, 0, null,
    153                     baseAddress + target, literal);
    154         }
    155 
    156         @Override
    157         public void encode(DecodedInstruction insn, CodeOutput out) {
    158             short relativeTarget = insn.getTargetUnit(out.cursor());
    159             out.write(insn.getOpcodeUnit(), relativeTarget);
    160         }
    161     },
    162 
    163     FORMAT_20BC() {
    164         @Override
    165         public DecodedInstruction decode(int opcodeUnit,
    166                 CodeInput in) throws EOFException {
    167             // Note: We use the literal field to hold the decoded AA value.
    168             int opcode = byte0(opcodeUnit);
    169             int literal = byte1(opcodeUnit);
    170             int index = in.read();
    171             return new ZeroRegisterDecodedInstruction(
    172                     this, opcode, index, IndexType.VARIES,
    173                     0, literal);
    174         }
    175 
    176         @Override
    177         public void encode(DecodedInstruction insn, CodeOutput out) {
    178             out.write(
    179                     codeUnit(insn.getOpcode(), insn.getLiteralByte()),
    180                     insn.getIndexUnit());
    181         }
    182     },
    183 
    184     FORMAT_22X() {
    185         @Override
    186         public DecodedInstruction decode(int opcodeUnit,
    187                 CodeInput in) throws EOFException {
    188             int opcode = byte0(opcodeUnit);
    189             int a = byte1(opcodeUnit);
    190             int b = in.read();
    191             return new TwoRegisterDecodedInstruction(
    192                     this, opcode, 0, null,
    193                     0, 0L,
    194                     a, b);
    195         }
    196 
    197         @Override
    198         public void encode(DecodedInstruction insn, CodeOutput out) {
    199             out.write(
    200                     codeUnit(insn.getOpcode(), insn.getA()),
    201                     insn.getBUnit());
    202         }
    203     },
    204 
    205     FORMAT_21T() {
    206         @Override
    207         public DecodedInstruction decode(int opcodeUnit,
    208                 CodeInput in) throws EOFException {
    209             int baseAddress = in.cursor() - 1;
    210             int opcode = byte0(opcodeUnit);
    211             int a = byte1(opcodeUnit);
    212             int target = (short) in.read(); // sign-extend
    213             return new OneRegisterDecodedInstruction(
    214                     this, opcode, 0, null,
    215                     baseAddress + target, 0L,
    216                     a);
    217         }
    218 
    219         @Override
    220         public void encode(DecodedInstruction insn, CodeOutput out) {
    221             short relativeTarget = insn.getTargetUnit(out.cursor());
    222             out.write(codeUnit(insn.getOpcode(), insn.getA()), relativeTarget);
    223         }
    224     },
    225 
    226     FORMAT_21S() {
    227         @Override
    228         public DecodedInstruction decode(int opcodeUnit,
    229                 CodeInput in) throws EOFException {
    230             int opcode = byte0(opcodeUnit);
    231             int a = byte1(opcodeUnit);
    232             int literal = (short) in.read(); // sign-extend
    233             return new OneRegisterDecodedInstruction(
    234                     this, opcode, 0, null,
    235                     0, literal,
    236                     a);
    237         }
    238 
    239         @Override
    240         public void encode(DecodedInstruction insn, CodeOutput out) {
    241             out.write(
    242                     codeUnit(insn.getOpcode(), insn.getA()),
    243                     insn.getLiteralUnit());
    244         }
    245     },
    246 
    247     FORMAT_21H() {
    248         @Override
    249         public DecodedInstruction decode(int opcodeUnit,
    250                 CodeInput in) throws EOFException {
    251             int opcode = byte0(opcodeUnit);
    252             int a = byte1(opcodeUnit);
    253             long literal = (short) in.read(); // sign-extend
    254 
    255             /*
    256              * Format 21h decodes differently depending on the opcode,
    257              * because the "signed hat" might represent either a 32-
    258              * or 64- bit value.
    259              */
    260             literal <<= (opcode == Opcodes.CONST_HIGH16) ? 16 : 48;
    261 
    262             return new OneRegisterDecodedInstruction(
    263                     this, opcode, 0, null,
    264                     0, literal,
    265                     a);
    266         }
    267 
    268         @Override
    269         public void encode(DecodedInstruction insn, CodeOutput out) {
    270             // See above.
    271             int opcode = insn.getOpcode();
    272             int shift = (opcode == Opcodes.CONST_HIGH16) ? 16 : 48;
    273             short literal = (short) (insn.getLiteral() >> shift);
    274 
    275             out.write(codeUnit(opcode, insn.getA()), literal);
    276         }
    277     },
    278 
    279     FORMAT_21C() {
    280         @Override
    281         public DecodedInstruction decode(int opcodeUnit,
    282                 CodeInput in) throws EOFException {
    283             int opcode = byte0(opcodeUnit);
    284             int a = byte1(opcodeUnit);
    285             int index = in.read();
    286             IndexType indexType = OpcodeInfo.getIndexType(opcode);
    287             return new OneRegisterDecodedInstruction(
    288                     this, opcode, index, indexType,
    289                     0, 0L,
    290                     a);
    291         }
    292 
    293         @Override
    294         public void encode(DecodedInstruction insn, CodeOutput out) {
    295             out.write(
    296                     codeUnit(insn.getOpcode(), insn.getA()),
    297                     insn.getIndexUnit());
    298         }
    299     },
    300 
    301     FORMAT_23X() {
    302         @Override
    303         public DecodedInstruction decode(int opcodeUnit,
    304                 CodeInput in) throws EOFException {
    305             int opcode = byte0(opcodeUnit);
    306             int a = byte1(opcodeUnit);
    307             int bc = in.read();
    308             int b = byte0(bc);
    309             int c = byte1(bc);
    310             return new ThreeRegisterDecodedInstruction(
    311                     this, opcode, 0, null,
    312                     0, 0L,
    313                     a, b, c);
    314         }
    315 
    316         @Override
    317         public void encode(DecodedInstruction insn, CodeOutput out) {
    318             out.write(
    319                     codeUnit(insn.getOpcode(), insn.getA()),
    320                     codeUnit(insn.getB(), insn.getC()));
    321         }
    322     },
    323 
    324     FORMAT_22B() {
    325         @Override
    326         public DecodedInstruction decode(int opcodeUnit,
    327                 CodeInput in) throws EOFException {
    328             int opcode = byte0(opcodeUnit);
    329             int a = byte1(opcodeUnit);
    330             int bc = in.read();
    331             int b = byte0(bc);
    332             int literal = (byte) byte1(bc); // sign-extend
    333             return new TwoRegisterDecodedInstruction(
    334                     this, opcode, 0, null,
    335                     0, literal,
    336                     a, b);
    337         }
    338 
    339         @Override
    340         public void encode(DecodedInstruction insn, CodeOutput out) {
    341             out.write(
    342                     codeUnit(insn.getOpcode(), insn.getA()),
    343                     codeUnit(insn.getB(),
    344                              insn.getLiteralByte()));
    345         }
    346     },
    347 
    348     FORMAT_22T() {
    349         @Override
    350         public DecodedInstruction decode(int opcodeUnit,
    351                 CodeInput in) throws EOFException {
    352             int baseAddress = in.cursor() - 1;
    353             int opcode = byte0(opcodeUnit);
    354             int a = nibble2(opcodeUnit);
    355             int b = nibble3(opcodeUnit);
    356             int target = (short) in.read(); // sign-extend
    357             return new TwoRegisterDecodedInstruction(
    358                     this, opcode, 0, null,
    359                     baseAddress + target, 0L,
    360                     a, b);
    361         }
    362 
    363         @Override
    364         public void encode(DecodedInstruction insn, CodeOutput out) {
    365             short relativeTarget = insn.getTargetUnit(out.cursor());
    366             out.write(
    367                     codeUnit(insn.getOpcode(),
    368                              makeByte(insn.getA(), insn.getB())),
    369                     relativeTarget);
    370         }
    371     },
    372 
    373     FORMAT_22S() {
    374         @Override
    375         public DecodedInstruction decode(int opcodeUnit,
    376                 CodeInput in) throws EOFException {
    377             int opcode = byte0(opcodeUnit);
    378             int a = nibble2(opcodeUnit);
    379             int b = nibble3(opcodeUnit);
    380             int literal = (short) in.read(); // sign-extend
    381             return new TwoRegisterDecodedInstruction(
    382                     this, opcode, 0, null,
    383                     0, literal,
    384                     a, b);
    385         }
    386 
    387         @Override
    388         public void encode(DecodedInstruction insn, CodeOutput out) {
    389             out.write(
    390                     codeUnit(insn.getOpcode(),
    391                              makeByte(insn.getA(), insn.getB())),
    392                     insn.getLiteralUnit());
    393         }
    394     },
    395 
    396     FORMAT_22C() {
    397         @Override
    398         public DecodedInstruction decode(int opcodeUnit,
    399                 CodeInput in) throws EOFException {
    400             int opcode = byte0(opcodeUnit);
    401             int a = nibble2(opcodeUnit);
    402             int b = nibble3(opcodeUnit);
    403             int index = in.read();
    404             IndexType indexType = OpcodeInfo.getIndexType(opcode);
    405             return new TwoRegisterDecodedInstruction(
    406                     this, opcode, index, indexType,
    407                     0, 0L,
    408                     a, b);
    409         }
    410 
    411         @Override
    412         public void encode(DecodedInstruction insn, CodeOutput out) {
    413             out.write(
    414                     codeUnit(insn.getOpcode(),
    415                              makeByte(insn.getA(), insn.getB())),
    416                     insn.getIndexUnit());
    417         }
    418     },
    419 
    420     FORMAT_22CS() {
    421         @Override
    422         public DecodedInstruction decode(int opcodeUnit,
    423                 CodeInput in) throws EOFException {
    424             int opcode = byte0(opcodeUnit);
    425             int a = nibble2(opcodeUnit);
    426             int b = nibble3(opcodeUnit);
    427             int index = in.read();
    428             return new TwoRegisterDecodedInstruction(
    429                     this, opcode, index, IndexType.FIELD_OFFSET,
    430                     0, 0L,
    431                     a, b);
    432         }
    433 
    434         @Override
    435         public void encode(DecodedInstruction insn, CodeOutput out) {
    436             out.write(
    437                     codeUnit(insn.getOpcode(),
    438                              makeByte(insn.getA(), insn.getB())),
    439                     insn.getIndexUnit());
    440         }
    441     },
    442 
    443     FORMAT_30T() {
    444         @Override
    445         public DecodedInstruction decode(int opcodeUnit,
    446                 CodeInput in) throws EOFException {
    447             int baseAddress = in.cursor() - 1;
    448             int opcode = byte0(opcodeUnit);
    449             int literal = byte1(opcodeUnit); // should be zero
    450             int target = in.readInt();
    451             return new ZeroRegisterDecodedInstruction(
    452                     this, opcode, 0, null,
    453                     baseAddress + target, literal);
    454         }
    455 
    456         @Override
    457         public void encode(DecodedInstruction insn, CodeOutput out) {
    458             int relativeTarget = insn.getTarget(out.cursor());
    459             out.write(insn.getOpcodeUnit(),
    460                     unit0(relativeTarget), unit1(relativeTarget));
    461         }
    462     },
    463 
    464     FORMAT_32X() {
    465         @Override
    466         public DecodedInstruction decode(int opcodeUnit,
    467                 CodeInput in) throws EOFException {
    468             int opcode = byte0(opcodeUnit);
    469             int literal = byte1(opcodeUnit); // should be zero
    470             int a = in.read();
    471             int b = in.read();
    472             return new TwoRegisterDecodedInstruction(
    473                     this, opcode, 0, null,
    474                     0, literal,
    475                     a, b);
    476         }
    477 
    478         @Override
    479         public void encode(DecodedInstruction insn, CodeOutput out) {
    480             out.write(insn.getOpcodeUnit(), insn.getAUnit(), insn.getBUnit());
    481         }
    482     },
    483 
    484     FORMAT_31I() {
    485         @Override
    486         public DecodedInstruction decode(int opcodeUnit,
    487                 CodeInput in) throws EOFException {
    488             int opcode = byte0(opcodeUnit);
    489             int a = byte1(opcodeUnit);
    490             int literal = in.readInt();
    491             return new OneRegisterDecodedInstruction(
    492                     this, opcode, 0, null,
    493                     0, literal,
    494                     a);
    495         }
    496 
    497         @Override
    498         public void encode(DecodedInstruction insn, CodeOutput out) {
    499             int literal = insn.getLiteralInt();
    500             out.write(
    501                     codeUnit(insn.getOpcode(), insn.getA()),
    502                     unit0(literal),
    503                     unit1(literal));
    504         }
    505     },
    506 
    507     FORMAT_31T() {
    508         @Override
    509         public DecodedInstruction decode(int opcodeUnit,
    510                 CodeInput in) throws EOFException {
    511             int baseAddress = in.cursor() - 1;
    512             int opcode = byte0(opcodeUnit);
    513             int a = byte1(opcodeUnit);
    514             int target = baseAddress + in.readInt();
    515 
    516             /*
    517              * Switch instructions need to "forward" their addresses to their
    518              * payload target instructions.
    519              */
    520             switch (opcode) {
    521                 case Opcodes.PACKED_SWITCH:
    522                 case Opcodes.SPARSE_SWITCH: {
    523                     in.setBaseAddress(target, baseAddress);
    524                     break;
    525                 }
    526                 default: // fall out
    527             }
    528 
    529             return new OneRegisterDecodedInstruction(
    530                     this, opcode, 0, null,
    531                     target, 0L,
    532                     a);
    533         }
    534 
    535         @Override
    536         public void encode(DecodedInstruction insn, CodeOutput out) {
    537             int relativeTarget = insn.getTarget(out.cursor());
    538             out.write(
    539                     codeUnit(insn.getOpcode(), insn.getA()),
    540                     unit0(relativeTarget), unit1(relativeTarget));
    541         }
    542     },
    543 
    544     FORMAT_31C() {
    545         @Override
    546         public DecodedInstruction decode(int opcodeUnit,
    547                 CodeInput in) throws EOFException {
    548             int opcode = byte0(opcodeUnit);
    549             int a = byte1(opcodeUnit);
    550             int index = in.readInt();
    551             IndexType indexType = OpcodeInfo.getIndexType(opcode);
    552             return new OneRegisterDecodedInstruction(
    553                     this, opcode, index, indexType,
    554                     0, 0L,
    555                     a);
    556         }
    557 
    558         @Override
    559         public void encode(DecodedInstruction insn, CodeOutput out) {
    560             int index = insn.getIndex();
    561             out.write(
    562                     codeUnit(insn.getOpcode(), insn.getA()),
    563                     unit0(index),
    564                     unit1(index));
    565         }
    566     },
    567 
    568     FORMAT_35C() {
    569         @Override
    570         public DecodedInstruction decode(int opcodeUnit,
    571                 CodeInput in) throws EOFException {
    572             return decodeRegisterList(this, opcodeUnit, in);
    573         }
    574 
    575         @Override
    576         public void encode(DecodedInstruction insn, CodeOutput out) {
    577             encodeRegisterList(insn, out);
    578         }
    579     },
    580 
    581     FORMAT_35MS() {
    582         @Override
    583         public DecodedInstruction decode(int opcodeUnit,
    584                 CodeInput in) throws EOFException {
    585             return decodeRegisterList(this, opcodeUnit, in);
    586         }
    587 
    588         @Override
    589         public void encode(DecodedInstruction insn, CodeOutput out) {
    590             encodeRegisterList(insn, out);
    591         }
    592     },
    593 
    594     FORMAT_35MI() {
    595         @Override
    596         public DecodedInstruction decode(int opcodeUnit,
    597                 CodeInput in) throws EOFException {
    598             return decodeRegisterList(this, opcodeUnit, in);
    599         }
    600 
    601         @Override
    602         public void encode(DecodedInstruction insn, CodeOutput out) {
    603             encodeRegisterList(insn, out);
    604         }
    605     },
    606 
    607     FORMAT_3RC() {
    608         @Override
    609         public DecodedInstruction decode(int opcodeUnit,
    610                 CodeInput in) throws EOFException {
    611             return decodeRegisterRange(this, opcodeUnit, in);
    612         }
    613 
    614         @Override
    615         public void encode(DecodedInstruction insn, CodeOutput out) {
    616             encodeRegisterRange(insn, out);
    617         }
    618     },
    619 
    620     FORMAT_3RMS() {
    621         @Override
    622         public DecodedInstruction decode(int opcodeUnit,
    623                 CodeInput in) throws EOFException {
    624             return decodeRegisterRange(this, opcodeUnit, in);
    625         }
    626 
    627         @Override
    628         public void encode(DecodedInstruction insn, CodeOutput out) {
    629             encodeRegisterRange(insn, out);
    630         }
    631     },
    632 
    633     FORMAT_3RMI() {
    634         @Override
    635         public DecodedInstruction decode(int opcodeUnit,
    636                 CodeInput in) throws EOFException {
    637             return decodeRegisterRange(this, opcodeUnit, in);
    638         }
    639 
    640         @Override
    641         public void encode(DecodedInstruction insn, CodeOutput out) {
    642             encodeRegisterRange(insn, out);
    643         }
    644     },
    645 
    646     FORMAT_51L() {
    647         @Override
    648         public DecodedInstruction decode(int opcodeUnit,
    649                 CodeInput in) throws EOFException {
    650             int opcode = byte0(opcodeUnit);
    651             int a = byte1(opcodeUnit);
    652             long literal = in.readLong();
    653             return new OneRegisterDecodedInstruction(
    654                     this, opcode, 0, null,
    655                     0, literal,
    656                     a);
    657         }
    658 
    659         @Override
    660         public void encode(DecodedInstruction insn, CodeOutput out) {
    661             long literal = insn.getLiteral();
    662             out.write(
    663                     codeUnit(insn.getOpcode(), insn.getA()),
    664                     unit0(literal),
    665                     unit1(literal),
    666                     unit2(literal),
    667                     unit3(literal));
    668         }
    669     },
    670 
    671     FORMAT_45CC() {
    672         @Override
    673         public DecodedInstruction decode(int opcodeUnit,
    674                 CodeInput in) throws EOFException {
    675             int opcode = byte0(opcodeUnit);
    676             if (opcode != Opcodes.INVOKE_POLYMORPHIC) {
    677               // 45cc isn't currently used for anything other than invoke-polymorphic.
    678               // If that changes, add a more general DecodedInstruction for this format.
    679               throw new UnsupportedOperationException(String.valueOf(opcode));
    680             }
    681             int g = nibble2(opcodeUnit);
    682             int registerCount = nibble3(opcodeUnit);
    683             int methodIndex = in.read();
    684             int cdef = in.read();
    685             int c = nibble0(cdef);
    686             int d = nibble1(cdef);
    687             int e = nibble2(cdef);
    688             int f = nibble3(cdef);
    689             int protoIndex = in.read();
    690             IndexType indexType = OpcodeInfo.getIndexType(opcode);
    691 
    692             if (registerCount < 1 || registerCount > 5) {
    693                 throw new DexException("bogus registerCount: " + Hex.uNibble(registerCount));
    694             }
    695             int[] registers = {c, d, e, f, g};
    696             registers = Arrays.copyOfRange(registers, 0, registerCount);
    697 
    698             return new InvokePolymorphicDecodedInstruction(
    699                     this, opcode, methodIndex, indexType, protoIndex, registers);
    700         }
    701 
    702         @Override
    703         public void encode(DecodedInstruction insn, CodeOutput out) {
    704             InvokePolymorphicDecodedInstruction polyInsn =
    705                     (InvokePolymorphicDecodedInstruction) insn;
    706             out.write(codeUnit(polyInsn.getOpcode(),
    707                             makeByte(polyInsn.getG(), polyInsn.getRegisterCount())),
    708                     polyInsn.getIndexUnit(),
    709                     codeUnit(polyInsn.getC(), polyInsn.getD(), polyInsn.getE(), polyInsn.getF()),
    710                     polyInsn.getProtoIndex());
    711 
    712         }
    713     },
    714 
    715     FORMAT_4RCC() {
    716         @Override
    717         public DecodedInstruction decode(int opcodeUnit,
    718                 CodeInput in) throws EOFException {
    719             int opcode = byte0(opcodeUnit);
    720             if (opcode != Opcodes.INVOKE_POLYMORPHIC_RANGE) {
    721               // 4rcc isn't currently used for anything other than invoke-polymorphic.
    722               // If that changes, add a more general DecodedInstruction for this format.
    723               throw new UnsupportedOperationException(String.valueOf(opcode));
    724             }
    725             int registerCount = byte1(opcodeUnit);
    726             int methodIndex = in.read();
    727             int c = in.read();
    728             int protoIndex = in.read();
    729             IndexType indexType = OpcodeInfo.getIndexType(opcode);
    730             return new InvokePolymorphicRangeDecodedInstruction(
    731                     this, opcode, methodIndex, indexType, c, registerCount, protoIndex);
    732 
    733         }
    734 
    735         @Override
    736         public void encode(DecodedInstruction insn, CodeOutput out) {
    737             out.write(
    738                     codeUnit(insn.getOpcode(), insn.getRegisterCount()),
    739                     insn.getIndexUnit(),
    740                     insn.getCUnit(),
    741                     insn.getProtoIndex());
    742 
    743         }
    744     },
    745 
    746     FORMAT_PACKED_SWITCH_PAYLOAD() {
    747         @Override
    748         public DecodedInstruction decode(int opcodeUnit,
    749                 CodeInput in) throws EOFException {
    750             int baseAddress = in.baseAddressForCursor() - 1; // already read opcode
    751             int size = in.read();
    752             int firstKey = in.readInt();
    753             int[] targets = new int[size];
    754 
    755             for (int i = 0; i < size; i++) {
    756                 targets[i] = baseAddress + in.readInt();
    757             }
    758 
    759             return new PackedSwitchPayloadDecodedInstruction(
    760                     this, opcodeUnit, firstKey, targets);
    761         }
    762 
    763         @Override
    764         public void encode(DecodedInstruction insn, CodeOutput out) {
    765             PackedSwitchPayloadDecodedInstruction payload =
    766                 (PackedSwitchPayloadDecodedInstruction) insn;
    767             int[] targets = payload.getTargets();
    768             int baseAddress = out.baseAddressForCursor();
    769 
    770             out.write(payload.getOpcodeUnit());
    771             out.write(asUnsignedUnit(targets.length));
    772             out.writeInt(payload.getFirstKey());
    773 
    774             for (int target : targets) {
    775                 out.writeInt(target - baseAddress);
    776             }
    777         }
    778     },
    779 
    780     FORMAT_SPARSE_SWITCH_PAYLOAD() {
    781         @Override
    782         public DecodedInstruction decode(int opcodeUnit,
    783                 CodeInput in) throws EOFException {
    784             int baseAddress = in.baseAddressForCursor() - 1; // already read opcode
    785             int size = in.read();
    786             int[] keys = new int[size];
    787             int[] targets = new int[size];
    788 
    789             for (int i = 0; i < size; i++) {
    790                 keys[i] = in.readInt();
    791             }
    792 
    793             for (int i = 0; i < size; i++) {
    794                 targets[i] = baseAddress + in.readInt();
    795             }
    796 
    797             return new SparseSwitchPayloadDecodedInstruction(
    798                     this, opcodeUnit, keys, targets);
    799         }
    800 
    801         @Override
    802         public void encode(DecodedInstruction insn, CodeOutput out) {
    803             SparseSwitchPayloadDecodedInstruction payload =
    804                 (SparseSwitchPayloadDecodedInstruction) insn;
    805             int[] keys = payload.getKeys();
    806             int[] targets = payload.getTargets();
    807             int baseAddress = out.baseAddressForCursor();
    808 
    809             out.write(payload.getOpcodeUnit());
    810             out.write(asUnsignedUnit(targets.length));
    811 
    812             for (int key : keys) {
    813                 out.writeInt(key);
    814             }
    815 
    816             for (int target : targets) {
    817                 out.writeInt(target - baseAddress);
    818             }
    819         }
    820     },
    821 
    822     FORMAT_FILL_ARRAY_DATA_PAYLOAD() {
    823         @Override
    824         public DecodedInstruction decode(int opcodeUnit,
    825                 CodeInput in) throws EOFException {
    826             int elementWidth = in.read();
    827             int size = in.readInt();
    828 
    829             switch (elementWidth) {
    830                 case 1: {
    831                     byte[] array = new byte[size];
    832                     boolean even = true;
    833                     for (int i = 0, value = 0; i < size; i++, even = !even) {
    834                         if (even) {
    835                             value = in.read();
    836                         }
    837                         array[i] = (byte) (value & 0xff);
    838                         value >>= 8;
    839                     }
    840                     return new FillArrayDataPayloadDecodedInstruction(
    841                             this, opcodeUnit, array);
    842                 }
    843                 case 2: {
    844                     short[] array = new short[size];
    845                     for (int i = 0; i < size; i++) {
    846                         array[i] = (short) in.read();
    847                     }
    848                     return new FillArrayDataPayloadDecodedInstruction(
    849                             this, opcodeUnit, array);
    850                 }
    851                 case 4: {
    852                     int[] array = new int[size];
    853                     for (int i = 0; i < size; i++) {
    854                         array[i] = in.readInt();
    855                     }
    856                     return new FillArrayDataPayloadDecodedInstruction(
    857                             this, opcodeUnit, array);
    858                 }
    859                 case 8: {
    860                     long[] array = new long[size];
    861                     for (int i = 0; i < size; i++) {
    862                         array[i] = in.readLong();
    863                     }
    864                     return new FillArrayDataPayloadDecodedInstruction(
    865                             this, opcodeUnit, array);
    866                 }
    867                 default: // fall out
    868             }
    869 
    870             throw new DexException("bogus element_width: "
    871                     + Hex.u2(elementWidth));
    872         }
    873 
    874         @Override
    875         public void encode(DecodedInstruction insn, CodeOutput out) {
    876             FillArrayDataPayloadDecodedInstruction payload =
    877                 (FillArrayDataPayloadDecodedInstruction) insn;
    878             short elementWidth = payload.getElementWidthUnit();
    879             Object data = payload.getData();
    880 
    881             out.write(payload.getOpcodeUnit());
    882             out.write(elementWidth);
    883             out.writeInt(payload.getSize());
    884 
    885             switch (elementWidth) {
    886                 case 1: out.write((byte[]) data);  break;
    887                 case 2: out.write((short[]) data); break;
    888                 case 4: out.write((int[]) data);   break;
    889                 case 8: out.write((long[]) data);  break;
    890                 default: {
    891                     throw new DexException("bogus element_width: "
    892                             + Hex.u2(elementWidth));
    893                 }
    894             }
    895         }
    896     };
    897 
    898     /**
    899      * Decodes an instruction specified by the given opcode unit, reading
    900      * any required additional code units from the given input source.
    901      */
    902     public abstract DecodedInstruction decode(int opcodeUnit, CodeInput in)
    903         throws EOFException;
    904 
    905     /**
    906      * Encodes the given instruction.
    907      */
    908     public abstract void encode(DecodedInstruction insn, CodeOutput out);
    909 
    910     /**
    911      * Helper method that decodes any of the register-list formats.
    912      */
    913     private static DecodedInstruction decodeRegisterList(
    914             InstructionCodec format, int opcodeUnit, CodeInput in)
    915             throws EOFException {
    916         int opcode = byte0(opcodeUnit);
    917         int e = nibble2(opcodeUnit);
    918         int registerCount = nibble3(opcodeUnit);
    919         int index = in.read();
    920         int abcd = in.read();
    921         int a = nibble0(abcd);
    922         int b = nibble1(abcd);
    923         int c = nibble2(abcd);
    924         int d = nibble3(abcd);
    925         IndexType indexType = OpcodeInfo.getIndexType(opcode);
    926 
    927         // TODO: Having to switch like this is less than ideal.
    928         switch (registerCount) {
    929             case 0:
    930                 return new ZeroRegisterDecodedInstruction(
    931                         format, opcode, index, indexType,
    932                         0, 0L);
    933             case 1:
    934                 return new OneRegisterDecodedInstruction(
    935                         format, opcode, index, indexType,
    936                         0, 0L,
    937                         a);
    938             case 2:
    939                 return new TwoRegisterDecodedInstruction(
    940                         format, opcode, index, indexType,
    941                         0, 0L,
    942                         a, b);
    943             case 3:
    944                 return new ThreeRegisterDecodedInstruction(
    945                         format, opcode, index, indexType,
    946                         0, 0L,
    947                         a, b, c);
    948             case 4:
    949                 return new FourRegisterDecodedInstruction(
    950                         format, opcode, index, indexType,
    951                         0, 0L,
    952                         a, b, c, d);
    953             case 5:
    954                 return new FiveRegisterDecodedInstruction(
    955                         format, opcode, index, indexType,
    956                         0, 0L,
    957                         a, b, c, d, e);
    958             default: // fall out
    959         }
    960 
    961         throw new DexException("bogus registerCount: "
    962                 + Hex.uNibble(registerCount));
    963     }
    964 
    965     /**
    966      * Helper method that encodes any of the register-list formats.
    967      */
    968     private static void encodeRegisterList(DecodedInstruction insn,
    969             CodeOutput out) {
    970         out.write(codeUnit(insn.getOpcode(),
    971                         makeByte(insn.getE(), insn.getRegisterCount())),
    972                 insn.getIndexUnit(),
    973                 codeUnit(insn.getA(), insn.getB(), insn.getC(), insn.getD()));
    974     }
    975 
    976     /**
    977      * Helper method that decodes any of the three-unit register-range formats.
    978      */
    979     private static DecodedInstruction decodeRegisterRange(
    980             InstructionCodec format, int opcodeUnit, CodeInput in)
    981             throws EOFException {
    982         int opcode = byte0(opcodeUnit);
    983         int registerCount = byte1(opcodeUnit);
    984         int index = in.read();
    985         int a = in.read();
    986         IndexType indexType = OpcodeInfo.getIndexType(opcode);
    987         return new RegisterRangeDecodedInstruction(
    988                 format, opcode, index, indexType,
    989                 0, 0L,
    990                 a, registerCount);
    991     }
    992 
    993     /**
    994      * Helper method that encodes any of the three-unit register-range formats.
    995      */
    996     private static void encodeRegisterRange(DecodedInstruction insn,
    997             CodeOutput out) {
    998         out.write(codeUnit(insn.getOpcode(), insn.getRegisterCount()),
    999                 insn.getIndexUnit(),
   1000                 insn.getAUnit());
   1001     }
   1002 
   1003     private static short codeUnit(int lowByte, int highByte) {
   1004         if ((lowByte & ~0xff) != 0) {
   1005             throw new IllegalArgumentException("bogus lowByte");
   1006         }
   1007 
   1008         if ((highByte & ~0xff) != 0) {
   1009             throw new IllegalArgumentException("bogus highByte");
   1010         }
   1011 
   1012         return (short) (lowByte | (highByte << 8));
   1013     }
   1014 
   1015     private static short codeUnit(int nibble0, int nibble1, int nibble2,
   1016             int nibble3) {
   1017         if ((nibble0 & ~0xf) != 0) {
   1018             throw new IllegalArgumentException("bogus nibble0");
   1019         }
   1020 
   1021         if ((nibble1 & ~0xf) != 0) {
   1022             throw new IllegalArgumentException("bogus nibble1");
   1023         }
   1024 
   1025         if ((nibble2 & ~0xf) != 0) {
   1026             throw new IllegalArgumentException("bogus nibble2");
   1027         }
   1028 
   1029         if ((nibble3 & ~0xf) != 0) {
   1030             throw new IllegalArgumentException("bogus nibble3");
   1031         }
   1032 
   1033         return (short) (nibble0 | (nibble1 << 4)
   1034                 | (nibble2 << 8) | (nibble3 << 12));
   1035     }
   1036 
   1037     private static int makeByte(int lowNibble, int highNibble) {
   1038         if ((lowNibble & ~0xf) != 0) {
   1039             throw new IllegalArgumentException("bogus lowNibble");
   1040         }
   1041 
   1042         if ((highNibble & ~0xf) != 0) {
   1043             throw new IllegalArgumentException("bogus highNibble");
   1044         }
   1045 
   1046         return lowNibble | (highNibble << 4);
   1047     }
   1048 
   1049     private static short asUnsignedUnit(int value) {
   1050         if ((value & ~0xffff) != 0) {
   1051             throw new IllegalArgumentException("bogus unsigned code unit");
   1052         }
   1053 
   1054         return (short) value;
   1055     }
   1056 
   1057     private static short unit0(int value) {
   1058         return (short) value;
   1059     }
   1060 
   1061     private static short unit1(int value) {
   1062         return (short) (value >> 16);
   1063     }
   1064 
   1065     private static short unit0(long value) {
   1066         return (short) value;
   1067     }
   1068 
   1069     private static short unit1(long value) {
   1070         return (short) (value >> 16);
   1071     }
   1072 
   1073     private static short unit2(long value) {
   1074         return (short) (value >> 32);
   1075     }
   1076 
   1077     private static short unit3(long value) {
   1078         return (short) (value >> 48);
   1079     }
   1080 
   1081     private static int byte0(int value) {
   1082         return value & 0xff;
   1083     }
   1084 
   1085     private static int byte1(int value) {
   1086         return (value >> 8) & 0xff;
   1087     }
   1088 
   1089     private static int byte2(int value) {
   1090         return (value >> 16) & 0xff;
   1091     }
   1092 
   1093     private static int byte3(int value) {
   1094         return value >>> 24;
   1095     }
   1096 
   1097     private static int nibble0(int value) {
   1098         return value & 0xf;
   1099     }
   1100 
   1101     private static int nibble1(int value) {
   1102         return (value >> 4) & 0xf;
   1103     }
   1104 
   1105     private static int nibble2(int value) {
   1106         return (value >> 8) & 0xf;
   1107     }
   1108 
   1109     private static int nibble3(int value) {
   1110         return (value >> 12) & 0xf;
   1111     }
   1112 }
   1113