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             public int compare (PositionList.Entry a, PositionList.Entry b) {
505                 return a.getAddress() - b.getAddress();
506             }
507 
508             public boolean equals (Object obj) {
509                return obj == this;
510             }
511         });
512         return result;
513     }
514 
515     /**
516      * Gets the register that begins the method's parameter range (including
517      * the 'this' parameter for non-static methods). The range continues until
518      * {@code regSize}
519      *
520      * @return register as noted above
521      */
getParamBase()522     private int getParamBase() {
523         return regSize
524                 - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1);
525     }
526 
527     /**
528      * Extracts method arguments from a locals list. These will be collected
529      * from the input list and sorted by ascending register in the
530      * returned list.
531      *
532      * @return list of non-{@code this} method argument locals,
533      * sorted by ascending register
534      */
extractMethodArguments()535     private ArrayList<LocalList.Entry> extractMethodArguments() {
536         ArrayList<LocalList.Entry> result
537                 = new ArrayList(desc.getParameterTypes().size());
538         int argBase = getParamBase();
539         BitSet seen = new BitSet(regSize - argBase);
540         int sz = locals.size();
541 
542         for (int i = 0; i < sz; i++) {
543             LocalList.Entry e = locals.get(i);
544             int reg = e.getRegister();
545 
546             if (reg < argBase) {
547                 continue;
548             }
549 
550             // only the lowest-start-address entry is included.
551             if (seen.get(reg - argBase)) {
552                 continue;
553             }
554 
555             seen.set(reg - argBase);
556             result.add(e);
557         }
558 
559         // Sort by ascending register.
560         Collections.sort(result, new Comparator<LocalList.Entry>() {
561             public int compare(LocalList.Entry a, LocalList.Entry b) {
562                 return a.getRegister() - b.getRegister();
563             }
564 
565             public boolean equals(Object obj) {
566                return obj == this;
567             }
568         });
569 
570         return result;
571     }
572 
573     /**
574      * Returns a string representation of this LocalList entry that is
575      * appropriate for emitting as an annotation.
576      *
577      * @param e {@code non-null;} entry
578      * @return {@code non-null;} annotation string
579      */
entryAnnotationString(LocalList.Entry e)580     private String entryAnnotationString(LocalList.Entry e) {
581         StringBuilder sb = new StringBuilder();
582 
583         sb.append(RegisterSpec.PREFIX);
584         sb.append(e.getRegister());
585         sb.append(' ');
586 
587         CstString name = e.getName();
588         if (name == null) {
589             sb.append("null");
590         } else {
591             sb.append(name.toHuman());
592         }
593         sb.append(' ');
594 
595         CstType type = e.getType();
596         if (type == null) {
597             sb.append("null");
598         } else {
599             sb.append(type.toHuman());
600         }
601 
602         CstString signature = e.getSignature();
603 
604         if (signature != null) {
605             sb.append(' ');
606             sb.append(signature.toHuman());
607         }
608 
609         return sb.toString();
610     }
611 
612     /**
613      * Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL}
614      * sequence.
615      *
616      * @param entry entry associated with this restart
617      * @throws IOException
618      */
emitLocalRestart(LocalList.Entry entry)619     private void emitLocalRestart(LocalList.Entry entry)
620             throws IOException {
621 
622         int mark = output.getCursor();
623 
624         output.writeByte(DBG_RESTART_LOCAL);
625         emitUnsignedLeb128(entry.getRegister());
626 
627         if (annotateTo != null || debugPrint != null) {
628             annotate(output.getCursor() - mark,
629                     String.format("%04x: +local restart %s",
630                             address, entryAnnotationString(entry)));
631         }
632 
633         if (DEBUG) {
634             System.err.println("emit local restart");
635         }
636     }
637 
638     /**
639      * Emits a string index as an unsigned LEB128. The actual value written
640      * is shifted by 1, so that the '0' value is reserved for "null". The
641      * null symbol is used in some cases by the parameter name list
642      * at the beginning of the sequence.
643      *
644      * @param string {@code null-ok;} string to emit
645      * @throws IOException
646      */
emitStringIndex(CstString string)647     private void emitStringIndex(CstString string) throws IOException {
648         if ((string == null) || (file == null)) {
649             output.writeUleb128(0);
650         } else {
651             output.writeUleb128(
652                     1 + file.getStringIds().indexOf(string));
653         }
654 
655         if (DEBUG) {
656             System.err.printf("Emit string %s\n",
657                     string == null ? "<null>" : string.toQuoted());
658         }
659     }
660 
661     /**
662      * Emits a type index as an unsigned LEB128. The actual value written
663      * is shifted by 1, so that the '0' value is reserved for "null".
664      *
665      * @param type {@code null-ok;} type to emit
666      * @throws IOException
667      */
emitTypeIndex(CstType type)668     private void emitTypeIndex(CstType type) throws IOException {
669         if ((type == null) || (file == null)) {
670             output.writeUleb128(0);
671         } else {
672             output.writeUleb128(
673                     1 + file.getTypeIds().indexOf(type));
674         }
675 
676         if (DEBUG) {
677             System.err.printf("Emit type %s\n",
678                     type == null ? "<null>" : type.toHuman());
679         }
680     }
681 
682     /**
683      * Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or
684      * {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
685      * DBG_START_LOCAL_EXTENDED} sequence.
686      *
687      * @param entry entry to emit
688      * @throws IOException
689      */
emitLocalStart(LocalList.Entry entry)690     private void emitLocalStart(LocalList.Entry entry)
691         throws IOException {
692 
693         if (entry.getSignature() != null) {
694             emitLocalStartExtended(entry);
695             return;
696         }
697 
698         int mark = output.getCursor();
699 
700         output.writeByte(DBG_START_LOCAL);
701 
702         emitUnsignedLeb128(entry.getRegister());
703         emitStringIndex(entry.getName());
704         emitTypeIndex(entry.getType());
705 
706         if (annotateTo != null || debugPrint != null) {
707             annotate(output.getCursor() - mark,
708                     String.format("%04x: +local %s", address,
709                             entryAnnotationString(entry)));
710         }
711 
712         if (DEBUG) {
713             System.err.println("emit local start");
714         }
715     }
716 
717     /**
718      * Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
719      * DBG_START_LOCAL_EXTENDED} sequence.
720      *
721      * @param entry entry to emit
722      * @throws IOException
723      */
emitLocalStartExtended(LocalList.Entry entry)724     private void emitLocalStartExtended(LocalList.Entry entry)
725         throws IOException {
726 
727         int mark = output.getCursor();
728 
729         output.writeByte(DBG_START_LOCAL_EXTENDED);
730 
731         emitUnsignedLeb128(entry.getRegister());
732         emitStringIndex(entry.getName());
733         emitTypeIndex(entry.getType());
734         emitStringIndex(entry.getSignature());
735 
736         if (annotateTo != null || debugPrint != null) {
737             annotate(output.getCursor() - mark,
738                     String.format("%04x: +localx %s", address,
739                             entryAnnotationString(entry)));
740         }
741 
742         if (DEBUG) {
743             System.err.println("emit local start");
744         }
745     }
746 
747     /**
748      * Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence.
749      *
750      * @param entry {@code entry non-null;} entry associated with end.
751      * @throws IOException
752      */
emitLocalEnd(LocalList.Entry entry)753     private void emitLocalEnd(LocalList.Entry entry)
754             throws IOException {
755 
756         int mark = output.getCursor();
757 
758         output.writeByte(DBG_END_LOCAL);
759         output.writeUleb128(entry.getRegister());
760 
761         if (annotateTo != null || debugPrint != null) {
762             annotate(output.getCursor() - mark,
763                     String.format("%04x: -local %s", address,
764                             entryAnnotationString(entry)));
765         }
766 
767         if (DEBUG) {
768             System.err.println("emit local end");
769         }
770     }
771 
772     /**
773      * Emits the necessary byte sequences to emit the given position table
774      * entry. This will typically be a single special opcode, although
775      * it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE.
776      *
777      * @param entry position entry to emit.
778      * @throws IOException
779      */
emitPosition(PositionList.Entry entry)780     private void emitPosition(PositionList.Entry entry)
781             throws IOException {
782 
783         SourcePosition pos = entry.getPosition();
784         int newLine = pos.getLine();
785         int newAddress = entry.getAddress();
786 
787         int opcode;
788 
789         int deltaLines = newLine - line;
790         int deltaAddress = newAddress - address;
791 
792         if (deltaAddress < 0) {
793             throw new RuntimeException(
794                     "Position entries must be in ascending address order");
795         }
796 
797         if ((deltaLines < DBG_LINE_BASE)
798                 || (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1))) {
799             emitAdvanceLine(deltaLines);
800             deltaLines = 0;
801         }
802 
803         opcode = computeOpcode (deltaLines, deltaAddress);
804 
805         if ((opcode & ~0xff) > 0) {
806             emitAdvancePc(deltaAddress);
807             deltaAddress = 0;
808             opcode = computeOpcode (deltaLines, deltaAddress);
809 
810             if ((opcode & ~0xff) > 0) {
811                 emitAdvanceLine(deltaLines);
812                 deltaLines = 0;
813                 opcode = computeOpcode (deltaLines, deltaAddress);
814             }
815         }
816 
817         output.writeByte(opcode);
818 
819         line += deltaLines;
820         address += deltaAddress;
821 
822         if (annotateTo != null || debugPrint != null) {
823             annotate(1,
824                     String.format("%04x: line %d", address, line));
825         }
826     }
827 
828     /**
829      * Computes a special opcode that will encode the given position change.
830      * If the return value is > 0xff, then the request cannot be fulfilled.
831      * Essentially the same as described in "DWARF Debugging Format Version 3"
832      * section 6.2.5.1.
833      *
834      * @param deltaLines {@code >= DBG_LINE_BASE, <= DBG_LINE_BASE +
835      * DBG_LINE_RANGE;} the line change to encode
836      * @param deltaAddress {@code >= 0;} the address change to encode
837      * @return {@code <= 0xff} if in range, otherwise parameters are out
838      * of range
839      */
computeOpcode(int deltaLines, int deltaAddress)840     private static int computeOpcode(int deltaLines, int deltaAddress) {
841         if (deltaLines < DBG_LINE_BASE
842                 || deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) {
843 
844             throw new RuntimeException("Parameter out of range");
845         }
846 
847         return (deltaLines - DBG_LINE_BASE)
848             + (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL;
849     }
850 
851     /**
852      * Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE}
853      * sequence.
854      *
855      * @param deltaLines amount to change line number register by
856      * @throws IOException
857      */
emitAdvanceLine(int deltaLines)858     private void emitAdvanceLine(int deltaLines) throws IOException {
859         int mark = output.getCursor();
860 
861         output.writeByte(DBG_ADVANCE_LINE);
862         output.writeSleb128(deltaLines);
863         line += deltaLines;
864 
865         if (annotateTo != null || debugPrint != null) {
866             annotate(output.getCursor() - mark,
867                     String.format("line = %d", line));
868         }
869 
870         if (DEBUG) {
871             System.err.printf("Emitting advance_line for %d\n", deltaLines);
872         }
873     }
874 
875     /**
876      * Emits an  {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC}
877      * sequence.
878      *
879      * @param deltaAddress {@code >= 0;} amount to change program counter by
880      * @throws IOException
881      */
emitAdvancePc(int deltaAddress)882     private void emitAdvancePc(int deltaAddress) throws IOException {
883         int mark = output.getCursor();
884 
885         output.writeByte(DBG_ADVANCE_PC);
886         output.writeUleb128(deltaAddress);
887         address += deltaAddress;
888 
889         if (annotateTo != null || debugPrint != null) {
890             annotate(output.getCursor() - mark,
891                     String.format("%04x: advance pc", address));
892         }
893 
894         if (DEBUG) {
895             System.err.printf("Emitting advance_pc for %d\n", deltaAddress);
896         }
897     }
898 
899     /**
900      * Emits an unsigned LEB128 value.
901      *
902      * @param n {@code >= 0;} value to emit. Note that, although this can
903      * represent integers larger than Integer.MAX_VALUE, we currently don't
904      * allow that.
905      * @throws IOException
906      */
emitUnsignedLeb128(int n)907     private void emitUnsignedLeb128(int n) throws IOException {
908         // We'll never need the top end of the unsigned range anyway.
909         if (n < 0) {
910             throw new RuntimeException(
911                     "Signed value where unsigned required: " + n);
912         }
913 
914         output.writeUleb128(n);
915     }
916 
917     /**
918      * Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE}
919      * bytecode.
920      */
emitEndSequence()921     private void emitEndSequence() {
922         output.writeByte(DBG_END_SEQUENCE);
923 
924         if (annotateTo != null || debugPrint != null) {
925             annotate(1, "end sequence");
926         }
927     }
928 }
929