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