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