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 17 package com.android.dx.dex.file; 18 19 import com.android.dex.util.ExceptionWithContext; 20 import com.android.dx.dex.code.LocalList; 21 import com.android.dx.dex.code.PositionList; 22 import static com.android.dx.dex.file.DebugInfoConstants.DBG_ADVANCE_LINE; 23 import static com.android.dx.dex.file.DebugInfoConstants.DBG_ADVANCE_PC; 24 import static com.android.dx.dex.file.DebugInfoConstants.DBG_END_LOCAL; 25 import static com.android.dx.dex.file.DebugInfoConstants.DBG_END_SEQUENCE; 26 import static com.android.dx.dex.file.DebugInfoConstants.DBG_FIRST_SPECIAL; 27 import static com.android.dx.dex.file.DebugInfoConstants.DBG_LINE_BASE; 28 import static com.android.dx.dex.file.DebugInfoConstants.DBG_LINE_RANGE; 29 import static com.android.dx.dex.file.DebugInfoConstants.DBG_RESTART_LOCAL; 30 import static com.android.dx.dex.file.DebugInfoConstants.DBG_SET_PROLOGUE_END; 31 import static com.android.dx.dex.file.DebugInfoConstants.DBG_START_LOCAL; 32 import static com.android.dx.dex.file.DebugInfoConstants.DBG_START_LOCAL_EXTENDED; 33 import com.android.dx.rop.code.RegisterSpec; 34 import com.android.dx.rop.code.SourcePosition; 35 import com.android.dx.rop.cst.CstMethodRef; 36 import com.android.dx.rop.cst.CstString; 37 import com.android.dx.rop.cst.CstType; 38 import com.android.dx.rop.type.Prototype; 39 import com.android.dx.rop.type.StdTypeList; 40 import com.android.dx.rop.type.Type; 41 import com.android.dx.util.AnnotatedOutput; 42 import com.android.dx.util.ByteArrayAnnotatedOutput; 43 import java.io.IOException; 44 import java.io.PrintWriter; 45 import java.util.ArrayList; 46 import java.util.BitSet; 47 import java.util.Collections; 48 import 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 */ 63 public 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 */ DebugInfoEncoder(PositionList positions, LocalList locals, DexFile file, int codeSize, int regSize, boolean isStatic, CstMethodRef ref)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 */ annotate(int length, String message)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 */ convert()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 */ convertAndAnnotate(String prefix, PrintWriter debugPrint, AnnotatedOutput out, boolean consume)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 convert0()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 */ emitLocalsAtAddress(int curLocalIdx)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 */ emitPositionsAtAddress(int curPositionIdx, ArrayList<PositionList.Entry> sortedPositions)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 */ emitHeader(ArrayList<PositionList.Entry> sortedPositions, ArrayList<LocalList.Entry> methodArgs)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 */ buildSortedPositions()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 */ getParamBase()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 */ extractMethodArguments()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 */ entryAnnotationString(LocalList.Entry e)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 */ emitLocalRestart(LocalList.Entry entry)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 */ emitStringIndex(CstString string)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 */ emitTypeIndex(CstType type)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 */ emitLocalStart(LocalList.Entry entry)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 */ emitLocalStartExtended(LocalList.Entry entry)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 */ emitLocalEnd(LocalList.Entry entry)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 */ emitPosition(PositionList.Entry entry)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 */ computeOpcode(int deltaLines, int deltaAddress)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 */ emitAdvanceLine(int deltaLines)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 */ emitAdvancePc(int deltaAddress)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 */ emitUnsignedLeb128(int n)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 */ emitEndSequence()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