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