Home | History | Annotate | Download | only in Util
      1 /*
      2  * [The "BSD licence"]
      3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 package org.jf.dexlib.Util;
     30 
     31 import org.jf.dexlib.*;
     32 
     33 import java.util.ArrayList;
     34 import java.util.List;
     35 
     36 /**
     37  * This class is intended to provide an easy to use container to build up a method's debug info. You can easily add
     38  * an "event" at a specific address, where an event is something like a line number, start/end local, etc.
     39  * The events must be added such that the code addresses increase monotonically. This matches how a parser would
     40  * generally behave, and is intended to increase performance.
     41  */
     42 public class DebugInfoBuilder
     43 {
     44     private static final int LINE_BASE = -4;
     45     private static final int LINE_RANGE = 15;
     46     private static final int FIRST_SPECIAL = 0x0a;
     47 
     48     private int lineStart = 0;
     49     private ArrayList<String> parameterNames = new ArrayList<String>();
     50     private ArrayList<Event> events = new ArrayList<Event>();
     51     private int lastAddress = 0;
     52 
     53     private boolean hasData;
     54 
     55     private int currentAddress;
     56     private int currentLine;
     57 
     58     public DebugInfoBuilder() {
     59     }
     60 
     61     private void checkAddress(int address) {
     62         if (lastAddress > address) {
     63             throw new RuntimeException("Cannot add an event with an address before the address of the prior event");
     64         }
     65     }
     66 
     67     public void addParameterName(String parameterName) {
     68         if (parameterName != null) {
     69             hasData = true;
     70         }
     71 
     72         parameterNames.add(parameterName);
     73     }
     74 
     75     public void addLine(int address, int line) {
     76         hasData = true;
     77 
     78         checkAddress(address);
     79 
     80         if (lineStart == 0) {
     81             lineStart = line;
     82         }
     83 
     84         events.add(new LineEvent(address, line));
     85     }
     86 
     87     public void addLocal(int address, int registerNumber, String localName, String localType) {
     88         hasData = true;
     89 
     90         checkAddress(address);
     91 
     92         events.add(new StartLocalEvent(address, registerNumber, localName, localType));
     93     }
     94 
     95     public void addLocalExtended(int address, int registerNumber, String localName, String localType,
     96                                 String signature) {
     97         hasData = true;
     98 
     99         checkAddress(address);
    100 
    101         events.add(new StartLocalExtendedEvent(address, registerNumber, localName, localType, signature));
    102     }
    103 
    104     public void addEndLocal(int address, int registerNumber) {
    105         hasData = true;
    106 
    107         checkAddress(address);
    108 
    109         events.add(new EndLocalEvent(address, registerNumber));
    110     }
    111 
    112     public void addRestartLocal(int address, int registerNumber) {
    113         hasData = true;
    114 
    115         checkAddress(address);
    116 
    117         events.add(new RestartLocalEvent(address, registerNumber));
    118     }
    119 
    120     public void addPrologue(int address) {
    121         hasData = true;
    122 
    123         checkAddress(address);
    124 
    125         events.add(new PrologueEvent(address));
    126     }
    127 
    128     public void addEpilogue(int address) {
    129         hasData = true;
    130 
    131         checkAddress(address);
    132 
    133         events.add(new EpilogueEvent(address));
    134     }
    135 
    136     public void addSetFile(int address, String fileName) {
    137         hasData = true;
    138 
    139         checkAddress(address);
    140 
    141         events.add(new SetFileEvent(address, fileName));
    142     }
    143 
    144     public int getParameterNameCount() {
    145         return parameterNames.size();
    146     }
    147 
    148     public DebugInfoItem encodeDebugInfo(DexFile dexFile) {
    149         if (!hasData) {
    150             return null;
    151         }
    152 
    153         ByteArrayOutput out = new ByteArrayOutput();
    154         StringIdItem[] parameterNamesArray = new StringIdItem[parameterNames.size()];
    155         ArrayList<Item> referencedItems = new ArrayList<Item>();
    156 
    157         if (lineStart == 0) {
    158             lineStart = 1;
    159         }
    160 
    161         currentLine = lineStart;
    162 
    163         for (Event event: events) {
    164             event.emit(dexFile, out, referencedItems);
    165         }
    166         emitEndSequence(out);
    167 
    168         int index = 0;
    169         for (String parameterName: parameterNames) {
    170             if (parameterName == null) {
    171                 parameterNamesArray[index++] = null;
    172             } else {
    173                 parameterNamesArray[index++] = StringIdItem.internStringIdItem(dexFile, parameterName);
    174             }
    175         }
    176 
    177         Item[] referencedItemsArray = new Item[referencedItems.size()];
    178         referencedItems.toArray(referencedItemsArray);
    179         return DebugInfoItem.internDebugInfoItem(dexFile, lineStart, parameterNamesArray, out.toByteArray(),
    180                 referencedItemsArray);
    181     }
    182 
    183     public static byte calculateSpecialOpcode(int lineDelta, int addressDelta) {
    184         return (byte)(FIRST_SPECIAL + (addressDelta * LINE_RANGE) + (lineDelta - LINE_BASE));
    185     }
    186 
    187     private interface Event
    188     {
    189         int getAddress();
    190         void emit(DexFile dexFile, Output out, List<Item> referencedItems);
    191     }
    192 
    193     private void emitEndSequence(Output out) {
    194         out.writeByte(0);
    195     }
    196 
    197     private void emitAdvancePC(Output out, int address) {
    198         int addressDelta = address-currentAddress;
    199 
    200         if (addressDelta > 0) {
    201             out.writeByte(1);
    202             out.writeUnsignedLeb128(addressDelta);
    203             currentAddress = address;
    204         }
    205     }
    206 
    207     private void emitAdvanceLine(Output out, int lineDelta) {
    208         out.writeByte(2);
    209         out.writeSignedLeb128(lineDelta);
    210     }
    211 
    212     private void emitStartLocal(Output out, int registerNum) {
    213         out.writeByte(3);
    214         out.writeUnsignedLeb128(registerNum);
    215         out.writeByte(1);
    216         out.writeByte(1);
    217     }
    218 
    219     private void emitStartLocalExtended(Output out, int registerNum) {
    220         out.writeByte(4);
    221         out.writeUnsignedLeb128(registerNum);
    222         out.writeByte(1);
    223         out.writeByte(1);
    224         out.writeByte(1);
    225     }
    226 
    227     private void emitEndLocal(Output out, int registerNum) {
    228         out.writeByte(5);
    229         out.writeUnsignedLeb128(registerNum);
    230     }
    231 
    232     private void emitRestartLocal(Output out, int registerNum) {
    233         out.writeByte(6);
    234         out.writeUnsignedLeb128(registerNum);
    235     }
    236 
    237     private void emitSetPrologueEnd(Output out) {
    238         out.writeByte(7);
    239     }
    240 
    241     private void emitSetEpilogueBegin(Output out) {
    242         out.writeByte(8);
    243     }
    244 
    245     private void emitSetFile(Output out) {
    246         out.writeByte(9);
    247         out.writeByte(1);
    248     }
    249 
    250     private void emitSpecialOpcode(Output out, byte opcode) {
    251         out.writeByte(opcode);
    252     }
    253 
    254     private class LineEvent implements Event
    255     {
    256         private final int address;
    257         private final int line;
    258 
    259         public LineEvent(int address, int line) {
    260             this.address = address;
    261             this.line = line;
    262         }
    263 
    264         public int getAddress() {
    265             return address;
    266         }
    267 
    268         public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
    269             int lineDelta = line - currentLine;
    270             int addressDelta = address - currentAddress;
    271 
    272             if (lineDelta < -4 || lineDelta > 10) {
    273                 emitAdvanceLine(out, lineDelta);
    274                 lineDelta = 0;
    275             }
    276             if (lineDelta < 2 && addressDelta > 16 || lineDelta > 1 && addressDelta > 15) {
    277                 emitAdvancePC(out, address);
    278                 addressDelta = 0;
    279             }
    280 
    281             //TODO: need to handle the case when the line delta is larger than a signed int
    282             emitSpecialOpcode(out, calculateSpecialOpcode(lineDelta, addressDelta));
    283 
    284             currentAddress = address;
    285             currentLine = line;
    286         }
    287     }
    288 
    289     private class StartLocalEvent implements Event
    290     {
    291         private final int address;
    292         private final int registerNum;
    293         private final String localName;
    294         private final String localType;
    295 
    296         public StartLocalEvent(int address, int registerNum, String localName, String localType) {
    297             this.address = address;
    298             this.registerNum = registerNum;
    299             this.localName = localName;
    300             this.localType = localType;
    301         }
    302 
    303         public int getAddress() {
    304             return address;
    305         }
    306 
    307         public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
    308             emitAdvancePC(out, address);
    309             emitStartLocal(out, registerNum);
    310             referencedItems.add(localName==null?null:StringIdItem.internStringIdItem(dexFile, localName));
    311             referencedItems.add(localType==null?null:TypeIdItem.internTypeIdItem(dexFile,
    312                     StringIdItem.internStringIdItem(dexFile, localType)));
    313         }
    314     }
    315 
    316     private class StartLocalExtendedEvent implements Event
    317     {
    318         private final int address;
    319         private final int registerNum;
    320         private final String localName;
    321         private final String localType;
    322         private final String signature;
    323 
    324         public StartLocalExtendedEvent(int address, int registerNum, String localName, String localType,
    325                                        String signature) {
    326             this.address = address;
    327             this.registerNum = registerNum;
    328             this.localName = localName;
    329             this.localType = localType;
    330             this.signature = signature;
    331         }
    332 
    333         public int getAddress() {
    334             return address;
    335         }
    336 
    337         public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
    338             emitAdvancePC(out, address);
    339             emitStartLocalExtended(out, registerNum);
    340             if (localName != null) {
    341                 referencedItems.add(StringIdItem.internStringIdItem(dexFile, localName));
    342             }
    343             if (localType != null) {
    344                 referencedItems.add(TypeIdItem.internTypeIdItem(dexFile,
    345                     StringIdItem.internStringIdItem(dexFile, localType)));
    346             }
    347             if (signature != null) {
    348                 referencedItems.add(StringIdItem.internStringIdItem(dexFile, signature));
    349             }
    350         }
    351     }
    352 
    353     private class EndLocalEvent implements Event
    354     {
    355         private final int address;
    356         private final int registerNum;
    357 
    358         public EndLocalEvent(int address, int registerNum) {
    359             this.address = address;
    360             this.registerNum = registerNum;
    361         }
    362 
    363         public int getAddress() {
    364             return address;
    365         }
    366 
    367         public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
    368             emitAdvancePC(out, address);
    369             emitEndLocal(out, registerNum);
    370         }
    371     }
    372 
    373     private class RestartLocalEvent implements Event
    374     {
    375         private final int address;
    376         private final int registerNum;
    377 
    378         public RestartLocalEvent(int address, int registerNum) {
    379             this.address = address;
    380             this.registerNum = registerNum;
    381         }
    382 
    383         public int getAddress() {
    384             return address;
    385         }
    386 
    387         public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
    388             emitAdvancePC(out, address);
    389             emitRestartLocal(out, registerNum);
    390         }
    391     }
    392 
    393     private class PrologueEvent implements Event
    394     {
    395         private final int address;
    396 
    397         public PrologueEvent(int address) {
    398             this.address = address;
    399         }
    400 
    401         public int getAddress() {
    402             return address;
    403         }
    404 
    405         public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
    406             emitAdvancePC(out, address);
    407             emitSetPrologueEnd(out);
    408         }
    409     }
    410 
    411     private class EpilogueEvent implements Event
    412     {
    413         private final int address;
    414 
    415         public EpilogueEvent(int address) {
    416             this.address = address;
    417         }
    418 
    419         public int getAddress() {
    420             return address;
    421         }
    422 
    423         public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
    424             emitAdvancePC(out, address);
    425             emitSetEpilogueBegin(out);
    426         }
    427     }
    428 
    429     private class SetFileEvent implements Event
    430     {
    431         private final int address;
    432         private final String fileName;
    433 
    434         public SetFileEvent(int address, String fileName) {
    435             this.address = address;
    436             this.fileName = fileName;
    437         }
    438 
    439         public int getAddress() {
    440             return address;
    441         }
    442 
    443         public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
    444             emitAdvancePC(out, address);
    445             emitSetFile(out);
    446             if (fileName != null) {
    447                 referencedItems.add(StringIdItem.internStringIdItem(dexFile, fileName));
    448             }
    449         }
    450     }
    451 }
    452