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 29 package org.jf.baksmali.Adaptors; 30 31 import com.google.common.collect.ImmutableList; 32 import com.google.common.collect.Lists; 33 import org.jf.baksmali.Adaptors.Debug.DebugMethodItem; 34 import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory; 35 import org.jf.baksmali.BaksmaliOptions; 36 import org.jf.dexlib2.AccessFlags; 37 import org.jf.dexlib2.Format; 38 import org.jf.dexlib2.Opcode; 39 import org.jf.dexlib2.ReferenceType; 40 import org.jf.dexlib2.analysis.AnalysisException; 41 import org.jf.dexlib2.analysis.AnalyzedInstruction; 42 import org.jf.dexlib2.analysis.MethodAnalyzer; 43 import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex; 44 import org.jf.dexlib2.iface.*; 45 import org.jf.dexlib2.iface.debug.DebugItem; 46 import org.jf.dexlib2.iface.instruction.Instruction; 47 import org.jf.dexlib2.iface.instruction.OffsetInstruction; 48 import org.jf.dexlib2.iface.instruction.ReferenceInstruction; 49 import org.jf.dexlib2.iface.instruction.formats.Instruction31t; 50 import org.jf.dexlib2.iface.reference.MethodReference; 51 import org.jf.dexlib2.immutable.instruction.ImmutableInstruction31t; 52 import org.jf.dexlib2.util.InstructionOffsetMap; 53 import org.jf.dexlib2.util.InstructionOffsetMap.InvalidInstructionOffset; 54 import org.jf.dexlib2.util.ReferenceUtil; 55 import org.jf.dexlib2.util.SyntheticAccessorResolver; 56 import org.jf.dexlib2.util.SyntheticAccessorResolver.AccessedMember; 57 import org.jf.dexlib2.util.TypeUtils; 58 import org.jf.util.ExceptionWithContext; 59 import org.jf.util.IndentingWriter; 60 import org.jf.util.SparseIntArray; 61 62 import javax.annotation.Nonnull; 63 import javax.annotation.Nullable; 64 import java.io.IOException; 65 import java.util.*; 66 67 public class MethodDefinition { 68 @Nonnull public final ClassDefinition classDef; 69 @Nonnull public final Method method; 70 @Nonnull public final MethodImplementation methodImpl; 71 @Nonnull public final ImmutableList<Instruction> instructions; 72 @Nonnull public final List<Instruction> effectiveInstructions; 73 74 @Nonnull public final ImmutableList<MethodParameter> methodParameters; 75 public RegisterFormatter registerFormatter; 76 77 @Nonnull private final LabelCache labelCache = new LabelCache(); 78 79 @Nonnull private final SparseIntArray packedSwitchMap; 80 @Nonnull private final SparseIntArray sparseSwitchMap; 81 @Nonnull private final InstructionOffsetMap instructionOffsetMap; 82 MethodDefinition(@onnull ClassDefinition classDef, @Nonnull Method method, @Nonnull MethodImplementation methodImpl)83 public MethodDefinition(@Nonnull ClassDefinition classDef, @Nonnull Method method, 84 @Nonnull MethodImplementation methodImpl) { 85 this.classDef = classDef; 86 this.method = method; 87 this.methodImpl = methodImpl; 88 89 try { 90 //TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh. 91 92 instructions = ImmutableList.copyOf(methodImpl.getInstructions()); 93 methodParameters = ImmutableList.copyOf(method.getParameters()); 94 95 effectiveInstructions = Lists.newArrayList(instructions); 96 97 packedSwitchMap = new SparseIntArray(0); 98 sparseSwitchMap = new SparseIntArray(0); 99 instructionOffsetMap = new InstructionOffsetMap(instructions); 100 101 int endOffset = instructionOffsetMap.getInstructionCodeOffset(instructions.size()-1) + 102 instructions.get(instructions.size()-1).getCodeUnits(); 103 104 for (int i=0; i<instructions.size(); i++) { 105 Instruction instruction = instructions.get(i); 106 107 Opcode opcode = instruction.getOpcode(); 108 if (opcode == Opcode.PACKED_SWITCH) { 109 boolean valid = true; 110 int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i); 111 int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset(); 112 try { 113 targetOffset = findPayloadOffset(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD); 114 } catch (InvalidSwitchPayload ex) { 115 valid = false; 116 } 117 if (valid) { 118 if (packedSwitchMap.get(targetOffset, -1) != -1) { 119 Instruction payloadInstruction = 120 findSwitchPayload(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD); 121 targetOffset = endOffset; 122 effectiveInstructions.set(i, new ImmutableInstruction31t(opcode, 123 ((Instruction31t)instruction).getRegisterA(), targetOffset-codeOffset)); 124 effectiveInstructions.add(payloadInstruction); 125 endOffset += payloadInstruction.getCodeUnits(); 126 } 127 packedSwitchMap.append(targetOffset, codeOffset); 128 } 129 } else if (opcode == Opcode.SPARSE_SWITCH) { 130 boolean valid = true; 131 int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i); 132 int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset(); 133 try { 134 targetOffset = findPayloadOffset(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD); 135 } catch (InvalidSwitchPayload ex) { 136 valid = false; 137 // The offset to the payload instruction was invalid. Nothing to do, except that we won't 138 // add this instruction to the map. 139 } 140 if (valid) { 141 if (sparseSwitchMap.get(targetOffset, -1) != -1) { 142 Instruction payloadInstruction = 143 findSwitchPayload(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD); 144 targetOffset = endOffset; 145 effectiveInstructions.set(i, new ImmutableInstruction31t(opcode, 146 ((Instruction31t)instruction).getRegisterA(), targetOffset-codeOffset)); 147 effectiveInstructions.add(payloadInstruction); 148 endOffset += payloadInstruction.getCodeUnits(); 149 } 150 sparseSwitchMap.append(targetOffset, codeOffset); 151 } 152 } 153 } 154 } catch (Exception ex) { 155 String methodString; 156 try { 157 methodString = ReferenceUtil.getMethodDescriptor(method); 158 } catch (Exception ex2) { 159 throw ExceptionWithContext.withContext(ex, "Error while processing method"); 160 } 161 throw ExceptionWithContext.withContext(ex, "Error while processing method %s", methodString); 162 } 163 } 164 writeEmptyMethodTo(IndentingWriter writer, Method method, BaksmaliOptions options)165 public static void writeEmptyMethodTo(IndentingWriter writer, Method method, 166 BaksmaliOptions options) throws IOException { 167 writer.write(".method "); 168 writeAccessFlags(writer, method.getAccessFlags()); 169 writer.write(method.getName()); 170 writer.write("("); 171 ImmutableList<MethodParameter> methodParameters = ImmutableList.copyOf(method.getParameters()); 172 for (MethodParameter parameter: methodParameters) { 173 writer.write(parameter.getType()); 174 } 175 writer.write(")"); 176 writer.write(method.getReturnType()); 177 writer.write('\n'); 178 179 writer.indent(4); 180 writeParameters(writer, method, methodParameters, options); 181 182 String containingClass = null; 183 if (options.implicitReferences) { 184 containingClass = method.getDefiningClass(); 185 } 186 AnnotationFormatter.writeTo(writer, method.getAnnotations(), containingClass); 187 188 writer.deindent(4); 189 writer.write(".end method\n"); 190 } 191 writeTo(IndentingWriter writer)192 public void writeTo(IndentingWriter writer) throws IOException { 193 int parameterRegisterCount = 0; 194 if (!AccessFlags.STATIC.isSet(method.getAccessFlags())) { 195 parameterRegisterCount++; 196 } 197 198 writer.write(".method "); 199 writeAccessFlags(writer, method.getAccessFlags()); 200 writer.write(method.getName()); 201 writer.write("("); 202 for (MethodParameter parameter: methodParameters) { 203 String type = parameter.getType(); 204 writer.write(type); 205 parameterRegisterCount++; 206 if (TypeUtils.isWideType(type)) { 207 parameterRegisterCount++; 208 } 209 } 210 writer.write(")"); 211 writer.write(method.getReturnType()); 212 writer.write('\n'); 213 214 writer.indent(4); 215 if (classDef.options.localsDirective) { 216 writer.write(".locals "); 217 writer.printSignedIntAsDec(methodImpl.getRegisterCount() - parameterRegisterCount); 218 } else { 219 writer.write(".registers "); 220 writer.printSignedIntAsDec(methodImpl.getRegisterCount()); 221 } 222 writer.write('\n'); 223 writeParameters(writer, method, methodParameters, classDef.options); 224 225 if (registerFormatter == null) { 226 registerFormatter = new RegisterFormatter(classDef.options, methodImpl.getRegisterCount(), 227 parameterRegisterCount); 228 } 229 230 String containingClass = null; 231 if (classDef.options.implicitReferences) { 232 containingClass = method.getDefiningClass(); 233 } 234 AnnotationFormatter.writeTo(writer, method.getAnnotations(), containingClass); 235 236 writer.write('\n'); 237 238 List<MethodItem> methodItems = getMethodItems(); 239 for (MethodItem methodItem: methodItems) { 240 if (methodItem.writeTo(writer)) { 241 writer.write('\n'); 242 } 243 } 244 writer.deindent(4); 245 writer.write(".end method\n"); 246 } 247 findSwitchPayload(int targetOffset, Opcode type)248 public Instruction findSwitchPayload(int targetOffset, Opcode type) { 249 int targetIndex; 250 try { 251 targetIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(targetOffset); 252 } catch (InvalidInstructionOffset ex) { 253 throw new InvalidSwitchPayload(targetOffset); 254 } 255 256 //TODO: does dalvik let you pad with multiple nops? 257 //TODO: does dalvik let a switch instruction point to a non-payload instruction? 258 259 Instruction instruction = instructions.get(targetIndex); 260 if (instruction.getOpcode() != type) { 261 // maybe it's pointing to a NOP padding instruction. Look at the next instruction 262 if (instruction.getOpcode() == Opcode.NOP) { 263 targetIndex += 1; 264 if (targetIndex < instructions.size()) { 265 instruction = instructions.get(targetIndex); 266 if (instruction.getOpcode() == type) { 267 return instruction; 268 } 269 } 270 } 271 throw new InvalidSwitchPayload(targetOffset); 272 } else { 273 return instruction; 274 } 275 } 276 findPayloadOffset(int targetOffset, Opcode type)277 public int findPayloadOffset(int targetOffset, Opcode type) { 278 int targetIndex; 279 try { 280 targetIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(targetOffset); 281 } catch (InvalidInstructionOffset ex) { 282 throw new InvalidSwitchPayload(targetOffset); 283 } 284 285 //TODO: does dalvik let you pad with multiple nops? 286 //TODO: does dalvik let a switch instruction point to a non-payload instruction? 287 288 Instruction instruction = instructions.get(targetIndex); 289 if (instruction.getOpcode() != type) { 290 // maybe it's pointing to a NOP padding instruction. Look at the next instruction 291 if (instruction.getOpcode() == Opcode.NOP) { 292 targetIndex += 1; 293 if (targetIndex < instructions.size()) { 294 instruction = instructions.get(targetIndex); 295 if (instruction.getOpcode() == type) { 296 return instructionOffsetMap.getInstructionCodeOffset(targetIndex); 297 } 298 } 299 } 300 throw new InvalidSwitchPayload(targetOffset); 301 } else { 302 return targetOffset; 303 } 304 } 305 writeAccessFlags(IndentingWriter writer, int accessFlags)306 private static void writeAccessFlags(IndentingWriter writer, int accessFlags) 307 throws IOException { 308 for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(accessFlags)) { 309 writer.write(accessFlag.toString()); 310 writer.write(' '); 311 } 312 } 313 writeParameters(IndentingWriter writer, Method method, List<? extends MethodParameter> parameters, BaksmaliOptions options)314 private static void writeParameters(IndentingWriter writer, Method method, 315 List<? extends MethodParameter> parameters, 316 BaksmaliOptions options) throws IOException { 317 boolean isStatic = AccessFlags.STATIC.isSet(method.getAccessFlags()); 318 int registerNumber = isStatic?0:1; 319 for (MethodParameter parameter: parameters) { 320 String parameterType = parameter.getType(); 321 String parameterName = parameter.getName(); 322 Collection<? extends Annotation> annotations = parameter.getAnnotations(); 323 if ((options.debugInfo && parameterName != null) || annotations.size() != 0) { 324 writer.write(".param p"); 325 writer.printSignedIntAsDec(registerNumber); 326 327 if (parameterName != null && options.debugInfo) { 328 writer.write(", "); 329 ReferenceFormatter.writeStringReference(writer, parameterName); 330 } 331 writer.write(" # "); 332 writer.write(parameterType); 333 writer.write("\n"); 334 if (annotations.size() > 0) { 335 writer.indent(4); 336 337 String containingClass = null; 338 if (options.implicitReferences) { 339 containingClass = method.getDefiningClass(); 340 } 341 AnnotationFormatter.writeTo(writer, annotations, containingClass); 342 writer.deindent(4); 343 writer.write(".end param\n"); 344 } 345 } 346 347 registerNumber++; 348 if (TypeUtils.isWideType(parameterType)) { 349 registerNumber++; 350 } 351 } 352 } 353 getLabelCache()354 @Nonnull public LabelCache getLabelCache() { 355 return labelCache; 356 } 357 getPackedSwitchBaseAddress(int packedSwitchPayloadCodeOffset)358 public int getPackedSwitchBaseAddress(int packedSwitchPayloadCodeOffset) { 359 return packedSwitchMap.get(packedSwitchPayloadCodeOffset, -1); 360 } 361 getSparseSwitchBaseAddress(int sparseSwitchPayloadCodeOffset)362 public int getSparseSwitchBaseAddress(int sparseSwitchPayloadCodeOffset) { 363 return sparseSwitchMap.get(sparseSwitchPayloadCodeOffset, -1); 364 } 365 getMethodItems()366 private List<MethodItem> getMethodItems() { 367 ArrayList<MethodItem> methodItems = new ArrayList<MethodItem>(); 368 369 if ((classDef.options.registerInfo != 0) || (classDef.options.normalizeVirtualMethods) || 370 (classDef.options.deodex && needsAnalyzed())) { 371 addAnalyzedInstructionMethodItems(methodItems); 372 } else { 373 addInstructionMethodItems(methodItems); 374 } 375 376 addTries(methodItems); 377 if (classDef.options.debugInfo) { 378 addDebugInfo(methodItems); 379 } 380 381 if (classDef.options.sequentialLabels) { 382 setLabelSequentialNumbers(); 383 } 384 385 for (LabelMethodItem labelMethodItem: labelCache.getLabels()) { 386 methodItems.add(labelMethodItem); 387 } 388 389 Collections.sort(methodItems); 390 391 return methodItems; 392 } 393 needsAnalyzed()394 private boolean needsAnalyzed() { 395 for (Instruction instruction: methodImpl.getInstructions()) { 396 if (instruction.getOpcode().odexOnly()) { 397 return true; 398 } 399 } 400 return false; 401 } 402 addInstructionMethodItems(List<MethodItem> methodItems)403 private void addInstructionMethodItems(List<MethodItem> methodItems) { 404 int currentCodeAddress = 0; 405 406 for (int i=0; i<effectiveInstructions.size(); i++) { 407 Instruction instruction = effectiveInstructions.get(i); 408 409 MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this, 410 currentCodeAddress, instruction); 411 412 methodItems.add(methodItem); 413 414 if (i != effectiveInstructions.size() - 1) { 415 methodItems.add(new BlankMethodItem(currentCodeAddress)); 416 } 417 418 if (classDef.options.codeOffsets) { 419 methodItems.add(new MethodItem(currentCodeAddress) { 420 421 @Override 422 public double getSortOrder() { 423 return -1000; 424 } 425 426 @Override 427 public boolean writeTo(IndentingWriter writer) throws IOException { 428 writer.write("#@"); 429 writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFFL); 430 return true; 431 } 432 }); 433 } 434 435 if (classDef.options.accessorComments && classDef.options.syntheticAccessorResolver != null && 436 (instruction instanceof ReferenceInstruction)) { 437 Opcode opcode = instruction.getOpcode(); 438 439 if (opcode.referenceType == ReferenceType.METHOD) { 440 MethodReference methodReference = null; 441 try { 442 methodReference = (MethodReference)((ReferenceInstruction)instruction).getReference(); 443 } catch (InvalidItemIndex ex) { 444 // just ignore it for now. We'll deal with it later, when processing the instructions 445 // themselves 446 } 447 448 if (methodReference != null && 449 SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodReference.getName())) { 450 AccessedMember accessedMember = 451 classDef.options.syntheticAccessorResolver.getAccessedMember(methodReference); 452 if (accessedMember != null) { 453 methodItems.add(new SyntheticAccessCommentMethodItem(accessedMember, currentCodeAddress)); 454 } 455 } 456 } 457 } 458 459 currentCodeAddress += instruction.getCodeUnits(); 460 } 461 } 462 addAnalyzedInstructionMethodItems(List<MethodItem> methodItems)463 private void addAnalyzedInstructionMethodItems(List<MethodItem> methodItems) { 464 MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classDef.options.classPath, method, 465 classDef.options.inlineResolver, classDef.options.normalizeVirtualMethods); 466 467 AnalysisException analysisException = methodAnalyzer.getAnalysisException(); 468 if (analysisException != null) { 469 // TODO: need to keep track of whether any errors occurred, so we can exit with a non-zero result 470 methodItems.add(new CommentMethodItem( 471 String.format("AnalysisException: %s", analysisException.getMessage()), 472 analysisException.codeAddress, Integer.MIN_VALUE)); 473 analysisException.printStackTrace(System.err); 474 } 475 476 List<AnalyzedInstruction> instructions = methodAnalyzer.getAnalyzedInstructions(); 477 478 int currentCodeAddress = 0; 479 for (int i=0; i<instructions.size(); i++) { 480 AnalyzedInstruction instruction = instructions.get(i); 481 482 MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem( 483 this, currentCodeAddress, instruction.getInstruction()); 484 485 methodItems.add(methodItem); 486 487 if (instruction.getInstruction().getOpcode().format == Format.UnresolvedOdexInstruction) { 488 methodItems.add(new CommentedOutMethodItem( 489 InstructionMethodItemFactory.makeInstructionFormatMethodItem( 490 this, currentCodeAddress, instruction.getOriginalInstruction()))); 491 } 492 493 if (i != instructions.size() - 1) { 494 methodItems.add(new BlankMethodItem(currentCodeAddress)); 495 } 496 497 if (classDef.options.codeOffsets) { 498 methodItems.add(new MethodItem(currentCodeAddress) { 499 500 @Override 501 public double getSortOrder() { 502 return -1000; 503 } 504 505 @Override 506 public boolean writeTo(IndentingWriter writer) throws IOException { 507 writer.write("#@"); 508 writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFFL); 509 return true; 510 } 511 }); 512 } 513 514 if (classDef.options.registerInfo != 0 && 515 !instruction.getInstruction().getOpcode().format.isPayloadFormat) { 516 methodItems.add( 517 new PreInstructionRegisterInfoMethodItem(classDef.options.registerInfo, 518 methodAnalyzer, registerFormatter, instruction, currentCodeAddress)); 519 520 methodItems.add( 521 new PostInstructionRegisterInfoMethodItem(registerFormatter, instruction, currentCodeAddress)); 522 } 523 524 currentCodeAddress += instruction.getInstruction().getCodeUnits(); 525 } 526 } 527 addTries(List<MethodItem> methodItems)528 private void addTries(List<MethodItem> methodItems) { 529 List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks = methodImpl.getTryBlocks(); 530 if (tryBlocks.size() == 0) { 531 return; 532 } 533 534 int lastInstructionAddress = instructionOffsetMap.getInstructionCodeOffset(instructions.size() - 1); 535 int codeSize = lastInstructionAddress + instructions.get(instructions.size() - 1).getCodeUnits(); 536 537 for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) { 538 int startAddress = tryBlock.getStartCodeAddress(); 539 int endAddress = startAddress + tryBlock.getCodeUnitCount(); 540 541 if (startAddress >= codeSize) { 542 throw new RuntimeException(String.format("Try start offset %d is past the end of the code block.", 543 startAddress)); 544 } 545 // Note: not >=. endAddress == codeSize is valid, when the try covers the last instruction 546 if (endAddress > codeSize) { 547 throw new RuntimeException(String.format("Try end offset %d is past the end of the code block.", 548 endAddress)); 549 } 550 551 /** 552 * The end address points to the address immediately after the end of the last 553 * instruction that the try block covers. We want the .catch directive and end_try 554 * label to be associated with the last covered instruction, so we need to get 555 * the address for that instruction 556 */ 557 558 int lastCoveredIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(endAddress - 1, false); 559 int lastCoveredAddress = instructionOffsetMap.getInstructionCodeOffset(lastCoveredIndex); 560 561 for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) { 562 int handlerAddress = handler.getHandlerCodeAddress(); 563 if (handlerAddress >= codeSize) { 564 throw new ExceptionWithContext( 565 "Exception handler offset %d is past the end of the code block.", handlerAddress); 566 } 567 568 //use the address from the last covered instruction 569 CatchMethodItem catchMethodItem = new CatchMethodItem(classDef.options, labelCache, lastCoveredAddress, 570 handler.getExceptionType(), startAddress, endAddress, handlerAddress); 571 methodItems.add(catchMethodItem); 572 } 573 } 574 } 575 addDebugInfo(final List<MethodItem> methodItems)576 private void addDebugInfo(final List<MethodItem> methodItems) { 577 for (DebugItem debugItem: methodImpl.getDebugItems()) { 578 methodItems.add(DebugMethodItem.build(registerFormatter, debugItem)); 579 } 580 } 581 setLabelSequentialNumbers()582 private void setLabelSequentialNumbers() { 583 HashMap<String, Integer> nextLabelSequenceByType = new HashMap<String, Integer>(); 584 ArrayList<LabelMethodItem> sortedLabels = new ArrayList<LabelMethodItem>(labelCache.getLabels()); 585 586 //sort the labels by their location in the method 587 Collections.sort(sortedLabels); 588 589 for (LabelMethodItem labelMethodItem: sortedLabels) { 590 Integer labelSequence = nextLabelSequenceByType.get(labelMethodItem.getLabelPrefix()); 591 if (labelSequence == null) { 592 labelSequence = 0; 593 } 594 labelMethodItem.setLabelSequence(labelSequence); 595 nextLabelSequenceByType.put(labelMethodItem.getLabelPrefix(), labelSequence + 1); 596 } 597 } 598 599 @Nullable getContainingClassForImplicitReference()600 private String getContainingClassForImplicitReference() { 601 if (classDef.options.implicitReferences) { 602 return classDef.classDef.getType(); 603 } 604 return null; 605 } 606 607 public static class LabelCache { 608 protected HashMap<LabelMethodItem, LabelMethodItem> labels = new HashMap<LabelMethodItem, LabelMethodItem>(); 609 LabelCache()610 public LabelCache() { 611 } 612 internLabel(LabelMethodItem labelMethodItem)613 public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) { 614 LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem); 615 if (internedLabelMethodItem != null) { 616 return internedLabelMethodItem; 617 } 618 labels.put(labelMethodItem, labelMethodItem); 619 return labelMethodItem; 620 } 621 622 getLabels()623 public Collection<LabelMethodItem> getLabels() { 624 return labels.values(); 625 } 626 } 627 628 public static class InvalidSwitchPayload extends ExceptionWithContext { 629 private final int payloadOffset; 630 InvalidSwitchPayload(int payloadOffset)631 public InvalidSwitchPayload(int payloadOffset) { 632 super("No switch payload at offset: %d", payloadOffset); 633 this.payloadOffset = payloadOffset; 634 } 635 getPayloadOffset()636 public int getPayloadOffset() { 637 return payloadOffset; 638 } 639 } 640 } 641