MethodDefinition.java revision 7bb8ace93bb02750a95d3a68fe99153980abdee8
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 List<AnalyzedInstruction> instructions = methodAnalyzer.getInstructions(); 72 73 packedSwitchMap = new SparseIntArray(1); 74 sparseSwitchMap = new SparseIntArray(1); 75 instructionMap = new SparseIntArray(instructions.size()); 76 77 registerCount = encodedMethod.codeItem.getRegisterCount(); 78 79 int currentCodeAddress = 0; 80 for (int i=0; i<instructions.size(); i++) { 81 AnalyzedInstruction instruction = instructions.get(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 if (baksmali.registerInfo != 0 || baksmali.deodex || baksmali.verify) { 252 methodAnalyzer.analyze(); 253 254 ValidationException validationException = methodAnalyzer.getValidationException(); 255 if (validationException != null) { 256 methodItems.add(new CommentMethodItem(stg, 257 String.format("ValidationException: %s" ,validationException.getMessage()), 258 validationException.getCodeAddress(), Integer.MIN_VALUE)); 259 } else if (baksmali.verify) { 260 methodAnalyzer.verify(); 261 262 validationException = methodAnalyzer.getValidationException(); 263 if (validationException != null) { 264 methodItems.add(new CommentMethodItem(stg, 265 String.format("ValidationException: %s" ,validationException.getMessage()), 266 validationException.getCodeAddress(), Integer.MIN_VALUE)); 267 } 268 } 269 } 270 List<AnalyzedInstruction> instructions = methodAnalyzer.getInstructions(); 271 272 AnalyzedInstruction lastInstruction = null; 273 274 for (int i=instructions.size()-1; i>=0; i--) { 275 AnalyzedInstruction instruction = instructions.get(i); 276 277 if (!instruction.isDead()) { 278 lastInstruction = instruction; 279 break; 280 } 281 } 282 283 BitSet printPreRegister = new BitSet(registerCount); 284 BitSet printPostRegister = new BitSet(registerCount); 285 286 int currentCodeAddress = 0; 287 for (int i=0; i<instructions.size(); i++) { 288 AnalyzedInstruction instruction = instructions.get(i); 289 290 MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this, 291 encodedMethod.codeItem, currentCodeAddress, stg, instruction.getInstruction(), 292 instruction == lastInstruction); 293 294 if (instruction.isDead()) { 295 methodItems.add(new CommentedOutMethodItem(stg, methodItem)); 296 } else { 297 methodItems.add(methodItem); 298 } 299 300 if (instruction.getInstruction().getFormat() == Format.UnresolvedNullReference) { 301 methodItems.add(new CommentedOutMethodItem(stg, InstructionMethodItemFactory.makeInstructionFormatMethodItem(this, 302 encodedMethod.codeItem, currentCodeAddress, stg, instruction.getOriginalInstruction(), false))); 303 } 304 305 if (i != instructions.size() - 1) { 306 methodItems.add(new BlankMethodItem(stg, currentCodeAddress)); 307 } 308 309 if (baksmali.addCodeOffsets) { 310 methodItems.add(new CommentMethodItem(stg, String.format("@%x", currentCodeAddress), 311 currentCodeAddress, -1000)); 312 } 313 314 if (baksmali.registerInfo != 0 && !instruction.getInstruction().getFormat().variableSizeFormat) { 315 printPreRegister.clear(); 316 printPostRegister.clear(); 317 318 if ((baksmali.registerInfo & main.ALL) != 0) { 319 printPreRegister.set(0, registerCount); 320 printPostRegister.set(0, registerCount); 321 } else { 322 if ((baksmali.registerInfo & main.ALLPRE) != 0) { 323 printPreRegister.set(0, registerCount); 324 } else { 325 if ((baksmali.registerInfo & main.ARGS) != 0) { 326 addArgsRegs(printPreRegister, instruction); 327 } 328 if ((baksmali.registerInfo & main.MERGE) != 0) { 329 addMergeRegs(printPreRegister, instruction); 330 } else if ((baksmali.registerInfo & main.FULLMERGE) != 0 && 331 (i == 0 || instruction.isBeginningInstruction())) { 332 addParamRegs(printPreRegister); 333 } 334 } 335 336 if ((baksmali.registerInfo & main.ALLPOST) != 0) { 337 printPostRegister.set(0, registerCount); 338 } else if ((baksmali.registerInfo & main.DEST) != 0) { 339 addDestRegs(printPostRegister, instruction); 340 } 341 } 342 343 if ((baksmali.registerInfo & main.FULLMERGE) != 0) { 344 addFullMergeRegs(printPreRegister, methodItems, methodAnalyzer, instruction, currentCodeAddress); 345 } 346 347 if (!printPreRegister.isEmpty()) { 348 String comment = getPreInstructionRegisterString(instruction, printPreRegister); 349 if (comment != null && comment.length() > 0) { 350 methodItems.add(new CommentMethodItem(stg, comment, currentCodeAddress, 99.9)); 351 } 352 } 353 354 if (!printPostRegister.isEmpty()) { 355 String comment = getPostInstructionRegisterString(instruction, printPostRegister); 356 if (comment != null && comment.length() > 0) { 357 methodItems.add(new CommentMethodItem(stg, comment, currentCodeAddress, 100.1)); 358 } 359 } 360 } 361 362 currentCodeAddress += instruction.getInstruction().getSize(currentCodeAddress); 363 } 364 365 addTries(methodItems); 366 addDebugInfo(methodItems); 367 368 if (baksmali.useSequentialLabels) { 369 setLabelSequentialNumbers(); 370 } 371 372 373 for (LabelMethodItem labelMethodItem: labelCache.getLabels()) { 374 if (labelMethodItem.isCommentedOut()) { 375 methodItems.add(new CommentedOutMethodItem(stg, labelMethodItem)); 376 } else { 377 methodItems.add(labelMethodItem); 378 } 379 } 380 381 Collections.sort(methodItems); 382 383 return methodItems; 384 } 385 386 private void addArgsRegs(BitSet printPreRegister, AnalyzedInstruction analyzedInstruction) { 387 if (analyzedInstruction.getInstruction() instanceof RegisterRangeInstruction) { 388 RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.getInstruction(); 389 390 printPreRegister.set(instruction.getStartRegister(), 391 instruction.getStartRegister() + instruction.getRegCount()); 392 } else if (analyzedInstruction.getInstruction() instanceof FiveRegisterInstruction) { 393 FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.getInstruction(); 394 int regCount = instruction.getRegCount(); 395 switch (regCount) { 396 case 5: 397 printPreRegister.set(instruction.getRegisterA()); 398 //fall through 399 case 4: 400 printPreRegister.set(instruction.getRegisterG()); 401 //fall through 402 case 3: 403 printPreRegister.set(instruction.getRegisterF()); 404 //fall through 405 case 2: 406 printPreRegister.set(instruction.getRegisterE()); 407 //fall through 408 case 1: 409 printPreRegister.set(instruction.getRegisterD()); 410 } 411 } else if (analyzedInstruction.getInstruction() instanceof ThreeRegisterInstruction) { 412 ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.getInstruction(); 413 printPreRegister.set(instruction.getRegisterA()); 414 printPreRegister.set(instruction.getRegisterB()); 415 printPreRegister.set(instruction.getRegisterC()); 416 } else if (analyzedInstruction.getInstruction() instanceof TwoRegisterInstruction) { 417 TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.getInstruction(); 418 printPreRegister.set(instruction.getRegisterA()); 419 printPreRegister.set(instruction.getRegisterB()); 420 } else if (analyzedInstruction.getInstruction() instanceof SingleRegisterInstruction) { 421 SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.getInstruction(); 422 printPreRegister.set(instruction.getRegisterA()); 423 } 424 } 425 426 private void addFullMergeRegs(BitSet printPreRegister, List<MethodItem> methodItems, MethodAnalyzer methodAnalyzer, 427 AnalyzedInstruction instruction, int currentCodeAddress) { 428 if (instruction.getPredecessorCount() <= 1) { 429 return; 430 } 431 432 StringBuffer sb = new StringBuffer(); 433 434 for (int registerNum=0; registerNum<registerCount; registerNum++) { 435 sb.setLength(0); 436 sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem, registerNum)); 437 sb.append('='); 438 sb.append(instruction.getPreInstructionRegisterType(registerNum)); 439 sb.append(":merge{"); 440 441 RegisterType mergedRegisterType = null; 442 boolean addRegister = false; 443 444 boolean first = true; 445 for (AnalyzedInstruction predecessor: instruction.getPredecessors()) { 446 RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum); 447 448 if (!first) { 449 if (!addRegister) { 450 sb.append(','); 451 if (mergedRegisterType != predecessorRegisterType) { 452 addRegister = true; 453 } 454 mergedRegisterType = mergedRegisterType.merge(predecessorRegisterType); 455 } 456 } else { 457 mergedRegisterType = predecessorRegisterType; 458 } 459 460 if (predecessor.getInstructionIndex() == -1) { 461 //the fake "StartOfMethod" instruction 462 sb.append("Start:"); 463 } else { 464 sb.append("0x"); 465 sb.append(Integer.toHexString(methodAnalyzer.getInstructionAddress(predecessor))); 466 sb.append(':'); 467 } 468 sb.append(predecessorRegisterType.toString()); 469 first = false; 470 } 471 472 if (!addRegister) { 473 continue; 474 } 475 476 sb.append("}"); 477 478 methodItems.add(new CommentMethodItem(stg, sb.toString(), currentCodeAddress, 99.8)); 479 printPreRegister.clear(registerNum); 480 } 481 } 482 483 private void addMergeRegs(BitSet printPreRegister, AnalyzedInstruction instruction) { 484 if (instruction.isBeginningInstruction()) { 485 addParamRegs(printPreRegister); 486 } 487 488 if (instruction.getPredecessorCount() <= 1) { 489 //in the common case of an instruction that only has a single predecessor which is the previous 490 //instruction, the pre-instruction registers will always match the previous instruction's 491 //post-instruction registers 492 return; 493 } 494 495 for (int registerNum=0; registerNum<registerCount; registerNum++) { 496 RegisterType mergedRegisterType = instruction.getPreInstructionRegisterType(registerNum); 497 498 for (AnalyzedInstruction predecessor: instruction.getPredecessors()) { 499 if (predecessor.getPostInstructionRegisterType(registerNum) != mergedRegisterType) { 500 printPreRegister.set(registerNum); 501 continue; 502 } 503 } 504 } 505 } 506 507 private void addParamRegs(BitSet printPreRegister) { 508 int registerCount = encodedMethod.codeItem.getRegisterCount(); 509 510 int parameterRegisterCount = encodedMethod.method.getPrototype().getParameterRegisterCount(); 511 if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) { 512 parameterRegisterCount++; 513 } 514 515 printPreRegister.set(registerCount-parameterRegisterCount, registerCount); 516 } 517 518 private void addDestRegs(BitSet printPostRegister, AnalyzedInstruction analyzedInstruction) { 519 for (int registerNum=0; registerNum<registerCount; registerNum++) { 520 if (analyzedInstruction.getPreInstructionRegisterType(registerNum) != 521 analyzedInstruction.getPostInstructionRegisterType(registerNum)) { 522 printPostRegister.set(registerNum); 523 } 524 } 525 } 526 527 528 private String getPreInstructionRegisterString(AnalyzedInstruction instruction, BitSet registers) { 529 StringBuilder sb = new StringBuilder(); 530 531 for (int registerNum = registers.nextSetBit(0); registerNum >= 0; 532 registerNum = registers.nextSetBit(registerNum + 1)) { 533 534 RegisterType registerType = instruction.getPreInstructionRegisterType(registerNum); 535 sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem,registerNum)); 536 sb.append("="); 537 if (registerType == null) { 538 sb.append("null"); 539 } else { 540 sb.append(registerType.toString()); 541 } 542 sb.append(";"); 543 } 544 545 return sb.toString(); 546 } 547 548 private String getPostInstructionRegisterString(AnalyzedInstruction instruction, BitSet registers) { 549 StringBuilder sb = new StringBuilder(); 550 551 for (int registerNum = registers.nextSetBit(0); registerNum >= 0; 552 registerNum = registers.nextSetBit(registerNum + 1)) { 553 554 RegisterType registerType = instruction.getPostInstructionRegisterType(registerNum); 555 sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem,registerNum)); 556 sb.append("="); 557 if (registerType == null) { 558 sb.append("null"); 559 } else { 560 sb.append(registerType.toString()); 561 } 562 sb.append(";"); 563 } 564 565 return sb.toString(); 566 } 567 568 private void addTries(List<MethodItem> methodItems) { 569 if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) { 570 return; 571 } 572 573 Instruction[] instructions = encodedMethod.codeItem.getInstructions(); 574 575 for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { 576 int startAddress = tryItem.getStartCodeAddress(); 577 int endAddress = tryItem.getStartCodeAddress() + tryItem.getTryLength(); 578 579 /** 580 * The end address points to the address immediately after the end of the last 581 * instruction that the try block covers. We want the .catch directive and end_try 582 * label to be associated with the last covered instruction, so we need to get 583 * the address for that instruction 584 */ 585 586 int index = instructionMap.get(endAddress, -1); 587 int lastInstructionAddress; 588 589 /** 590 * If we couldn't find the index, then the try block probably extends to the last instruction in the 591 * method, and so endAddress would be the address immediately after the end of the last instruction. 592 * Check to make sure this is the case, if not, throw an exception. 593 */ 594 if (index == -1) { 595 Instruction lastInstruction = instructions[instructions.length - 1]; 596 lastInstructionAddress = instructionMap.keyAt(instructionMap.size() - 1); 597 598 if (endAddress != lastInstructionAddress + lastInstruction.getSize(lastInstructionAddress)) { 599 throw new RuntimeException("Invalid code offset " + endAddress + " for the try block end address"); 600 } 601 } else { 602 if (index == 0) { 603 throw new RuntimeException("Unexpected instruction index"); 604 } 605 Instruction lastInstruction = instructions[index - 1]; 606 607 if (lastInstruction.getFormat().variableSizeFormat) { 608 throw new RuntimeException("This try block unexpectedly ends on a switch/array data block."); 609 } 610 611 //getSize for non-variable size formats should return the same size regardless of code address, so just 612 //use a dummy address of "0" 613 lastInstructionAddress = endAddress - lastInstruction.getSize(0); 614 } 615 616 //add the catch all handler if it exists 617 int catchAllAddress = tryItem.encodedCatchHandler.getCatchAllHandlerAddress(); 618 if (catchAllAddress != -1) { 619 CatchMethodItem catchAllMethodItem = new CatchMethodItem(labelCache, lastInstructionAddress, stg, null, 620 startAddress, endAddress, catchAllAddress); 621 methodItems.add(catchAllMethodItem); 622 } 623 624 //add the rest of the handlers 625 for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { 626 //use the address from the last covered instruction 627 CatchMethodItem catchMethodItem = new CatchMethodItem(labelCache, lastInstructionAddress, stg, 628 handler.exceptionType, startAddress, endAddress, handler.getHandlerAddress()); 629 methodItems.add(catchMethodItem); 630 } 631 } 632 } 633 634 private void addDebugInfo(final List<MethodItem> methodItems) { 635 if (encodedMethod.codeItem == null || encodedMethod.codeItem.getDebugInfo() == null) { 636 return; 637 } 638 639 final CodeItem codeItem = encodedMethod.codeItem; 640 DebugInfoItem debugInfoItem = codeItem.getDebugInfo(); 641 642 DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(), 643 new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() { 644 @Override 645 public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name, 646 TypeIdItem type) { 647 methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal", 648 -1, registerNum, name, type, null)); 649 } 650 651 @Override 652 public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, 653 StringIdItem name, TypeIdItem type, 654 StringIdItem signature) { 655 methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal", 656 -1, registerNum, name, type, signature)); 657 } 658 659 @Override 660 public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, 661 TypeIdItem type, StringIdItem signature) { 662 methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "EndLocal", -1, 663 registerNum, name, type, signature)); 664 } 665 666 @Override 667 public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name, 668 TypeIdItem type, StringIdItem signature) { 669 methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "RestartLocal", -1, 670 registerNum, name, type, signature)); 671 } 672 673 @Override 674 public void ProcessSetPrologueEnd(int codeAddress) { 675 methodItems.add(new DebugMethodItem(codeAddress, stg, "EndPrologue", -4)); 676 } 677 678 @Override 679 public void ProcessSetEpilogueBegin(int codeAddress) { 680 methodItems.add(new DebugMethodItem(codeAddress, stg, "StartEpilogue", -4)); 681 } 682 683 @Override 684 public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) { 685 methodItems.add(new DebugMethodItem(codeAddress, stg, "SetFile", -3) { 686 @Override 687 protected void setAttributes(StringTemplate template) { 688 template.setAttribute("FileName", name.getStringValue()); 689 } 690 }); 691 } 692 693 @Override 694 public void ProcessLineEmit(int codeAddress, final int line) { 695 methodItems.add(new DebugMethodItem(codeAddress, stg, "Line", -2) { 696 @Override 697 protected void setAttributes(StringTemplate template) { 698 template.setAttribute("Line", line); 699 } 700 }); 701 } 702 }); 703 } 704 705 private void setLabelSequentialNumbers() { 706 HashMap<String, Integer> nextLabelSequenceByType = new HashMap<String, Integer>(); 707 ArrayList<LabelMethodItem> sortedLabels = new ArrayList<LabelMethodItem>(labelCache.getLabels()); 708 709 //sort the labels by their location in the method 710 Collections.sort(sortedLabels); 711 712 for (LabelMethodItem labelMethodItem: sortedLabels) { 713 Integer labelSequence = nextLabelSequenceByType.get(labelMethodItem.getLabelPrefix()); 714 if (labelSequence == null) { 715 labelSequence = 0; 716 } 717 labelMethodItem.setLabelSequence(labelSequence); 718 nextLabelSequenceByType.put(labelMethodItem.getLabelPrefix(), labelSequence + 1); 719 } 720 } 721 722 public static class LabelCache { 723 protected HashMap<LabelMethodItem, LabelMethodItem> labels = new HashMap<LabelMethodItem, LabelMethodItem>(); 724 725 public LabelCache() { 726 } 727 728 public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) { 729 LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem); 730 if (internedLabelMethodItem != null) { 731 if (!labelMethodItem.isCommentedOut()) { 732 internedLabelMethodItem.setUncommented(); 733 } 734 return internedLabelMethodItem; 735 } 736 labels.put(labelMethodItem, labelMethodItem); 737 return labelMethodItem; 738 } 739 740 741 public Collection<LabelMethodItem> getLabels() { 742 return labels.values(); 743 } 744 } 745} 746