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