Home | History | Annotate | Download | only in code
      1 /*
      2  * Copyright (C) 2007 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.dex.code;
     18 
     19 import com.android.dx.rop.code.RegisterSpec;
     20 import com.android.dx.rop.code.RegisterSpecList;
     21 import com.android.dx.rop.cst.Constant;
     22 import com.android.dx.rop.cst.CstInteger;
     23 import com.android.dx.rop.cst.CstKnownNull;
     24 import com.android.dx.rop.cst.CstLiteral64;
     25 import com.android.dx.rop.cst.CstLiteralBits;
     26 import com.android.dx.rop.cst.CstString;
     27 import com.android.dx.util.AnnotatedOutput;
     28 import com.android.dx.util.Hex;
     29 import java.util.BitSet;
     30 
     31 /**
     32  * Base class for all instruction format handlers. Instruction format
     33  * handlers know how to translate {@link DalvInsn} instances into
     34  * streams of code units, as well as human-oriented listing strings
     35  * representing such translations.
     36  */
     37 public abstract class InsnFormat {
     38     /**
     39      * flag to enable/disable the new extended opcode formats; meant as a
     40      * temporary measure until VM support for the salient opcodes is
     41      * added. TODO: Remove this declaration when the VM can deal.
     42      */
     43     public static final boolean ALLOW_EXTENDED_OPCODES = true;
     44 
     45     /**
     46      * Returns the string form, suitable for inclusion in a listing
     47      * dump, of the given instruction. The instruction must be of this
     48      * instance's format for proper operation.
     49      *
     50      * @param insn {@code non-null;} the instruction
     51      * @param noteIndices whether to include an explicit notation of
     52      * constant pool indices
     53      * @return {@code non-null;} the string form
     54      */
     55     public final String listingString(DalvInsn insn, boolean noteIndices) {
     56         String op = insn.getOpcode().getName();
     57         String arg = insnArgString(insn);
     58         String comment = insnCommentString(insn, noteIndices);
     59         StringBuilder sb = new StringBuilder(100);
     60 
     61         sb.append(op);
     62 
     63         if (arg.length() != 0) {
     64             sb.append(' ');
     65             sb.append(arg);
     66         }
     67 
     68         if (comment.length() != 0) {
     69             sb.append(" // ");
     70             sb.append(comment);
     71         }
     72 
     73         return sb.toString();
     74     }
     75 
     76     /**
     77      * Returns the string form of the arguments to the given instruction.
     78      * The instruction must be of this instance's format. If the instruction
     79      * has no arguments, then the result should be {@code ""}, not
     80      * {@code null}.
     81      *
     82      * <p>Subclasses must override this method.</p>
     83      *
     84      * @param insn {@code non-null;} the instruction
     85      * @return {@code non-null;} the string form
     86      */
     87     public abstract String insnArgString(DalvInsn insn);
     88 
     89     /**
     90      * Returns the associated comment for the given instruction, if any.
     91      * The instruction must be of this instance's format. If the instruction
     92      * has no comment, then the result should be {@code ""}, not
     93      * {@code null}.
     94      *
     95      * <p>Subclasses must override this method.</p>
     96      *
     97      * @param insn {@code non-null;} the instruction
     98      * @param noteIndices whether to include an explicit notation of
     99      * constant pool indices
    100      * @return {@code non-null;} the string form
    101      */
    102     public abstract String insnCommentString(DalvInsn insn,
    103             boolean noteIndices);
    104 
    105     /**
    106      * Gets the code size of instructions that use this format. The
    107      * size is a number of 16-bit code units, not bytes. This should
    108      * throw an exception if this format is of variable size.
    109      *
    110      * @return {@code >= 0;} the instruction length in 16-bit code units
    111      */
    112     public abstract int codeSize();
    113 
    114     /**
    115      * Returns whether or not the given instruction's arguments will
    116      * fit in this instance's format. This includes such things as
    117      * counting register arguments, checking register ranges, and
    118      * making sure that additional arguments are of appropriate types
    119      * and are in-range. If this format has a branch target but the
    120      * instruction's branch offset is unknown, this method will simply
    121      * not check the offset.
    122      *
    123      * <p>Subclasses must override this method.</p>
    124      *
    125      * @param insn {@code non-null;} the instruction to check
    126      * @return {@code true} iff the instruction's arguments are
    127      * appropriate for this instance, or {@code false} if not
    128      */
    129     public abstract boolean isCompatible(DalvInsn insn);
    130 
    131     /**
    132      * Returns which of a given instruction's registers will fit in
    133      * this instance's format.
    134      *
    135      * <p>The default implementation of this method always returns
    136      * an empty BitSet. Subclasses must override this method if they
    137      * have registers.</p>
    138      *
    139      * @param insn {@code non-null;} the instruction to check
    140      * @return {@code non-null;} a BitSet flagging registers in the
    141      * register list that are compatible to this format
    142      */
    143     public BitSet compatibleRegs(DalvInsn insn) {
    144         return new BitSet();
    145     }
    146 
    147     /**
    148      * Returns whether or not the given instruction's branch offset will
    149      * fit in this instance's format. This always returns {@code false}
    150      * for formats that don't include a branch offset.
    151      *
    152      * <p>The default implementation of this method always returns
    153      * {@code false}. Subclasses must override this method if they
    154      * include branch offsets.</p>
    155      *
    156      * @param insn {@code non-null;} the instruction to check
    157      * @return {@code true} iff the instruction's branch offset is
    158      * appropriate for this instance, or {@code false} if not
    159      */
    160     public boolean branchFits(TargetInsn insn) {
    161         return false;
    162     }
    163 
    164     /**
    165      * Writes the code units for the given instruction to the given
    166      * output destination. The instruction must be of this instance's format.
    167      *
    168      * <p>Subclasses must override this method.</p>
    169      *
    170      * @param out {@code non-null;} the output destination to write to
    171      * @param insn {@code non-null;} the instruction to write
    172      */
    173     public abstract void writeTo(AnnotatedOutput out, DalvInsn insn);
    174 
    175     /**
    176      * Helper method to return a register list string.
    177      *
    178      * @param list {@code non-null;} the list of registers
    179      * @return {@code non-null;} the string form
    180      */
    181     protected static String regListString(RegisterSpecList list) {
    182         int sz = list.size();
    183         StringBuffer sb = new StringBuffer(sz * 5 + 2);
    184 
    185         sb.append('{');
    186 
    187         for (int i = 0; i < sz; i++) {
    188             if (i != 0) {
    189                 sb.append(", ");
    190             }
    191             sb.append(list.get(i).regString());
    192         }
    193 
    194         sb.append('}');
    195 
    196         return sb.toString();
    197     }
    198 
    199     /**
    200      * Helper method to return a register range string.
    201      *
    202      * @param list {@code non-null;} the list of registers (which must be
    203      * sequential)
    204      * @return {@code non-null;} the string form
    205      */
    206     protected static String regRangeString(RegisterSpecList list) {
    207         int size = list.size();
    208         StringBuilder sb = new StringBuilder(30);
    209 
    210         sb.append("{");
    211 
    212         switch (size) {
    213             case 0: {
    214                 // Nothing to do.
    215                 break;
    216             }
    217             case 1: {
    218                 sb.append(list.get(0).regString());
    219                 break;
    220             }
    221             default: {
    222                 RegisterSpec lastReg = list.get(size - 1);
    223                 if (lastReg.getCategory() == 2) {
    224                     /*
    225                      * Add one to properly represent a list-final
    226                      * category-2 register.
    227                      */
    228                     lastReg = lastReg.withOffset(1);
    229                 }
    230 
    231                 sb.append(list.get(0).regString());
    232                 sb.append("..");
    233                 sb.append(lastReg.regString());
    234             }
    235         }
    236 
    237         sb.append("}");
    238 
    239         return sb.toString();
    240     }
    241 
    242     /**
    243      * Helper method to return a literal bits argument string.
    244      *
    245      * @param value the value
    246      * @return {@code non-null;} the string form
    247      */
    248     protected static String literalBitsString(CstLiteralBits value) {
    249         StringBuffer sb = new StringBuffer(100);
    250 
    251         sb.append('#');
    252 
    253         if (value instanceof CstKnownNull) {
    254             sb.append("null");
    255         } else {
    256             sb.append(value.typeName());
    257             sb.append(' ');
    258             sb.append(value.toHuman());
    259         }
    260 
    261         return sb.toString();
    262     }
    263 
    264     /**
    265      * Helper method to return a literal bits comment string.
    266      *
    267      * @param value the value
    268      * @param width the width of the constant, in bits (used for displaying
    269      * the uninterpreted bits; one of: {@code 4 8 16 32 64}
    270      * @return {@code non-null;} the comment
    271      */
    272     protected static String literalBitsComment(CstLiteralBits value,
    273             int width) {
    274         StringBuffer sb = new StringBuffer(20);
    275 
    276         sb.append("#");
    277 
    278         long bits;
    279 
    280         if (value instanceof CstLiteral64) {
    281             bits = ((CstLiteral64) value).getLongBits();
    282         } else {
    283             bits = value.getIntBits();
    284         }
    285 
    286         switch (width) {
    287             case 4:  sb.append(Hex.uNibble((int) bits)); break;
    288             case 8:  sb.append(Hex.u1((int) bits));      break;
    289             case 16: sb.append(Hex.u2((int) bits));      break;
    290             case 32: sb.append(Hex.u4((int) bits));      break;
    291             case 64: sb.append(Hex.u8(bits));            break;
    292             default: {
    293                 throw new RuntimeException("shouldn't happen");
    294             }
    295         }
    296 
    297         return sb.toString();
    298     }
    299 
    300     /**
    301      * Helper method to return a branch address string.
    302      *
    303      * @param insn {@code non-null;} the instruction in question
    304      * @return {@code non-null;} the string form of the instruction's
    305      * branch target
    306      */
    307     protected static String branchString(DalvInsn insn) {
    308         TargetInsn ti = (TargetInsn) insn;
    309         int address = ti.getTargetAddress();
    310 
    311         return (address == (char) address) ? Hex.u2(address) : Hex.u4(address);
    312     }
    313 
    314     /**
    315      * Helper method to return the comment for a branch.
    316      *
    317      * @param insn {@code non-null;} the instruction in question
    318      * @return {@code non-null;} the comment
    319      */
    320     protected static String branchComment(DalvInsn insn) {
    321         TargetInsn ti = (TargetInsn) insn;
    322         int offset = ti.getTargetOffset();
    323 
    324         return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset);
    325     }
    326 
    327     /**
    328      * Helper method to determine if a signed int value fits in a nibble.
    329      *
    330      * @param value the value in question
    331      * @return {@code true} iff it's in the range -8..+7
    332      */
    333     protected static boolean signedFitsInNibble(int value) {
    334         return (value >= -8) && (value <= 7);
    335     }
    336 
    337     /**
    338      * Helper method to determine if an unsigned int value fits in a nibble.
    339      *
    340      * @param value the value in question
    341      * @return {@code true} iff it's in the range 0..0xf
    342      */
    343     protected static boolean unsignedFitsInNibble(int value) {
    344         return value == (value & 0xf);
    345     }
    346 
    347     /**
    348      * Helper method to determine if a signed int value fits in a byte.
    349      *
    350      * @param value the value in question
    351      * @return {@code true} iff it's in the range -0x80..+0x7f
    352      */
    353     protected static boolean signedFitsInByte(int value) {
    354         return (byte) value == value;
    355     }
    356 
    357     /**
    358      * Helper method to determine if an unsigned int value fits in a byte.
    359      *
    360      * @param value the value in question
    361      * @return {@code true} iff it's in the range 0..0xff
    362      */
    363     protected static boolean unsignedFitsInByte(int value) {
    364         return value == (value & 0xff);
    365     }
    366 
    367     /**
    368      * Helper method to determine if a signed int value fits in a short.
    369      *
    370      * @param value the value in question
    371      * @return {@code true} iff it's in the range -0x8000..+0x7fff
    372      */
    373     protected static boolean signedFitsInShort(int value) {
    374         return (short) value == value;
    375     }
    376 
    377     /**
    378      * Helper method to determine if an unsigned int value fits in a short.
    379      *
    380      * @param value the value in question
    381      * @return {@code true} iff it's in the range 0..0xffff
    382      */
    383     protected static boolean unsignedFitsInShort(int value) {
    384         return value == (value & 0xffff);
    385     }
    386 
    387     /**
    388      * Helper method to determine if a list of registers are sequential,
    389      * including degenerate cases for empty or single-element lists.
    390      *
    391      * @param list {@code non-null;} the list of registers
    392      * @return {@code true} iff the list is sequentially ordered
    393      */
    394     protected static boolean isRegListSequential(RegisterSpecList list) {
    395         int sz = list.size();
    396 
    397         if (sz < 2) {
    398             return true;
    399         }
    400 
    401         int first = list.get(0).getReg();
    402         int next = first;
    403 
    404         for (int i = 0; i < sz; i++) {
    405             RegisterSpec one = list.get(i);
    406             if (one.getReg() != next) {
    407                 return false;
    408             }
    409             next += one.getCategory();
    410         }
    411 
    412         return true;
    413     }
    414 
    415     /**
    416      * Helper method to extract the callout-argument index from an
    417      * appropriate instruction.
    418      *
    419      * @param insn {@code non-null;} the instruction
    420      * @return {@code >= 0;} the callout argument index
    421      */
    422     protected static int argIndex(DalvInsn insn) {
    423         int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue();
    424 
    425         if (arg < 0) {
    426             throw new IllegalArgumentException("bogus insn");
    427         }
    428 
    429         return arg;
    430     }
    431 
    432     /**
    433      * Helper method to combine an opcode and a second byte of data into
    434      * the appropriate form for emitting into a code buffer.
    435      *
    436      * @param insn {@code non-null;} the instruction containing the opcode
    437      * @param arg {@code 0..255;} arbitrary other byte value
    438      * @return combined value
    439      */
    440     protected static short opcodeUnit(DalvInsn insn, int arg) {
    441         if ((arg & 0xff) != arg) {
    442             throw new IllegalArgumentException("arg out of range 0..255");
    443         }
    444 
    445         int opcode = insn.getOpcode().getOpcode();
    446 
    447         if ((opcode & 0xff) != opcode) {
    448             throw new IllegalArgumentException("opcode out of range 0..255");
    449         }
    450 
    451         return (short) (opcode | (arg << 8));
    452     }
    453 
    454     /**
    455      * Helper method to get an extended (16-bit) opcode out of an
    456      * instruction, returning it as a code unit. The opcode
    457      * <i>must</i> be an extended opcode.
    458      *
    459      * @param insn {@code non-null;} the instruction containing the
    460      * extended opcode
    461      * @return the opcode as a code unit
    462      */
    463     protected static short opcodeUnit(DalvInsn insn) {
    464         int opcode = insn.getOpcode().getOpcode();
    465 
    466         if ((opcode < 0x100) || (opcode > 0xffff)) {
    467             throw new IllegalArgumentException("opcode out of range 0..65535");
    468         }
    469 
    470         return (short) opcode;
    471     }
    472 
    473     /**
    474      * Helper method to combine two bytes into a code unit.
    475      *
    476      * @param low {@code 0..255;} low byte
    477      * @param high {@code 0..255;} high byte
    478      * @return combined value
    479      */
    480     protected static short codeUnit(int low, int high) {
    481         if ((low & 0xff) != low) {
    482             throw new IllegalArgumentException("low out of range 0..255");
    483         }
    484 
    485         if ((high & 0xff) != high) {
    486             throw new IllegalArgumentException("high out of range 0..255");
    487         }
    488 
    489         return (short) (low | (high << 8));
    490     }
    491 
    492     /**
    493      * Helper method to combine four nibbles into a code unit.
    494      *
    495      * @param n0 {@code 0..15;} low nibble
    496      * @param n1 {@code 0..15;} medium-low nibble
    497      * @param n2 {@code 0..15;} medium-high nibble
    498      * @param n3 {@code 0..15;} high nibble
    499      * @return combined value
    500      */
    501     protected static short codeUnit(int n0, int n1, int n2, int n3) {
    502         if ((n0 & 0xf) != n0) {
    503             throw new IllegalArgumentException("n0 out of range 0..15");
    504         }
    505 
    506         if ((n1 & 0xf) != n1) {
    507             throw new IllegalArgumentException("n1 out of range 0..15");
    508         }
    509 
    510         if ((n2 & 0xf) != n2) {
    511             throw new IllegalArgumentException("n2 out of range 0..15");
    512         }
    513 
    514         if ((n3 & 0xf) != n3) {
    515             throw new IllegalArgumentException("n3 out of range 0..15");
    516         }
    517 
    518         return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12));
    519     }
    520 
    521     /**
    522      * Helper method to combine two nibbles into a byte.
    523      *
    524      * @param low {@code 0..15;} low nibble
    525      * @param high {@code 0..15;} high nibble
    526      * @return {@code 0..255;} combined value
    527      */
    528     protected static int makeByte(int low, int high) {
    529         if ((low & 0xf) != low) {
    530             throw new IllegalArgumentException("low out of range 0..15");
    531         }
    532 
    533         if ((high & 0xf) != high) {
    534             throw new IllegalArgumentException("high out of range 0..15");
    535         }
    536 
    537         return low | (high << 4);
    538     }
    539 
    540     /**
    541      * Writes one code unit to the given output destination.
    542      *
    543      * @param out {@code non-null;} where to write to
    544      * @param c0 code unit to write
    545      */
    546     protected static void write(AnnotatedOutput out, short c0) {
    547         out.writeShort(c0);
    548     }
    549 
    550     /**
    551      * Writes two code units to the given output destination.
    552      *
    553      * @param out {@code non-null;} where to write to
    554      * @param c0 code unit to write
    555      * @param c1 code unit to write
    556      */
    557     protected static void write(AnnotatedOutput out, short c0, short c1) {
    558         out.writeShort(c0);
    559         out.writeShort(c1);
    560     }
    561 
    562     /**
    563      * Writes three code units to the given output destination.
    564      *
    565      * @param out {@code non-null;} where to write to
    566      * @param c0 code unit to write
    567      * @param c1 code unit to write
    568      * @param c2 code unit to write
    569      */
    570     protected static void write(AnnotatedOutput out, short c0, short c1,
    571             short c2) {
    572         out.writeShort(c0);
    573         out.writeShort(c1);
    574         out.writeShort(c2);
    575     }
    576 
    577     /**
    578      * Writes four code units to the given output destination.
    579      *
    580      * @param out {@code non-null;} where to write to
    581      * @param c0 code unit to write
    582      * @param c1 code unit to write
    583      * @param c2 code unit to write
    584      * @param c3 code unit to write
    585      */
    586     protected static void write(AnnotatedOutput out, short c0, short c1,
    587             short c2, short c3) {
    588         out.writeShort(c0);
    589         out.writeShort(c1);
    590         out.writeShort(c2);
    591         out.writeShort(c3);
    592     }
    593 
    594     /**
    595      * Writes five code units to the given output destination.
    596      *
    597      * @param out {@code non-null;} where to write to
    598      * @param c0 code unit to write
    599      * @param c1 code unit to write
    600      * @param c2 code unit to write
    601      * @param c3 code unit to write
    602      * @param c4 code unit to write
    603      */
    604     protected static void write(AnnotatedOutput out, short c0, short c1,
    605             short c2, short c3, short c4) {
    606         out.writeShort(c0);
    607         out.writeShort(c1);
    608         out.writeShort(c2);
    609         out.writeShort(c3);
    610         out.writeShort(c4);
    611     }
    612 
    613     /**
    614      * Writes three code units to the given output destination, where the
    615      * second and third are represented as single <code>int</code> and emitted
    616      * in little-endian order.
    617      *
    618      * @param out {@code non-null;} where to write to
    619      * @param c0 code unit to write
    620      * @param c1c2 code unit pair to write
    621      */
    622     protected static void write(AnnotatedOutput out, short c0, int c1c2) {
    623         write(out, c0, (short) c1c2, (short) (c1c2 >> 16));
    624     }
    625 
    626     /**
    627      * Writes four code units to the given output destination, where the
    628      * second and third are represented as single <code>int</code> and emitted
    629      * in little-endian order.
    630      *
    631      * @param out {@code non-null;} where to write to
    632      * @param c0 code unit to write
    633      * @param c1c2 code unit pair to write
    634      * @param c3 code unit to write
    635      */
    636     protected static void write(AnnotatedOutput out, short c0, int c1c2,
    637             short c3) {
    638         write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3);
    639     }
    640 
    641     /**
    642      * Writes five code units to the given output destination, where the
    643      * second and third are represented as single <code>int</code> and emitted
    644      * in little-endian order.
    645      *
    646      * @param out {@code non-null;} where to write to
    647      * @param c0 code unit to write
    648      * @param c1c2 code unit pair to write
    649      * @param c3 code unit to write
    650      * @param c4 code unit to write
    651      */
    652     protected static void write(AnnotatedOutput out, short c0, int c1c2,
    653             short c3, short c4) {
    654         write(out, c0, (short) c1c2, (short) (c1c2 >> 16), c3, c4);
    655     }
    656 
    657     /**
    658      * Writes five code units to the given output destination, where the
    659      * second through fifth are represented as single <code>long</code>
    660      * and emitted in little-endian order.
    661      *
    662      * @param out {@code non-null;} where to write to
    663      * @param c0 code unit to write
    664      * @param c1c2c3c4 code unit quad to write
    665      */
    666     protected static void write(AnnotatedOutput out, short c0, long c1c2c3c4) {
    667         write(out, c0, (short) c1c2c3c4, (short) (c1c2c3c4 >> 16),
    668                 (short) (c1c2c3c4 >> 32), (short) (c1c2c3c4 >> 48));
    669     }
    670 }
    671