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