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 29package org.jf.dexlib; 30 31import org.jf.dexlib.Util.AnnotatedOutput; 32import org.jf.dexlib.Util.Input; 33 34public class MethodIdItem extends Item<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 out.writeShort(classType.getIndex()); 116 out.writeShort(methodPrototype.getIndex()); 117 out.writeInt(methodName.getIndex()); 118 } 119 120 /** {@inheritDoc} */ 121 public ItemType getItemType() { 122 return ItemType.TYPE_METHOD_ID_ITEM; 123 } 124 125 /** {@inheritDoc} */ 126 public String getConciseIdentity() { 127 return "method_id_item: " + getMethodString(); 128 } 129 130 /** {@inheritDoc} */ 131 public int compareTo(MethodIdItem o) { 132 int result = classType.compareTo(o.classType); 133 if (result != 0) { 134 return result; 135 } 136 137 result = methodName.compareTo(o.methodName); 138 if (result != 0) { 139 return result; 140 } 141 142 return methodPrototype.compareTo(o.methodPrototype); 143 } 144 145 private String cachedMethodString = null; 146 /** 147 * @return a string formatted like LclassName;->methodName(TTTT..)R 148 */ 149 public String getMethodString() { 150 if (cachedMethodString == null) { 151 String classType = this.classType.getTypeDescriptor(); 152 String methodName = this.methodName.getStringValue(); 153 String prototypeString = methodPrototype.getPrototypeString(); 154 155 StringBuilder sb = new StringBuilder(classType.length() + methodName.length() + prototypeString.length() + 156 2); 157 sb.append(classType); 158 sb.append("->"); 159 sb.append(methodName); 160 sb.append(prototypeString); 161 cachedMethodString = sb.toString(); 162 } 163 return cachedMethodString; 164 } 165 166 private String cachedVirtualMethodString = null; 167 /** 168 * @return a string formatted like methodName(TTTT..)R 169 */ 170 public String getVirtualMethodString() { 171 if (cachedVirtualMethodString == null) { 172 String methodName = this.methodName.getStringValue(); 173 String prototypeString = methodPrototype.getPrototypeString(); 174 175 StringBuilder sb = new StringBuilder(methodName.length() + prototypeString.length()); 176 sb.append(methodName); 177 sb.append(prototypeString); 178 cachedVirtualMethodString = sb.toString(); 179 } 180 return cachedVirtualMethodString; 181 } 182 183 /** 184 * @return the method prototype 185 */ 186 public ProtoIdItem getPrototype() { 187 return methodPrototype; 188 } 189 190 /** 191 * @return the name of the method 192 */ 193 public StringIdItem getMethodName() { 194 return methodName; 195 } 196 197 /** 198 * @return the class this method is a member of 199 */ 200 public TypeIdItem getContainingClass() { 201 return classType; 202 } 203 204 /** 205 * calculate and cache the hashcode 206 */ 207 private void calcHashCode() { 208 hashCode = classType.hashCode(); 209 hashCode = 31 * hashCode + methodPrototype.hashCode(); 210 hashCode = 31 * hashCode + methodName.hashCode(); 211 } 212 213 @Override 214 public int hashCode() { 215 //there's a small possibility that the actual hash code will be 0. If so, we'll 216 //just end up recalculating it each time 217 if (hashCode == 0) 218 calcHashCode(); 219 return hashCode; 220 } 221 222 @Override 223 public boolean equals(Object o) { 224 if (this==o) { 225 return true; 226 } 227 if (o==null || !this.getClass().equals(o.getClass())) { 228 return false; 229 } 230 231 //This assumes that the referenced items have been interned in both objects. 232 //This is a valid assumption because all outside code must use the static 233 //"getInterned..." style methods to make new items, and any item created 234 //internally is guaranteed to be interned 235 MethodIdItem other = (MethodIdItem)o; 236 return (classType == other.classType && 237 methodPrototype == other.methodPrototype && 238 methodName == other.methodName); 239 } 240} 241