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.CstString; 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} excluded. A value of '0' indicates "no name" 50 * <li> A sequence of special or normal opcodes as defined in 51 * {@code DebugInfoConstants}. 52 * <li> A single terminating {@code OP_END_SEQUENCE} 53 * </ol> 54 */ 55public final class DebugInfoEncoder { 56 private static final boolean DEBUG = false; 57 58 /** {@code null-ok;} positions (line numbers) to encode */ 59 private final PositionList positions; 60 61 /** {@code null-ok;} local variables to encode */ 62 private final LocalList locals; 63 64 private final ByteArrayAnnotatedOutput output; 65 private final DexFile file; 66 private final int codeSize; 67 private final int regSize; 68 69 private final Prototype desc; 70 private final boolean isStatic; 71 72 /** current encoding state: bytecode address */ 73 private int address = 0; 74 75 /** current encoding state: line number */ 76 private int line = 1; 77 78 /** 79 * if non-null: the output to write annotations to. No normal 80 * output is written to this. 81 */ 82 private AnnotatedOutput annotateTo; 83 84 /** if non-null: another possible output for annotations */ 85 private PrintWriter debugPrint; 86 87 /** if non-null: the prefix for each annotation or debugPrint line */ 88 private String prefix; 89 90 /** true if output should be consumed during annotation */ 91 private boolean shouldConsume; 92 93 /** indexed by register; last local alive in register */ 94 private final LocalList.Entry[] lastEntryForReg; 95 96 /** 97 * Creates an instance. 98 * 99 * @param positions {@code null-ok;} positions (line numbers) to encode 100 * @param locals {@code null-ok;} local variables to encode 101 * @param file {@code null-ok;} may only be {@code null} if simply using 102 * this class to do a debug print 103 * @param codeSize 104 * @param regSize 105 * @param isStatic 106 * @param ref 107 */ 108 public DebugInfoEncoder(PositionList positions, LocalList locals, 109 DexFile file, int codeSize, int regSize, 110 boolean isStatic, CstMethodRef ref) { 111 this.positions = positions; 112 this.locals = locals; 113 this.file = file; 114 this.desc = ref.getPrototype(); 115 this.isStatic = isStatic; 116 this.codeSize = codeSize; 117 this.regSize = regSize; 118 119 output = new ByteArrayAnnotatedOutput(); 120 lastEntryForReg = new LocalList.Entry[regSize]; 121 } 122 123 /** 124 * Annotates or writes a message to the {@code debugPrint} writer 125 * if applicable. 126 * 127 * @param length the number of bytes associated with this message 128 * @param message the message itself 129 */ 130 private void annotate(int length, String message) { 131 if (prefix != null) { 132 message = prefix + message; 133 } 134 135 if (annotateTo != null) { 136 annotateTo.annotate(shouldConsume ? length : 0, message); 137 } 138 139 if (debugPrint != null) { 140 debugPrint.println(message); 141 } 142 } 143 144 /** 145 * Converts this (PositionList, LocalList) pair into a state machine 146 * sequence. 147 * 148 * @return {@code non-null;} encoded byte sequence without padding and 149 * terminated with a {@code 0x00} byte 150 */ 151 public byte[] convert() { 152 try { 153 byte[] ret; 154 ret = convert0(); 155 156 if (DEBUG) { 157 for (int i = 0 ; i < ret.length; i++) { 158 System.err.printf("byte %02x\n", (0xff & ret[i])); 159 } 160 } 161 162 return ret; 163 } catch (IOException ex) { 164 throw ExceptionWithContext 165 .withContext(ex, "...while encoding debug info"); 166 } 167 } 168 169 /** 170 * Converts and produces annotations on a stream. Does not write 171 * actual bits to the {@code AnnotatedOutput}. 172 * 173 * @param prefix {@code null-ok;} prefix to attach to each line of output 174 * @param debugPrint {@code null-ok;} if specified, an alternate output for 175 * annotations 176 * @param out {@code null-ok;} if specified, where annotations should go 177 * @param consume whether to claim to have consumed output for 178 * {@code out} 179 * @return {@code non-null;} encoded output 180 */ 181 public byte[] convertAndAnnotate(String prefix, PrintWriter debugPrint, 182 AnnotatedOutput out, boolean consume) { 183 this.prefix = prefix; 184 this.debugPrint = debugPrint; 185 annotateTo = out; 186 shouldConsume = consume; 187 188 byte[] result = convert(); 189 190 return result; 191 } 192 193 private byte[] convert0() throws IOException { 194 ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions(); 195 ArrayList<LocalList.Entry> methodArgs = extractMethodArguments(); 196 197 emitHeader(sortedPositions, methodArgs); 198 199 // TODO: Make this mark be the actual prologue end. 200 output.writeByte(DBG_SET_PROLOGUE_END); 201 202 if (annotateTo != null || debugPrint != null) { 203 annotate(1, String.format("%04x: prologue end",address)); 204 } 205 206 int positionsSz = sortedPositions.size(); 207 int localsSz = locals.size(); 208 209 // Current index in sortedPositions 210 int curPositionIdx = 0; 211 // Current index in locals 212 int curLocalIdx = 0; 213 214 for (;;) { 215 /* 216 * Emit any information for the current address. 217 */ 218 219 curLocalIdx = emitLocalsAtAddress(curLocalIdx); 220 curPositionIdx = 221 emitPositionsAtAddress(curPositionIdx, sortedPositions); 222 223 /* 224 * Figure out what the next important address is. 225 */ 226 227 int nextAddrL = Integer.MAX_VALUE; // local variable 228 int nextAddrP = Integer.MAX_VALUE; // position (line number) 229 230 if (curLocalIdx < localsSz) { 231 nextAddrL = locals.get(curLocalIdx).getAddress(); 232 } 233 234 if (curPositionIdx < positionsSz) { 235 nextAddrP = sortedPositions.get(curPositionIdx).getAddress(); 236 } 237 238 int next = Math.min(nextAddrP, nextAddrL); 239 240 // No next important address == done. 241 if (next == Integer.MAX_VALUE) { 242 break; 243 } 244 245 /* 246 * If the only work remaining are local ends at the end of the 247 * block, stop here. Those are implied anyway. 248 */ 249 if (next == codeSize 250 && nextAddrL == Integer.MAX_VALUE 251 && nextAddrP == Integer.MAX_VALUE) { 252 break; 253 } 254 255 if (next == nextAddrP) { 256 // Combined advance PC + position entry 257 emitPosition(sortedPositions.get(curPositionIdx++)); 258 } else { 259 emitAdvancePc(next - address); 260 } 261 } 262 263 emitEndSequence(); 264 265 return output.toByteArray(); 266 } 267 268 /** 269 * Emits all local variable activity that occurs at the current 270 * {@link #address} starting at the given index into {@code 271 * locals} and including all subsequent activity at the same 272 * address. 273 * 274 * @param curLocalIdx Current index in locals 275 * @return new value for {@code curLocalIdx} 276 * @throws IOException 277 */ 278 private int emitLocalsAtAddress(int curLocalIdx) 279 throws IOException { 280 int sz = locals.size(); 281 282 // TODO: Don't emit ends implied by starts. 283 284 while ((curLocalIdx < sz) 285 && (locals.get(curLocalIdx).getAddress() == address)) { 286 LocalList.Entry entry = locals.get(curLocalIdx++); 287 int reg = entry.getRegister(); 288 LocalList.Entry prevEntry = lastEntryForReg[reg]; 289 290 if (entry == prevEntry) { 291 /* 292 * Here we ignore locals entries for parameters, 293 * which have already been represented and placed in the 294 * lastEntryForReg array. 295 */ 296 continue; 297 } 298 299 // At this point we have a new entry one way or another. 300 lastEntryForReg[reg] = entry; 301 302 if (entry.isStart()) { 303 if ((prevEntry != null) && entry.matches(prevEntry)) { 304 /* 305 * The previous local in this register has the same 306 * name and type as the one being introduced now, so 307 * use the more efficient "restart" form. 308 */ 309 if (prevEntry.isStart()) { 310 /* 311 * We should never be handed a start when a 312 * a matching local is already active. 313 */ 314 throw new RuntimeException("shouldn't happen"); 315 } 316 emitLocalRestart(entry); 317 } else { 318 emitLocalStart(entry); 319 } 320 } else { 321 /* 322 * Only emit a local end if it is *not* due to a direct 323 * replacement. Direct replacements imply an end of the 324 * previous local in the same register. 325 * 326 * TODO: Make sure the runtime can deal with implied 327 * local ends from category-2 interactions, and when so, 328 * also stop emitting local ends for those cases. 329 */ 330 if (entry.getDisposition() 331 != LocalList.Disposition.END_REPLACED) { 332 emitLocalEnd(entry); 333 } 334 } 335 } 336 337 return curLocalIdx; 338 } 339 340 /** 341 * Emits all positions that occur at the current {@code address} 342 * 343 * @param curPositionIdx Current index in sortedPositions 344 * @param sortedPositions positions, sorted by ascending address 345 * @return new value for {@code curPositionIdx} 346 * @throws IOException 347 */ 348 private int emitPositionsAtAddress(int curPositionIdx, 349 ArrayList<PositionList.Entry> sortedPositions) 350 throws IOException { 351 int positionsSz = sortedPositions.size(); 352 while ((curPositionIdx < positionsSz) 353 && (sortedPositions.get(curPositionIdx).getAddress() 354 == address)) { 355 emitPosition(sortedPositions.get(curPositionIdx++)); 356 } 357 return curPositionIdx; 358 } 359 360 /** 361 * Emits the header sequence, which consists of LEB128-encoded initial 362 * line number and string indicies for names of all non-"this" arguments. 363 * 364 * @param sortedPositions positions, sorted by ascending address 365 * @param methodArgs local list entries for method argumens arguments, 366 * in left-to-right order omitting "this" 367 * @throws IOException 368 */ 369 private void emitHeader(ArrayList<PositionList.Entry> sortedPositions, 370 ArrayList<LocalList.Entry> methodArgs) throws IOException { 371 boolean annotate = (annotateTo != null) || (debugPrint != null); 372 int mark = output.getCursor(); 373 374 // Start by initializing the line number register. 375 if (sortedPositions.size() > 0) { 376 PositionList.Entry entry = sortedPositions.get(0); 377 line = entry.getPosition().getLine(); 378 } 379 output.writeUleb128(line); 380 381 if (annotate) { 382 annotate(output.getCursor() - mark, "line_start: " + line); 383 } 384 385 int curParam = getParamBase(); 386 // paramTypes will not include 'this' 387 StdTypeList paramTypes = desc.getParameterTypes(); 388 int szParamTypes = paramTypes.size(); 389 390 /* 391 * Initialize lastEntryForReg to have an initial 392 * entry for the 'this' pointer. 393 */ 394 if (!isStatic) { 395 for (LocalList.Entry arg : methodArgs) { 396 if (curParam == arg.getRegister()) { 397 lastEntryForReg[curParam] = arg; 398 break; 399 } 400 } 401 curParam++; 402 } 403 404 // Write out the number of parameter entries that will follow. 405 mark = output.getCursor(); 406 output.writeUleb128(szParamTypes); 407 408 if (annotate) { 409 annotate(output.getCursor() - mark, 410 String.format("parameters_size: %04x", szParamTypes)); 411 } 412 413 /* 414 * Then emit the string indicies of all the method parameters. 415 * Note that 'this', if applicable, is excluded. 416 */ 417 for (int i = 0; i < szParamTypes; i++) { 418 Type pt = paramTypes.get(i); 419 LocalList.Entry found = null; 420 421 mark = output.getCursor(); 422 423 for (LocalList.Entry arg : methodArgs) { 424 if (curParam == arg.getRegister()) { 425 found = arg; 426 427 if (arg.getSignature() != null) { 428 /* 429 * Parameters with signatures will be re-emitted 430 * in complete as LOCAL_START_EXTENDED's below. 431 */ 432 emitStringIndex(null); 433 } else { 434 emitStringIndex(arg.getName()); 435 } 436 lastEntryForReg[curParam] = arg; 437 438 break; 439 } 440 } 441 442 if (found == null) { 443 /* 444 * Emit a null symbol for "unnamed." This is common 445 * for, e.g., synthesized methods and inner-class 446 * this$0 arguments. 447 */ 448 emitStringIndex(null); 449 } 450 451 if (annotate) { 452 String parameterName 453 = (found == null || found.getSignature() != null) 454 ? "<unnamed>" : found.getName().toHuman(); 455 annotate(output.getCursor() - mark, 456 "parameter " + parameterName + " " 457 + RegisterSpec.PREFIX + curParam); 458 } 459 460 curParam += pt.getCategory(); 461 } 462 463 /* 464 * If anything emitted above has a type signature, emit it again as 465 * a LOCAL_RESTART_EXTENDED 466 */ 467 468 for (LocalList.Entry arg : lastEntryForReg) { 469 if (arg == null) { 470 continue; 471 } 472 473 CstString signature = arg.getSignature(); 474 475 if (signature != null) { 476 emitLocalStartExtended(arg); 477 } 478 } 479 } 480 481 /** 482 * Builds a list of position entries, sorted by ascending address. 483 * 484 * @return A sorted positions list 485 */ 486 private ArrayList<PositionList.Entry> buildSortedPositions() { 487 int sz = (positions == null) ? 0 : positions.size(); 488 ArrayList<PositionList.Entry> result = new ArrayList(sz); 489 490 for (int i = 0; i < sz; i++) { 491 result.add(positions.get(i)); 492 } 493 494 // Sort ascending by address. 495 Collections.sort (result, new Comparator<PositionList.Entry>() { 496 public int compare (PositionList.Entry a, PositionList.Entry b) { 497 return a.getAddress() - b.getAddress(); 498 } 499 500 public boolean equals (Object obj) { 501 return obj == this; 502 } 503 }); 504 return result; 505 } 506 507 /** 508 * Gets the register that begins the method's parameter range (including 509 * the 'this' parameter for non-static methods). The range continues until 510 * {@code regSize} 511 * 512 * @return register as noted above 513 */ 514 private int getParamBase() { 515 return regSize 516 - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1); 517 } 518 519 /** 520 * Extracts method arguments from a locals list. These will be collected 521 * from the input list and sorted by ascending register in the 522 * returned list. 523 * 524 * @return list of non-{@code this} method argument locals, 525 * sorted by ascending register 526 */ 527 private ArrayList<LocalList.Entry> extractMethodArguments() { 528 ArrayList<LocalList.Entry> result 529 = new ArrayList(desc.getParameterTypes().size()); 530 int argBase = getParamBase(); 531 BitSet seen = new BitSet(regSize - argBase); 532 int sz = locals.size(); 533 534 for (int i = 0; i < sz; i++) { 535 LocalList.Entry e = locals.get(i); 536 int reg = e.getRegister(); 537 538 if (reg < argBase) { 539 continue; 540 } 541 542 // only the lowest-start-address entry is included. 543 if (seen.get(reg - argBase)) { 544 continue; 545 } 546 547 seen.set(reg - argBase); 548 result.add(e); 549 } 550 551 // Sort by ascending register. 552 Collections.sort(result, new Comparator<LocalList.Entry>() { 553 public int compare(LocalList.Entry a, LocalList.Entry b) { 554 return a.getRegister() - b.getRegister(); 555 } 556 557 public boolean equals(Object obj) { 558 return obj == this; 559 } 560 }); 561 562 return result; 563 } 564 565 /** 566 * Returns a string representation of this LocalList entry that is 567 * appropriate for emitting as an annotation. 568 * 569 * @param e {@code non-null;} entry 570 * @return {@code non-null;} annotation string 571 */ 572 private String entryAnnotationString(LocalList.Entry e) { 573 StringBuilder sb = new StringBuilder(); 574 575 sb.append(RegisterSpec.PREFIX); 576 sb.append(e.getRegister()); 577 sb.append(' '); 578 579 CstString name = e.getName(); 580 if (name == null) { 581 sb.append("null"); 582 } else { 583 sb.append(name.toHuman()); 584 } 585 sb.append(' '); 586 587 CstType type = e.getType(); 588 if (type == null) { 589 sb.append("null"); 590 } else { 591 sb.append(type.toHuman()); 592 } 593 594 CstString signature = e.getSignature(); 595 596 if (signature != null) { 597 sb.append(' '); 598 sb.append(signature.toHuman()); 599 } 600 601 return sb.toString(); 602 } 603 604 /** 605 * Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL} 606 * sequence. 607 * 608 * @param entry entry associated with this restart 609 * @throws IOException 610 */ 611 private void emitLocalRestart(LocalList.Entry entry) 612 throws IOException { 613 614 int mark = output.getCursor(); 615 616 output.writeByte(DBG_RESTART_LOCAL); 617 emitUnsignedLeb128(entry.getRegister()); 618 619 if (annotateTo != null || debugPrint != null) { 620 annotate(output.getCursor() - mark, 621 String.format("%04x: +local restart %s", 622 address, entryAnnotationString(entry))); 623 } 624 625 if (DEBUG) { 626 System.err.println("emit local restart"); 627 } 628 } 629 630 /** 631 * Emits a string index as an unsigned LEB128. The actual value written 632 * is shifted by 1, so that the '0' value is reserved for "null". The 633 * null symbol is used in some cases by the parameter name list 634 * at the beginning of the sequence. 635 * 636 * @param string {@code null-ok;} string to emit 637 * @throws IOException 638 */ 639 private void emitStringIndex(CstString string) throws IOException { 640 if ((string == null) || (file == null)) { 641 output.writeUleb128(0); 642 } else { 643 output.writeUleb128( 644 1 + file.getStringIds().indexOf(string)); 645 } 646 647 if (DEBUG) { 648 System.err.printf("Emit string %s\n", 649 string == null ? "<null>" : string.toQuoted()); 650 } 651 } 652 653 /** 654 * Emits a type index as an unsigned LEB128. The actual value written 655 * is shifted by 1, so that the '0' value is reserved for "null". 656 * 657 * @param type {@code null-ok;} type to emit 658 * @throws IOException 659 */ 660 private void emitTypeIndex(CstType type) throws IOException { 661 if ((type == null) || (file == null)) { 662 output.writeUleb128(0); 663 } else { 664 output.writeUleb128( 665 1 + file.getTypeIds().indexOf(type)); 666 } 667 668 if (DEBUG) { 669 System.err.printf("Emit type %s\n", 670 type == null ? "<null>" : type.toHuman()); 671 } 672 } 673 674 /** 675 * Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or 676 * {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED 677 * DBG_START_LOCAL_EXTENDED} sequence. 678 * 679 * @param entry entry to emit 680 * @throws IOException 681 */ 682 private void emitLocalStart(LocalList.Entry entry) 683 throws IOException { 684 685 if (entry.getSignature() != null) { 686 emitLocalStartExtended(entry); 687 return; 688 } 689 690 int mark = output.getCursor(); 691 692 output.writeByte(DBG_START_LOCAL); 693 694 emitUnsignedLeb128(entry.getRegister()); 695 emitStringIndex(entry.getName()); 696 emitTypeIndex(entry.getType()); 697 698 if (annotateTo != null || debugPrint != null) { 699 annotate(output.getCursor() - mark, 700 String.format("%04x: +local %s", address, 701 entryAnnotationString(entry))); 702 } 703 704 if (DEBUG) { 705 System.err.println("emit local start"); 706 } 707 } 708 709 /** 710 * Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED 711 * DBG_START_LOCAL_EXTENDED} sequence. 712 * 713 * @param entry entry to emit 714 * @throws IOException 715 */ 716 private void emitLocalStartExtended(LocalList.Entry entry) 717 throws IOException { 718 719 int mark = output.getCursor(); 720 721 output.writeByte(DBG_START_LOCAL_EXTENDED); 722 723 emitUnsignedLeb128(entry.getRegister()); 724 emitStringIndex(entry.getName()); 725 emitTypeIndex(entry.getType()); 726 emitStringIndex(entry.getSignature()); 727 728 if (annotateTo != null || debugPrint != null) { 729 annotate(output.getCursor() - mark, 730 String.format("%04x: +localx %s", address, 731 entryAnnotationString(entry))); 732 } 733 734 if (DEBUG) { 735 System.err.println("emit local start"); 736 } 737 } 738 739 /** 740 * Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence. 741 * 742 * @param entry {@code entry non-null;} entry associated with end. 743 * @throws IOException 744 */ 745 private void emitLocalEnd(LocalList.Entry entry) 746 throws IOException { 747 748 int mark = output.getCursor(); 749 750 output.writeByte(DBG_END_LOCAL); 751 output.writeUleb128(entry.getRegister()); 752 753 if (annotateTo != null || debugPrint != null) { 754 annotate(output.getCursor() - mark, 755 String.format("%04x: -local %s", address, 756 entryAnnotationString(entry))); 757 } 758 759 if (DEBUG) { 760 System.err.println("emit local end"); 761 } 762 } 763 764 /** 765 * Emits the necessary byte sequences to emit the given position table 766 * entry. This will typically be a single special opcode, although 767 * it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE. 768 * 769 * @param entry position entry to emit. 770 * @throws IOException 771 */ 772 private void emitPosition(PositionList.Entry entry) 773 throws IOException { 774 775 SourcePosition pos = entry.getPosition(); 776 int newLine = pos.getLine(); 777 int newAddress = entry.getAddress(); 778 779 int opcode; 780 781 int deltaLines = newLine - line; 782 int deltaAddress = newAddress - address; 783 784 if (deltaAddress < 0) { 785 throw new RuntimeException( 786 "Position entries must be in ascending address order"); 787 } 788 789 if ((deltaLines < DBG_LINE_BASE) 790 || (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1))) { 791 emitAdvanceLine(deltaLines); 792 deltaLines = 0; 793 } 794 795 opcode = computeOpcode (deltaLines, deltaAddress); 796 797 if ((opcode & ~0xff) > 0) { 798 emitAdvancePc(deltaAddress); 799 deltaAddress = 0; 800 opcode = computeOpcode (deltaLines, deltaAddress); 801 802 if ((opcode & ~0xff) > 0) { 803 emitAdvanceLine(deltaLines); 804 deltaLines = 0; 805 opcode = computeOpcode (deltaLines, deltaAddress); 806 } 807 } 808 809 output.writeByte(opcode); 810 811 line += deltaLines; 812 address += deltaAddress; 813 814 if (annotateTo != null || debugPrint != null) { 815 annotate(1, 816 String.format("%04x: line %d", address, line)); 817 } 818 } 819 820 /** 821 * Computes a special opcode that will encode the given position change. 822 * If the return value is > 0xff, then the request cannot be fulfilled. 823 * Essentially the same as described in "DWARF Debugging Format Version 3" 824 * section 6.2.5.1. 825 * 826 * @param deltaLines {@code >= DBG_LINE_BASE, <= DBG_LINE_BASE + 827 * DBG_LINE_RANGE;} the line change to encode 828 * @param deltaAddress {@code >= 0;} the address change to encode 829 * @return {@code <= 0xff} if in range, otherwise parameters are out 830 * of range 831 */ 832 private static int computeOpcode(int deltaLines, int deltaAddress) { 833 if (deltaLines < DBG_LINE_BASE 834 || deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) { 835 836 throw new RuntimeException("Parameter out of range"); 837 } 838 839 return (deltaLines - DBG_LINE_BASE) 840 + (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL; 841 } 842 843 /** 844 * Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE} 845 * sequence. 846 * 847 * @param deltaLines amount to change line number register by 848 * @throws IOException 849 */ 850 private void emitAdvanceLine(int deltaLines) throws IOException { 851 int mark = output.getCursor(); 852 853 output.writeByte(DBG_ADVANCE_LINE); 854 output.writeSleb128(deltaLines); 855 line += deltaLines; 856 857 if (annotateTo != null || debugPrint != null) { 858 annotate(output.getCursor() - mark, 859 String.format("line = %d", line)); 860 } 861 862 if (DEBUG) { 863 System.err.printf("Emitting advance_line for %d\n", deltaLines); 864 } 865 } 866 867 /** 868 * Emits an {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC} 869 * sequence. 870 * 871 * @param deltaAddress {@code >= 0;} amount to change program counter by 872 * @throws IOException 873 */ 874 private void emitAdvancePc(int deltaAddress) throws IOException { 875 int mark = output.getCursor(); 876 877 output.writeByte(DBG_ADVANCE_PC); 878 output.writeUleb128(deltaAddress); 879 address += deltaAddress; 880 881 if (annotateTo != null || debugPrint != null) { 882 annotate(output.getCursor() - mark, 883 String.format("%04x: advance pc", address)); 884 } 885 886 if (DEBUG) { 887 System.err.printf("Emitting advance_pc for %d\n", deltaAddress); 888 } 889 } 890 891 /** 892 * Emits an unsigned LEB128 value. 893 * 894 * @param n {@code >= 0;} value to emit. Note that, although this can 895 * represent integers larger than Integer.MAX_VALUE, we currently don't 896 * allow that. 897 * @throws IOException 898 */ 899 private void emitUnsignedLeb128(int n) throws IOException { 900 // We'll never need the top end of the unsigned range anyway. 901 if (n < 0) { 902 throw new RuntimeException( 903 "Signed value where unsigned required: " + n); 904 } 905 906 output.writeUleb128(n); 907 } 908 909 /** 910 * Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE} 911 * bytecode. 912 */ 913 private void emitEndSequence() { 914 output.writeByte(DBG_END_SEQUENCE); 915 916 if (annotateTo != null || debugPrint != null) { 917 annotate(1, "end sequence"); 918 } 919 } 920} 921