Home | History | Annotate | Download | only in file
      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.dx.dex.code.LocalList;
     20 import com.android.dx.dex.code.PositionList;
     21 import com.android.dx.rop.code.RegisterSpec;
     22 import com.android.dx.rop.code.SourcePosition;
     23 import com.android.dx.rop.cst.CstMethodRef;
     24 import com.android.dx.rop.cst.CstType;
     25 import com.android.dx.rop.cst.CstString;
     26 import com.android.dx.rop.type.Prototype;
     27 import com.android.dx.rop.type.StdTypeList;
     28 import com.android.dx.rop.type.Type;
     29 import com.android.dx.util.ByteArrayAnnotatedOutput;
     30 import com.android.dx.util.AnnotatedOutput;
     31 import com.android.dx.util.ExceptionWithContext;
     32 
     33 import java.io.IOException;
     34 import java.io.PrintWriter;
     35 import java.util.ArrayList;
     36 import java.util.Collections;
     37 import java.util.Comparator;
     38 import java.util.BitSet;
     39 
     40 import 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  */
     55 public 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