DebugInstructionIterator.java revision 83b80f81d311b233188c281059aad4a9f5e8b4e6
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2009 Ben Gruver
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
29package org.jf.dexlib.Debug;
30
31import org.jf.dexlib.Util.Input;
32import org.jf.dexlib.Util.ByteArrayInput;
33import org.jf.dexlib.TypeIdItem;
34import org.jf.dexlib.StringIdItem;
35import org.jf.dexlib.DexFile;
36import org.jf.dexlib.DebugInfoItem;
37
38public class DebugInstructionIterator {
39    /**
40     * This method decodes the debug instructions in the given byte array and iterates over them, calling
41     * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction
42     * @param in an Input object that the debug instructions can be read from
43     * @param processDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called
44     * for each instruction that is encountered
45     */
46    public static void IterateInstructions(Input in, ProcessRawDebugInstructionDelegate processDebugInstruction) {
47        int startOffset;
48
49        while(true)
50        {
51            startOffset = in.getCursor();
52            byte debugOpcode = in.readByte();
53
54            switch (debugOpcode) {
55                case 0x00:
56                {
57                    processDebugInstruction.ProcessEndSequence(startOffset);
58                    return;
59                }
60                case 0x01:
61                {
62                    int addressDiff = in.readUnsignedLeb128();
63                    processDebugInstruction.ProcessAdvancePC(startOffset, in.getCursor() - startOffset, addressDiff);
64                    break;
65                }
66                case 0x02:
67                {
68                    int lineDiff = in.readSignedLeb128();
69                    processDebugInstruction.ProcessAdvanceLine(startOffset, in.getCursor() - startOffset, lineDiff);
70                    break;
71                }
72                case 0x03:
73                {
74                    int registerNum = in.readUnsignedLeb128();
75                    int nameIndex = in.readUnsignedLeb128() - 1;
76                    int typeIndex = in.readUnsignedLeb128() - 1;
77                    processDebugInstruction.ProcessStartLocal(startOffset, in.getCursor() - startOffset, registerNum,
78                            nameIndex, typeIndex);
79                    break;
80                }
81                case 0x04:
82                {
83                    int registerNum = in.readUnsignedLeb128();
84                    int nameIndex = in.readUnsignedLeb128() - 1;
85                    int typeIndex = in.readUnsignedLeb128() - 1;
86                    int signatureIndex = in.readUnsignedLeb128() - 1;
87                    processDebugInstruction.ProcessStartLocalExtended(startOffset, in.getCursor() - startOffset,
88                            registerNum, nameIndex, typeIndex, signatureIndex);
89                    break;
90                }
91                case 0x05:
92                {
93                    int registerNum = in.readUnsignedLeb128();
94                    processDebugInstruction.ProcessEndLocal(startOffset, in.getCursor() - startOffset, registerNum);
95                    break;
96                }
97                case 0x06:
98                {
99                    int registerNum = in.readUnsignedLeb128();
100                    processDebugInstruction.ProcessRestartLocal(startOffset, in.getCursor() - startOffset, registerNum);
101                    break;
102                }
103                case 0x07:
104                {
105                    processDebugInstruction.ProcessSetPrologueEnd(startOffset);
106                    break;
107                }
108                case 0x08:
109                {
110                    processDebugInstruction.ProcessSetEpilogueBegin(startOffset);
111                    break;
112                }
113                case 0x09:
114                {
115                    int nameIndex = in.readUnsignedLeb128();
116                    processDebugInstruction.ProcessSetFile(startOffset, in.getCursor() - startOffset, nameIndex);
117                    break;
118                }
119                default:
120                {
121                    byte base = (byte)((debugOpcode & 0xFF) - 0x0A);
122                    processDebugInstruction.ProcessSpecialOpcode(startOffset, debugOpcode, (base % 15) - 4, base / 15);
123                }
124            }
125        }
126    }
127
128    /**
129     * This method decodes the debug instructions in the given byte array and iterates over them, calling
130     * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction
131     * @param debugInfoItem the <code>DebugInfoItem</code> to iterate over
132     * @param registerCount the number of registers in the method that the given debug info is for
133     * @param processDecodedDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called
134     * for each instruction that is encountered
135     */
136    public static void DecodeInstructions(DebugInfoItem debugInfoItem, int registerCount,
137                                           ProcessDecodedDebugInstructionDelegate processDecodedDebugInstruction) {
138        int startOffset;
139        int address = 0;
140        int line = debugInfoItem.getLineStart();
141        Input in = new ByteArrayInput(debugInfoItem.getEncodedDebugInfo());
142        DexFile dexFile = debugInfoItem.getDexFile();
143
144        Local[] locals = new Local[registerCount];
145
146        while(true)
147        {
148            startOffset = in.getCursor();
149            byte debugOpcode = in.readByte();
150
151            switch (debugOpcode) {
152                case 0x00:
153                {
154                    return;
155                }
156                case 0x01:
157                {
158                    int addressDiff = in.readUnsignedLeb128();
159                    address += addressDiff;
160                    break;
161                }
162                case 0x02:
163                {
164                    int lineDiff = in.readSignedLeb128();
165                    line += lineDiff;
166                    break;
167                }
168                case 0x03:
169                {
170                    int registerNum = in.readUnsignedLeb128();
171                    StringIdItem name = dexFile.StringIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
172                    TypeIdItem type = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
173                    locals[registerNum] = new Local(registerNum, name, type, null);
174                    processDecodedDebugInstruction.ProcessStartLocal(startOffset, in.getCursor() - startOffset, registerNum,
175                            name, type);
176                    break;
177                }
178                case 0x04:
179                {
180                    int registerNum = in.readUnsignedLeb128();
181                    StringIdItem name = dexFile.StringIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
182                    TypeIdItem type = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
183                    StringIdItem signature = dexFile.StringIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
184                    locals[registerNum] = new Local(registerNum, name, type, signature);
185                    processDecodedDebugInstruction.ProcessStartLocalExtended(startOffset, in.getCursor() - startOffset,
186                            registerNum, name, type, signature);
187                    break;
188                }
189                case 0x05:
190                {
191                    int registerNum = in.readUnsignedLeb128();
192                    Local local = locals[registerNum];
193                    if (local == null) {
194                        processDecodedDebugInstruction.ProcessEndLocal(startOffset, in.getCursor() - startOffset, registerNum,
195                                null, null, null);
196                    } else {
197                        processDecodedDebugInstruction.ProcessEndLocal(startOffset, in.getCursor() - startOffset, registerNum,
198                                local.name, local.type, local.signature);
199                    }
200                    break;
201                }
202                case 0x06:
203                {
204                    int registerNum = in.readUnsignedLeb128();
205                    Local local = locals[registerNum];
206                    if (local == null) {
207                        processDecodedDebugInstruction.ProcessRestartLocal(startOffset, in.getCursor() - startOffset,
208                                registerNum, null, null, null);
209                    } else {
210                        processDecodedDebugInstruction.ProcessRestartLocal(startOffset, in.getCursor() - startOffset,
211                                registerNum, local.name, local.type, local.signature);
212                    }
213
214                    break;
215                }
216                case 0x07:
217                {
218                    processDecodedDebugInstruction.ProcessSetPrologueEnd(startOffset);
219                    break;
220                }
221                case 0x08:
222                {
223                    processDecodedDebugInstruction.ProcessSetEpilogueBegin(startOffset);
224                    break;
225                }
226                case 0x09:
227                {
228                    StringIdItem name = dexFile.StringIdsSection.getItemByIndex(in.readUnsignedLeb128() - 1);
229                    processDecodedDebugInstruction.ProcessSetFile(startOffset, in.getCursor() - startOffset, name);
230                    break;
231                }
232                default:
233                {
234                    byte base = (byte)((debugOpcode & 0xFF) - 0x0A);
235                    address += base / 15;
236                    line += (base % 15) - 4;
237                    processDecodedDebugInstruction.ProcessLineEmit(address, line);
238                }
239            }
240        }
241    }
242
243    public static class ProcessRawDebugInstructionDelegate
244    {
245        //TODO: add javadocs
246        public void ProcessEndSequence(int startOffset) {
247            ProcessStaticOpcode(startOffset, 1);
248        }
249
250        public void ProcessAdvancePC(int startOffset, int length, int addressDiff) {
251            ProcessStaticOpcode(startOffset, length);
252        }
253
254        public void ProcessAdvanceLine(int startOffset, int length, int lineDiff) {
255            ProcessStaticOpcode(startOffset, length);
256        }
257
258        public void ProcessStartLocal(int startOffset, int length, int registerNum, int nameIndex, int typeIndex) {
259        }
260
261        public void ProcessStartLocalExtended(int startOffset, int length, int registerNum, int nameIndex,
262                                              int typeIndex,int signatureIndex) {
263        }
264
265        public void ProcessEndLocal(int startOffset, int length, int registerNum) {
266            ProcessStaticOpcode(startOffset, length);
267        }
268
269        public void ProcessRestartLocal(int startOffset, int length, int registerNum) {
270            ProcessStaticOpcode(startOffset, length);
271        }
272
273        public void ProcessSetPrologueEnd(int startOffset) {
274            ProcessStaticOpcode(startOffset, 1);
275        }
276
277        public void ProcessSetEpilogueBegin(int startOffset) {
278            ProcessStaticOpcode(startOffset, 1);
279        }
280
281        public void ProcessSetFile(int startOffset, int length, int nameIndex) {
282        }
283
284        public void ProcessSpecialOpcode(int startOffset, int debugOpcode, int lineDiff, int addressDiff) {
285            ProcessStaticOpcode(startOffset, 1);
286        }
287
288        public void ProcessStaticOpcode(int startOffset, int length) {
289        }
290    }
291
292    public static class ProcessDecodedDebugInstructionDelegate
293    {
294        public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
295                                      TypeIdItem type) {
296        }
297
298        public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, StringIdItem name,
299                                              TypeIdItem type, StringIdItem signature) {
300        }
301
302        public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type,
303                                    StringIdItem signature) {
304        }
305
306        public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
307                                        TypeIdItem type, StringIdItem signature) {
308        }
309
310        public void ProcessSetPrologueEnd(int codeAddress) {
311        }
312
313        public void ProcessSetEpilogueBegin(int codeAddress) {
314        }
315
316        public void ProcessSetFile(int codeAddress, int length, StringIdItem name) {
317        }
318
319        public void ProcessLineEmit(int codeAddress, int line) {
320        }
321    }
322
323    private static class Local {
324        public final int register;
325        public final StringIdItem name;
326        public final TypeIdItem type;
327        public final StringIdItem signature;
328        public Local(int register, StringIdItem name, TypeIdItem type, StringIdItem signature) {
329            this.register = register;
330            this.name = name;
331            this.type = type;
332            this.signature = signature;
333        }
334
335    }
336}
337