DebugInfo.java revision 84c1762a62d7fc6638432c6c56e0422aa8cc6939
1/*
2 * Copyright 2012, 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
32package org.jf.dexlib2.dexbacked.util;
33
34import com.google.common.collect.Iterators;
35import org.jf.dexlib2.AccessFlags;
36import org.jf.dexlib2.DebugItemType;
37import org.jf.dexlib2.dexbacked.DexBackedMethod;
38import org.jf.dexlib2.dexbacked.DexBackedMethodImplementation;
39import org.jf.dexlib2.dexbacked.DexBackedDexFile;
40import org.jf.dexlib2.dexbacked.DexReader;
41import org.jf.dexlib2.iface.MethodParameter;
42import org.jf.dexlib2.iface.debug.DebugItem;
43import org.jf.dexlib2.iface.debug.EndLocal;
44import org.jf.dexlib2.iface.debug.LocalInfo;
45import org.jf.dexlib2.immutable.debug.*;
46
47import javax.annotation.Nonnull;
48import javax.annotation.Nullable;
49import java.util.Arrays;
50import java.util.Iterator;
51
52public abstract class DebugInfo implements Iterable<DebugItem> {
53    /**
54     * Gets an iterator that yields the parameter names from the debug_info_item
55     *
56     * @param reader Optional. If provided, the reader must be positioned at the debug_info_item.parameters_size
57     *               field, and will
58     * @return An iterator that yields the parameter names as strings
59     */
60    @Nonnull public abstract Iterator<String> getParameterNames(@Nullable DexReader reader);
61
62    public static DebugInfo newOrEmpty(@Nonnull DexBackedDexFile dexFile, int debugInfoOffset,
63                                       @Nonnull DexBackedMethodImplementation methodImpl) {
64        if (debugInfoOffset == 0) {
65            return EmptyDebugInfo.INSTANCE;
66        }
67        return new DebugInfoImpl(dexFile, debugInfoOffset, methodImpl);
68    }
69
70    private static class EmptyDebugInfo extends DebugInfo {
71        public static final EmptyDebugInfo INSTANCE = new EmptyDebugInfo();
72        private EmptyDebugInfo() {}
73        @Nonnull @Override public Iterator<DebugItem> iterator() { return Iterators.emptyIterator(); }
74        @Nonnull @Override public Iterator<String> getParameterNames(@Nullable DexReader reader) {
75            return Iterators.emptyIterator();
76        }
77    }
78
79    private static class DebugInfoImpl extends DebugInfo {
80        @Nonnull public final DexBackedDexFile dexFile;
81        private final int debugInfoOffset;
82        @Nonnull private final DexBackedMethodImplementation methodImpl;
83
84        public DebugInfoImpl(@Nonnull DexBackedDexFile dexFile,
85                         int debugInfoOffset,
86                         @Nonnull DexBackedMethodImplementation methodImpl) {
87            this.dexFile = dexFile;
88            this.debugInfoOffset = debugInfoOffset;
89            this.methodImpl = methodImpl;
90        }
91
92        private static final LocalInfo EMPTY_LOCAL_INFO = new LocalInfo() {
93            @Nullable @Override public String getName() { return null; }
94            @Nullable @Override public String getType() { return null; }
95            @Nullable @Override public String getSignature() { return null; }
96        };
97
98        @Nonnull
99        @Override
100        public Iterator<DebugItem> iterator() {
101            DexReader reader = dexFile.readerAt(debugInfoOffset);
102            // TODO: this unsigned value could legitimally be > MAX_INT
103            final int lineNumberStart = reader.readSmallUleb128();
104            int registerCount = methodImpl.getRegisterCount();
105
106            //TODO: does dalvik allow references to invalid registers?
107            final LocalInfo[] locals = new LocalInfo[registerCount];
108            Arrays.fill(locals, EMPTY_LOCAL_INFO);
109
110            DexBackedMethod method = methodImpl.method;
111
112            // Create a MethodParameter iterator that uses our DexReader instance to read the parameter names.
113            // After we have finished iterating over the parameters, reader will "point to" the beginning of the
114            // debug instructions
115            final Iterator<? extends MethodParameter> parameterIterator =
116                    new ParameterIterator(method.getParameterTypes(),
117                            method.getParameterAnnotations(),
118                            getParameterNames(reader));
119
120            // first, we grab all the parameters and temporarily store them at the beginning of locals,
121            // disregarding any wide types
122            int parameterIndex = 0;
123            if (!AccessFlags.STATIC.isSet(methodImpl.method.getAccessFlags())) {
124                // add the local info for the "this" parameter
125                locals[parameterIndex++] = new LocalInfo() {
126                    @Override public String getName() { return "this"; }
127                    @Override public String getType() { return methodImpl.method.getDefiningClass(); }
128                    @Override public String getSignature() { return null; }
129                };
130            }
131            while (parameterIterator.hasNext()) {
132                locals[parameterIndex++] = parameterIterator.next();
133            }
134
135            if (parameterIndex < registerCount) {
136                // now, we push the parameter locals back to their appropriate register, starting from the end
137                int localIndex = registerCount-1;
138                while(--parameterIndex > -1) {
139                    LocalInfo currentLocal = locals[parameterIndex];
140                    String type = currentLocal.getType();
141                    if (type != null && (type.equals("J") || type.equals("D"))) {
142                        localIndex--;
143                        if (localIndex == parameterIndex) {
144                            // there's no more room to push, the remaining registers are already in the correct place
145                            break;
146                        }
147                    }
148                    locals[localIndex] = currentLocal;
149                    locals[parameterIndex] = EMPTY_LOCAL_INFO;
150                    localIndex--;
151                }
152            }
153
154            return new VariableSizeLookaheadIterator<DebugItem>(dexFile, reader.getOffset()) {
155                private int codeAddress = 0;
156                private int lineNumber = lineNumberStart;
157
158                @Nullable
159                protected DebugItem readNextItem(@Nonnull DexReader reader) {
160                    while (true) {
161                        int next = reader.readUbyte();
162                        switch (next) {
163                            case DebugItemType.END_SEQUENCE: {
164                                return null;
165                            }
166                            case DebugItemType.ADVANCE_PC: {
167                                int addressDiff = reader.readSmallUleb128();
168                                codeAddress += addressDiff;
169                                continue;
170                            }
171                            case DebugItemType.ADVANCE_LINE: {
172                                int lineDiff = reader.readSleb128();
173                                lineNumber += lineDiff;
174                                continue;
175                            }
176                            case DebugItemType.START_LOCAL: {
177                                int register = reader.readSmallUleb128();
178                                String name = dexFile.getOptionalString(reader.readSmallUleb128() - 1);
179                                String type = dexFile.getOptionalType(reader.readSmallUleb128() - 1);
180                                ImmutableStartLocal startLocal =
181                                        new ImmutableStartLocal(codeAddress, register, name, type, null);
182                                locals[register] = startLocal;
183                                return startLocal;
184                            }
185                            case DebugItemType.START_LOCAL_EXTENDED: {
186                                int register = reader.readSmallUleb128();
187                                String name = dexFile.getOptionalString(reader.readSmallUleb128() - 1);
188                                String type = dexFile.getOptionalType(reader.readSmallUleb128() - 1);
189                                String signature = dexFile.getOptionalString(reader.readSmallUleb128() - 1);
190                                ImmutableStartLocal startLocal =
191                                        new ImmutableStartLocal(codeAddress, register, name, type, signature);
192                                locals[register] = startLocal;
193                                return startLocal;
194                            }
195                            case DebugItemType.END_LOCAL: {
196                                int register = reader.readSmallUleb128();
197                                LocalInfo localInfo = locals[register];
198                                boolean replaceLocalInTable = true;
199                                if (localInfo instanceof EndLocal) {
200                                    localInfo = EMPTY_LOCAL_INFO;
201                                    // don't replace the local info in locals. The new EndLocal won't have any info at all,
202                                    // and we dont want to wipe out what's there, so that it is available for a subsequent
203                                    // RestartLocal
204                                    replaceLocalInTable = false;
205                                }
206                                ImmutableEndLocal endLocal =
207                                        new ImmutableEndLocal(codeAddress, register, localInfo.getName(),
208                                                localInfo.getType(), localInfo.getSignature());
209                                if (replaceLocalInTable) {
210                                    locals[register] = endLocal;
211                                }
212                                return endLocal;
213                            }
214                            case DebugItemType.RESTART_LOCAL: {
215                                int register = reader.readSmallUleb128();
216                                LocalInfo localInfo = locals[register];
217                                ImmutableRestartLocal restartLocal =
218                                        new ImmutableRestartLocal(codeAddress, register, localInfo.getName(),
219                                                localInfo.getType(), localInfo.getSignature());
220                                locals[register] = restartLocal;
221                                return restartLocal;
222                            }
223                            case DebugItemType.PROLOGUE_END: {
224                                return new ImmutablePrologueEnd(codeAddress);
225                            }
226                            case DebugItemType.EPILOGUE_BEGIN: {
227                                return new ImmutableEpilogueBegin(codeAddress);
228                            }
229                            case DebugItemType.SET_SOURCE_FILE: {
230                                String sourceFile = dexFile.getOptionalString(reader.readSmallUleb128() - 1);
231                                return new ImmutableSetSourceFile(codeAddress, sourceFile);
232                            }
233                            default: {
234                                int adjusted = next - 0x0A;
235                                codeAddress += adjusted / 15;
236                                lineNumber += (adjusted % 15) - 4;
237                                return new ImmutableLineNumber(codeAddress, lineNumber);
238                            }
239                        }
240                    }
241                }
242            };
243        }
244
245        @Nonnull
246        @Override
247        public VariableSizeIterator<String> getParameterNames(@Nullable DexReader reader) {
248            if (reader == null) {
249                reader = dexFile.readerAt(debugInfoOffset);
250                reader.skipUleb128();
251            }
252            //TODO: make sure dalvik doesn't allow more parameter names than we have parameters
253            final int parameterNameCount = reader.readSmallUleb128();
254            return new VariableSizeIterator<String>(reader, parameterNameCount) {
255                @Override protected String readNextItem(@Nonnull DexReader reader, int index) {
256                    return dexFile.getOptionalString(reader.readSmallUleb128() - 1);
257                }
258            };
259        }
260    }
261}
262