MethodDefinition.java revision b6547e8fd56242dde90275d9b0ba6f3639083a61
1/* 2 * [The "BSD licence"] 3 * Copyright (c) 2009 Ben Gruver 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.baksmali.Adaptors; 30 31import org.jf.baksmali.Adaptors.Format.*; 32import org.jf.dexlib.*; 33import org.jf.dexlib.code.Format.*; 34import org.jf.dexlib.code.Instruction; 35import org.jf.dexlib.code.InstructionField; 36import org.jf.dexlib.code.Opcode; 37import org.jf.dexlib.util.AccessFlags; 38import org.jf.dexlib.util.DebugInfoDecoder; 39 40import java.util.*; 41 42public class MethodDefinition { 43 private ClassDataItem.EncodedMethod encodedMethod; 44 private MethodIdItem methodIdItem; 45 private CodeItem codeItem; 46 private AnnotationSetItem annotationSet; 47 48 public MethodDefinition(ClassDataItem.EncodedMethod encodedMethod, AnnotationSetItem annotationSet) { 49 this.encodedMethod = encodedMethod; 50 this.methodIdItem = encodedMethod.getMethod(); 51 this.codeItem = encodedMethod.getCodeItem(); 52 this.annotationSet = annotationSet; 53 } 54 55 private String methodName = null; 56 public String getMethodName() { 57 if (methodName == null) { 58 methodName = methodIdItem.getMethodName(); 59 } 60 return methodName; 61 } 62 63 private List<String> accessFlags = null; 64 public List<String> getAccessFlags() { 65 if (accessFlags == null) { 66 accessFlags = new ArrayList<String>(); 67 68 for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.getAccessFlags())) { 69 accessFlags.add(accessFlag.toString()); 70 } 71 } 72 return accessFlags; 73 } 74 75 private String prototype = null; 76 public String getPrototype() { 77 if (prototype == null) { 78 prototype = methodIdItem.getPrototype().getPrototypeString(); 79 } 80 return prototype; 81 } 82 83 private Boolean hasCode = null; 84 public boolean getHasCode() { 85 if (hasCode == null) { 86 hasCode = (codeItem != null); 87 } 88 return hasCode; 89 } 90 91 private String registerCount = null; 92 public String getRegisterCount() { 93 if (registerCount == null) { 94 if (codeItem == null) { 95 registerCount = "0"; 96 } else { 97 registerCount = Integer.toString(codeItem.getRegisterCount()); 98 } 99 } 100 return registerCount; 101 } 102 103 public List<AnnotationAdaptor> getAnnotations() { 104 if (annotationSet == null) { 105 return null; 106 } 107 108 List<AnnotationAdaptor> annotationAdaptors = new ArrayList<AnnotationAdaptor>(); 109 110 for (AnnotationItem annotationItem: annotationSet.getAnnotationItems()) { 111 annotationAdaptors.add(new AnnotationAdaptor(annotationItem)); 112 } 113 return annotationAdaptors; 114 } 115 116 117 private List<MethodItem> methodItems = null; 118 public List<MethodItem> getMethodItems() { 119 if (methodItems == null) { 120 MethodItemList methodItemList = new MethodItemList(); 121 methodItemList.generateMethodItemList(); 122 123 methodItems = new ArrayList<MethodItem>(); 124 125 methodItems.addAll(methodItemList.labels); 126 methodItems.addAll(methodItemList.instructions); 127 methodItems.addAll(methodItemList.blanks); 128 methodItems.addAll(methodItemList.catches); 129 methodItems.addAll(methodItemList.debugItems); 130 Collections.sort(methodItems); 131 } 132 return methodItems; 133 } 134 135 136 private class MethodItemList { 137 public HashSet<LabelMethodItem> labels = new HashSet<LabelMethodItem>(); 138 public List<MethodItem> instructions = new ArrayList<MethodItem>(); 139 public List<BlankMethodItem> blanks = new ArrayList<BlankMethodItem>(); 140 public List<CatchMethodItem> catches = new ArrayList<CatchMethodItem>(); 141 public List<MethodItem> debugItems = new ArrayList<MethodItem>(); 142 143 private HashMap<Integer, Integer> packedSwitchMap = new HashMap<Integer, Integer>(); 144 private HashMap<Integer, Integer> sparseSwitchMap = new HashMap<Integer, Integer>(); 145 146 147 public void generateMethodItemList() { 148 if (codeItem == null) { 149 return; 150 } 151 152 int offset = 0; 153 for (InstructionField instructionField: codeItem.getInstructions()) { 154 Instruction instruction = instructionField.getInstruction(); 155 if (instruction.getOpcode() == Opcode.PACKED_SWITCH) { 156 packedSwitchMap.put(offset+((Instruction31t)instruction).getOffset(), offset); 157 } else if (instruction.getOpcode() == Opcode.SPARSE_SWITCH) { 158 sparseSwitchMap.put(offset+((Instruction31t)instruction).getOffset(), offset); 159 } 160 161 offset += instructionField.getSize(offset * 2) / 2; 162 } 163 164 offset = 0; 165 for (InstructionField instructionField: codeItem.getInstructions()) { 166 addMethodItemsForInstruction(offset, instructionField); 167 blanks.add(new BlankMethodItem(offset)); 168 offset += instructionField.getSize(offset*2) / 2; 169 } 170 blanks.remove(blanks.size()-1); 171 172 addTries(); 173 174 addDebugInfo(); 175 } 176 177 private void addMethodItemsForInstruction(int offset, InstructionField instructionField) { 178 Instruction instruction = instructionField.getInstruction(); 179 180 switch (instruction.getFormat()) { 181 case Format10t: 182 instructions.add(new Instruction10tMethodItem(offset, (Instruction10t)instruction)); 183 labels.add(new LabelMethodItem(offset + ((Instruction10t)instruction).getOffset(), "goto_")); 184 return; 185 case Format10x: 186 instructions.add(new Instruction10xMethodItem(offset, (Instruction10x)instruction)); 187 return; 188 case Format11n: 189 instructions.add(new Instruction11nMethodItem(offset, (Instruction11n)instruction)); 190 return; 191 case Format11x: 192 instructions.add(new Instruction11xMethodItem(offset, (Instruction11x)instruction)); 193 return; 194 case Format12x: 195 instructions.add(new Instruction12xMethodItem(offset, (Instruction12x)instruction)); 196 return; 197 case Format20t: 198 instructions.add(new Instruction20tMethodItem(offset, (Instruction20t)instruction)); 199 labels.add(new LabelMethodItem(offset + ((Instruction20t)instruction).getOffset(), "goto_")); 200 return; 201 case Format21c: 202 instructions.add(new Instruction21cMethodItem(offset, (Instruction21c)instruction)); 203 return; 204 case Format21h: 205 instructions.add(new Instruction21hMethodItem(offset, (Instruction21h)instruction)); 206 return; 207 case Format21s: 208 instructions.add(new Instruction21sMethodItem(offset, (Instruction21s)instruction)); 209 return; 210 case Format21t: 211 instructions.add(new Instruction21tMethodItem(offset, (Instruction21t)instruction)); 212 labels.add(new LabelMethodItem(offset + ((Instruction21t)instruction).getOffset(), "cond_")); 213 return; 214 case Format22b: 215 instructions.add(new Instruction22bMethodItem(offset, (Instruction22b)instruction)); 216 return; 217 case Format22c: 218 instructions.add(new Instruction22cMethodItem(offset, (Instruction22c)instruction)); 219 return; 220 case Format22s: 221 instructions.add(new Instruction22sMethodItem(offset, (Instruction22s)instruction)); 222 return; 223 case Format22t: 224 instructions.add(new Instruction22tMethodItem(offset, (Instruction22t)instruction)); 225 labels.add(new LabelMethodItem(offset + ((Instruction22t)instruction).getOffset(), "cond_")); 226 return; 227 case Format22x: 228 instructions.add(new Instruction22xMethodItem(offset, (Instruction22x)instruction)); 229 return; 230 case Format23x: 231 instructions.add(new Instruction23xMethodItem(offset, (Instruction23x)instruction)); 232 return; 233 case Format30t: 234 instructions.add(new Instruction30tMethodItem(offset, (Instruction30t)instruction)); 235 labels.add(new LabelMethodItem(offset + ((Instruction30t)instruction).getOffset(), "goto_")); 236 return; 237 case Format31c: 238 instructions.add(new Instruction31cMethodItem(offset, (Instruction31c)instruction)); 239 return; 240 case Format31i: 241 instructions.add(new Instruction31iMethodItem(offset, (Instruction31i)instruction)); 242 return; 243 case Format31t: 244 instructions.add(new Instruction31tMethodItem(offset, (Instruction31t)instruction)); 245 if (instruction.getOpcode() == Opcode.FILL_ARRAY_DATA) { 246 labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "array_")); 247 } else if (instruction.getOpcode() == Opcode.PACKED_SWITCH) { 248 labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "pswitch_data_")); 249 } else if (instruction.getOpcode() == Opcode.SPARSE_SWITCH) { 250 labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "sswitch_data_")); 251 } 252 return; 253 case Format32x: 254 instructions.add(new Instruction32xMethodItem(offset, (Instruction32x)instruction)); 255 return; 256 case Format35c: 257 instructions.add(new Instruction35cMethodItem(offset, (Instruction35c)instruction)); 258 return; 259 case Format3rc: 260 instructions.add(new Instruction3rcMethodItem(offset, (Instruction3rc)instruction)); 261 return; 262 case Format51l: 263 instructions.add(new Instruction51lMethodItem(offset, (Instruction51l)instruction)); 264 return; 265 case ArrayData: 266 instructions.add(new ArrayDataMethodItem(offset, (ArrayDataPseudoInstruction)instruction)); 267 return; 268 case PackedSwitchData: 269 { 270 Integer baseAddress = packedSwitchMap.get(offset); 271 272 if (baseAddress != null) { 273 instructions.add(new PackedSwitchMethodItem(offset, 274 (PackedSwitchDataPseudoInstruction)instruction, baseAddress)); 275 for (int target: ((PackedSwitchDataPseudoInstruction)instruction).getTargets()) { 276 labels.add(new LabelMethodItem(baseAddress + target, "pswitch_")); 277 } 278 } 279 return; 280 } 281 case SparseSwitchData: 282 { 283 Integer baseAddress = sparseSwitchMap.get(offset); 284 if (baseAddress != null) { 285 instructions.add(new SparseSwitchMethodItem(offset, 286 (SparseSwitchDataPseudoInstruction)instruction, baseAddress)); 287 for (int target: ((SparseSwitchDataPseudoInstruction)instruction).getTargets()) { 288 labels.add(new LabelMethodItem(baseAddress + target, "sswitch_")); 289 } 290 } 291 return; 292 } 293 } 294 } 295 296 private void addTries() { 297 for (CodeItem.TryItem tryItem: codeItem.getTries()) { 298 int startAddress = tryItem.getStartAddress(); 299 int endAddress = tryItem.getEndAddress(); 300 301 /** 302 * The end address points to the address immediately after the end of the last 303 * instruction that the try block covers. We want the .catch directive and end_try 304 * label to be associated with the last covered instruction, so we need to get 305 * the offset for that instruction 306 */ 307 int index = Collections.binarySearch(instructions, new BlankMethodItem(endAddress)); 308 if (index < 0) { 309 index = (index * -1) - 1; 310 } 311 //index should never by 0, so this should be safe 312 if (index == instructions.size()) { 313 index--; 314 } else { 315 index -= 2; 316 } 317 318 int lastInstructionOffset = instructions.get(index).getOffset(); 319 320 //add the catch all handler if it exists 321 int catchAllAddress = tryItem.getHandler().getCatchAllAddress(); 322 if (catchAllAddress != -1) { 323 CatchMethodItem catchMethodItem = new CatchMethodItem(lastInstructionOffset, null, startAddress, 324 endAddress, catchAllAddress) { 325 public String getTemplate() { 326 return "CatchAll"; 327 } 328 }; 329 catches.add(catchMethodItem); 330 331 labels.add(new LabelMethodItem(startAddress, "try_start_")); 332 //use the offset from the last covered instruction, but make the label 333 //name refer to the address of the next instruction 334 labels.add(new EndTryLabelMethodItem(lastInstructionOffset, endAddress)); 335 labels.add(new LabelMethodItem(catchAllAddress, "handler_")); 336 337 } 338 339 //add the rest of the handlers 340 //TODO: find adjacent handlers for the same type and combine them 341 for (CodeItem.EncodedTypeAddrPair handler: tryItem.getHandler().getHandlers()) { 342 //use the offset from the last covered instruction 343 CatchMethodItem catchMethodItem = new CatchMethodItem(lastInstructionOffset, 344 handler.getTypeReferenceField(), startAddress, endAddress, handler.getHandlerAddress()); 345 catches.add(catchMethodItem); 346 347 labels.add(new LabelMethodItem(startAddress, "try_start_")); 348 //use the offset from the last covered instruction, but make the label 349 //name refer to the address of the next instruction 350 labels.add(new EndTryLabelMethodItem(lastInstructionOffset, endAddress)); 351 labels.add(new LabelMethodItem(handler.getHandlerAddress(), "handler_")); 352 } 353 } 354 } 355 356 private void addDebugInfo() { 357 DebugInfoItem debugInfoItem = codeItem.getDebugInfo(); 358 if (debugInfoItem == null) { 359 return; 360 } 361 362 DebugInfoDecoder decoder = new DebugInfoDecoder(debugInfoItem, new DebugInfoDelegate(), 363 codeItem.getRegisterCount()); 364 decoder.decode(); 365 } 366 367 private class DebugInfoDelegate implements DebugInfoDecoder.DebugInfoDelegate { 368 369 public void endPrologue(int address) { 370 debugItems.add(new DebugMethodItem(address, "EndPrologue", -4)); 371 } 372 373 public void startEpilogue(int address) { 374 debugItems.add(new DebugMethodItem(address, "StartEpilogue", -4)); 375 } 376 377 public void startLocal(int address, DebugInfoDecoder.Local local) { 378 debugItems.add(new LocalDebugMethodItem(address, "StartLocal", -1, local)); 379 } 380 381 public void endLocal(int address, DebugInfoDecoder.Local local) { 382 debugItems.add(new LocalDebugMethodItem(address, "EndLocal", -1, local)); 383 } 384 385 public void restartLocal(int address, DebugInfoDecoder.Local local) { 386 debugItems.add(new LocalDebugMethodItem(address, "RestartLocal", -1, local)); 387 } 388 389 public void setFile(int address, final StringIdItem fileName) { 390 debugItems.add(new DebugMethodItem(address, "SetFile", -3) { 391 public String getFileName() { 392 return fileName.getStringValue(); 393 } 394 }); 395 } 396 397 public void line(int address, final int line) { 398 debugItems.add(new DebugMethodItem(address, "Line", -2) { 399 public int getLine() { 400 return line; 401 } 402 }); 403 } 404 } 405 } 406} 407