Home | History | Annotate | Download | only in writer
      1 /*
      2  * Copyright 2013, Google Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 package org.jf.dexlib2.writer;
     33 
     34 import org.jf.dexlib2.DebugItemType;
     35 import org.jf.util.ExceptionWithContext;
     36 
     37 import javax.annotation.Nonnull;
     38 import javax.annotation.Nullable;
     39 import java.io.IOException;
     40 
     41 public class DebugWriter<StringKey extends CharSequence, TypeKey extends CharSequence> {
     42     @Nonnull private final StringSection<StringKey, ?> stringSection;
     43     @Nonnull private final TypeSection<StringKey, TypeKey, ?> typeSection;
     44     @Nonnull private final DexDataWriter writer;
     45     private int currentAddress;
     46     private int currentLine;
     47 
     48     DebugWriter(@Nonnull StringSection<StringKey, ?> stringSection,
     49                 @Nonnull TypeSection<StringKey, TypeKey, ?> typeSection,
     50                 @Nonnull DexDataWriter writer) {
     51         this.stringSection = stringSection;
     52         this.typeSection = typeSection;
     53         this.writer = writer;
     54     }
     55 
     56     void reset(int startLine) {
     57         this.currentAddress = 0;
     58         this.currentLine = startLine;
     59     }
     60 
     61     public void writeStartLocal(int codeAddress, int register,
     62                                 @Nullable StringKey name,
     63                                 @Nullable TypeKey type,
     64                                 @Nullable StringKey signature) throws IOException {
     65         int nameIndex = stringSection.getNullableItemIndex(name);
     66         int typeIndex = typeSection.getNullableItemIndex(type);
     67         int signatureIndex = stringSection.getNullableItemIndex(signature);
     68 
     69         writeAdvancePC(codeAddress);
     70         if (signatureIndex == DexWriter.NO_INDEX) {
     71             writer.write(DebugItemType.START_LOCAL);
     72             writer.writeUleb128(register);
     73             writer.writeUleb128(nameIndex + 1);
     74             writer.writeUleb128(typeIndex + 1);
     75         } else {
     76             writer.write(DebugItemType.START_LOCAL_EXTENDED);
     77             writer.writeUleb128(register);
     78             writer.writeUleb128(nameIndex + 1);
     79             writer.writeUleb128(typeIndex + 1);
     80             writer.writeUleb128(signatureIndex + 1);
     81         }
     82     }
     83 
     84     public void writeEndLocal(int codeAddress, int register) throws IOException {
     85         writeAdvancePC(codeAddress);
     86         writer.write(DebugItemType.END_LOCAL);
     87         writer.writeUleb128(register);
     88     }
     89 
     90     public void writeRestartLocal(int codeAddress, int register) throws IOException {
     91         writeAdvancePC(codeAddress);
     92         writer.write(DebugItemType.RESTART_LOCAL);
     93         writer.writeUleb128(register);
     94     }
     95 
     96     public void writePrologueEnd(int codeAddress) throws IOException {
     97         writeAdvancePC(codeAddress);
     98         writer.write(DebugItemType.PROLOGUE_END);
     99     }
    100 
    101     public void writeEpilogueBegin(int codeAddress) throws IOException {
    102         writeAdvancePC(codeAddress);
    103         writer.write(DebugItemType.EPILOGUE_BEGIN);
    104     }
    105 
    106     public void writeLineNumber(int codeAddress, int lineNumber) throws IOException {
    107         int lineDelta = lineNumber - currentLine;
    108         int addressDelta = codeAddress - currentAddress;
    109 
    110         if (addressDelta < 0) {
    111             throw new ExceptionWithContext("debug info items must have non-decreasing code addresses");
    112         }
    113         if (lineDelta < -4 || lineDelta > 10) {
    114             writeAdvanceLine(lineNumber);
    115             lineDelta = 0;
    116         } // no else is intentional here. we might need to advance the PC as well as the line
    117         if ((lineDelta < 2 && addressDelta > 16) || (lineDelta > 1 && addressDelta > 15)) {
    118             writeAdvancePC(codeAddress);
    119             addressDelta = 0;
    120         }
    121 
    122         // we need to emit the special opcode even if both lineDelta and addressDelta are 0, otherwise a positions
    123         // entry isn't generated
    124         writeSpecialOpcode(lineDelta, addressDelta);
    125     }
    126 
    127     public void writeSetSourceFile(int codeAddress, @Nullable StringKey sourceFile) throws IOException {
    128         writeAdvancePC(codeAddress);
    129         writer.write(DebugItemType.SET_SOURCE_FILE);
    130         writer.writeUleb128(stringSection.getNullableItemIndex(sourceFile) + 1);
    131     }
    132 
    133     private void writeAdvancePC(int address) throws IOException {
    134         int addressDelta = address - currentAddress;
    135 
    136         if (addressDelta > 0) {
    137             writer.write(1);
    138             writer.writeUleb128(addressDelta);
    139             currentAddress = address;
    140         } /*else if (addressDelta < 0) {
    141             throw new ExceptionWithContext("debug info items must have non-decreasing code addresses");
    142         }*/
    143     }
    144 
    145     private void writeAdvanceLine(int line) throws IOException {
    146         int lineDelta = line - currentLine;
    147         if (lineDelta != 0) {
    148             writer.write(2);
    149             writer.writeSleb128(lineDelta);
    150             currentLine = line;
    151         }
    152     }
    153 
    154     private static final int LINE_BASE     = -4;
    155     private static final int LINE_RANGE    = 15;
    156     private static final int FIRST_SPECIAL = 0x0a;
    157 
    158     private void writeSpecialOpcode(int lineDelta, int addressDelta) throws IOException {
    159         writer.write((byte)(FIRST_SPECIAL + (addressDelta * LINE_RANGE) + (lineDelta - LINE_BASE)));
    160         currentLine += lineDelta;
    161         currentAddress += addressDelta;
    162     }
    163 }
    164