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