• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.dx.dex.file;
18 
19 import com.android.dx.dex.code.LocalList;
20 import com.android.dx.dex.code.PositionList;
21 import com.android.dx.rop.code.RegisterSpec;
22 import com.android.dx.rop.code.SourcePosition;
23 import com.android.dx.rop.cst.CstMethodRef;
24 import com.android.dx.rop.cst.CstType;
25 import com.android.dx.rop.cst.CstString;
26 import com.android.dx.rop.type.Prototype;
27 import com.android.dx.rop.type.StdTypeList;
28 import com.android.dx.rop.type.Type;
29 import com.android.dx.util.ByteArrayAnnotatedOutput;
30 import com.android.dx.util.AnnotatedOutput;
31 import com.android.dx.util.ExceptionWithContext;
32 
33 import java.io.IOException;
34 import java.io.PrintWriter;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.BitSet;
39 
40 import 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  */
55 public 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      */
DebugInfoEncoder(PositionList positions, LocalList locals, DexFile file, int codeSize, int regSize, boolean isStatic, CstMethodRef ref)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      */
annotate(int length, String message)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      */
convert()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      */
convertAndAnnotate(String prefix, PrintWriter debugPrint, AnnotatedOutput out, boolean consume)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 
convert0()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      */
emitLocalsAtAddress(int curLocalIdx)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      */
emitPositionsAtAddress(int curPositionIdx, ArrayList<PositionList.Entry> sortedPositions)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      */
emitHeader(ArrayList<PositionList.Entry> sortedPositions, ArrayList<LocalList.Entry> methodArgs)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      */
buildSortedPositions()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      */
getParamBase()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      */
extractMethodArguments()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      */
entryAnnotationString(LocalList.Entry e)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      */
emitLocalRestart(LocalList.Entry entry)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      */
emitStringIndex(CstString string)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      */
emitTypeIndex(CstType type)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      */
emitLocalStart(LocalList.Entry entry)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      */
emitLocalStartExtended(LocalList.Entry entry)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      */
emitLocalEnd(LocalList.Entry entry)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      */
emitPosition(PositionList.Entry entry)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      */
computeOpcode(int deltaLines, int deltaAddress)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      */
emitAdvanceLine(int deltaLines)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      */
emitAdvancePc(int deltaAddress)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      */
emitUnsignedLeb128(int n)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      */
emitEndSequence()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