DebugInfoEncoder.java revision 446b11cd80658706d77d58996670b8eead3683a4
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 positionlist;
60
61    /** null-ok; local variables to encode */
62    private final LocalList locallist;
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.positionlist = pl;
112        this.locallist = 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> sortedLocalsStart = buildLocalsStart();
197
198        // Parameter locals are removed from sortedLocalsStart here.
199        ArrayList<LocalList.Entry> methodArgs
200                = extractMethodArguments(sortedLocalsStart);
201
202        ArrayList<LocalList.Entry> sortedLocalsEnd
203                = buildLocalsEnd(sortedLocalsStart);
204
205        emitHeader(sortedPositions, methodArgs);
206
207        // TODO: Make this mark 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 szp = sortedPositions.size();
215        int szl = sortedLocalsStart.size();
216
217        // Current index in sortedPositions
218        int curp = 0;
219        // Current index in sortedLocalsStart
220        int curls = 0;
221        // Current index in sortedLocalsEnd
222        int curle = 0;
223
224        for (;;) {
225            /*
226             * Emit any information for the current address.
227             */
228
229            curle = emitLocalEndsAtAddress(curle, sortedLocalsEnd, curls,
230                    sortedLocalsStart);
231
232            /*
233             * Our locals-sorted-by-range-end has reached the end
234             * of the code block. Ignore everything else.
235             */
236            if (address == codeSize) {
237                curle = szl;
238            }
239
240            curls = emitLocalStartsAtAddress(curls, sortedLocalsStart);
241
242            curp = emitPositionsAtAddress(curp, sortedPositions);
243
244            /*
245             * Figure out what the next important address is.
246             */
247
248            int nextAddrLS = Integer.MAX_VALUE; // local start
249            int nextAddrLE = Integer.MAX_VALUE; // local end
250            int nextAddrP = Integer.MAX_VALUE;  // position (line number)
251
252            if (curls < szl) {
253                nextAddrLS = sortedLocalsStart.get(curls).getStart();
254            }
255
256            if (curle < szl) {
257                nextAddrLE = sortedLocalsEnd.get(curle).getEnd();
258            }
259
260            if (curp < szp) {
261                nextAddrP = sortedPositions.get(curp).getAddress();
262            }
263
264            int next = Math.min(nextAddrP, Math.min(nextAddrLS, nextAddrLE));
265
266            // No next important address == done.
267            if (next == Integer.MAX_VALUE) {
268                break;
269            }
270
271            /*
272             * If the only work remaining are local ends at the end of the
273             * block, stop here. Those are implied anyway.
274             */
275            if (next == codeSize
276                    && nextAddrLS == Integer.MAX_VALUE
277                    && nextAddrP == Integer.MAX_VALUE) {
278                break;
279            }
280
281            if (next == nextAddrP) {
282                // Combined advance PC + position entry
283                emitPosition(sortedPositions.get(curp++));
284            } else {
285                emitAdvancePc(next - address);
286            }
287        }
288
289        emitEndSequence();
290
291        return output.toByteArray();
292    }
293
294    /**
295     * Emits all local ends that occur at the current <code>address</code>
296     *
297     * @param curle Current index in sortedLocalsEnd
298     * @param sortedLocalsEnd Locals, sorted by ascending end address
299     * @param curls Current index in sortedLocalsStart
300     * @param sortedLocalsStart Locals, sorted by ascending start address
301     * @return new value for <code>curle</code>
302     * @throws IOException
303     */
304    private int emitLocalEndsAtAddress(int curle,
305            ArrayList<LocalList.Entry> sortedLocalsEnd, int curls,
306            ArrayList<LocalList.Entry> sortedLocalsStart)
307            throws IOException {
308
309        int szl = sortedLocalsEnd.size();
310
311        // Ignore "local ends" at end of code.
312        while (curle < szl
313                && sortedLocalsEnd.get(curle).getEnd() == address
314                && address != codeSize) {
315
316            boolean skipLocalEnd = false;
317
318            /*
319             * Check to see if there's a range-start that appears at
320             * the same address for the same register. If so, the
321             * end-range is implicit so skip it.
322             */
323            for (int j = curls; j < szl
324                    && sortedLocalsStart.get(j).getStart() == address
325                    ; j++) {
326
327                if (sortedLocalsStart.get(j).getRegister()
328                        == sortedLocalsEnd.get(curle).getRegister()) {
329                    skipLocalEnd = true;
330
331                    if (DEBUG) {
332                        System.err.printf("skip local end v%d\n",
333                                sortedLocalsEnd.get(curle).getRegister());
334                    }
335                    break;
336                }
337            }
338
339            if (!skipLocalEnd) {
340                emitLocalEnd(sortedLocalsEnd.get(curle));
341            }
342
343            curle++;
344        }
345        return curle;
346    }
347
348    /**
349     * Emits all local starts that occur at the current <code>address</code>
350     *
351     * @param curls Current index in sortedLocalsStart
352     * @param sortedLocalsStart Locals, sorted by ascending start address
353     * @return new value for <code>curls</code>
354     * @throws IOException
355     */
356    private int emitLocalStartsAtAddress(int curls,
357            ArrayList<LocalList.Entry> sortedLocalsStart)
358            throws IOException {
359
360        int szl = sortedLocalsStart.size();
361
362        while (curls < szl
363                && sortedLocalsStart.get(curls).getStart() == address) {
364            LocalList.Entry lle = sortedLocalsStart.get(curls++);
365            int reg = lle.getRegister();
366            LocalList.Entry prevlle = lastEntryForReg[reg];
367
368            if (lle == prevlle) {
369                /*
370                 * Here we ignore locals entries for parameters,
371                 * which have already been represented and placed in the
372                 * lastEntryForReg array.
373                 */
374                continue;
375            }
376
377            // At this point we have a new live entry one way or another.
378            lastEntryForReg[reg] = lle;
379
380            if ((prevlle != null) && lle.matches(prevlle)) {
381                if (prevlle.getEnd() == lle.getStart()) {
382                    /*
383                     * There is nothing more to do in this case: It's
384                     * an adjacent range with the same register. The
385                     * previous emitLocalEndsAtAddress() call skipped
386                     * this local end, so we'll skip this local start
387                     * as well.
388                     */
389                } else {
390                    emitLocalRestart(lle);
391                }
392            } else {
393                emitLocalStart(lle);
394            }
395        }
396        return curls;
397    }
398
399    /**
400     * Emits all positions that occur at the current <code>address</code>
401     *
402     * @param curp Current index in sortedPositions
403     * @param sortedPositions positions, sorted by ascending address
404     * @return new value for <code>curp</code>
405     * @throws IOException
406     */
407    private int emitPositionsAtAddress(int curp,
408            ArrayList<PositionList.Entry> sortedPositions)
409            throws IOException {
410
411        int szp = sortedPositions.size();
412        while (curp < szp
413                && sortedPositions.get(curp).getAddress() == address) {
414            emitPosition(sortedPositions.get(curp++));
415        }
416        return curp;
417    }
418
419    /**
420     * Emits the header sequence, which consists of LEB128-encoded initial
421     * line number and string indicies for names of all non-"this" arguments.
422     *
423     * @param sortedPositions positions, sorted by ascending address
424     * @param methodArgs local list entries for method argumens arguments,
425     * in left-to-right order omitting "this"
426     * @throws IOException
427     */
428    private void emitHeader(ArrayList<PositionList.Entry> sortedPositions,
429            ArrayList<LocalList.Entry> methodArgs) throws IOException {
430        boolean annotate = (annotateTo != null) || (debugPrint != null);
431        int mark = output.getCursor();
432
433        // Start by initializing the line number register.
434        if (sortedPositions.size() > 0) {
435            PositionList.Entry entry = sortedPositions.get(0);
436            line = entry.getPosition().getLine();
437        }
438        output.writeUnsignedLeb128(line);
439
440        if (annotate) {
441            annotate(output.getCursor() - mark, "line_start: " + line);
442        }
443
444        int curParam = getParamBase();
445        // paramTypes will not include 'this'
446        StdTypeList paramTypes = desc.getParameterTypes();
447        int szParamTypes = paramTypes.size();
448
449        /*
450         * Initialize lastEntryForReg to have an initial
451         * entry for the 'this' pointer.
452         */
453        if (!isStatic) {
454            for (LocalList.Entry arg: methodArgs) {
455                if (curParam == arg.getRegister()) {
456                    lastEntryForReg[curParam] = arg;
457                    break;
458                }
459            }
460            curParam++;
461        }
462
463        // Write out the number of parameter entries that will follow.
464        mark = output.getCursor();
465        output.writeUnsignedLeb128(szParamTypes);
466
467        if (annotate) {
468            annotate(output.getCursor() - mark,
469                    String.format("parameters_size: %04x", szParamTypes));
470        }
471
472        /*
473         * Then emit the string indicies of all the method parameters.
474         * Note that 'this', if applicable, is excluded.
475         */
476        for (int i = 0; i < szParamTypes; i++) {
477            Type pt = paramTypes.get(i);
478            LocalList.Entry found = null;
479
480            mark = output.getCursor();
481
482            for (LocalList.Entry arg: methodArgs) {
483                if (curParam == arg.getRegister()) {
484                    found = arg;
485
486                    if (arg.getSignature() != null) {
487                        /*
488                         * Parameters with signatures will be re-emitted
489                         * in complete as LOCAL_START_EXTENDED's below.
490                         */
491                        emitStringIndex(null);
492                    } else {
493                        emitStringIndex(arg.getName());
494                    }
495                    lastEntryForReg[curParam] = arg;
496
497                    break;
498                }
499            }
500
501            if (found == null) {
502                /*
503                 * Emit a null symbol for "unnamed." This is common
504                 * for, e.g., synthesized methods and inner-class
505                 * this$0 arguments.
506                 */
507                emitStringIndex(null);
508            }
509
510            if (annotate) {
511                String parameterName
512                        = (found == null || found.getSignature() != null)
513                                ? "<unnamed>" : found.getName().toHuman();
514                annotate(output.getCursor() - mark,
515                        "parameter " + parameterName + " "
516                                + RegisterSpec.PREFIX + curParam);
517            }
518
519            curParam += pt.getCategory();
520        }
521
522        /*
523         * If anything emitted above has a type signature, emit it again as
524         * a LOCAL_RESTART_EXTENDED
525         */
526
527        for (LocalList.Entry arg: lastEntryForReg) {
528            if (arg == null) {
529                continue;
530            }
531
532            CstUtf8 signature = arg.getSignature();
533
534            if (signature != null) {
535                emitLocalStartExtended(arg);
536            }
537        }
538    }
539
540    /**
541     * Builds a list of position entries, sorted by ascending address.
542     *
543     * @return A sorted positions list
544     */
545    private ArrayList<PositionList.Entry> buildSortedPositions() {
546        int sz = (positionlist == null) ? 0 : positionlist.size();
547        ArrayList<PositionList.Entry> result = new ArrayList(sz);
548
549        for (int i = 0; i < sz; i++) {
550            result.add(positionlist.get(i));
551        }
552
553        // Sort ascending by address.
554        Collections.sort (result, new Comparator<PositionList.Entry>() {
555            public int compare (PositionList.Entry a, PositionList.Entry b) {
556                return a.getAddress() - b.getAddress();
557            }
558
559            public boolean equals (Object obj) {
560               return obj == this;
561            }
562        });
563        return result;
564    }
565
566    /**
567     * Builds a list of locals entries sorted by ascending start address.
568     *
569     * @return A sorted locals list list
570     */
571    private ArrayList<LocalList.Entry> buildLocalsStart() {
572        int sz = (locallist == null) ? 0 : locallist.size();
573        ArrayList<LocalList.Entry> result = new ArrayList(sz);
574
575        // Add all the entries
576        for (int i = 0; i < sz; i++) {
577            LocalList.Entry e = locallist.get(i);
578            result.add(locallist.get(i));
579        }
580
581        // Sort ascending by start address.
582        Collections.sort (result, new Comparator<LocalList.Entry>() {
583            public int compare (LocalList.Entry a, LocalList.Entry b) {
584                return a.getStart() - b.getStart();
585            }
586
587            public boolean equals (Object obj) {
588               return obj == this;
589            }
590        });
591        return result;
592    }
593
594    /**
595     * Builds a list of locals entries sorted by ascending end address.
596     *
597     * @param list locals list in any order
598     * @return a sorted locals list
599     */
600    private ArrayList<LocalList.Entry> buildLocalsEnd(
601            ArrayList<LocalList.Entry> list) {
602
603        ArrayList<LocalList.Entry> sortedLocalsEnd  = new ArrayList(list);
604
605        // Sort ascending by end address.
606        Collections.sort (sortedLocalsEnd, new Comparator<LocalList.Entry>() {
607            public int compare (LocalList.Entry a, LocalList.Entry b) {
608                return a.getEnd() - b.getEnd();
609            }
610
611            public boolean equals (Object obj) {
612               return obj == this;
613            }
614        });
615        return sortedLocalsEnd;
616    }
617
618    /**
619     * Gets the register that begins the method's parameter range (including
620     * the 'this' parameter for non-static methods). The range continues until
621     * <code>regSize</code>
622     *
623     * @return register as noted above
624     */
625    private int getParamBase() {
626        return regSize
627                - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1);
628    }
629
630    /**
631     * Extracts method arguments from a locals list. These will be collected
632     * from the input list and sorted by ascending register in the
633     * returned list.
634     *
635     * @param sortedLocals locals list, sorted by ascending start address,
636     * to process; left unmodified
637     * @return list of non-<code>this</code> method argument locals,
638     * sorted by ascending register
639     */
640    private ArrayList<LocalList.Entry> extractMethodArguments (
641            ArrayList<LocalList.Entry> sortedLocals) {
642
643        ArrayList<LocalList.Entry> result
644                = new ArrayList(desc.getParameterTypes().size());
645
646        int argBase = getParamBase();
647
648        BitSet seen = new BitSet(regSize - argBase);
649
650        int sz = sortedLocals.size();
651        for (int i = 0; i < sz; i++) {
652            LocalList.Entry e = sortedLocals.get(i);
653            int reg = e.getRegister();
654
655            if (reg < argBase) {
656                continue;
657            }
658
659            // only the lowest-start-address entry is included.
660            if (seen.get(reg - argBase)) {
661                continue;
662            }
663
664            seen.set(reg - argBase);
665            result.add(e);
666        }
667
668        // Sort by ascending register.
669        Collections.sort (result, new Comparator<LocalList.Entry>() {
670            public int compare (LocalList.Entry a, LocalList.Entry b) {
671                return a.getRegister() - b.getRegister();
672            }
673
674            public boolean equals (Object obj) {
675               return obj == this;
676            }
677        });
678
679        return result;
680    }
681
682    /**
683     * Returns a string representation of this LocalList entry that is
684     * appropriate for emitting as an annotation.
685     *
686     * @param e non-null; entry
687     * @return non-null; annotation string
688     */
689    private String entryAnnotationString(LocalList.Entry e) {
690        StringBuilder sb = new StringBuilder();
691
692        sb.append(RegisterSpec.PREFIX);
693        sb.append(e.getRegister());
694        sb.append(' ');
695
696        CstUtf8 name = e.getName();
697        if (name == null) {
698            sb.append("null");
699        } else {
700            sb.append(name.toHuman());
701        }
702        sb.append(' ');
703
704        CstType type = e.getType();
705        if (type == null) {
706            sb.append("null");
707        } else {
708            sb.append(type.toHuman());
709        }
710
711        CstUtf8 signature = e.getSignature();
712
713        if (signature != null) {
714            sb.append(' ');
715            sb.append(signature.toHuman());
716        }
717
718        return sb.toString();
719    }
720
721    /**
722     * Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL}
723     * sequence.
724     *
725     * @param entry entry associated with this restart
726     * @throws IOException
727     */
728    private void emitLocalRestart(LocalList.Entry entry)
729            throws IOException {
730
731        int mark = output.getCursor();
732
733        output.writeByte(DBG_RESTART_LOCAL);
734        emitUnsignedLeb128(entry.getRegister());
735
736        if (annotateTo != null || debugPrint != null) {
737            annotate(output.getCursor() - mark,
738                    String.format("%04x: +local restart %s",
739                            address, entryAnnotationString(entry)));
740        }
741
742        if (DEBUG) {
743            System.err.println("emit local restart");
744        }
745    }
746
747    /**
748     * Emits a string index as an unsigned LEB128. The actual value written
749     * is shifted by 1, so that the '0' value is reserved for "null". The
750     * null symbol is used in some cases by the parameter name list
751     * at the beginning of the sequence.
752     *
753     * @param string null-ok; string to emit
754     * @throws IOException
755     */
756    private void emitStringIndex(CstUtf8 string) throws IOException {
757        if ((string == null) || (file == null)) {
758            output.writeUnsignedLeb128(0);
759        } else {
760            output.writeUnsignedLeb128(
761                1 + file.getStringIds().indexOf(string));
762        }
763
764        if (DEBUG) {
765            System.err.printf("Emit string %s\n",
766                    string == null ? "<null>" : string.toQuoted());
767        }
768    }
769
770    /**
771     * Emits a type index as an unsigned LEB128. The actual value written
772     * is shifted by 1, so that the '0' value is reserved for "null".
773     *
774     * @param type null-ok; type to emit
775     * @throws IOException
776     */
777    private void emitTypeIndex(CstType type) throws IOException {
778        if ((type == null) || (file == null)) {
779            output.writeUnsignedLeb128(0);
780        } else {
781            output.writeUnsignedLeb128(
782                1 + file.getTypeIds().indexOf(type));
783        }
784
785        if (DEBUG) {
786            System.err.printf("Emit type %s\n",
787                    type == null ? "<null>" : type.toHuman());
788        }
789    }
790
791    /**
792     * Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or
793     * {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
794     * DBG_START_LOCAL_EXTENDED} sequence.
795     *
796     * @param entry entry to emit
797     * @throws IOException
798     */
799    private void emitLocalStart(LocalList.Entry entry)
800        throws IOException {
801
802        if (entry.getSignature() != null) {
803            emitLocalStartExtended(entry);
804            return;
805        }
806
807        int mark = output.getCursor();
808
809        output.writeByte(DBG_START_LOCAL);
810
811        emitUnsignedLeb128(entry.getRegister());
812        emitStringIndex(entry.getName());
813        emitTypeIndex(entry.getType());
814
815        if (annotateTo != null || debugPrint != null) {
816            annotate(output.getCursor() - mark,
817                    String.format("%04x: +local %s", address,
818                            entryAnnotationString(entry)));
819        }
820
821        if (DEBUG) {
822            System.err.println("emit local start");
823        }
824    }
825
826    /**
827     * Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
828     * DBG_START_LOCAL_EXTENDED} sequence.
829     *
830     * @param entry entry to emit
831     * @throws IOException
832     */
833    private void emitLocalStartExtended(LocalList.Entry entry)
834        throws IOException {
835
836        int mark = output.getCursor();
837
838        output.writeByte(DBG_START_LOCAL_EXTENDED);
839
840        emitUnsignedLeb128(entry.getRegister());
841        emitStringIndex(entry.getName());
842        emitTypeIndex(entry.getType());
843        emitStringIndex(entry.getSignature());
844
845        if (annotateTo != null || debugPrint != null) {
846            annotate(output.getCursor() - mark,
847                    String.format("%04x: +localx %s", address,
848                            entryAnnotationString(entry)));
849        }
850
851        if (DEBUG) {
852            System.err.println("emit local start");
853        }
854    }
855
856    /**
857     * Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence.
858     *
859     * @param entry entry non-null; entry associated with end.
860     * @throws IOException
861     */
862    private void emitLocalEnd(LocalList.Entry entry)
863            throws IOException {
864
865        int mark = output.getCursor();
866
867        output.writeByte(DBG_END_LOCAL);
868        output.writeUnsignedLeb128(entry.getRegister());
869
870        if (annotateTo != null || debugPrint != null) {
871            annotate(output.getCursor() - mark,
872                    String.format("%04x: -local %s", address,
873                            entryAnnotationString(entry)));
874        }
875
876        if (DEBUG) {
877            System.err.println("emit local end");
878        }
879    }
880
881    /**
882     * Emits the necessary byte sequences to emit the given position table
883     * entry. This will typically be a single special opcode, although
884     * it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE.
885     *
886     * @param entry position entry to emit.
887     * @throws IOException
888     */
889    private void emitPosition(PositionList.Entry entry)
890            throws IOException {
891
892        SourcePosition pos = entry.getPosition();
893        int newLine = pos.getLine();
894        int newAddress = entry.getAddress();
895
896        int opcode;
897
898        int deltaLines = newLine - line;
899        int deltaAddress = newAddress - address;
900
901        if (deltaAddress < 0) {
902            throw new RuntimeException(
903                    "Position entries must be in ascending address order");
904        }
905
906        if ((deltaLines < DBG_LINE_BASE)
907                || (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1))) {
908            emitAdvanceLine(deltaLines);
909            deltaLines = 0;
910        }
911
912        opcode = computeOpcode (deltaLines, deltaAddress);
913
914        if ((opcode & ~0xff) > 0) {
915            emitAdvancePc(deltaAddress);
916            deltaAddress = 0;
917            opcode = computeOpcode (deltaLines, deltaAddress);
918
919            if ((opcode & ~0xff) > 0) {
920                emitAdvanceLine(deltaLines);
921                deltaLines = 0;
922                opcode = computeOpcode (deltaLines, deltaAddress);
923            }
924        }
925
926        output.writeByte(opcode);
927
928        line += deltaLines;
929        address += deltaAddress;
930
931        if (annotateTo != null || debugPrint != null) {
932            annotate(1,
933                    String.format("%04x: line %d", address, line));
934        }
935    }
936
937    /**
938     * Computes a special opcode that will encode the given position change.
939     * If the return value is > 0xff, then the request cannot be fulfilled.
940     * Essentially the same as described in "DWARF Debugging Format Version 3"
941     * section 6.2.5.1.
942     *
943     * @param deltaLines &gt;= DBG_LINE_BASE and &lt;= DBG_LINE_BASE +
944     * DBG_LINE_RANGE, the line change to encode
945     * @param deltaAddress &gt;= 0; the address change to encode
946     * @return &lt;= 0xff if in range, otherwise parameters are out of range
947     */
948    private static int computeOpcode(int deltaLines, int deltaAddress) {
949        if (deltaLines < DBG_LINE_BASE
950                || deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) {
951
952            throw new RuntimeException("Parameter out of range");
953        }
954
955        return (deltaLines - DBG_LINE_BASE)
956            + (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL;
957    }
958
959    /**
960     * Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE}
961     * sequence.
962     *
963     * @param deltaLines amount to change line number register by
964     * @throws IOException
965     */
966    private void emitAdvanceLine(int deltaLines) throws IOException {
967        int mark = output.getCursor();
968
969        output.writeByte(DBG_ADVANCE_LINE);
970        output.writeSignedLeb128(deltaLines);
971        line += deltaLines;
972
973        if (annotateTo != null || debugPrint != null) {
974            annotate(output.getCursor() - mark,
975                    String.format("line = %d", line));
976        }
977
978        if (DEBUG) {
979            System.err.printf("Emitting advance_line for %d\n", deltaLines);
980        }
981    }
982
983    /**
984     * Emits an  {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC}
985     * sequence.
986     *
987     * @param deltaAddress &gt;= 0 amount to change program counter by
988     * @throws IOException
989     */
990    private void emitAdvancePc(int deltaAddress) throws IOException {
991        int mark = output.getCursor();
992
993        output.writeByte(DBG_ADVANCE_PC);
994        output.writeUnsignedLeb128(deltaAddress);
995        address += deltaAddress;
996
997        if (annotateTo != null || debugPrint != null) {
998            annotate(output.getCursor() - mark,
999                    String.format("%04x: advance pc", address));
1000        }
1001
1002        if (DEBUG) {
1003            System.err.printf("Emitting advance_pc for %d\n", deltaAddress);
1004        }
1005    }
1006
1007    /**
1008     * Emits an unsigned LEB128 value.
1009     *
1010     * @param n &gt= 0 vallue to emit. Note that, although this can represent
1011     * integers larger than Integer.MAX_VALUE, we currently don't allow that.
1012     * @throws IOException
1013     */
1014    private void emitUnsignedLeb128(int n) throws IOException {
1015        // We'll never need the top end of the unsigned range anyway.
1016        if (n < 0) {
1017            throw new RuntimeException(
1018                    "Signed value where unsigned required: " + n);
1019        }
1020
1021        output.writeSignedLeb128(n);
1022    }
1023
1024    /**
1025     * Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE}
1026     * bytecode.
1027     */
1028    private void emitEndSequence() {
1029        output.writeByte(DBG_END_SEQUENCE);
1030
1031        if (annotateTo != null || debugPrint != null) {
1032            annotate(1, "end sequence");
1033        }
1034    }
1035}
1036