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 ProtoIdItem extends Item<ProtoIdItem> { 35 private int hashCode = 0; 36 37 private StringIdItem shortyDescriptor; 38 private TypeIdItem returnType; 39 private TypeListItem parameters; 40 41 /** 42 * Creates a new uninitialized <code>ProtoIdItem</code> 43 * @param dexFile The <code>DexFile</code> that this item belongs to 44 */ 45 protected ProtoIdItem(DexFile dexFile) { 46 super(dexFile); 47 } 48 49 /** 50 * Creates a new <code>ProtoIdItem</code> with the given values 51 * @param dexFile The <code>DexFile</code> that this item belongs to 52 * @param returnType the return type 53 * @param parameters a <code>TypeListItem</code> containing a list of the parameter types 54 */ 55 private ProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) { 56 this(dexFile); 57 58 String shortyString = returnType.toShorty(); 59 if (parameters != null) { 60 shortyString += parameters.getShortyString(); 61 } 62 this.shortyDescriptor = StringIdItem.internStringIdItem(dexFile, shortyString); 63 this.returnType = returnType; 64 this.parameters = parameters; 65 } 66 67 /** 68 * Returns a <code>ProtoIdItem</code> for the given values, and that has been interned into 69 * the given <code>DexFile</code> 70 * @param dexFile The <code>DexFile</code> that this item belongs to 71 * @param returnType the return type 72 * @param parameters a <code>TypeListItem</code> containing a list of the parameter types 73 * @return a <code>ProtoIdItem</code> for the given values, and that has been interned into 74 * the given <code>DexFile</code> 75 */ 76 public static ProtoIdItem internProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) { 77 ProtoIdItem protoIdItem = new ProtoIdItem(dexFile, returnType, parameters); 78 return dexFile.ProtoIdsSection.intern(protoIdItem); 79 } 80 81 /** 82 * Looks up the <code>ProtoIdItem</code> from the given <code>DexFile</code> for the given 83 * values 84 * @param dexFile the <code>Dexfile</code> to find the type in 85 * @param returnType the return type 86 * @param parameters a <code>TypeListItem</code> containing a list of the parameter types 87 * @return a <code>ProtoIdItem</code> from the given <code>DexFile</code> for the given 88 * values, or null if it doesn't exist 89 */ 90 public static ProtoIdItem lookupProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) { 91 ProtoIdItem protoIdItem = new ProtoIdItem(dexFile, returnType, parameters); 92 return dexFile.ProtoIdsSection.getInternedItem(protoIdItem); 93 } 94 95 /** {@inheritDoc} */ 96 protected void readItem(Input in, ReadContext readContext) { 97 shortyDescriptor = dexFile.StringIdsSection.getItemByIndex(in.readInt()); 98 returnType = dexFile.TypeIdsSection.getItemByIndex(in.readInt()); 99 parameters = (TypeListItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST, in.readInt()); 100 } 101 102 /** {@inheritDoc} */ 103 protected int placeItem(int offset) { 104 return offset + 12; 105 } 106 107 /** {@inheritDoc} */ 108 protected void writeItem(AnnotatedOutput out) { 109 if (out.annotates()) { 110 out.annotate(4, "shorty_descriptor: " + shortyDescriptor.getStringValue()); 111 out.annotate(4, "return_type: " + returnType.getTypeDescriptor()); 112 113 if (parameters == null) { 114 out.annotate(4, "parameters:"); 115 } else { 116 out.annotate(4, "parameters: " + parameters.getTypeListString("")); 117 } 118 } 119 120 out.writeInt(shortyDescriptor.getIndex()); 121 out.writeInt(returnType.getIndex()); 122 out.writeInt(parameters == null?0:parameters.getOffset()); 123 } 124 125 /** {@inheritDoc} */ 126 public ItemType getItemType() { 127 return ItemType.TYPE_PROTO_ID_ITEM; 128 } 129 130 /** {@inheritDoc} */ 131 public int compareTo(ProtoIdItem o) { 132 int result = returnType.compareTo(o.returnType); 133 if (result != 0) { 134 return result; 135 } 136 137 if (parameters == null) { 138 if (o.parameters == null) { 139 return 0; 140 } 141 return -1; 142 } else if (o.parameters == null) { 143 return 1; 144 } 145 146 return parameters.compareTo(o.parameters); 147 } 148 149 /** {@inheritDoc} */ 150 public String getConciseIdentity() { 151 return "proto_id_item: " + getPrototypeString(); 152 } 153 154 private String cachedPrototypeString = null; 155 /** 156 * @return a string in the format (TTTT..)R where TTTT.. are the parameter types and R is the return type 157 */ 158 public String getPrototypeString() { 159 if (cachedPrototypeString == null) { 160 StringBuilder sb = new StringBuilder("("); 161 if (parameters != null) { 162 sb.append(parameters.getTypeListString("")); 163 } 164 sb.append(")"); 165 sb.append(returnType.getTypeDescriptor()); 166 167 cachedPrototypeString = sb.toString(); 168 } 169 return cachedPrototypeString; 170 } 171 172 /** 173 * @return the return type of the method 174 */ 175 public TypeIdItem getReturnType() { 176 return returnType; 177 } 178 179 /** 180 * @return a <code>TypeListItem</code> containing the method parameter types 181 */ 182 public TypeListItem getParameters() { 183 return parameters; 184 } 185 186 /** 187 * @return the number of registers required for the parameters of this <code>ProtoIdItem</code> 188 */ 189 public int getParameterRegisterCount() { 190 if (parameters == null) { 191 return 0; 192 } else { 193 return parameters.getRegisterCount(); 194 } 195 } 196 197 /** 198 * calculate and cache the hashcode 199 */ 200 private void calcHashCode() { 201 hashCode = returnType.hashCode(); 202 hashCode = 31 * hashCode + (parameters==null?0:parameters.hashCode()); 203 } 204 205 @Override 206 public int hashCode() { 207 //there's a small possibility that the actual hash code will be 0. If so, we'll 208 //just end up recalculating it each time 209 if (hashCode == 0) 210 calcHashCode(); 211 return hashCode; 212 } 213 214 @Override 215 public boolean equals(Object o) { 216 if (this==o) { 217 return true; 218 } 219 if (o==null || !this.getClass().equals(o.getClass())) { 220 return false; 221 } 222 223 //This assumes that the referenced items have been interned in both objects. 224 //This is a valid assumption because all outside code must use the static 225 //"getInterned..." style methods to make new items, and any item created 226 //internally is guaranteed to be interned 227 ProtoIdItem other = (ProtoIdItem)o; 228 return (returnType == other.returnType && 229 parameters == other.parameters); 230 } 231 } 232