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