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.dex.util.ExceptionWithContext;
     20 import com.android.dx.io.Opcodes;
     21 import com.android.dx.rop.cst.Constant;
     22 import com.android.dx.rop.cst.CstBaseMethodRef;
     23 import com.android.dx.rop.cst.CstProtoRef;
     24 import com.android.dx.util.AnnotatedOutput;
     25 import com.android.dx.util.FixedSizeList;
     26 import com.android.dx.util.IndentingWriter;
     27 import java.io.IOException;
     28 import java.io.OutputStream;
     29 import java.io.OutputStreamWriter;
     30 import java.io.Writer;
     31 import java.util.ArrayList;
     32 
     33 /**
     34  * List of {@link DalvInsn} instances.
     35  */
     36 public final class DalvInsnList extends FixedSizeList {
     37 
     38     /**
     39      * The amount of register space, in register units, required for this
     40      * code block. This may be greater than the largest observed register+
     41      * category because the method this code block exists in may
     42      * specify arguments that are unused by the method.
     43      */
     44     private final int regCount;
     45 
     46     /**
     47      * Constructs and returns an immutable instance whose elements are
     48      * identical to the ones in the given list, in the same order.
     49      *
     50      * @param list {@code non-null;} the list to use for elements
     51      * @param regCount count, in register-units, of the number of registers
     52      * this code block requires.
     53      * @return {@code non-null;} an appropriately-constructed instance of this
     54      * class
     55      */
     56     public static DalvInsnList makeImmutable(ArrayList<DalvInsn> list,
     57             int regCount) {
     58         int size = list.size();
     59         DalvInsnList result = new DalvInsnList(size, regCount);
     60 
     61         for (int i = 0; i < size; i++) {
     62             result.set(i, list.get(i));
     63         }
     64 
     65         result.setImmutable();
     66         return result;
     67     }
     68 
     69     /**
     70      * Constructs an instance. All indices initially contain {@code null}.
     71      *
     72      * @param size the size of the list
     73      * @param regCount count, in register-units, of the number of registers
     74      * this code block requires.
     75      */
     76     public DalvInsnList(int size, int regCount) {
     77         super(size);
     78         this.regCount = regCount;
     79     }
     80 
     81     /**
     82      * Gets the element at the given index. It is an error to call
     83      * this with the index for an element which was never set; if you
     84      * do that, this will throw {@code NullPointerException}.
     85      *
     86      * @param n {@code >= 0, < size();} which index
     87      * @return {@code non-null;} element at that index
     88      */
     89     public DalvInsn get(int n) {
     90         return (DalvInsn) get0(n);
     91     }
     92 
     93     /**
     94      * Sets the instruction at the given index.
     95      *
     96      * @param n {@code >= 0, < size();} which index
     97      * @param insn {@code non-null;} the instruction to set at {@code n}
     98      */
     99     public void set(int n, DalvInsn insn) {
    100         set0(n, insn);
    101     }
    102 
    103     /**
    104      * Gets the size of this instance, in 16-bit code units. This will only
    105      * return a meaningful result if the instructions in this instance all
    106      * have valid addresses.
    107      *
    108      * @return {@code >= 0;} the size
    109      */
    110     public int codeSize() {
    111         int sz = size();
    112 
    113         if (sz == 0) {
    114             return 0;
    115         }
    116 
    117         DalvInsn last = get(sz - 1);
    118         return last.getNextAddress();
    119     }
    120 
    121     /**
    122      * Writes all the instructions in this instance to the given output
    123      * destination.
    124      *
    125      * @param out {@code non-null;} where to write to
    126      */
    127     public void writeTo(AnnotatedOutput out) {
    128         int startCursor = out.getCursor();
    129         int sz = size();
    130 
    131         if (out.annotates()) {
    132             boolean verbose = out.isVerbose();
    133 
    134             for (int i = 0; i < sz; i++) {
    135                 DalvInsn insn = (DalvInsn) get0(i);
    136                 int codeBytes = insn.codeSize() * 2;
    137                 String s;
    138 
    139                 if ((codeBytes != 0) || verbose) {
    140                     s = insn.listingString("  ", out.getAnnotationWidth(),
    141                             true);
    142                 } else {
    143                     s = null;
    144                 }
    145 
    146                 if (s != null) {
    147                     out.annotate(codeBytes, s);
    148                 } else if (codeBytes != 0) {
    149                     out.annotate(codeBytes, "");
    150                 }
    151             }
    152         }
    153 
    154         for (int i = 0; i < sz; i++) {
    155             DalvInsn insn = (DalvInsn) get0(i);
    156             try {
    157                 insn.writeTo(out);
    158             } catch (RuntimeException ex) {
    159                 throw ExceptionWithContext.withContext(ex,
    160                         "...while writing " + insn);
    161             }
    162         }
    163 
    164         // Sanity check of the amount written.
    165         int written = (out.getCursor() - startCursor) / 2;
    166         if (written != codeSize()) {
    167             throw new RuntimeException("write length mismatch; expected " +
    168                     codeSize() + " but actually wrote " + written);
    169         }
    170     }
    171 
    172     /**
    173      * Gets the minimum required register count implied by this
    174      * instance.  This includes any unused parameters that could
    175      * potentially be at the top of the register space.
    176      * @return {@code >= 0;} the required registers size
    177      */
    178     public int getRegistersSize() {
    179         return regCount;
    180     }
    181 
    182     /**
    183      * Gets the size of the outgoing arguments area required by this
    184      * method. This is equal to the largest argument word count of any
    185      * method referred to by this instance.
    186      *
    187      * @return {@code >= 0;} the required outgoing arguments size
    188      */
    189     public int getOutsSize() {
    190         int sz = size();
    191         int result = 0;
    192 
    193         for (int i = 0; i < sz; i++) {
    194             DalvInsn insn = (DalvInsn) get0(i);
    195             int count = 0;
    196 
    197             if (insn instanceof CstInsn) {
    198                 Constant cst = ((CstInsn) insn).getConstant();
    199                 if (cst instanceof CstBaseMethodRef) {
    200                     CstBaseMethodRef methodRef = (CstBaseMethodRef) cst;
    201                     boolean isStatic =
    202                         (insn.getOpcode().getFamily() == Opcodes.INVOKE_STATIC);
    203                     count = methodRef.getParameterWordCount(isStatic);
    204                 }
    205             } else if (insn instanceof MultiCstInsn) {
    206                 if (insn.getOpcode().getFamily() != Opcodes.INVOKE_POLYMORPHIC) {
    207                     throw new RuntimeException("Expecting invoke-polymorphic");
    208                 }
    209                 MultiCstInsn mci = (MultiCstInsn) insn;
    210                 // Invoke-polymorphic has two constants: [0] method-ref and
    211                 // [1] call site prototype. The number of arguments is based
    212                 // on the call site prototype since these are the arguments
    213                 // presented. The method-ref is always MethodHandle.invoke(Object[])
    214                 // or MethodHandle.invokeExact(Object[]).
    215                 CstProtoRef proto = (CstProtoRef) mci.getConstant(1);
    216                 count = proto.getPrototype().getParameterTypes().getWordCount();
    217                 count = count + 1; // And one for receiver (method handle).
    218             } else {
    219                 continue;
    220             }
    221 
    222             if (count > result) {
    223                 result = count;
    224             }
    225         }
    226 
    227         return result;
    228     }
    229 
    230     /**
    231      * Does a human-friendly dump of this instance.
    232      *
    233      * @param out {@code non-null;} where to dump
    234      * @param prefix {@code non-null;} prefix to attach to each line of output
    235      * @param verbose whether to be verbose; verbose output includes
    236      * lines for zero-size instructions and explicit constant pool indices
    237      */
    238     public void debugPrint(Writer out, String prefix, boolean verbose) {
    239         IndentingWriter iw = new IndentingWriter(out, 0, prefix);
    240         int sz = size();
    241 
    242         try {
    243             for (int i = 0; i < sz; i++) {
    244                 DalvInsn insn = (DalvInsn) get0(i);
    245                 String s;
    246 
    247                 if ((insn.codeSize() != 0) || verbose) {
    248                     s = insn.listingString("", 0, verbose);
    249                 } else {
    250                     s = null;
    251                 }
    252 
    253                 if (s != null) {
    254                     iw.write(s);
    255                 }
    256             }
    257 
    258             iw.flush();
    259         } catch (IOException ex) {
    260             throw new RuntimeException(ex);
    261         }
    262     }
    263 
    264     /**
    265      * Does a human-friendly dump of this instance.
    266      *
    267      * @param out {@code non-null;} where to dump
    268      * @param prefix {@code non-null;} prefix to attach to each line of output
    269      * @param verbose whether to be verbose; verbose output includes
    270      * lines for zero-size instructions
    271      */
    272     public void debugPrint(OutputStream out, String prefix, boolean verbose) {
    273         Writer w = new OutputStreamWriter(out);
    274         debugPrint(w, prefix, verbose);
    275 
    276         try {
    277             w.flush();
    278         } catch (IOException ex) {
    279             throw new RuntimeException(ex);
    280         }
    281     }
    282 }
    283