MethodDefinition.java revision 00fc68adf2e39aeb9fed35293f2576bbe729ec4b
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 29package org.jf.baksmali.Adaptors; 30 31import org.jf.baksmali.Adaptors.Format.*; 32import org.jf.baksmali.baksmali; 33import org.jf.baksmali.main; 34import org.jf.dexlib.*; 35import org.jf.dexlib.Code.*; 36import org.jf.dexlib.Code.Analysis.AnalyzedInstruction; 37import org.jf.dexlib.Code.Analysis.MethodAnalyzer; 38import org.jf.dexlib.Code.Analysis.RegisterType; 39import org.jf.dexlib.Code.Analysis.ValidationException; 40import org.jf.dexlib.Code.Format.Format; 41import org.jf.dexlib.Debug.DebugInstructionIterator; 42import org.jf.dexlib.Util.AccessFlags; 43import org.antlr.stringtemplate.StringTemplateGroup; 44import org.antlr.stringtemplate.StringTemplate; 45import org.jf.dexlib.Util.Hex; 46import org.jf.dexlib.Util.SparseIntArray; 47 48import java.util.*; 49 50public class MethodDefinition { 51 private final StringTemplateGroup stg; 52 private final ClassDataItem.EncodedMethod encodedMethod; 53 private final MethodAnalyzer methodAnalyzer; 54 55 private final LabelCache labelCache = new LabelCache(); 56 57 private final SparseIntArray packedSwitchMap; 58 private final SparseIntArray sparseSwitchMap; 59 private final SparseIntArray instructionMap; 60 61 private final int registerCount; 62 63 public MethodDefinition(StringTemplateGroup stg, ClassDataItem.EncodedMethod encodedMethod) { 64 this.stg = stg; 65 this.encodedMethod = encodedMethod; 66 67 //TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh. 68 69 if (encodedMethod.codeItem != null) { 70 methodAnalyzer = new MethodAnalyzer(encodedMethod, baksmali.deodex); 71 AnalyzedInstruction[] instructions = methodAnalyzer.makeInstructionArray(); 72 73 packedSwitchMap = new SparseIntArray(1); 74 sparseSwitchMap = new SparseIntArray(1); 75 instructionMap = new SparseIntArray(instructions.length); 76 77 registerCount = encodedMethod.codeItem.getRegisterCount(); 78 79 int currentCodeAddress = 0; 80 for (int i=0; i<instructions.length; i++) { 81 AnalyzedInstruction instruction = instructions[i]; 82 if (instruction.getInstruction().opcode == Opcode.PACKED_SWITCH) { 83 packedSwitchMap.append( 84 currentCodeAddress + ((OffsetInstruction)instruction.getInstruction()).getTargetAddressOffset(), 85 currentCodeAddress); 86 } else if (instruction.getInstruction().opcode == Opcode.SPARSE_SWITCH) { 87 sparseSwitchMap.append( 88 currentCodeAddress + ((OffsetInstruction)instruction.getInstruction()).getTargetAddressOffset(), 89 currentCodeAddress); 90 } 91 instructionMap.append(currentCodeAddress, i); 92 currentCodeAddress += instruction.getInstruction().getSize(currentCodeAddress); 93 } 94 } else { 95 packedSwitchMap = null; 96 sparseSwitchMap = null; 97 instructionMap = null; 98 methodAnalyzer = null; 99 registerCount = 0; 100 } 101 } 102 103 public StringTemplate createTemplate(AnnotationSetItem annotationSet, 104 AnnotationSetRefList parameterAnnotations) { 105 106 CodeItem codeItem = encodedMethod.codeItem; 107 108 StringTemplate template = stg.getInstanceOf("method"); 109 110 template.setAttribute("AccessFlags", getAccessFlags(encodedMethod)); 111 template.setAttribute("MethodName", encodedMethod.method.getMethodName().getStringValue()); 112 template.setAttribute("Prototype", encodedMethod.method.getPrototype().getPrototypeString()); 113 template.setAttribute("HasCode", codeItem != null); 114 template.setAttribute("RegistersDirective", baksmali.useLocalsDirective?".locals":".registers"); 115 template.setAttribute("RegisterCount", codeItem==null?"0":Integer.toString(getRegisterCount(encodedMethod))); 116 template.setAttribute("Parameters", getParameters(stg, codeItem, parameterAnnotations)); 117 template.setAttribute("Annotations", getAnnotations(stg, annotationSet)); 118 template.setAttribute("MethodItems", getMethodItems()); 119 120 return template; 121 } 122 123 private static int getRegisterCount(ClassDataItem.EncodedMethod encodedMethod) 124 { 125 int totalRegisters = encodedMethod.codeItem.getRegisterCount(); 126 if (baksmali.useLocalsDirective) { 127 int parameterRegisters = encodedMethod.method.getPrototype().getParameterRegisterCount(); 128 if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) { 129 parameterRegisters++; 130 } 131 return totalRegisters - parameterRegisters; 132 } 133 return totalRegisters; 134 } 135 136 private static List<String> getAccessFlags(ClassDataItem.EncodedMethod encodedMethod) { 137 List<String> accessFlags = new ArrayList<String>(); 138 139 for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.accessFlags)) { 140 accessFlags.add(accessFlag.toString()); 141 } 142 143 return accessFlags; 144 } 145 146 private static List<StringTemplate> getParameters(StringTemplateGroup stg, CodeItem codeItem, 147 AnnotationSetRefList parameterAnnotations) { 148 DebugInfoItem debugInfoItem = null; 149 if (baksmali.outputDebugInfo && codeItem != null) { 150 debugInfoItem = codeItem.getDebugInfo(); 151 } 152 153 int parameterCount = 0; 154 155 List<AnnotationSetItem> annotations = new ArrayList<AnnotationSetItem>(); 156 if (parameterAnnotations != null) { 157 AnnotationSetItem[] _annotations = parameterAnnotations.getAnnotationSets(); 158 if (_annotations != null) { 159 annotations.addAll(Arrays.asList(_annotations)); 160 } 161 162 parameterCount = annotations.size(); 163 } 164 165 List<String> parameterNames = new ArrayList<String>(); 166 if (debugInfoItem != null) { 167 StringIdItem[] _parameterNames = debugInfoItem.getParameterNames(); 168 if (_parameterNames != null) { 169 for (StringIdItem parameterName: _parameterNames) { 170 parameterNames.add(parameterName==null?null:parameterName.getStringValue()); 171 } 172 } 173 174 if (parameterCount < parameterNames.size()) { 175 parameterCount = parameterNames.size(); 176 } 177 } 178 179 List<StringTemplate> parameters = new ArrayList<StringTemplate>(); 180 for (int i=0; i<parameterCount; i++) { 181 AnnotationSetItem annotationSet = null; 182 if (i < annotations.size()) { 183 annotationSet = annotations.get(i); 184 } 185 186 String parameterName = null; 187 if (i < parameterNames.size()) { 188 parameterName = parameterNames.get(i); 189 } 190 191 parameters.add(ParameterAdaptor.createTemplate(stg, parameterName, annotationSet)); 192 } 193 194 return parameters; 195 } 196 197 public LabelCache getLabelCache() { 198 return labelCache; 199 } 200 201 public ValidationException getValidationException() { 202 if (methodAnalyzer == null) { 203 return null; 204 } 205 206 return methodAnalyzer.getValidationException(); 207 } 208 209 public int getPackedSwitchBaseAddress(int packedSwitchDataAddress) { 210 int packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress, -1); 211 212 if (packedSwitchBaseAddress == -1) { 213 throw new RuntimeException("Could not find the packed switch statement corresponding to the packed " + 214 "switch data at address " + packedSwitchDataAddress); 215 } 216 217 return packedSwitchBaseAddress; 218 } 219 220 public int getSparseSwitchBaseAddress(int sparseSwitchDataAddress) { 221 int sparseSwitchBaseAddress = this.sparseSwitchMap.get(sparseSwitchDataAddress, -1); 222 223 if (sparseSwitchBaseAddress == -1) { 224 throw new RuntimeException("Could not find the sparse switch statement corresponding to the sparse " + 225 "switch data at address " + sparseSwitchDataAddress); 226 } 227 228 return sparseSwitchBaseAddress; 229 } 230 231 private static List<StringTemplate> getAnnotations(StringTemplateGroup stg, AnnotationSetItem annotationSet) { 232 if (annotationSet == null) { 233 return null; 234 } 235 236 List<StringTemplate> annotationAdaptors = new ArrayList<StringTemplate>(); 237 238 for (AnnotationItem annotationItem: annotationSet.getAnnotations()) { 239 annotationAdaptors.add(AnnotationAdaptor.createTemplate(stg, annotationItem)); 240 } 241 return annotationAdaptors; 242 } 243 244 private List<MethodItem> getMethodItems() { 245 List<MethodItem> methodItems = new ArrayList<MethodItem>(); 246 247 if (encodedMethod.codeItem == null) { 248 return methodItems; 249 } 250 251 AnalyzedInstruction[] instructions; 252 if (baksmali.registerInfo != 0 || baksmali.deodex) { 253 instructions = methodAnalyzer.analyze(); 254 255 ValidationException validationException = methodAnalyzer.getValidationException(); 256 if (validationException != null) { 257 methodItems.add(new CommentMethodItem(stg, 258 String.format("ValidationException: %s" ,validationException.getMessage()), 259 validationException.getCodeAddress(), Integer.MIN_VALUE)); 260 } 261 } else { 262 instructions = methodAnalyzer.makeInstructionArray(); 263 } 264 265 AnalyzedInstruction lastInstruction = null; 266 267 for (int i=instructions.length-1; i>=0; i--) { 268 AnalyzedInstruction instruction = instructions[i]; 269 270 if (!instruction.isDead()) { 271 lastInstruction = instruction; 272 break; 273 } 274 } 275 276 BitSet printPreRegister = new BitSet(registerCount); 277 BitSet printPostRegister = new BitSet(registerCount); 278 279 int currentCodeAddress = 0; 280 for (int i=0; i<instructions.length; i++) { 281 AnalyzedInstruction instruction = instructions[i]; 282 283 MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this, 284 encodedMethod.codeItem, currentCodeAddress, stg, instruction.getInstruction(), 285 instruction == lastInstruction); 286 287 if (instruction.isDead()) { 288 methodItems.add(new CommentedOutMethodItem(stg, methodItem)); 289 } else { 290 methodItems.add(methodItem); 291 } 292 293 if (instruction.getInstruction().getFormat() == Format.UnresolvedNullReference) { 294 methodItems.add(new CommentedOutMethodItem(stg, InstructionMethodItemFactory.makeInstructionFormatMethodItem(this, 295 encodedMethod.codeItem, currentCodeAddress, stg, instruction.getOriginalInstruction(), false))); 296 } 297 298 if (i != instructions.length - 1) { 299 methodItems.add(new BlankMethodItem(stg, currentCodeAddress)); 300 } 301 302 if (baksmali.addCodeOffsets) { 303 methodItems.add(new CommentMethodItem(stg, String.format("@%x", currentCodeAddress), 304 currentCodeAddress, -1000)); 305 } 306 307 if (baksmali.registerInfo != 0 && !instruction.getInstruction().getFormat().variableSizeFormat) { 308 printPreRegister.clear(); 309 printPostRegister.clear(); 310 311 if ((baksmali.registerInfo & main.ALL) != 0) { 312 printPreRegister.set(0, registerCount); 313 printPostRegister.set(0, registerCount); 314 } else { 315 if ((baksmali.registerInfo & main.ALLPRE) != 0) { 316 printPreRegister.set(0, registerCount); 317 } else { 318 if ((baksmali.registerInfo & main.ARGS) != 0) { 319 addArgsRegs(printPreRegister, instruction); 320 } 321 if ((baksmali.registerInfo & main.MERGE) != 0) { 322 addMergeRegs(printPreRegister, instructions, i); 323 } else if ((baksmali.registerInfo & main.FULLMERGE) != 0 && 324 (i == 0 || instruction.isBeginningInstruction())) { 325 addParamRegs(printPreRegister); 326 } 327 } 328 329 if ((baksmali.registerInfo & main.ALLPOST) != 0) { 330 printPostRegister.set(0, registerCount); 331 } else if ((baksmali.registerInfo & main.DEST) != 0) { 332 addDestRegs(printPostRegister, instruction); 333 } 334 } 335 336 if ((baksmali.registerInfo & main.FULLMERGE) != 0) { 337 addFullMergeRegs(printPreRegister, methodItems, methodAnalyzer, i, currentCodeAddress); 338 } 339 340 if (!printPreRegister.isEmpty()) { 341 String comment = getPreInstructionRegisterString(instruction, printPreRegister); 342 if (comment != null && comment.length() > 0) { 343 methodItems.add(new CommentMethodItem(stg, comment, currentCodeAddress, 99.9)); 344 } 345 } 346 347 if (!printPostRegister.isEmpty()) { 348 String comment = getPostInstructionRegisterString(instruction, printPostRegister); 349 if (comment != null && comment.length() > 0) { 350 methodItems.add(new CommentMethodItem(stg, comment, currentCodeAddress, 100.1)); 351 } 352 } 353 } 354 355 currentCodeAddress += instruction.getInstruction().getSize(currentCodeAddress); 356 } 357 358 addTries(methodItems); 359 addDebugInfo(methodItems); 360 361 if (baksmali.useSequentialLabels) { 362 setLabelSequentialNumbers(); 363 } 364 365 366 for (LabelMethodItem labelMethodItem: labelCache.getLabels()) { 367 if (labelMethodItem.isCommentedOut()) { 368 methodItems.add(new CommentedOutMethodItem(stg, labelMethodItem)); 369 } else { 370 methodItems.add(labelMethodItem); 371 } 372 } 373 374 Collections.sort(methodItems); 375 376 return methodItems; 377 } 378 379 private void addArgsRegs(BitSet printPreRegister, AnalyzedInstruction analyzedInstruction) { 380 if (analyzedInstruction.getInstruction() instanceof RegisterRangeInstruction) { 381 RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.getInstruction(); 382 383 printPreRegister.set(instruction.getStartRegister(), 384 instruction.getStartRegister() + instruction.getRegCount()); 385 } else if (analyzedInstruction.getInstruction() instanceof FiveRegisterInstruction) { 386 FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.getInstruction(); 387 int regCount = instruction.getRegCount(); 388 switch (regCount) { 389 case 5: 390 printPreRegister.set(instruction.getRegisterA()); 391 //fall through 392 case 4: 393 printPreRegister.set(instruction.getRegisterG()); 394 //fall through 395 case 3: 396 printPreRegister.set(instruction.getRegisterF()); 397 //fall through 398 case 2: 399 printPreRegister.set(instruction.getRegisterE()); 400 //fall through 401 case 1: 402 printPreRegister.set(instruction.getRegisterD()); 403 } 404 } else if (analyzedInstruction.getInstruction() instanceof ThreeRegisterInstruction) { 405 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.getInstruction(); 406 printPreRegister.set(instruction.getRegisterA()); 407 printPreRegister.set(instruction.getRegisterB()); 408 printPreRegister.set(instruction.getRegisterC()); 409 } else if (analyzedInstruction.getInstruction() instanceof TwoRegisterInstruction) { 410 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.getInstruction(); 411 printPreRegister.set(instruction.getRegisterA()); 412 printPreRegister.set(instruction.getRegisterB()); 413 } else if (analyzedInstruction.getInstruction() instanceof SingleRegisterInstruction) { 414 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.getInstruction(); 415 printPreRegister.set(instruction.getRegisterA()); 416 } 417 } 418 419 private void addFullMergeRegs(BitSet printPreRegister, List<MethodItem> methodItems, MethodAnalyzer methodAnalyzer, 420 int instructionNum, int currentCodeAddress) { 421 if (instructionNum == 0) { 422 return; 423 } 424 425 //MethodAnalyzer cached the analyzedInstructions, so we're not actually re-analyzing, just getting the 426 //instructions that have already been analyzed 427 AnalyzedInstruction[] instructions = methodAnalyzer.analyze(); 428 AnalyzedInstruction instruction = instructions[instructionNum]; 429 430 if (instruction.getPredecessorCount() <= 1) { 431 return; 432 } 433 434 StringBuffer sb = new StringBuffer(); 435 436 for (int registerNum=0; registerNum<registerCount; registerNum++) { 437 sb.setLength(0); 438 sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem, registerNum)); 439 sb.append('='); 440 sb.append(instruction.getPreInstructionRegisterType(registerNum)); 441 sb.append(":merge{"); 442 443 RegisterType mergedRegisterType = null; 444 boolean addRegister = false; 445 446 boolean first = true; 447 for (AnalyzedInstruction predecessor: instruction.getPredecessors()) { 448 RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum); 449 450 if (!first) { 451 if (!addRegister) { 452 sb.append(','); 453 if (mergedRegisterType != predecessorRegisterType) { 454 addRegister = true; 455 } 456 mergedRegisterType = mergedRegisterType.merge(predecessorRegisterType); 457 } 458 } else { 459 mergedRegisterType = predecessorRegisterType; 460 } 461 462 if (predecessor.getInstructionIndex() == -1) { 463 //the fake "StartOfMethod" instruction 464 sb.append("Start:"); 465 } else { 466 sb.append("0x"); 467 sb.append(Integer.toHexString(methodAnalyzer.getInstructionAddress(predecessor))); 468 sb.append(':'); 469 } 470 sb.append(predecessorRegisterType.toString()); 471 first = false; 472 } 473 474 if (!addRegister) { 475 continue; 476 } 477 478 sb.append("}"); 479 480 methodItems.add(new CommentMethodItem(stg, sb.toString(), currentCodeAddress, 99.8)); 481 printPreRegister.clear(registerNum); 482 } 483 } 484 485 private void addMergeRegs(BitSet printPreRegister, AnalyzedInstruction instructions[], int instructionNum) { 486 //MethodAnalyzer cached the analyzedInstructions, so we're not actually re-analyzing, just getting the 487 //instructions that have already been analyzed 488 AnalyzedInstruction instruction = instructions[instructionNum]; 489 490 if (instructionNum == 0) { 491 addParamRegs(printPreRegister); 492 } else { 493 if (instruction.isBeginningInstruction()) { 494 addParamRegs(printPreRegister); 495 } 496 497 if (instruction.getPredecessorCount() <= 1) { 498 //in the common case of an instruction that only has a single predecessor which is the previous 499 //instruction, the pre-instruction registers will always match the previous instruction's 500 //post-instruction registers 501 return; 502 } 503 504 for (int registerNum=0; registerNum<registerCount; registerNum++) { 505 RegisterType mergedRegisterType = instruction.getPreInstructionRegisterType(registerNum); 506 507 for (AnalyzedInstruction predecessor: instruction.getPredecessors()) { 508 if (predecessor.getPostInstructionRegisterType(registerNum) != mergedRegisterType) { 509 printPreRegister.set(registerNum); 510 continue; 511 } 512 } 513 } 514 } 515 } 516 517 private void addParamRegs(BitSet printPreRegister) { 518 int registerCount = encodedMethod.codeItem.getRegisterCount(); 519 520 int parameterRegisterCount = encodedMethod.method.getPrototype().getParameterRegisterCount(); 521 if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) { 522 parameterRegisterCount++; 523 } 524 525 printPreRegister.set(registerCount-parameterRegisterCount, registerCount); 526 } 527 528 private void addDestRegs(BitSet printPostRegister, AnalyzedInstruction analyzedInstruction) { 529 for (int registerNum=0; registerNum<registerCount; registerNum++) { 530 if (analyzedInstruction.getPreInstructionRegisterType(registerNum) != 531 analyzedInstruction.getPostInstructionRegisterType(registerNum)) { 532 printPostRegister.set(registerNum); 533 } 534 } 535 } 536 537 538 private String getPreInstructionRegisterString(AnalyzedInstruction instruction, BitSet registers) { 539 StringBuilder sb = new StringBuilder(); 540 541 for (int registerNum = registers.nextSetBit(0); registerNum >= 0; 542 registerNum = registers.nextSetBit(registerNum + 1)) { 543 544 RegisterType registerType = instruction.getPreInstructionRegisterType(registerNum); 545 sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem,registerNum)); 546 sb.append("="); 547 if (registerType == null) { 548 sb.append("null"); 549 } else { 550 sb.append(registerType.toString()); 551 } 552 sb.append(";"); 553 } 554 555 return sb.toString(); 556 } 557 558 private String getPostInstructionRegisterString(AnalyzedInstruction instruction, BitSet registers) { 559 StringBuilder sb = new StringBuilder(); 560 561 for (int registerNum = registers.nextSetBit(0); registerNum >= 0; 562 registerNum = registers.nextSetBit(registerNum + 1)) { 563 564 RegisterType registerType = instruction.getPostInstructionRegisterType(registerNum); 565 sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem,registerNum)); 566 sb.append("="); 567 if (registerType == null) { 568 sb.append("null"); 569 } else { 570 sb.append(registerType.toString()); 571 } 572 sb.append(";"); 573 } 574 575 return sb.toString(); 576 } 577 578 private void addTries(List<MethodItem> methodItems) { 579 if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) { 580 return; 581 } 582 583 Instruction[] instructions = encodedMethod.codeItem.getInstructions(); 584 585 for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { 586 int startAddress = tryItem.getStartCodeAddress(); 587 int endAddress = tryItem.getStartCodeAddress() + tryItem.getTryLength(); 588 589 /** 590 * The end address points to the address immediately after the end of the last 591 * instruction that the try block covers. We want the .catch directive and end_try 592 * label to be associated with the last covered instruction, so we need to get 593 * the address for that instruction 594 */ 595 596 int index = instructionMap.get(endAddress, -1); 597 int lastInstructionAddress; 598 599 /** 600 * If we couldn't find the index, then the try block probably extends to the last instruction in the 601 * method, and so endAddress would be the address immediately after the end of the last instruction. 602 * Check to make sure this is the case, if not, throw an exception. 603 */ 604 if (index == -1) { 605 Instruction lastInstruction = instructions[instructions.length - 1]; 606 lastInstructionAddress = instructionMap.keyAt(instructionMap.size() - 1); 607 608 if (endAddress != lastInstructionAddress + lastInstruction.getSize(lastInstructionAddress)) { 609 throw new RuntimeException("Invalid code offset " + endAddress + " for the try block end address"); 610 } 611 } else { 612 if (index == 0) { 613 throw new RuntimeException("Unexpected instruction index"); 614 } 615 Instruction lastInstruction = instructions[index - 1]; 616 617 if (lastInstruction.getFormat().variableSizeFormat) { 618 throw new RuntimeException("This try block unexpectedly ends on a switch/array data block."); 619 } 620 621 //getSize for non-variable size formats should return the same size regardless of code address, so just 622 //use a dummy address of "0" 623 lastInstructionAddress = endAddress - lastInstruction.getSize(0); 624 } 625 626 //add the catch all handler if it exists 627 int catchAllAddress = tryItem.encodedCatchHandler.getCatchAllHandlerAddress(); 628 if (catchAllAddress != -1) { 629 CatchMethodItem catchAllMethodItem = new CatchMethodItem(labelCache, lastInstructionAddress, stg, null, 630 startAddress, endAddress, catchAllAddress); 631 methodItems.add(catchAllMethodItem); 632 } 633 634 //add the rest of the handlers 635 for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { 636 //use the address from the last covered instruction 637 CatchMethodItem catchMethodItem = new CatchMethodItem(labelCache, lastInstructionAddress, stg, 638 handler.exceptionType, startAddress, endAddress, handler.getHandlerAddress()); 639 methodItems.add(catchMethodItem); 640 } 641 } 642 } 643 644 private void addDebugInfo(final List<MethodItem> methodItems) { 645 if (encodedMethod.codeItem == null || encodedMethod.codeItem.getDebugInfo() == null) { 646 return; 647 } 648 649 final CodeItem codeItem = encodedMethod.codeItem; 650 DebugInfoItem debugInfoItem = codeItem.getDebugInfo(); 651 652 DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(), 653 new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() { 654 @Override 655 public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name, 656 TypeIdItem type) { 657 methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal", 658 -1, registerNum, name, type, null)); 659 } 660 661 @Override 662 public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, 663 StringIdItem name, TypeIdItem type, 664 StringIdItem signature) { 665 methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal", 666 -1, registerNum, name, type, signature)); 667 } 668 669 @Override 670 public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, 671 TypeIdItem type, StringIdItem signature) { 672 methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "EndLocal", -1, 673 registerNum, name, type, signature)); 674 } 675 676 @Override 677 public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name, 678 TypeIdItem type, StringIdItem signature) { 679 methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "RestartLocal", -1, 680 registerNum, name, type, signature)); 681 } 682 683 @Override 684 public void ProcessSetPrologueEnd(int codeAddress) { 685 methodItems.add(new DebugMethodItem(codeAddress, stg, "EndPrologue", -4)); 686 } 687 688 @Override 689 public void ProcessSetEpilogueBegin(int codeAddress) { 690 methodItems.add(new DebugMethodItem(codeAddress, stg, "StartEpilogue", -4)); 691 } 692 693 @Override 694 public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) { 695 methodItems.add(new DebugMethodItem(codeAddress, stg, "SetFile", -3) { 696 @Override 697 protected void setAttributes(StringTemplate template) { 698 template.setAttribute("FileName", name.getStringValue()); 699 } 700 }); 701 } 702 703 @Override 704 public void ProcessLineEmit(int codeAddress, final int line) { 705 methodItems.add(new DebugMethodItem(codeAddress, stg, "Line", -2) { 706 @Override 707 protected void setAttributes(StringTemplate template) { 708 template.setAttribute("Line", line); 709 } 710 }); 711 } 712 }); 713 } 714 715 private void setLabelSequentialNumbers() { 716 HashMap<String, Integer> nextLabelSequenceByType = new HashMap<String, Integer>(); 717 ArrayList<LabelMethodItem> sortedLabels = new ArrayList<LabelMethodItem>(labelCache.getLabels()); 718 719 //sort the labels by their location in the method 720 Collections.sort(sortedLabels); 721 722 for (LabelMethodItem labelMethodItem: sortedLabels) { 723 Integer labelSequence = nextLabelSequenceByType.get(labelMethodItem.getLabelPrefix()); 724 if (labelSequence == null) { 725 labelSequence = 0; 726 } 727 labelMethodItem.setLabelSequence(labelSequence); 728 nextLabelSequenceByType.put(labelMethodItem.getLabelPrefix(), labelSequence + 1); 729 } 730 } 731 732 public static class LabelCache { 733 protected HashMap<LabelMethodItem, LabelMethodItem> labels = new HashMap<LabelMethodItem, LabelMethodItem>(); 734 735 public LabelCache() { 736 } 737 738 public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) { 739 LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem); 740 if (internedLabelMethodItem != null) { 741 if (!labelMethodItem.isCommentedOut()) { 742 internedLabelMethodItem.setUncommented(); 743 } 744 return internedLabelMethodItem; 745 } 746 labels.put(labelMethodItem, labelMethodItem); 747 return labelMethodItem; 748 } 749 750 751 public Collection<LabelMethodItem> getLabels() { 752 return labels.values(); 753 } 754 } 755} 756