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