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