DebugInfoEncoder.java revision 2ad60cfc28e14ee8f0bb038720836a4696c478ad
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dx.dex.file; 18 19import com.android.dx.dex.code.LocalList; 20import com.android.dx.dex.code.PositionList; 21import com.android.dx.rop.code.RegisterSpec; 22import com.android.dx.rop.code.SourcePosition; 23import com.android.dx.rop.cst.CstMethodRef; 24import com.android.dx.rop.cst.CstType; 25import com.android.dx.rop.cst.CstUtf8; 26import com.android.dx.rop.type.Prototype; 27import com.android.dx.rop.type.StdTypeList; 28import com.android.dx.rop.type.Type; 29import com.android.dx.util.ByteArrayAnnotatedOutput; 30import com.android.dx.util.AnnotatedOutput; 31import com.android.dx.util.ExceptionWithContext; 32 33import java.io.IOException; 34import java.io.PrintWriter; 35import java.util.ArrayList; 36import java.util.Collections; 37import java.util.Comparator; 38import java.util.BitSet; 39 40import static com.android.dx.dex.file.DebugInfoConstants.*; 41 42/** 43 * An encoder for the dex debug info state machine format. The format 44 * for each method enrty is as follows: 45 * <ol> 46 * <li> signed LEB128: initial value for line register. 47 * <li> n instances of signed LEB128: string indicies (offset by 1) 48 * for each method argument in left-to-right order 49 * with <code>this</code> excluded. A value of '0' indicates "no name" 50 * <li> A sequence of special or normal opcodes as defined in 51 * <code>DebugInfoConstants</code>. 52 * <li> A single terminating <code>OP_END_SEQUENCE</code> 53 * </ol> 54 */ 55public final class DebugInfoEncoder { 56 private static final boolean DEBUG = false; 57 58 private final PositionList positionlist; 59 private final LocalList locallist; 60 private final ByteArrayAnnotatedOutput output; 61 private final DexFile file; 62 private final int codeSize; 63 private final int regSize; 64 65 private final Prototype desc; 66 private final boolean isStatic; 67 68 /** current encoding state: bytecode address */ 69 private int address = 0; 70 71 /** current encoding state: line number */ 72 private int line = 1; 73 74 /** 75 * if non-null: the output to write annotations to. No normal 76 * output is written to this. 77 */ 78 private AnnotatedOutput annotateTo; 79 80 /** if non-null: another possible output for annotations */ 81 private PrintWriter debugPrint; 82 83 /** if non-null: the prefix for each annotation or debugPrint line */ 84 private String prefix; 85 86 /** true if output should be consumed during annotation */ 87 private boolean shouldConsume; 88 89 /** indexed by register; last local alive in register */ 90 private final LocalList.Entry[] lastEntryForReg; 91 92 /** 93 * Creates an instance. 94 * 95 * @param pl null-ok 96 * @param ll null-ok 97 * @param file null-ok; may only be <code>null</code> if simply using 98 * this class to do a debug print 99 * @param codeSize 100 * @param regSize 101 * @param isStatic 102 * @param ref 103 */ 104 public DebugInfoEncoder(PositionList pl, LocalList ll, 105 DexFile file, int codeSize, int regSize, 106 boolean isStatic, CstMethodRef ref) { 107 108 this.positionlist = pl; 109 this.locallist = ll; 110 this.file = file; 111 output = new ByteArrayAnnotatedOutput(); 112 this.desc = ref.getPrototype(); 113 this.isStatic = isStatic; 114 115 this.codeSize = codeSize; 116 this.regSize = regSize; 117 118 lastEntryForReg = new LocalList.Entry[regSize]; 119 } 120 121 /** 122 * Annotates or writes a message to the <code>debugPrint</code> writer 123 * if applicable. 124 * 125 * @param length the number of bytes associated with this message 126 * @param message the message itself 127 */ 128 private void annotate(int length, String message) { 129 if (prefix != null) { 130 message = prefix + message; 131 } 132 133 if (annotateTo != null) { 134 annotateTo.annotate(shouldConsume ? length : 0, message); 135 } 136 137 if (debugPrint != null) { 138 debugPrint.println(message); 139 } 140 } 141 142 /** 143 * Converts this (PositionList, LocalList) pair into a state machine 144 * sequence. 145 * 146 * @return encoded byte sequence without padding and 147 * terminated with a <code>'\00'</code> 148 */ 149 public byte[] convert() { 150 try { 151 byte[] ret; 152 ret = convert0(); 153 154 if (DEBUG) { 155 for (int i = 0 ; i < ret.length; i++) { 156 System.err.printf("byte %02x\n", (0xff & ret[i])); 157 } 158 } 159 160 return ret; 161 } catch (IOException ex) { 162 throw ExceptionWithContext 163 .withContext(ex, "...while encoding debug info"); 164 } 165 } 166 167 /** 168 * Converts and produces annotations on a stream. Does not write 169 * actual bits to the <code>AnnotatedOutput</code>. 170 * 171 * @param prefix null-ok; prefix to attach to each line of output 172 * @param debugPrint null-ok; if specified, an alternate output for 173 * annotations 174 * @param out null-ok; if specified, where annotations should go 175 * @param consume whether to claim to have consumed output for 176 * <code>out</code> 177 * @return output sequence 178 */ 179 public byte[] convertAndAnnotate(String prefix, PrintWriter debugPrint, 180 AnnotatedOutput out, boolean consume) { 181 this.prefix = prefix; 182 this.debugPrint = debugPrint; 183 annotateTo = out; 184 shouldConsume = consume; 185 186 byte[] result = convert(); 187 188 return result; 189 } 190 191 private byte[] convert0() throws IOException { 192 ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions(); 193 ArrayList<LocalList.Entry> sortedLocalsStart = buildLocalsStart(); 194 195 // Parameter locals are removed from sortedLocalsStart here. 196 ArrayList<LocalList.Entry> methodArgs 197 = extractMethodArguments(sortedLocalsStart); 198 199 ArrayList<LocalList.Entry> sortedLocalsEnd 200 = buildLocalsEnd(sortedLocalsStart); 201 202 emitHeader(sortedPositions, methodArgs); 203 204 // TODO: Make this mark the actual prologue end. 205 output.writeByte(DBG_SET_PROLOGUE_END); 206 207 if (annotateTo != null || debugPrint != null) { 208 annotate(1, String.format("%04x: prologue end",address)); 209 } 210 211 int szp = sortedPositions.size(); 212 int szl = sortedLocalsStart.size(); 213 214 // Current index in sortedPositions 215 int curp = 0; 216 // Current index in sortedLocalsStart 217 int curls = 0; 218 // Current index in sortedLocalsEnd 219 int curle = 0; 220 221 for (;;) { 222 /* 223 * Emit any information for the current address. 224 */ 225 226 curle = emitLocalEndsAtAddress(curle, sortedLocalsEnd, curls, 227 sortedLocalsStart); 228 229 /* 230 * Our locals-sorted-by-range-end has reached the end 231 * of the code block. Ignore everything else. 232 */ 233 if (address == codeSize) { 234 curle = szl; 235 } 236 237 curls = emitLocalStartsAtAddress(curls, sortedLocalsStart); 238 239 curp = emitPositionsAtAddress(curp, sortedPositions); 240 241 /* 242 * Figure out what the next important address is. 243 */ 244 245 int nextAddrLS = Integer.MAX_VALUE; 246 int nextAddrLE = Integer.MAX_VALUE; 247 int nextAddrP = Integer.MAX_VALUE; 248 249 if (curls < szl) { 250 nextAddrLS = sortedLocalsStart.get(curls).getStart(); 251 } 252 253 if (curle < szl) { 254 nextAddrLE = sortedLocalsEnd.get(curle).getEnd(); 255 } 256 257 if (curp < szp) { 258 nextAddrP = sortedPositions.get(curp).getAddress(); 259 } 260 261 int next = Math.min(nextAddrP, Math.min(nextAddrLS, nextAddrLE)); 262 263 // No next important address == done. 264 if (next == Integer.MAX_VALUE) { 265 break; 266 } 267 268 /* 269 * If the only work remaining are local ends at the end of the 270 * block, stop here. Those are implied anyway. 271 */ 272 if (next == codeSize 273 && nextAddrLS == Integer.MAX_VALUE 274 && nextAddrP == Integer.MAX_VALUE) { 275 break; 276 } 277 278 if (next == nextAddrP) { 279 // Combined advance PC + position entry 280 emitPosition(sortedPositions.get(curp++)); 281 } else { 282 emitAdvancePc(next - address); 283 } 284 } 285 286 emitEndSequence(); 287 288 byte[] result = output.toByteArray(); 289 290 if (DEBUG) { 291 int origSize = 0; 292 int newSize = result.length; 293 294 if (positionlist != null) { 295 origSize += (positionlist.size() * 6); 296 } 297 298 if (locallist != null) { 299 origSize += (4 * 5 * locallist.size()); 300 } 301 302 System.err.printf( 303 "Lines+Locals table was %d bytes is now %d bytes\n", 304 origSize, newSize); 305 } 306 307 return result; 308 } 309 310 /** 311 * Emits all local ends that occur at the current <code>address</code> 312 * 313 * @param curle Current index in sortedLocalsEnd 314 * @param sortedLocalsEnd Locals, sorted by ascending end address 315 * @param curls Current index in sortedLocalsStart 316 * @param sortedLocalsStart Locals, sorted by ascending start address 317 * @return new value for <code>curle</code> 318 * @throws IOException 319 */ 320 private int emitLocalEndsAtAddress(int curle, 321 ArrayList<LocalList.Entry> sortedLocalsEnd, int curls, 322 ArrayList<LocalList.Entry> sortedLocalsStart) 323 throws IOException { 324 325 int szl = sortedLocalsEnd.size(); 326 327 // Ignore "local ends" at end of code. 328 while (curle < szl 329 && sortedLocalsEnd.get(curle).getEnd() == address 330 && address != codeSize) { 331 332 boolean skipLocalEnd = false; 333 334 /* 335 * Check to see if there's a range-start that appears at 336 * the same address for the same register. If so, the 337 * end-range is implicit so skip it. 338 */ 339 for (int j = curls; j < szl 340 && sortedLocalsStart.get(j).getStart() == address 341 ; j++) { 342 343 if (sortedLocalsStart.get(j).getRegister() 344 == sortedLocalsEnd.get(curle).getRegister()) { 345 skipLocalEnd = true; 346 347 if (DEBUG) { 348 System.err.printf("skip local end v%d\n", 349 sortedLocalsEnd.get(curle).getRegister()); 350 } 351 curle++; 352 break; 353 } 354 } 355 356 if (!skipLocalEnd) { 357 emitLocalEnd(sortedLocalsEnd.get(curle++)); 358 } 359 } 360 return curle; 361 } 362 363 /** 364 * Emits all local starts that occur at the current <code>address</code> 365 * 366 * @param curls Current index in sortedLocalsStart 367 * @param sortedLocalsStart Locals, sorted by ascending start address 368 * @return new value for <code>curls</code> 369 * @throws IOException 370 */ 371 private int emitLocalStartsAtAddress(int curls, 372 ArrayList<LocalList.Entry> sortedLocalsStart) 373 throws IOException { 374 375 int szl = sortedLocalsStart.size(); 376 377 while (curls < szl 378 && sortedLocalsStart.get(curls).getStart() == address) { 379 LocalList.Entry lle = sortedLocalsStart.get(curls++); 380 LocalList.Entry prevlle = lastEntryForReg[lle.getRegister()]; 381 382 if (lle == prevlle) { 383 /* 384 * Here we ignore locals entries for parameters, 385 * which have already been represented and placed in the 386 * lastEntryForReg array. 387 */ 388 continue; 389 } else if (prevlle != null && lle.matches(prevlle)) { 390 if (prevlle.getEnd() == lle.getStart()) { 391 /* 392 * An adjacent range with the same register. 393 * The previous emitLocalEndsAtAddress() call skipped 394 * this local end, so we'll skip this local start as well. 395 */ 396 continue; 397 } else { 398 emitLocalRestart(lle); 399 } 400 } else { 401 lastEntryForReg[lle.getRegister()] = lle; 402 emitLocalStart(lle); 403 } 404 } 405 return curls; 406 } 407 408 /** 409 * Emits all positions that occur at the current <code>address</code> 410 * 411 * @param curp Current index in sortedPositions 412 * @param sortedPositions positions, sorted by ascending address 413 * @return new value for <code>curp</code> 414 * @throws IOException 415 */ 416 private int emitPositionsAtAddress(int curp, 417 ArrayList<PositionList.Entry> sortedPositions) 418 throws IOException { 419 420 int szp = sortedPositions.size(); 421 while (curp < szp 422 && sortedPositions.get(curp).getAddress() == address) { 423 emitPosition(sortedPositions.get(curp++)); 424 } 425 return curp; 426 } 427 428 /** 429 * Emits the header sequence, which consists of LEB128-encoded initial 430 * line number and string indicies for names of all non-"this" arguments. 431 * 432 * @param sortedPositions positions, sorted by ascending address 433 * @param methodArgs local list entries for method argumens arguments, 434 * in left-to-right order omitting "this" 435 * @throws IOException 436 */ 437 private void emitHeader(ArrayList<PositionList.Entry> sortedPositions, 438 ArrayList<LocalList.Entry> methodArgs) throws IOException { 439 boolean annotate = (annotateTo != null) || (debugPrint != null); 440 int mark = output.getCursor(); 441 442 // Start by initializing the line number register. 443 if (sortedPositions.size() > 0) { 444 PositionList.Entry entry = sortedPositions.get(0); 445 line = entry.getPosition().getLine(); 446 } 447 output.writeUnsignedLeb128(line); 448 449 if (annotate) { 450 annotate(output.getCursor() - mark, "line_start: " + line); 451 } 452 453 int curParam = getParamBase(); 454 // paramTypes will not include 'this' 455 StdTypeList paramTypes = desc.getParameterTypes(); 456 int szParamTypes = paramTypes.size(); 457 458 /* 459 * Initialize lastEntryForReg to have an initial 460 * entry for the 'this' pointer. 461 */ 462 if (!isStatic) { 463 for (LocalList.Entry arg: methodArgs) { 464 if (curParam == arg.getRegister()) { 465 lastEntryForReg[curParam] = arg; 466 break; 467 } 468 } 469 curParam++; 470 } 471 472 // Write out the number of parameter entries that will follow. 473 mark = output.getCursor(); 474 output.writeUnsignedLeb128(szParamTypes); 475 476 if (annotate) { 477 annotate(output.getCursor() - mark, 478 String.format("parameters_size: %04x", szParamTypes)); 479 } 480 481 /* 482 * Then emit the string indicies of all the method parameters. 483 * Note that 'this', if applicable, is excluded. 484 */ 485 for (int i = 0; i < szParamTypes; i++) { 486 Type pt = paramTypes.get(i); 487 LocalList.Entry found = null; 488 489 mark = output.getCursor(); 490 491 for (LocalList.Entry arg: methodArgs) { 492 if (curParam == arg.getRegister()) { 493 found = arg; 494 495 if (arg.getSignature() != null) { 496 /* 497 * Parameters with signatures will be re-emitted 498 * in complete as LOCAL_START_EXTENDED's below. 499 */ 500 emitStringIndex(null); 501 } else { 502 emitStringIndex(arg.getName()); 503 } 504 lastEntryForReg[curParam] = arg; 505 506 break; 507 } 508 } 509 510 if (found == null) { 511 /* 512 * Emit a null symbol for "unnamed." This is common 513 * for, e.g., synthesized methods and inner-class 514 * this$0 arguments. 515 */ 516 emitStringIndex(null); 517 } 518 519 if (annotate) { 520 String parameterName 521 = (found == null || found.getSignature() != null) 522 ? "<unnamed>" : found.getName().toHuman(); 523 annotate(output.getCursor() - mark, 524 "parameter " + parameterName + " " 525 + RegisterSpec.PREFIX + curParam); 526 } 527 528 curParam += pt.getCategory(); 529 } 530 531 /* 532 * If anything emitted above has a type signature, emit it again as 533 * a LOCAL_RESTART_EXTENDED 534 */ 535 536 for (LocalList.Entry arg: lastEntryForReg) { 537 if (arg == null) { 538 continue; 539 } 540 541 CstUtf8 signature = arg.getSignature(); 542 543 if (signature != null) { 544 emitLocalStartExtended(arg); 545 } 546 } 547 } 548 549 /** 550 * Builds a list of position entries, sorted by ascending address. 551 * 552 * @return A sorted positions list 553 */ 554 private ArrayList<PositionList.Entry> buildSortedPositions() { 555 int sz = (positionlist == null) ? 0 : positionlist.size(); 556 ArrayList<PositionList.Entry> result = new ArrayList(sz); 557 558 for (int i = 0; i < sz; i++) { 559 result.add(positionlist.get(i)); 560 } 561 562 // Sort ascending by address. 563 Collections.sort (result, new Comparator<PositionList.Entry>() { 564 public int compare (PositionList.Entry a, PositionList.Entry b) { 565 return a.getAddress() - b.getAddress(); 566 } 567 568 public boolean equals (Object obj) { 569 return obj == this; 570 } 571 }); 572 return result; 573 } 574 575 /** 576 * Builds a list of locals entries sorted by ascending start address. 577 * 578 * @return A sorted locals list list 579 */ 580 private ArrayList<LocalList.Entry> buildLocalsStart() { 581 int sz = (locallist == null) ? 0 : locallist.size(); 582 ArrayList<LocalList.Entry> result = new ArrayList(sz); 583 584 // Add all the entries 585 for (int i = 0; i < sz; i++) { 586 LocalList.Entry e = locallist.get(i); 587 result.add(locallist.get(i)); 588 } 589 590 // Sort ascending by start address. 591 Collections.sort (result, new Comparator<LocalList.Entry>() { 592 public int compare (LocalList.Entry a, LocalList.Entry b) { 593 return a.getStart() - b.getStart(); 594 } 595 596 public boolean equals (Object obj) { 597 return obj == this; 598 } 599 }); 600 return result; 601 } 602 603 /** 604 * Builds a list of locals entries sorted by ascending end address. 605 * 606 * @param list locals list in any order 607 * @return a sorted locals list 608 */ 609 private ArrayList<LocalList.Entry> buildLocalsEnd( 610 ArrayList<LocalList.Entry> list) { 611 612 ArrayList<LocalList.Entry> sortedLocalsEnd = new ArrayList(list); 613 614 // Sort ascending by end address. 615 Collections.sort (sortedLocalsEnd, new Comparator<LocalList.Entry>() { 616 public int compare (LocalList.Entry a, LocalList.Entry b) { 617 return a.getEnd() - b.getEnd(); 618 } 619 620 public boolean equals (Object obj) { 621 return obj == this; 622 } 623 }); 624 return sortedLocalsEnd; 625 } 626 627 /** 628 * Gets the register that begins the method's parameter range (including 629 * the 'this' parameter for non-static methods). The range continues until 630 * <code>regSize</code> 631 * 632 * @return register as noted above 633 */ 634 private int getParamBase() { 635 return regSize 636 - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1); 637 } 638 639 /** 640 * Extracts method arguments from a locals list. These will be collected 641 * from the input list and sorted by ascending register in the 642 * returned list. 643 * 644 * @param sortedLocals locals list, sorted by ascending start address, 645 * to process; left unmodified 646 * @return list of non-<code>this</code> method argument locals, 647 * sorted by ascending register 648 */ 649 private ArrayList<LocalList.Entry> extractMethodArguments ( 650 ArrayList<LocalList.Entry> sortedLocals) { 651 652 ArrayList<LocalList.Entry> result 653 = new ArrayList(desc.getParameterTypes().size()); 654 655 int argBase = getParamBase(); 656 657 BitSet seen = new BitSet(regSize - argBase); 658 659 int sz = sortedLocals.size(); 660 for (int i = 0; i < sz; i++) { 661 LocalList.Entry e = sortedLocals.get(i); 662 int reg = e.getRegister(); 663 664 if (reg < argBase) { 665 continue; 666 } 667 668 // only the lowest-start-address entry is included. 669 if (seen.get(reg - argBase)) { 670 continue; 671 } 672 673 seen.set(reg - argBase); 674 result.add(e); 675 } 676 677 // Sort by ascending register. 678 Collections.sort (result, new Comparator<LocalList.Entry>() { 679 public int compare (LocalList.Entry a, LocalList.Entry b) { 680 return a.getRegister() - b.getRegister(); 681 } 682 683 public boolean equals (Object obj) { 684 return obj == this; 685 } 686 }); 687 688 return result; 689 } 690 691 /** 692 * Returns a string representation of this LocalList entry that is 693 * appropriate for emitting as an annotation. 694 * 695 * @param e non-null; entry 696 * @return non-null; annotation string 697 */ 698 private String entryAnnotationString(LocalList.Entry e) { 699 StringBuilder sb = new StringBuilder(); 700 701 sb.append(RegisterSpec.PREFIX); 702 sb.append(e.getRegister()); 703 sb.append(' '); 704 705 CstUtf8 name = e.getName(); 706 if (name == null) { 707 sb.append("null"); 708 } else { 709 sb.append(name.toHuman()); 710 } 711 sb.append(' '); 712 713 CstType type = e.getType(); 714 if (type == null) { 715 sb.append("null"); 716 } else { 717 sb.append(type.toHuman()); 718 } 719 720 CstUtf8 signature = e.getSignature(); 721 722 if (signature != null) { 723 sb.append(' '); 724 sb.append(signature.toHuman()); 725 } 726 727 return sb.toString(); 728 } 729 730 /** 731 * Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL} 732 * sequence. 733 * 734 * @param entry entry associated with this restart 735 * @throws IOException 736 */ 737 private void emitLocalRestart(LocalList.Entry entry) 738 throws IOException { 739 740 int mark = output.getCursor(); 741 742 output.writeByte(DBG_RESTART_LOCAL); 743 emitUnsignedLeb128(entry.getRegister()); 744 745 if (annotateTo != null || debugPrint != null) { 746 annotate(output.getCursor() - mark, 747 String.format("%04x: +local restart %s", 748 address, entryAnnotationString(entry))); 749 } 750 751 if (DEBUG) { 752 System.err.println("emit local restart"); 753 } 754 } 755 756 /** 757 * Emits a string index as an unsigned LEB128. The actual value written 758 * is shifted by 1, so that the '0' value is reserved for "null". The 759 * null symbol is used in some cases by the parameter name list 760 * at the beginning of the sequence. 761 * 762 * @param string null-ok; string to emit 763 * @throws IOException 764 */ 765 private void emitStringIndex(CstUtf8 string) throws IOException { 766 if ((string == null) || (file == null)) { 767 output.writeUnsignedLeb128(0); 768 } else { 769 output.writeUnsignedLeb128( 770 1 + file.getStringIds().indexOf(string)); 771 } 772 773 if (DEBUG) { 774 System.err.printf("Emit string %s\n", 775 string == null ? "<null>" : string.toQuoted()); 776 } 777 } 778 779 /** 780 * Emits a type index as an unsigned LEB128. The actual value written 781 * is shifted by 1, so that the '0' value is reserved for "null". 782 * 783 * @param type null-ok; type to emit 784 * @throws IOException 785 */ 786 private void emitTypeIndex(CstType type) throws IOException { 787 if ((type == null) || (file == null)) { 788 output.writeUnsignedLeb128(0); 789 } else { 790 output.writeUnsignedLeb128( 791 1 + file.getTypeIds().indexOf(type)); 792 } 793 794 if (DEBUG) { 795 System.err.printf("Emit type %s\n", 796 type == null ? "<null>" : type.toHuman()); 797 } 798 } 799 800 /** 801 * Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or 802 * {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED 803 * DBG_START_LOCAL_EXTENDED} sequence. 804 * 805 * @param entry entry to emit 806 * @throws IOException 807 */ 808 private void emitLocalStart(LocalList.Entry entry) 809 throws IOException { 810 811 if (entry.getSignature() != null) { 812 emitLocalStartExtended(entry); 813 return; 814 } 815 816 int mark = output.getCursor(); 817 818 output.writeByte(DBG_START_LOCAL); 819 820 emitUnsignedLeb128 (entry.getRegister()); 821 emitStringIndex(entry.getName()); 822 emitTypeIndex(entry.getType()); 823 824 if (annotateTo != null || debugPrint != null) { 825 annotate(output.getCursor() - mark, 826 String.format("%04x: +local %s", address, 827 entryAnnotationString(entry))); 828 } 829 830 if (DEBUG) { 831 System.err.println("emit local start"); 832 } 833 } 834 835 /** 836 * Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED 837 * DBG_START_LOCAL_EXTENDED} sequence. 838 * 839 * @param entry entry to emit 840 * @throws IOException 841 */ 842 private void emitLocalStartExtended(LocalList.Entry entry) 843 throws IOException { 844 845 int mark = output.getCursor(); 846 847 output.writeByte(DBG_START_LOCAL_EXTENDED); 848 849 emitUnsignedLeb128 (entry.getRegister()); 850 emitStringIndex(entry.getName()); 851 emitTypeIndex(entry.getType()); 852 emitStringIndex(entry.getSignature()); 853 854 if (annotateTo != null || debugPrint != null) { 855 annotate(output.getCursor() - mark, 856 String.format("%04x: +localx %s", address, 857 entryAnnotationString(entry))); 858 } 859 860 if (DEBUG) { 861 System.err.println("emit local start"); 862 } 863 } 864 865 /** 866 * Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence. 867 * 868 * @param entry entry non-null; entry associated with end. 869 * @throws IOException 870 */ 871 private void emitLocalEnd(LocalList.Entry entry) 872 throws IOException { 873 874 int mark = output.getCursor(); 875 876 output.writeByte(DBG_END_LOCAL); 877 output.writeUnsignedLeb128(entry.getRegister()); 878 879 if (annotateTo != null || debugPrint != null) { 880 annotate(output.getCursor() - mark, 881 String.format("%04x: -local %s", address, 882 entryAnnotationString(entry))); 883 } 884 885 if (DEBUG) { 886 System.err.println("emit local end"); 887 } 888 } 889 890 /** 891 * Emits the necessary byte sequences to emit the given position table 892 * entry. This will typically be a single special opcode, although 893 * it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE. 894 * 895 * @param entry position entry to emit. 896 * @throws IOException 897 */ 898 private void emitPosition(PositionList.Entry entry) 899 throws IOException { 900 901 SourcePosition pos = entry.getPosition(); 902 int newLine = pos.getLine(); 903 int newAddress = entry.getAddress(); 904 905 int opcode; 906 907 int deltaLines = newLine - line; 908 int deltaAddress = newAddress - address; 909 910 if (deltaAddress < 0) { 911 throw new RuntimeException( 912 "Position entries must be in ascending address order"); 913 } 914 915 if ((deltaLines < DBG_LINE_BASE) 916 || (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1))) { 917 emitAdvanceLine(deltaLines); 918 deltaLines = 0; 919 } 920 921 opcode = computeOpcode (deltaLines, deltaAddress); 922 923 if ((opcode & ~0xff) > 0) { 924 emitAdvancePc(deltaAddress); 925 deltaAddress = 0; 926 opcode = computeOpcode (deltaLines, deltaAddress); 927 928 if ((opcode & ~0xff) > 0) { 929 emitAdvanceLine(deltaLines); 930 deltaLines = 0; 931 opcode = computeOpcode (deltaLines, deltaAddress); 932 } 933 } 934 935 output.writeByte(opcode); 936 937 line += deltaLines; 938 address += deltaAddress; 939 940 if (annotateTo != null || debugPrint != null) { 941 annotate(1, 942 String.format ("%04x: line %d", address, line)); 943 } 944 } 945 946 /** 947 * Computes a special opcode that will encode the given position change. 948 * If the return value is > 0xff, then the request cannot be fulfilled. 949 * Essentially the same as described in "DWARF Debugging Format Version 3" 950 * section 6.2.5.1. 951 * 952 * @param deltaLines >= DBG_LINE_BASE and <= DBG_LINE_BASE + 953 * DBG_LINE_RANGE, the line change to encode 954 * @param deltaAddress >= 0; the address change to encode 955 * @return <= 0xff if in range, otherwise parameters are out of range 956 */ 957 private static int computeOpcode(int deltaLines, int deltaAddress) { 958 if (deltaLines < DBG_LINE_BASE 959 || deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) { 960 961 throw new RuntimeException ("Parameter out of range"); 962 } 963 964 return (deltaLines - DBG_LINE_BASE) 965 + (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL; 966 } 967 968 /** 969 * Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE} 970 * sequence. 971 * 972 * @param deltaLines amount to change line number register by 973 * @throws IOException 974 */ 975 private void emitAdvanceLine(int deltaLines) throws IOException { 976 int mark = output.getCursor(); 977 978 output.writeByte(DBG_ADVANCE_LINE); 979 output.writeSignedLeb128(deltaLines); 980 line += deltaLines; 981 982 if (annotateTo != null || debugPrint != null) { 983 annotate(output.getCursor() - mark, 984 String.format("line = %d", line)); 985 } 986 987 if (DEBUG) { 988 System.err.printf("Emitting advance_line for %d\n", deltaLines); 989 } 990 } 991 992 /** 993 * Emits an {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC} 994 * sequence. 995 * 996 * @param deltaAddress >= 0 amount to change program counter by 997 * @throws IOException 998 */ 999 private void emitAdvancePc(int deltaAddress) throws IOException { 1000 int mark = output.getCursor(); 1001 1002 output.writeByte(DBG_ADVANCE_PC); 1003 output.writeUnsignedLeb128(deltaAddress); 1004 address += deltaAddress; 1005 1006 if (annotateTo != null || debugPrint != null) { 1007 annotate(output.getCursor() - mark, 1008 String.format("%04x: advance pc", address)); 1009 } 1010 1011 if (DEBUG) { 1012 System.err.printf("Emitting advance_pc for %d\n", deltaAddress); 1013 } 1014 } 1015 1016 /** 1017 * Emits an unsigned LEB128 value. 1018 * 1019 * @param n >= 0 vallue to emit. Note that, although this can represent 1020 * integers larger than Integer.MAX_VALUE, we currently don't allow that. 1021 * @throws IOException 1022 */ 1023 private void emitUnsignedLeb128(int n) throws IOException { 1024 // We'll never need the top end of the unsigned range anyway. 1025 if (n < 0) { 1026 throw new RuntimeException( 1027 "Signed value where unsigned required: " + n); 1028 } 1029 1030 output.writeSignedLeb128(n); 1031 } 1032 1033 /** 1034 * Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE} 1035 * bytecode. 1036 */ 1037 private void emitEndSequence() { 1038 output.writeByte(DBG_END_SEQUENCE); 1039 1040 if (annotateTo != null || debugPrint != null) { 1041 annotate(1, "end sequence"); 1042 } 1043 } 1044} 1045