1/* 2 * Copyright 2012, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32package org.jf.dexlib2.dexbacked; 33 34import com.google.common.collect.ImmutableList; 35import com.google.common.collect.ImmutableSet; 36import org.jf.dexlib2.base.reference.BaseMethodReference; 37import org.jf.dexlib2.dexbacked.raw.MethodIdItem; 38import org.jf.dexlib2.dexbacked.raw.ProtoIdItem; 39import org.jf.dexlib2.dexbacked.raw.TypeListItem; 40import org.jf.dexlib2.dexbacked.reference.DexBackedMethodReference; 41import org.jf.dexlib2.dexbacked.util.AnnotationsDirectory; 42import org.jf.dexlib2.dexbacked.util.FixedSizeList; 43import org.jf.dexlib2.dexbacked.util.ParameterIterator; 44import org.jf.dexlib2.iface.Annotation; 45import org.jf.dexlib2.iface.Method; 46import org.jf.dexlib2.iface.MethodImplementation; 47import org.jf.dexlib2.iface.MethodParameter; 48import org.jf.util.AbstractForwardSequentialList; 49 50import javax.annotation.Nonnull; 51import javax.annotation.Nullable; 52import java.util.Iterator; 53import java.util.List; 54import java.util.Set; 55 56public class DexBackedMethod extends BaseMethodReference implements Method { 57 @Nonnull public final DexBackedDexFile dexFile; 58 @Nonnull public final DexBackedClassDef classDef; 59 60 public final int accessFlags; 61 62 private final int codeOffset; 63 private final int parameterAnnotationSetListOffset; 64 private final int methodAnnotationSetOffset; 65 66 public final int methodIndex; 67 private final int startOffset; 68 69 private int methodIdItemOffset; 70 private int protoIdItemOffset; 71 private int parametersOffset = -1; 72 73 public DexBackedMethod(@Nonnull DexReader reader, 74 @Nonnull DexBackedClassDef classDef, 75 int previousMethodIndex) { 76 this.dexFile = reader.dexBuf; 77 this.classDef = classDef; 78 startOffset = reader.getOffset(); 79 80 // large values may be used for the index delta, which cause the cumulative index to overflow upon 81 // addition, effectively allowing out of order entries. 82 int methodIndexDiff = reader.readLargeUleb128(); 83 this.methodIndex = methodIndexDiff + previousMethodIndex; 84 this.accessFlags = reader.readSmallUleb128(); 85 this.codeOffset = reader.readSmallUleb128(); 86 87 this.methodAnnotationSetOffset = 0; 88 this.parameterAnnotationSetListOffset = 0; 89 } 90 91 public DexBackedMethod(@Nonnull DexReader reader, 92 @Nonnull DexBackedClassDef classDef, 93 int previousMethodIndex, 94 @Nonnull AnnotationsDirectory.AnnotationIterator methodAnnotationIterator, 95 @Nonnull AnnotationsDirectory.AnnotationIterator paramaterAnnotationIterator) { 96 this.dexFile = reader.dexBuf; 97 this.classDef = classDef; 98 startOffset = reader.getOffset(); 99 100 // large values may be used for the index delta, which cause the cumulative index to overflow upon 101 // addition, effectively allowing out of order entries. 102 int methodIndexDiff = reader.readLargeUleb128(); 103 this.methodIndex = methodIndexDiff + previousMethodIndex; 104 this.accessFlags = reader.readSmallUleb128(); 105 this.codeOffset = reader.readSmallUleb128(); 106 107 this.methodAnnotationSetOffset = methodAnnotationIterator.seekTo(methodIndex); 108 this.parameterAnnotationSetListOffset = paramaterAnnotationIterator.seekTo(methodIndex); 109 } 110 111 public int getMethodIndex() { return methodIndex; } 112 @Nonnull @Override public String getDefiningClass() { return classDef.getType(); } 113 @Override public int getAccessFlags() { return accessFlags; } 114 115 @Nonnull 116 @Override 117 public String getName() { 118 return dexFile.getString(dexFile.readSmallUint(getMethodIdItemOffset() + MethodIdItem.NAME_OFFSET)); 119 } 120 121 @Nonnull 122 @Override 123 public String getReturnType() { 124 return dexFile.getType(dexFile.readSmallUint(getProtoIdItemOffset() + ProtoIdItem.RETURN_TYPE_OFFSET)); 125 } 126 127 @Nonnull 128 @Override 129 public List<? extends MethodParameter> getParameters() { 130 int parametersOffset = getParametersOffset(); 131 if (parametersOffset > 0) { 132 final List<String> parameterTypes = getParameterTypes(); 133 134 return new AbstractForwardSequentialList<MethodParameter>() { 135 @Nonnull @Override public Iterator<MethodParameter> iterator() { 136 return new ParameterIterator(parameterTypes, 137 getParameterAnnotations(), 138 getParameterNames()); 139 } 140 141 @Override public int size() { 142 return parameterTypes.size(); 143 } 144 }; 145 } 146 return ImmutableList.of(); 147 } 148 149 @Nonnull 150 public List<? extends Set<? extends DexBackedAnnotation>> getParameterAnnotations() { 151 return AnnotationsDirectory.getParameterAnnotations(dexFile, parameterAnnotationSetListOffset); 152 } 153 154 @Nonnull 155 public Iterator<String> getParameterNames() { 156 DexBackedMethodImplementation methodImpl = getImplementation(); 157 if (methodImpl != null) { 158 return methodImpl.getParameterNames(null); 159 } 160 return ImmutableSet.<String>of().iterator(); 161 } 162 163 @Nonnull 164 @Override 165 public List<String> getParameterTypes() { 166 final int parametersOffset = getParametersOffset(); 167 if (parametersOffset > 0) { 168 final int parameterCount = dexFile.readSmallUint(parametersOffset + TypeListItem.SIZE_OFFSET); 169 final int paramListStart = parametersOffset + TypeListItem.LIST_OFFSET; 170 return new FixedSizeList<String>() { 171 @Nonnull 172 @Override 173 public String readItem(final int index) { 174 return dexFile.getType(dexFile.readUshort(paramListStart + 2*index)); 175 } 176 @Override public int size() { return parameterCount; } 177 }; 178 } 179 return ImmutableList.of(); 180 } 181 182 @Nonnull 183 @Override 184 public Set<? extends Annotation> getAnnotations() { 185 return AnnotationsDirectory.getAnnotations(dexFile, methodAnnotationSetOffset); 186 } 187 188 @Nullable 189 @Override 190 public DexBackedMethodImplementation getImplementation() { 191 if (codeOffset > 0) { 192 return new DexBackedMethodImplementation(dexFile, this, codeOffset); 193 } 194 return null; 195 } 196 197 private int getMethodIdItemOffset() { 198 if (methodIdItemOffset == 0) { 199 methodIdItemOffset = dexFile.getMethodIdItemOffset(methodIndex); 200 } 201 return methodIdItemOffset; 202 } 203 204 private int getProtoIdItemOffset() { 205 if (protoIdItemOffset == 0) { 206 int protoIndex = dexFile.readUshort(getMethodIdItemOffset() + MethodIdItem.PROTO_OFFSET); 207 protoIdItemOffset = dexFile.getProtoIdItemOffset(protoIndex); 208 } 209 return protoIdItemOffset; 210 } 211 212 private int getParametersOffset() { 213 if (parametersOffset == -1) { 214 parametersOffset = dexFile.readSmallUint(getProtoIdItemOffset() + ProtoIdItem.PARAMETERS_OFFSET); 215 } 216 return parametersOffset; 217 } 218 219 /** 220 * Skips the reader over the specified number of encoded_method structures 221 * 222 * @param reader The reader to skip 223 * @param count The number of encoded_method structures to skip over 224 */ 225 public static void skipMethods(@Nonnull DexReader reader, int count) { 226 for (int i=0; i<count; i++) { 227 reader.skipUleb128(); 228 reader.skipUleb128(); 229 reader.skipUleb128(); 230 } 231 } 232 233 /** 234 * Calculate and return the private size of a method definition. 235 * 236 * Calculated as: method_idx_diff + access_flags + code_off + 237 * implementation size + reference size 238 * 239 * @return size in bytes 240 */ 241 public int getSize() { 242 int size = 0; 243 244 DexReader reader = dexFile.readerAt(startOffset); 245 reader.readLargeUleb128(); //method_idx_diff 246 reader.readSmallUleb128(); //access_flags 247 reader.readSmallUleb128(); //code_off 248 size += reader.getOffset() - startOffset; 249 250 DexBackedMethodImplementation impl = getImplementation(); 251 if (impl != null) { 252 size += impl.getSize(); 253 } 254 255 DexBackedMethodReference methodRef = new DexBackedMethodReference(dexFile, methodIndex); 256 size += methodRef.getSize(); 257 258 return size; 259 } 260} 261