DebugInfoEncoder.java revision 6dcac3deb3c19dc634470eb30b2daedf2b201bd4
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.dex.file;
18
19import com.android.dx.dex.code.LocalList;
20import com.android.dx.dex.code.PositionList;
21import com.android.dx.rop.code.RegisterSpec;
22import com.android.dx.rop.code.SourcePosition;
23import com.android.dx.rop.cst.CstMethodRef;
24import com.android.dx.rop.cst.CstType;
25import com.android.dx.rop.cst.CstUtf8;
26import com.android.dx.rop.type.Prototype;
27import com.android.dx.rop.type.StdTypeList;
28import com.android.dx.rop.type.Type;
29import com.android.dx.util.ByteArrayAnnotatedOutput;
30import com.android.dx.util.AnnotatedOutput;
31import com.android.dx.util.ExceptionWithContext;
32
33import java.io.IOException;
34import java.io.PrintWriter;
35import java.util.ArrayList;
36import java.util.Collections;
37import java.util.Comparator;
38import java.util.BitSet;
39
40import static com.android.dx.dex.file.DebugInfoConstants.*;
41
42/**
43 * An encoder for the dex debug info state machine format. The format
44 * for each method enrty is as follows:
45 * <ol>
46 * <li> signed LEB128: initial value for line register.
47 * <li> n instances of signed LEB128: string indicies (offset by 1)
48 * for each method argument in left-to-right order
49 * with <code>this</code> excluded. A value of '0' indicates "no name"
50 * <li> A sequence of special or normal opcodes as defined in
51 * <code>DebugInfoConstants</code>.
52 * <li> A single terminating <code>OP_END_SEQUENCE</code>
53 * </ol>
54 */
55public final class DebugInfoEncoder {
56    private static final boolean DEBUG = false;
57
58    /** null-ok; positions (line numbers) to encode */
59    private final PositionList positions;
60
61    /** 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 pl null-ok; positions (line numbers) to encode
100     * @param ll null-ok; local variables to encode
101     * @param file null-ok; may only be <code>null</code> 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 pl, LocalList ll,
109            DexFile file, int codeSize, int regSize,
110            boolean isStatic, CstMethodRef ref) {
111        this.positions = pl;
112        this.locals = ll;
113        this.file = file;
114        output = new ByteArrayAnnotatedOutput();
115        this.desc = ref.getPrototype();
116        this.isStatic = isStatic;
117
118        this.codeSize = codeSize;
119        this.regSize = regSize;
120
121        lastEntryForReg = new LocalList.Entry[regSize];
122    }
123
124    /**
125     * Annotates or writes a message to the <code>debugPrint</code> writer
126     * if applicable.
127     *
128     * @param length the number of bytes associated with this message
129     * @param message the message itself
130     */
131    private void annotate(int length, String message) {
132        if (prefix != null) {
133            message = prefix + message;
134        }
135
136        if (annotateTo != null) {
137            annotateTo.annotate(shouldConsume ? length : 0, message);
138        }
139
140        if (debugPrint != null) {
141            debugPrint.println(message);
142        }
143    }
144
145    /**
146     * Converts this (PositionList, LocalList) pair into a state machine
147     * sequence.
148     *
149     * @return encoded byte sequence without padding and
150     * terminated with a <code>'\00'</code>
151     */
152    public byte[] convert() {
153        try {
154            byte[] ret;
155            ret = convert0();
156
157            if (DEBUG) {
158                for (int i = 0 ; i < ret.length; i++) {
159                    System.err.printf("byte %02x\n", (0xff & ret[i]));
160                }
161            }
162
163            return ret;
164        } catch (IOException ex) {
165            throw ExceptionWithContext
166                    .withContext(ex, "...while encoding debug info");
167        }
168    }
169
170    /**
171     * Converts and produces annotations on a stream. Does not write
172     * actual bits to the <code>AnnotatedOutput</code>.
173     *
174     * @param prefix null-ok; prefix to attach to each line of output
175     * @param debugPrint null-ok; if specified, an alternate output for
176     * annotations
177     * @param out null-ok; if specified, where annotations should go
178     * @param consume whether to claim to have consumed output for
179     * <code>out</code>
180     * @return output sequence
181     */
182    public byte[] convertAndAnnotate(String prefix, PrintWriter debugPrint,
183            AnnotatedOutput out, boolean consume) {
184        this.prefix = prefix;
185        this.debugPrint = debugPrint;
186        annotateTo = out;
187        shouldConsume = consume;
188
189        byte[] result = convert();
190
191        return result;
192    }
193
194    private byte[] convert0() throws IOException {
195        ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions();
196        ArrayList<LocalList.Entry> methodArgs = extractMethodArguments();
197
198        emitHeader(sortedPositions, methodArgs);
199
200        // TODO: Make this mark be the actual prologue end.
201        output.writeByte(DBG_SET_PROLOGUE_END);
202
203        if (annotateTo != null || debugPrint != null) {
204            annotate(1, String.format("%04x: prologue end",address));
205        }
206
207        int szp = sortedPositions.size();
208        int szl = locals.size();
209
210        // Current index in sortedPositions
211        int curp = 0;
212        // Current index in locals
213        int curl = 0;
214
215        for (;;) {
216            /*
217             * Emit any information for the current address.
218             */
219
220            curl = emitLocalsAtAddress(curl);
221            curp = emitPositionsAtAddress(curp, 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 (curl < szl) {
231                nextAddrL = locals.get(curl).getAddress();
232            }
233
234            if (curp < szp) {
235                nextAddrP = sortedPositions.get(curp).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(curp++));
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 curl Current index in locals
275     * @return new value for <code>curl</code>
276     * @throws IOException
277     */
278    private int emitLocalsAtAddress(int curl)
279            throws IOException {
280        int sz = locals.size();
281
282        // TODO: Don't emit ends implied by starts.
283
284        while ((curl < sz)
285                && (locals.get(curl).getAddress() == address)) {
286            LocalList.Entry lle = locals.get(curl++);
287            int reg = lle.getRegister();
288            LocalList.Entry prevlle = lastEntryForReg[reg];
289
290            if (lle == prevlle) {
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] = lle;
301
302            if (lle.isStart()) {
303                if ((prevlle != null) && lle.matches(prevlle)) {
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 (prevlle.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(lle);
317                } else {
318                    emitLocalStart(lle);
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 (lle.getDisposition()
331                        != LocalList.Disposition.END_REPLACED) {
332                    emitLocalEnd(lle);
333                }
334            }
335        }
336
337        return curl;
338    }
339
340    /**
341     * Emits all positions that occur at the current <code>address</code>
342     *
343     * @param curp Current index in sortedPositions
344     * @param sortedPositions positions, sorted by ascending address
345     * @return new value for <code>curp</code>
346     * @throws IOException
347     */
348    private int emitPositionsAtAddress(int curp,
349            ArrayList<PositionList.Entry> sortedPositions)
350            throws IOException {
351
352        int szp = sortedPositions.size();
353        while (curp < szp
354                && sortedPositions.get(curp).getAddress() == address) {
355            emitPosition(sortedPositions.get(curp++));
356        }
357        return curp;
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.writeUnsignedLeb128(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.writeUnsignedLeb128(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            CstUtf8 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</code>
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</code> 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 non-null; entry
570     * @return 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        CstUtf8 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        CstUtf8 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 null-ok; string to emit
637     * @throws IOException
638     */
639    private void emitStringIndex(CstUtf8 string) throws IOException {
640        if ((string == null) || (file == null)) {
641            output.writeUnsignedLeb128(0);
642        } else {
643            output.writeUnsignedLeb128(
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 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.writeUnsignedLeb128(0);
663        } else {
664            output.writeUnsignedLeb128(
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 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.writeUnsignedLeb128(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 &gt;= DBG_LINE_BASE and &lt;= DBG_LINE_BASE +
827     * DBG_LINE_RANGE, the line change to encode
828     * @param deltaAddress &gt;= 0; the address change to encode
829     * @return &lt;= 0xff if in range, otherwise parameters are out of range
830     */
831    private static int computeOpcode(int deltaLines, int deltaAddress) {
832        if (deltaLines < DBG_LINE_BASE
833                || deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) {
834
835            throw new RuntimeException("Parameter out of range");
836        }
837
838        return (deltaLines - DBG_LINE_BASE)
839            + (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL;
840    }
841
842    /**
843     * Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE}
844     * sequence.
845     *
846     * @param deltaLines amount to change line number register by
847     * @throws IOException
848     */
849    private void emitAdvanceLine(int deltaLines) throws IOException {
850        int mark = output.getCursor();
851
852        output.writeByte(DBG_ADVANCE_LINE);
853        output.writeSignedLeb128(deltaLines);
854        line += deltaLines;
855
856        if (annotateTo != null || debugPrint != null) {
857            annotate(output.getCursor() - mark,
858                    String.format("line = %d", line));
859        }
860
861        if (DEBUG) {
862            System.err.printf("Emitting advance_line for %d\n", deltaLines);
863        }
864    }
865
866    /**
867     * Emits an  {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC}
868     * sequence.
869     *
870     * @param deltaAddress &gt;= 0 amount to change program counter by
871     * @throws IOException
872     */
873    private void emitAdvancePc(int deltaAddress) throws IOException {
874        int mark = output.getCursor();
875
876        output.writeByte(DBG_ADVANCE_PC);
877        output.writeUnsignedLeb128(deltaAddress);
878        address += deltaAddress;
879
880        if (annotateTo != null || debugPrint != null) {
881            annotate(output.getCursor() - mark,
882                    String.format("%04x: advance pc", address));
883        }
884
885        if (DEBUG) {
886            System.err.printf("Emitting advance_pc for %d\n", deltaAddress);
887        }
888    }
889
890    /**
891     * Emits an unsigned LEB128 value.
892     *
893     * @param n &gt= 0 vallue to emit. Note that, although this can represent
894     * integers larger than Integer.MAX_VALUE, we currently don't allow that.
895     * @throws IOException
896     */
897    private void emitUnsignedLeb128(int n) throws IOException {
898        // We'll never need the top end of the unsigned range anyway.
899        if (n < 0) {
900            throw new RuntimeException(
901                    "Signed value where unsigned required: " + n);
902        }
903
904        output.writeSignedLeb128(n);
905    }
906
907    /**
908     * Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE}
909     * bytecode.
910     */
911    private void emitEndSequence() {
912        output.writeByte(DBG_END_SEQUENCE);
913
914        if (annotateTo != null || debugPrint != null) {
915            annotate(1, "end sequence");
916        }
917    }
918}
919