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