Home | History | Annotate | Download | only in dexlib
      1 /*
      2  * [The "BSD licence"]
      3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 package org.jf.dexlib;
     30 
     31 import org.jf.dexlib.Util.AnnotatedOutput;
     32 import org.jf.dexlib.Util.Input;
     33 
     34 public class MethodIdItem extends Item<MethodIdItem> implements Convertible<MethodIdItem> {
     35     private int hashCode = 0;
     36 
     37     private TypeIdItem classType;
     38     private ProtoIdItem methodPrototype;
     39     private StringIdItem methodName;
     40 
     41     /**
     42      * Creates a new uninitialized <code>MethodIdItem</code>
     43      * @param dexFile The <code>DexFile</code> that this item belongs to
     44      */
     45     protected MethodIdItem(DexFile dexFile) {
     46         super(dexFile);
     47     }
     48 
     49     /**
     50      * Creates a new <code>MethodIdItem</code> for the given class, type and name
     51      * @param dexFile The <code>DexFile</code> that this item belongs to
     52      * @param classType the class that the method is a member of
     53      * @param methodPrototype the type of the method
     54      * @param methodName the name of the method
     55      */
     56     private MethodIdItem(DexFile dexFile, TypeIdItem classType, ProtoIdItem methodPrototype, StringIdItem methodName) {
     57         this(dexFile);
     58         this.classType = classType;
     59         this.methodPrototype = methodPrototype;
     60         this.methodName = methodName;
     61     }
     62 
     63     /**
     64      * Returns a <code>MethodIdItem</code> for the given values, and that has been interned into
     65      * the given <code>DexFile</code>
     66      * @param dexFile The <code>DexFile</code> that this item belongs to
     67      * @param classType the class that the method is a member of
     68      * @param methodPrototype the type of the method
     69      * @param methodName the name of the method
     70      * @return a <code>MethodIdItem</code> for the given values, and that has been interned into
     71      * the given <code>DexFile</code>
     72      */
     73     public static MethodIdItem internMethodIdItem(DexFile dexFile, TypeIdItem classType,
     74                                                        ProtoIdItem methodPrototype, StringIdItem methodName) {
     75         MethodIdItem methodIdItem = new MethodIdItem(dexFile, classType, methodPrototype, methodName);
     76         return dexFile.MethodIdsSection.intern(methodIdItem);
     77     }
     78 
     79     /**
     80      * Looks up a <code>MethodIdItem</code> from the given <code>DexFile</code> for the given
     81      * values
     82      * @param dexFile The <code>DexFile</code> that this item belongs to
     83      * @param classType the class that the method is a member of
     84      * @param methodPrototype the type of the method
     85      * @param methodName the name of the method
     86      * @return a <code>MethodIdItem</code> from the given <code>DexFile</code> for the given
     87      * values, or null if it doesn't exist
     88      */
     89     public static MethodIdItem lookupMethodIdItem(DexFile dexFile, TypeIdItem classType,
     90                                                        ProtoIdItem methodPrototype, StringIdItem methodName) {
     91         MethodIdItem methodIdItem = new MethodIdItem(dexFile, classType, methodPrototype, methodName);
     92         return dexFile.MethodIdsSection.getInternedItem(methodIdItem);
     93     }
     94 
     95     /** {@inheritDoc} */
     96     protected void readItem(Input in, ReadContext readContext) {
     97         classType = dexFile.TypeIdsSection.getItemByIndex(in.readShort());
     98         methodPrototype = dexFile.ProtoIdsSection.getItemByIndex(in.readShort());
     99         methodName = dexFile.StringIdsSection.getItemByIndex(in.readInt());
    100     }
    101 
    102     /** {@inheritDoc} */
    103     protected int placeItem(int offset) {
    104         return offset + 8;
    105     }
    106 
    107     /** {@inheritDoc} */
    108     protected void writeItem(AnnotatedOutput out) {
    109         if (out.annotates()) {
    110             out.annotate(2, "class_type: " + classType.getTypeDescriptor());
    111             out.annotate(2, "method_prototype: " + methodPrototype.getPrototypeString());
    112             out.annotate(4, "method_name: " + methodName.getStringValue());
    113         }
    114 
    115         int classIndex = classType.getIndex();
    116         if (classIndex > 0xffff) {
    117             throw new RuntimeException(String.format("Error writing method_id_item for %s. The type index of " +
    118                     "defining class %s is too large", getMethodString(), classType.getTypeDescriptor()));
    119         }
    120         out.writeShort(classIndex);
    121 
    122         int prototypeIndex = methodPrototype.getIndex();
    123         if (prototypeIndex > 0xffff) {
    124             throw new RuntimeException(String.format("Error writing method_id_item for %0. The prototype index of " +
    125                     "method prototype %s is too large", getMethodString(), methodPrototype.getPrototypeString()));
    126         }
    127         out.writeShort(prototypeIndex);
    128 
    129         out.writeInt(methodName.getIndex());
    130     }
    131 
    132     /** {@inheritDoc} */
    133     public ItemType getItemType() {
    134         return ItemType.TYPE_METHOD_ID_ITEM;
    135     }
    136 
    137     /** {@inheritDoc} */
    138     public String getConciseIdentity() {
    139         return "method_id_item: " + getMethodString();
    140     }
    141 
    142     /** {@inheritDoc} */
    143     public int compareTo(MethodIdItem o) {
    144         int result = classType.compareTo(o.classType);
    145         if (result != 0) {
    146             return result;
    147         }
    148 
    149         result = methodName.compareTo(o.methodName);
    150         if (result != 0) {
    151             return result;
    152         }
    153 
    154         return methodPrototype.compareTo(o.methodPrototype);
    155     }
    156 
    157     private String cachedMethodString = null;
    158     /**
    159      * @return a string formatted like LclassName;->methodName(TTTT..)R
    160      */
    161     public String getMethodString() {
    162         if (cachedMethodString == null) {
    163             String classType = this.classType.getTypeDescriptor();
    164             String methodName = this.methodName.getStringValue();
    165             String prototypeString = methodPrototype.getPrototypeString();
    166 
    167             StringBuilder sb = new StringBuilder(classType.length() + methodName.length() + prototypeString.length() +
    168                     2);
    169             sb.append(classType);
    170             sb.append("->");
    171             sb.append(methodName);
    172             sb.append(prototypeString);
    173             cachedMethodString = sb.toString();
    174         }
    175         return cachedMethodString;
    176     }
    177 
    178     private String cachedShortMethodString = null;
    179     /**
    180      * @return a string formatted like methodName(TTTT..)R
    181      */
    182     public String getShortMethodString() {
    183         if (cachedShortMethodString == null) {
    184             String methodName = this.methodName.getStringValue();
    185             String prototypeString = methodPrototype.getPrototypeString();
    186 
    187             StringBuilder sb = new StringBuilder(methodName.length() + prototypeString.length());
    188             sb.append(methodName);
    189             sb.append(prototypeString);
    190             cachedShortMethodString = sb.toString();
    191         }
    192         return cachedShortMethodString;
    193     }
    194 
    195     /**
    196      * @return the method prototype
    197      */
    198     public ProtoIdItem getPrototype() {
    199         return methodPrototype;
    200     }
    201 
    202     /**
    203      * @return the name of the method
    204      */
    205     public StringIdItem getMethodName() {
    206         return methodName;
    207     }
    208 
    209     /**
    210      * @return the class this method is a member of
    211      */
    212     public TypeIdItem getContainingClass() {
    213         return classType;
    214     }
    215 
    216     /**
    217      * calculate and cache the hashcode
    218      */
    219     private void calcHashCode() {
    220         hashCode = classType.hashCode();
    221         hashCode = 31 * hashCode + methodPrototype.hashCode();
    222         hashCode = 31 * hashCode + methodName.hashCode();
    223     }
    224 
    225     @Override
    226     public int hashCode() {
    227         //there's a small possibility that the actual hash code will be 0. If so, we'll
    228         //just end up recalculating it each time
    229         if (hashCode == 0)
    230             calcHashCode();
    231         return hashCode;
    232     }
    233 
    234     @Override
    235     public boolean equals(Object o) {
    236         if (this==o) {
    237             return true;
    238         }
    239         if (o==null || !this.getClass().equals(o.getClass())) {
    240             return false;
    241         }
    242 
    243         //This assumes that the referenced items have been interned in both objects.
    244         //This is a valid assumption because all outside code must use the static
    245         //"getInterned..." style methods to make new items, and any item created
    246         //internally is guaranteed to be interned
    247         MethodIdItem other = (MethodIdItem)o;
    248         return (classType == other.classType &&
    249                 methodPrototype == other.methodPrototype &&
    250                 methodName == other.methodName);
    251     }
    252 
    253     public MethodIdItem convert() {
    254         return this;
    255     }
    256 }
    257