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