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