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.CstString;
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} 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 */
55public 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