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.code.SourcePosition;
     22 import com.android.dx.util.AnnotatedOutput;
     23 import com.android.dx.util.Hex;
     24 import com.android.dx.util.TwoColumnOutput;
     25 
     26 import java.util.BitSet;
     27 
     28 /**
     29  * Base class for Dalvik instructions.
     30  */
     31 public abstract class DalvInsn {
     32     /**
     33      * the actual output address of this instance, if known, or
     34      * {@code -1} if not
     35      */
     36     private int address;
     37 
     38     /** the opcode; one of the constants from {@link Dops} */
     39     private final Dop opcode;
     40 
     41     /** {@code non-null;} source position */
     42     private final SourcePosition position;
     43 
     44     /** {@code non-null;} list of register arguments */
     45     private final RegisterSpecList registers;
     46 
     47     /**
     48      * Makes a move instruction, appropriate and ideal for the given arguments.
     49      *
     50      * @param position {@code non-null;} source position information
     51      * @param dest {@code non-null;} destination register
     52      * @param src {@code non-null;} source register
     53      * @return {@code non-null;} an appropriately-constructed instance
     54      */
     55     public static SimpleInsn makeMove(SourcePosition position,
     56             RegisterSpec dest, RegisterSpec src) {
     57         boolean category1 = dest.getCategory() == 1;
     58         boolean reference = dest.getType().isReference();
     59         int destReg = dest.getReg();
     60         int srcReg = src.getReg();
     61         Dop opcode;
     62 
     63         if ((srcReg | destReg) < 16) {
     64             opcode = reference ? Dops.MOVE_OBJECT :
     65                 (category1 ? Dops.MOVE : Dops.MOVE_WIDE);
     66         } else if (destReg < 256) {
     67             opcode = reference ? Dops.MOVE_OBJECT_FROM16 :
     68                 (category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16);
     69         } else {
     70             opcode = reference ? Dops.MOVE_OBJECT_16 :
     71                 (category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16);
     72         }
     73 
     74         return new SimpleInsn(opcode, position,
     75                               RegisterSpecList.make(dest, src));
     76     }
     77 
     78     /**
     79      * Constructs an instance. The output address of this instance is initially
     80      * unknown ({@code -1}).
     81      *
     82      * <p><b>Note:</b> In the unlikely event that an instruction takes
     83      * absolutely no registers (e.g., a {@code nop} or a
     84      * no-argument no-result static method call), then the given
     85      * register list may be passed as {@link
     86      * RegisterSpecList#EMPTY}.</p>
     87      *
     88      * @param opcode the opcode; one of the constants from {@link Dops}
     89      * @param position {@code non-null;} source position
     90      * @param registers {@code non-null;} register list, including a
     91      * result register if appropriate (that is, registers may be either
     92      * ins and outs)
     93      */
     94     public DalvInsn(Dop opcode, SourcePosition position,
     95                     RegisterSpecList registers) {
     96         if (opcode == null) {
     97             throw new NullPointerException("opcode == null");
     98         }
     99 
    100         if (position == null) {
    101             throw new NullPointerException("position == null");
    102         }
    103 
    104         if (registers == null) {
    105             throw new NullPointerException("registers == null");
    106         }
    107 
    108         this.address = -1;
    109         this.opcode = opcode;
    110         this.position = position;
    111         this.registers = registers;
    112     }
    113 
    114     /** {@inheritDoc} */
    115     @Override
    116     public final String toString() {
    117         StringBuffer sb = new StringBuffer(100);
    118 
    119         sb.append(identifierString());
    120         sb.append(' ');
    121         sb.append(position);
    122 
    123         sb.append(": ");
    124         sb.append(opcode.getName());
    125 
    126         boolean needComma = false;
    127         if (registers.size() != 0) {
    128             sb.append(registers.toHuman(" ", ", ", null));
    129             needComma = true;
    130         }
    131 
    132         String extra = argString();
    133         if (extra != null) {
    134             if (needComma) {
    135                 sb.append(',');
    136             }
    137             sb.append(' ');
    138             sb.append(extra);
    139         }
    140 
    141         return sb.toString();
    142     }
    143 
    144     /**
    145      * Gets whether the address of this instruction is known.
    146      *
    147      * @see #getAddress
    148      * @see #setAddress
    149      */
    150     public final boolean hasAddress() {
    151         return (address >= 0);
    152     }
    153 
    154     /**
    155      * Gets the output address of this instruction, if it is known. This throws
    156      * a {@code RuntimeException} if it has not yet been set.
    157      *
    158      * @see #setAddress
    159      *
    160      * @return {@code >= 0;} the output address
    161      */
    162     public final int getAddress() {
    163         if (address < 0) {
    164             throw new RuntimeException("address not yet known");
    165         }
    166 
    167         return address;
    168     }
    169 
    170     /**
    171      * Gets the opcode.
    172      *
    173      * @return {@code non-null;} the opcode
    174      */
    175     public final Dop getOpcode() {
    176         return opcode;
    177     }
    178 
    179     /**
    180      * Gets the source position.
    181      *
    182      * @return {@code non-null;} the source position
    183      */
    184     public final SourcePosition getPosition() {
    185         return position;
    186     }
    187 
    188     /**
    189      * Gets the register list for this instruction.
    190      *
    191      * @return {@code non-null;} the registers
    192      */
    193     public final RegisterSpecList getRegisters() {
    194         return registers;
    195     }
    196 
    197     /**
    198      * Returns whether this instance's opcode uses a result register.
    199      * This method is a convenient shorthand for
    200      * {@code getOpcode().hasResult()}.
    201      *
    202      * @return {@code true} iff this opcode uses a result register
    203      */
    204     public final boolean hasResult() {
    205         return opcode.hasResult();
    206     }
    207 
    208     /**
    209      * Gets the minimum distinct registers required for this instruction.
    210      * Uses the given BitSet to determine which registers require
    211      * replacement, and ignores registers that are already compatible.
    212      * This assumes that the result (if any) can share registers with the
    213      * sources (if any), that each source register is unique, and that
    214      * (to be explicit here) category-2 values take up two consecutive
    215      * registers.
    216      *
    217      * @param compatRegs {@code non-null;} set of compatible registers
    218      * @return {@code >= 0;} the minimum distinct register requirement
    219      */
    220     public final int getMinimumRegisterRequirement(BitSet compatRegs) {
    221         boolean hasResult = hasResult();
    222         int regSz = registers.size();
    223         int resultRequirement = 0;
    224         int sourceRequirement = 0;
    225 
    226         if (hasResult && !compatRegs.get(0)) {
    227             resultRequirement = registers.get(0).getCategory();
    228         }
    229 
    230         for (int i = hasResult ? 1 : 0; i < regSz; i++) {
    231             if (!compatRegs.get(i)) {
    232                 sourceRequirement += registers.get(i).getCategory();
    233             }
    234         }
    235 
    236         return Math.max(sourceRequirement, resultRequirement);
    237     }
    238 
    239     /**
    240      * Gets the instruction that is equivalent to this one, except that
    241      * it uses sequential registers starting at {@code 0} (storing
    242      * the result, if any, in register {@code 0} as well).
    243      *
    244      * @return {@code non-null;} the replacement
    245      */
    246     public DalvInsn getLowRegVersion() {
    247         RegisterSpecList regs =
    248             registers.withExpandedRegisters(0, hasResult(), null);
    249         return withRegisters(regs);
    250     }
    251 
    252     /**
    253      * Gets the instruction prefix required, if any, to use in an expanded
    254      * version of this instance. Will not generate moves for registers
    255      * marked compatible to the format by the given BitSet.
    256      *
    257      * @see #expandedVersion
    258      *
    259      * @param compatRegs {@code non-null;} set of compatible registers
    260      * @return {@code null-ok;} the prefix, if any
    261      */
    262     public DalvInsn expandedPrefix(BitSet compatRegs) {
    263         RegisterSpecList regs = registers;
    264         boolean firstBit = compatRegs.get(0);
    265 
    266         if (hasResult()) compatRegs.set(0);
    267 
    268         regs = regs.subset(compatRegs);
    269 
    270         if (hasResult()) compatRegs.set(0, firstBit);
    271 
    272         if (regs.size() == 0) return null;
    273 
    274         return new HighRegisterPrefix(position, regs);
    275     }
    276 
    277     /**
    278      * Gets the instruction suffix required, if any, to use in an expanded
    279      * version of this instance. Will not generate a move for a register
    280      * marked compatible to the format by the given BitSet.
    281      *
    282      * @see #expandedVersion
    283      *
    284      * @param compatRegs {@code non-null;} set of compatible registers
    285      * @return {@code null-ok;} the suffix, if any
    286      */
    287     public DalvInsn expandedSuffix(BitSet compatRegs) {
    288         if (hasResult() && !compatRegs.get(0)) {
    289             RegisterSpec r = registers.get(0);
    290             return makeMove(position, r, r.withReg(0));
    291         } else {
    292             return null;
    293         }
    294     }
    295 
    296     /**
    297      * Gets the instruction that is equivalent to this one, except that
    298      * it replaces incompatible registers with sequential registers
    299      * starting at {@code 0} (storing the result, if any, in register
    300      * {@code 0} as well). The sequence of instructions from
    301      * {@link #expandedPrefix} and {@link #expandedSuffix} (if non-null)
    302      * surrounding the result of a call to this method are the expanded
    303      * transformation of this instance, and it is guaranteed that the
    304      * number of low registers used will be the number returned by
    305      * {@link #getMinimumRegisterRequirement}.
    306      *
    307      * @param compatRegs {@code non-null;} set of compatible registers
    308      * @return {@code non-null;} the replacement
    309      */
    310     public DalvInsn expandedVersion(BitSet compatRegs) {
    311         RegisterSpecList regs =
    312             registers.withExpandedRegisters(0, hasResult(), compatRegs);
    313         return withRegisters(regs);
    314     }
    315 
    316     /**
    317      * Gets the short identifier for this instruction. This is its
    318      * address, if assigned, or its identity hashcode if not.
    319      *
    320      * @return {@code non-null;} the identifier
    321      */
    322     public final String identifierString() {
    323         if (address != -1) {
    324             return String.format("%04x", address);
    325         }
    326 
    327         return Hex.u4(System.identityHashCode(this));
    328     }
    329 
    330     /**
    331      * Returns the string form of this instance suitable for inclusion in
    332      * a human-oriented listing dump. This method will return {@code null}
    333      * if this instance should not appear in a listing.
    334      *
    335      * @param prefix {@code non-null;} prefix before the address; each follow-on
    336      * line will be indented to match as well
    337      * @param width {@code >= 0;} the width of the output or {@code 0} for
    338      * unlimited width
    339      * @param noteIndices whether to include an explicit notation of
    340      * constant pool indices
    341      * @return {@code null-ok;} the string form or {@code null} if this
    342      * instance should not appear in a listing
    343      */
    344     public final String listingString(String prefix, int width,
    345             boolean noteIndices) {
    346         String insnPerSe = listingString0(noteIndices);
    347 
    348         if (insnPerSe == null) {
    349             return null;
    350         }
    351 
    352         String addr = prefix + identifierString() + ": ";
    353         int w1 = addr.length();
    354         int w2 = (width == 0) ? insnPerSe.length() : (width - w1);
    355 
    356         return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2);
    357     }
    358 
    359     /**
    360      * Sets the output address.
    361      *
    362      * @param address {@code >= 0;} the output address
    363      */
    364     public final void setAddress(int address) {
    365         if (address < 0) {
    366             throw new IllegalArgumentException("address < 0");
    367         }
    368 
    369         this.address = address;
    370     }
    371 
    372     /**
    373      * Gets the address immediately after this instance. This is only
    374      * calculable if this instance's address is known, and it is equal
    375      * to the address plus the length of the instruction format of this
    376      * instance's opcode.
    377      *
    378      * @return {@code >= 0;} the next address
    379      */
    380     public final int getNextAddress() {
    381         return getAddress() + codeSize();
    382     }
    383 
    384     /**
    385      * Gets the size of this instruction, in 16-bit code units.
    386      *
    387      * @return {@code >= 0;} the code size of this instruction
    388      */
    389     public abstract int codeSize();
    390 
    391     /**
    392      * Writes this instance to the given output. This method should
    393      * never annotate the output.
    394      *
    395      * @param out {@code non-null;} where to write to
    396      */
    397     public abstract void writeTo(AnnotatedOutput out);
    398 
    399     /**
    400      * Returns an instance that is just like this one, except that its
    401      * opcode is replaced by the one given, and its address is reset.
    402      *
    403      * @param opcode {@code non-null;} the new opcode
    404      * @return {@code non-null;} an appropriately-constructed instance
    405      */
    406     public abstract DalvInsn withOpcode(Dop opcode);
    407 
    408     /**
    409      * Returns an instance that is just like this one, except that all
    410      * register references have been offset by the given delta, and its
    411      * address is reset.
    412      *
    413      * @param delta the amount to offset register references by
    414      * @return {@code non-null;} an appropriately-constructed instance
    415      */
    416     public abstract DalvInsn withRegisterOffset(int delta);
    417 
    418     /**
    419      * Returns an instance that is just like this one, except that the
    420      * register list is replaced by the given one, and its address is
    421      * reset.
    422      *
    423      * @param registers {@code non-null;} new register list
    424      * @return {@code non-null;} an appropriately-constructed instance
    425      */
    426     public abstract DalvInsn withRegisters(RegisterSpecList registers);
    427 
    428     /**
    429      * Gets the string form for any arguments to this instance. Subclasses
    430      * must override this.
    431      *
    432      * @return {@code null-ok;} the string version of any arguments or
    433      * {@code null} if there are none
    434      */
    435     protected abstract String argString();
    436 
    437     /**
    438      * Helper for {@link #listingString}, which returns the string
    439      * form of this instance suitable for inclusion in a
    440      * human-oriented listing dump, not including the instruction
    441      * address and without respect for any output formatting. This
    442      * method should return {@code null} if this instance should
    443      * not appear in a listing.
    444      *
    445      * @param noteIndices whether to include an explicit notation of
    446      * constant pool indices
    447      * @return {@code null-ok;} the listing string
    448      */
    449     protected abstract String listingString0(boolean noteIndices);
    450 }
    451