Home | History | Annotate | Download | only in file
      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.file;
     18 
     19 import com.android.dex.util.ExceptionWithContext;
     20 import com.android.dx.dex.code.DalvCode;
     21 import com.android.dx.dex.code.DalvInsnList;
     22 import com.android.dx.rop.cst.Constant;
     23 import com.android.dx.rop.cst.CstMethodRef;
     24 import com.android.dx.rop.type.StdTypeList;
     25 import com.android.dx.rop.type.Type;
     26 import com.android.dx.rop.type.TypeList;
     27 import com.android.dx.util.AnnotatedOutput;
     28 import com.android.dx.util.Hex;
     29 import java.io.PrintWriter;
     30 
     31 /**
     32  * Representation of all the parts needed for concrete methods in a
     33  * {@code dex} file.
     34  */
     35 public final class CodeItem extends OffsettedItem {
     36     /** file alignment of this class, in bytes */
     37     private static final int ALIGNMENT = 4;
     38 
     39     /** write size of the header of this class, in bytes */
     40     private static final int HEADER_SIZE = 16;
     41 
     42     /** {@code non-null;} method that this code implements */
     43     private final CstMethodRef ref;
     44 
     45     /** {@code non-null;} the bytecode instructions and associated data */
     46     private final DalvCode code;
     47 
     48     /** {@code null-ok;} the catches, if needed; set in {@link #addContents} */
     49     private CatchStructs catches;
     50 
     51     /** whether this instance is for a {@code static} method */
     52     private final boolean isStatic;
     53 
     54     /**
     55      * {@code non-null;} list of possibly-thrown exceptions; just used in
     56      * generating debugging output (listings)
     57      */
     58     private final TypeList throwsList;
     59 
     60     /**
     61      * {@code null-ok;} the debug info or {@code null} if there is none;
     62      * set in {@link #addContents}
     63      */
     64     private DebugInfoItem debugInfo;
     65 
     66     /**
     67      * Constructs an instance.
     68      *
     69      * @param ref {@code non-null;} method that this code implements
     70      * @param code {@code non-null;} the underlying code
     71      * @param isStatic whether this instance is for a {@code static}
     72      * method
     73      * @param throwsList {@code non-null;} list of possibly-thrown exceptions,
     74      * just used in generating debugging output (listings)
     75      */
     76     public CodeItem(CstMethodRef ref, DalvCode code, boolean isStatic,
     77             TypeList throwsList) {
     78         super(ALIGNMENT, -1);
     79 
     80         if (ref == null) {
     81             throw new NullPointerException("ref == null");
     82         }
     83 
     84         if (code == null) {
     85             throw new NullPointerException("code == null");
     86         }
     87 
     88         if (throwsList == null) {
     89             throw new NullPointerException("throwsList == null");
     90         }
     91 
     92         this.ref = ref;
     93         this.code = code;
     94         this.isStatic = isStatic;
     95         this.throwsList = throwsList;
     96         this.catches = null;
     97         this.debugInfo = null;
     98     }
     99 
    100     /** {@inheritDoc} */
    101     @Override
    102     public ItemType itemType() {
    103         return ItemType.TYPE_CODE_ITEM;
    104     }
    105 
    106     /** {@inheritDoc} */
    107     public void addContents(DexFile file) {
    108         MixedItemSection byteData = file.getByteData();
    109         TypeIdsSection typeIds = file.getTypeIds();
    110 
    111         if (code.hasPositions() || code.hasLocals()) {
    112             debugInfo = new DebugInfoItem(code, isStatic, ref);
    113             byteData.add(debugInfo);
    114         }
    115 
    116         if (code.hasAnyCatches()) {
    117             for (Type type : code.getCatchTypes()) {
    118                 typeIds.intern(type);
    119             }
    120             catches = new CatchStructs(code);
    121         }
    122 
    123         for (Constant c : code.getInsnConstants()) {
    124             file.internIfAppropriate(c);
    125         }
    126     }
    127 
    128     /** {@inheritDoc} */
    129     @Override
    130     public String toString() {
    131         return "CodeItem{" + toHuman() + "}";
    132     }
    133 
    134     /** {@inheritDoc} */
    135     @Override
    136     public String toHuman() {
    137         return ref.toHuman();
    138     }
    139 
    140     /**
    141      * Gets the reference to the method this instance implements.
    142      *
    143      * @return {@code non-null;} the method reference
    144      */
    145     public CstMethodRef getRef() {
    146         return ref;
    147     }
    148 
    149     /**
    150      * Does a human-friendly dump of this instance.
    151      *
    152      * @param out {@code non-null;} where to dump
    153      * @param prefix {@code non-null;} per-line prefix to use
    154      * @param verbose whether to be verbose with the output
    155      */
    156     public void debugPrint(PrintWriter out, String prefix, boolean verbose) {
    157         out.println(ref.toHuman() + ":");
    158 
    159         DalvInsnList insns = code.getInsns();
    160         out.println("regs: " + Hex.u2(getRegistersSize()) +
    161                 "; ins: " + Hex.u2(getInsSize()) + "; outs: " +
    162                 Hex.u2(getOutsSize()));
    163 
    164         insns.debugPrint(out, prefix, verbose);
    165 
    166         String prefix2 = prefix + "  ";
    167 
    168         if (catches != null) {
    169             out.print(prefix);
    170             out.println("catches");
    171             catches.debugPrint(out, prefix2);
    172         }
    173 
    174         if (debugInfo != null) {
    175             out.print(prefix);
    176             out.println("debug info");
    177             debugInfo.debugPrint(out, prefix2);
    178         }
    179     }
    180 
    181     /** {@inheritDoc} */
    182     @Override
    183     protected void place0(Section addedTo, int offset) {
    184         final DexFile file = addedTo.getFile();
    185         int catchesSize;
    186 
    187         /*
    188          * In order to get the catches and insns, all the code's
    189          * constants need to be assigned indices.
    190          */
    191         code.assignIndices(new DalvCode.AssignIndicesCallback() {
    192                 public int getIndex(Constant cst) {
    193                     IndexedItem item = file.findItemOrNull(cst);
    194                     if (item == null) {
    195                         return -1;
    196                     }
    197                     return item.getIndex();
    198                 }
    199             });
    200 
    201         if (catches != null) {
    202             catches.encode(file);
    203             catchesSize = catches.writeSize();
    204         } else {
    205             catchesSize = 0;
    206         }
    207 
    208         /*
    209          * The write size includes the header, two bytes per code
    210          * unit, post-code padding if necessary, and however much
    211          * space the catches need.
    212          */
    213 
    214         int insnsSize = code.getInsns().codeSize();
    215         if ((insnsSize & 1) != 0) {
    216             insnsSize++;
    217         }
    218 
    219         setWriteSize(HEADER_SIZE + (insnsSize * 2) + catchesSize);
    220     }
    221 
    222     /** {@inheritDoc} */
    223     @Override
    224     protected void writeTo0(DexFile file, AnnotatedOutput out) {
    225         boolean annotates = out.annotates();
    226         int regSz = getRegistersSize();
    227         int outsSz = getOutsSize();
    228         int insSz = getInsSize();
    229         int insnsSz = code.getInsns().codeSize();
    230         boolean needPadding = (insnsSz & 1) != 0;
    231         int triesSz = (catches == null) ? 0 : catches.triesSize();
    232         int debugOff = (debugInfo == null) ? 0 : debugInfo.getAbsoluteOffset();
    233 
    234         if (annotates) {
    235             out.annotate(0, offsetString() + ' ' + ref.toHuman());
    236             out.annotate(2, "  registers_size: " + Hex.u2(regSz));
    237             out.annotate(2, "  ins_size:       " + Hex.u2(insSz));
    238             out.annotate(2, "  outs_size:      " + Hex.u2(outsSz));
    239             out.annotate(2, "  tries_size:     " + Hex.u2(triesSz));
    240             out.annotate(4, "  debug_off:      " + Hex.u4(debugOff));
    241             out.annotate(4, "  insns_size:     " + Hex.u4(insnsSz));
    242 
    243             // This isn't represented directly here, but it is useful to see.
    244             int size = throwsList.size();
    245             if (size != 0) {
    246                 out.annotate(0, "  throws " + StdTypeList.toHuman(throwsList));
    247             }
    248         }
    249 
    250         out.writeShort(regSz);
    251         out.writeShort(insSz);
    252         out.writeShort(outsSz);
    253         out.writeShort(triesSz);
    254         out.writeInt(debugOff);
    255         out.writeInt(insnsSz);
    256 
    257         writeCodes(file, out);
    258 
    259         if (catches != null) {
    260             if (needPadding) {
    261                 if (annotates) {
    262                     out.annotate(2, "  padding: 0");
    263                 }
    264                 out.writeShort(0);
    265             }
    266 
    267             catches.writeTo(file, out);
    268         }
    269 
    270         if (annotates) {
    271             /*
    272              * These are pointed at in the code header (above), but it's less
    273              * distracting to expand on them at the bottom of the code.
    274              */
    275             if (debugInfo != null) {
    276                 out.annotate(0, "  debug info");
    277                 debugInfo.annotateTo(file, out, "    ");
    278             }
    279         }
    280     }
    281 
    282     /**
    283      * Helper for {@link #writeTo0} which writes out the actual bytecode.
    284      *
    285      * @param file {@code non-null;} file we are part of
    286      * @param out {@code non-null;} where to write to
    287      */
    288     private void writeCodes(DexFile file, AnnotatedOutput out) {
    289         DalvInsnList insns = code.getInsns();
    290 
    291         try {
    292             insns.writeTo(out);
    293         } catch (RuntimeException ex) {
    294             throw ExceptionWithContext.withContext(ex, "...while writing " +
    295                     "instructions for " + ref.toHuman());
    296         }
    297     }
    298 
    299     /**
    300      * Get the in registers count.
    301      *
    302      * @return the count
    303      */
    304     private int getInsSize() {
    305         return ref.getParameterWordCount(isStatic);
    306     }
    307 
    308     /**
    309      * Get the out registers count.
    310      *
    311      * @return the count
    312      */
    313     private int getOutsSize() {
    314         return code.getInsns().getOutsSize();
    315     }
    316 
    317     /**
    318      * Get the total registers count.
    319      *
    320      * @return the count
    321      */
    322     private int getRegistersSize() {
    323         return code.getInsns().getRegistersSize();
    324     }
    325 }
    326