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