Home | History | Annotate | Download | only in file
      1 /*
      2  * Copyright (C) 2008 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.dexgen.dex.file;
     18 
     19 import com.android.dexgen.rop.cst.Constant;
     20 import com.android.dexgen.rop.cst.CstArray;
     21 import com.android.dexgen.rop.cst.CstLiteralBits;
     22 import com.android.dexgen.rop.cst.CstType;
     23 import com.android.dexgen.rop.cst.Zeroes;
     24 import com.android.dexgen.util.AnnotatedOutput;
     25 import com.android.dexgen.util.ByteArrayAnnotatedOutput;
     26 import com.android.dexgen.util.Hex;
     27 import com.android.dexgen.util.Writers;
     28 
     29 import java.io.PrintWriter;
     30 import java.io.Writer;
     31 import java.util.ArrayList;
     32 import java.util.Arrays;
     33 import java.util.Collections;
     34 import java.util.List;
     35 import java.util.HashMap;
     36 
     37 /**
     38  * Representation of all the parts of a Dalvik class that are generally
     39  * "inflated" into an in-memory representation at runtime. Instances of
     40  * this class are represented in a compact streamable form in a
     41  * {@code dex} file, as opposed to a random-access form.
     42  */
     43 public final class ClassDataItem extends OffsettedItem {
     44     /** {@code non-null;} what class this data is for, just for listing generation */
     45     private final CstType thisClass;
     46 
     47     /** {@code non-null;} list of static fields */
     48     private final ArrayList<EncodedField> staticFields;
     49 
     50     /** {@code non-null;} list of initial values for static fields */
     51     private final HashMap<EncodedField, Constant> staticValues;
     52 
     53     /** {@code non-null;} list of instance fields */
     54     private final ArrayList<EncodedField> instanceFields;
     55 
     56     /** {@code non-null;} list of direct methods */
     57     private final ArrayList<EncodedMethod> directMethods;
     58 
     59     /** {@code non-null;} list of virtual methods */
     60     private final ArrayList<EncodedMethod> virtualMethods;
     61 
     62     /** {@code null-ok;} static initializer list; set in {@link #addContents} */
     63     private CstArray staticValuesConstant;
     64 
     65     /**
     66      * {@code null-ok;} encoded form, ready for writing to a file; set during
     67      * {@link #place0}
     68      */
     69     private byte[] encodedForm;
     70 
     71     /**
     72      * Constructs an instance. Its sets of members are initially
     73      * empty.
     74      *
     75      * @param thisClass {@code non-null;} what class this data is for, just
     76      * for listing generation
     77      */
     78     public ClassDataItem(CstType thisClass) {
     79         super(1, -1);
     80 
     81         if (thisClass == null) {
     82             throw new NullPointerException("thisClass == null");
     83         }
     84 
     85         this.thisClass = thisClass;
     86         this.staticFields = new ArrayList<EncodedField>(20);
     87         this.staticValues = new HashMap<EncodedField, Constant>(40);
     88         this.instanceFields = new ArrayList<EncodedField>(20);
     89         this.directMethods = new ArrayList<EncodedMethod>(20);
     90         this.virtualMethods = new ArrayList<EncodedMethod>(20);
     91         this.staticValuesConstant = null;
     92     }
     93 
     94     /** {@inheritDoc} */
     95     @Override
     96     public ItemType itemType() {
     97         return ItemType.TYPE_CLASS_DATA_ITEM;
     98     }
     99 
    100     /** {@inheritDoc} */
    101     @Override
    102     public String toHuman() {
    103         return toString();
    104     }
    105 
    106     /**
    107      * Returns whether this instance is empty.
    108      *
    109      * @return {@code true} if this instance is empty or
    110      * {@code false} if at least one element has been added to it
    111      */
    112     public boolean isEmpty() {
    113         return staticFields.isEmpty() && instanceFields.isEmpty()
    114             && directMethods.isEmpty() && virtualMethods.isEmpty();
    115     }
    116 
    117     /**
    118      * Adds a static field.
    119      *
    120      * @param field {@code non-null;} the field to add
    121      * @param value {@code null-ok;} initial value for the field, if any
    122      */
    123     public void addStaticField(EncodedField field, Constant value) {
    124         if (field == null) {
    125             throw new NullPointerException("field == null");
    126         }
    127 
    128         if (staticValuesConstant != null) {
    129             throw new UnsupportedOperationException(
    130                     "static fields already sorted");
    131         }
    132 
    133         staticFields.add(field);
    134         staticValues.put(field, value);
    135     }
    136 
    137     /**
    138      * Adds an instance field.
    139      *
    140      * @param field {@code non-null;} the field to add
    141      */
    142     public void addInstanceField(EncodedField field) {
    143         if (field == null) {
    144             throw new NullPointerException("field == null");
    145         }
    146 
    147         instanceFields.add(field);
    148     }
    149 
    150     /**
    151      * Adds a direct ({@code static} and/or {@code private}) method.
    152      *
    153      * @param method {@code non-null;} the method to add
    154      */
    155     public void addDirectMethod(EncodedMethod method) {
    156         if (method == null) {
    157             throw new NullPointerException("method == null");
    158         }
    159 
    160         directMethods.add(method);
    161     }
    162 
    163     /**
    164      * Adds a virtual method.
    165      *
    166      * @param method {@code non-null;} the method to add
    167      */
    168     public void addVirtualMethod(EncodedMethod method) {
    169         if (method == null) {
    170             throw new NullPointerException("method == null");
    171         }
    172 
    173         virtualMethods.add(method);
    174     }
    175 
    176     /**
    177      * Gets all the methods in this class. The returned list is not linked
    178      * in any way to the underlying lists contained in this instance, but
    179      * the objects contained in the list are shared.
    180      *
    181      * @return {@code non-null;} list of all methods
    182      */
    183     public ArrayList<EncodedMethod> getMethods() {
    184         int sz = directMethods.size() + virtualMethods.size();
    185         ArrayList<EncodedMethod> result = new ArrayList<EncodedMethod>(sz);
    186 
    187         result.addAll(directMethods);
    188         result.addAll(virtualMethods);
    189 
    190         return result;
    191     }
    192 
    193 
    194     /**
    195      * Prints out the contents of this instance, in a debugging-friendly
    196      * way.
    197      *
    198      * @param out {@code non-null;} where to output to
    199      * @param verbose whether to be verbose with the output
    200      */
    201     public void debugPrint(Writer out, boolean verbose) {
    202         PrintWriter pw = Writers.printWriterFor(out);
    203 
    204         int sz = staticFields.size();
    205         for (int i = 0; i < sz; i++) {
    206             pw.println("  sfields[" + i + "]: " + staticFields.get(i));
    207         }
    208 
    209         sz = instanceFields.size();
    210         for (int i = 0; i < sz; i++) {
    211             pw.println("  ifields[" + i + "]: " + instanceFields.get(i));
    212         }
    213 
    214         sz = directMethods.size();
    215         for (int i = 0; i < sz; i++) {
    216             pw.println("  dmeths[" + i + "]:");
    217             directMethods.get(i).debugPrint(pw, verbose);
    218         }
    219 
    220         sz = virtualMethods.size();
    221         for (int i = 0; i < sz; i++) {
    222             pw.println("  vmeths[" + i + "]:");
    223             virtualMethods.get(i).debugPrint(pw, verbose);
    224         }
    225     }
    226 
    227     /** {@inheritDoc} */
    228     @Override
    229     public void addContents(DexFile file) {
    230         if (!staticFields.isEmpty()) {
    231             getStaticValuesConstant(); // Force the fields to be sorted.
    232             for (EncodedField field : staticFields) {
    233                 field.addContents(file);
    234             }
    235         }
    236 
    237         if (!instanceFields.isEmpty()) {
    238             Collections.sort(instanceFields);
    239             for (EncodedField field : instanceFields) {
    240                 field.addContents(file);
    241             }
    242         }
    243 
    244         if (!directMethods.isEmpty()) {
    245             Collections.sort(directMethods);
    246             for (EncodedMethod method : directMethods) {
    247                 method.addContents(file);
    248             }
    249         }
    250 
    251         if (!virtualMethods.isEmpty()) {
    252             Collections.sort(virtualMethods);
    253             for (EncodedMethod method : virtualMethods) {
    254                 method.addContents(file);
    255             }
    256         }
    257     }
    258 
    259     /**
    260      * Gets a {@link CstArray} corresponding to {@link #staticValues} if
    261      * it contains any non-zero non-{@code null} values.
    262      *
    263      * @return {@code null-ok;} the corresponding constant or {@code null} if
    264      * there are no values to encode
    265      */
    266     public CstArray getStaticValuesConstant() {
    267         if ((staticValuesConstant == null) && (staticFields.size() != 0)) {
    268             staticValuesConstant = makeStaticValuesConstant();
    269         }
    270 
    271         return staticValuesConstant;
    272     }
    273 
    274     /**
    275      * Gets a {@link CstArray} corresponding to {@link #staticValues} if
    276      * it contains any non-zero non-{@code null} values.
    277      *
    278      * @return {@code null-ok;} the corresponding constant or {@code null} if
    279      * there are no values to encode
    280      */
    281     private CstArray makeStaticValuesConstant() {
    282         // First sort the statics into their final order.
    283         Collections.sort(staticFields);
    284 
    285         /*
    286          * Get the size of staticValues minus any trailing zeros/nulls (both
    287          * nulls per se as well as instances of CstKnownNull).
    288          */
    289 
    290         int size = staticFields.size();
    291         while (size > 0) {
    292             EncodedField field = staticFields.get(size - 1);
    293             Constant cst = staticValues.get(field);
    294             if (cst instanceof CstLiteralBits) {
    295                 // Note: CstKnownNull extends CstLiteralBits.
    296                 if (((CstLiteralBits) cst).getLongBits() != 0) {
    297                     break;
    298                 }
    299             } else if (cst != null) {
    300                 break;
    301             }
    302             size--;
    303         }
    304 
    305         if (size == 0) {
    306             return null;
    307         }
    308 
    309         // There is something worth encoding, so build up a result.
    310 
    311         CstArray.List list = new CstArray.List(size);
    312         for (int i = 0; i < size; i++) {
    313             EncodedField field = staticFields.get(i);
    314             Constant cst = staticValues.get(field);
    315             if (cst == null) {
    316                 cst = Zeroes.zeroFor(field.getRef().getType());
    317             }
    318             list.set(i, cst);
    319         }
    320         list.setImmutable();
    321 
    322         return new CstArray(list);
    323     }
    324 
    325     /** {@inheritDoc} */
    326     @Override
    327     protected void place0(Section addedTo, int offset) {
    328         // Encode the data and note the size.
    329 
    330         ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
    331 
    332         encodeOutput(addedTo.getFile(), out);
    333         encodedForm = out.toByteArray();
    334         setWriteSize(encodedForm.length);
    335     }
    336 
    337     /**
    338      * Writes out the encoded form of this instance.
    339      *
    340      * @param file {@code non-null;} file this instance is part of
    341      * @param out {@code non-null;} where to write to
    342      */
    343     private void encodeOutput(DexFile file, AnnotatedOutput out) {
    344         boolean annotates = out.annotates();
    345 
    346         if (annotates) {
    347             out.annotate(0, offsetString() + " class data for " +
    348                     thisClass.toHuman());
    349         }
    350 
    351         encodeSize(file, out, "static_fields", staticFields.size());
    352         encodeSize(file, out, "instance_fields", instanceFields.size());
    353         encodeSize(file, out, "direct_methods", directMethods.size());
    354         encodeSize(file, out, "virtual_methods", virtualMethods.size());
    355 
    356         encodeList(file, out, "static_fields", staticFields);
    357         encodeList(file, out, "instance_fields", instanceFields);
    358         encodeList(file, out, "direct_methods", directMethods);
    359         encodeList(file, out, "virtual_methods", virtualMethods);
    360 
    361         if (annotates) {
    362             out.endAnnotation();
    363         }
    364     }
    365 
    366     /**
    367      * Helper for {@link #encodeOutput}, which writes out the given
    368      * size value, annotating it as well (if annotations are enabled).
    369      *
    370      * @param file {@code non-null;} file this instance is part of
    371      * @param out {@code non-null;} where to write to
    372      * @param label {@code non-null;} the label for the purposes of annotation
    373      * @param size {@code >= 0;} the size to write
    374      */
    375     private static void encodeSize(DexFile file, AnnotatedOutput out,
    376             String label, int size) {
    377         if (out.annotates()) {
    378             out.annotate(String.format("  %-21s %08x", label + "_size:",
    379                             size));
    380         }
    381 
    382         out.writeUnsignedLeb128(size);
    383     }
    384 
    385     /**
    386      * Helper for {@link #encodeOutput}, which writes out the given
    387      * list. It also annotates the items (if any and if annotations
    388      * are enabled).
    389      *
    390      * @param file {@code non-null;} file this instance is part of
    391      * @param out {@code non-null;} where to write to
    392      * @param label {@code non-null;} the label for the purposes of annotation
    393      * @param list {@code non-null;} the list in question
    394      */
    395     private static void encodeList(DexFile file, AnnotatedOutput out,
    396             String label, ArrayList<? extends EncodedMember> list) {
    397         int size = list.size();
    398         int lastIndex = 0;
    399 
    400         if (size == 0) {
    401             return;
    402         }
    403 
    404         if (out.annotates()) {
    405             out.annotate(0, "  " + label + ":");
    406         }
    407 
    408         for (int i = 0; i < size; i++) {
    409             lastIndex = list.get(i).encode(file, out, lastIndex, i);
    410         }
    411     }
    412 
    413     /** {@inheritDoc} */
    414     @Override
    415     public void writeTo0(DexFile file, AnnotatedOutput out) {
    416         boolean annotates = out.annotates();
    417 
    418         if (annotates) {
    419             /*
    420              * The output is to be annotated, so redo the work previously
    421              * done by place0(), except this time annotations will actually
    422              * get emitted.
    423              */
    424             encodeOutput(file, out);
    425         } else {
    426             out.write(encodedForm);
    427         }
    428     }
    429 }
    430