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