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 final int lineNumberStart = reader.readBigUleb128(); 103 int registerCount = methodImpl.getRegisterCount(); 104 105 //TODO: does dalvik allow references to invalid registers? 106 final LocalInfo[] locals = new LocalInfo[registerCount]; 107 Arrays.fill(locals, EMPTY_LOCAL_INFO); 108 109 DexBackedMethod method = methodImpl.method; 110 111 // Create a MethodParameter iterator that uses our DexReader instance to read the parameter names. 112 // After we have finished iterating over the parameters, reader will "point to" the beginning of the 113 // debug instructions 114 final Iterator<? extends MethodParameter> parameterIterator = 115 new ParameterIterator(method.getParameterTypes(), 116 method.getParameterAnnotations(), 117 getParameterNames(reader)); 118 119 // first, we grab all the parameters and temporarily store them at the beginning of locals, 120 // disregarding any wide types 121 int parameterIndex = 0; 122 if (!AccessFlags.STATIC.isSet(methodImpl.method.getAccessFlags())) { 123 // add the local info for the "this" parameter 124 locals[parameterIndex++] = new LocalInfo() { 125 @Override public String getName() { return "this"; } 126 @Override public String getType() { return methodImpl.method.getDefiningClass(); } 127 @Override public String getSignature() { return null; } 128 }; 129 } 130 while (parameterIterator.hasNext()) { 131 locals[parameterIndex++] = parameterIterator.next(); 132 } 133 134 if (parameterIndex < registerCount) { 135 // now, we push the parameter locals back to their appropriate register, starting from the end 136 int localIndex = registerCount-1; 137 while(--parameterIndex > -1) { 138 LocalInfo currentLocal = locals[parameterIndex]; 139 String type = currentLocal.getType(); 140 if (type != null && (type.equals("J") || type.equals("D"))) { 141 localIndex--; 142 if (localIndex == parameterIndex) { 143 // there's no more room to push, the remaining registers are already in the correct place 144 break; 145 } 146 } 147 locals[localIndex] = currentLocal; 148 locals[parameterIndex] = EMPTY_LOCAL_INFO; 149 localIndex--; 150 } 151 } 152 153 return new VariableSizeLookaheadIterator<DebugItem>(dexFile, reader.getOffset()) { 154 private int codeAddress = 0; 155 private int lineNumber = lineNumberStart; 156 157 @Nullable 158 protected DebugItem readNextItem(@Nonnull DexReader reader) { 159 while (true) { 160 int next = reader.readUbyte(); 161 switch (next) { 162 case DebugItemType.END_SEQUENCE: { 163 return null; 164 } 165 case DebugItemType.ADVANCE_PC: { 166 int addressDiff = reader.readSmallUleb128(); 167 codeAddress += addressDiff; 168 continue; 169 } 170 case DebugItemType.ADVANCE_LINE: { 171 int lineDiff = reader.readSleb128(); 172 lineNumber += lineDiff; 173 continue; 174 } 175 case DebugItemType.START_LOCAL: { 176 int register = reader.readSmallUleb128(); 177 String name = dexFile.getOptionalString(reader.readSmallUleb128() - 1); 178 String type = dexFile.getOptionalType(reader.readSmallUleb128() - 1); 179 ImmutableStartLocal startLocal = 180 new ImmutableStartLocal(codeAddress, register, name, type, null); 181 locals[register] = startLocal; 182 return startLocal; 183 } 184 case DebugItemType.START_LOCAL_EXTENDED: { 185 int register = reader.readSmallUleb128(); 186 String name = dexFile.getOptionalString(reader.readSmallUleb128() - 1); 187 String type = dexFile.getOptionalType(reader.readSmallUleb128() - 1); 188 String signature = dexFile.getOptionalString(reader.readSmallUleb128() - 1); 189 ImmutableStartLocal startLocal = 190 new ImmutableStartLocal(codeAddress, register, name, type, signature); 191 locals[register] = startLocal; 192 return startLocal; 193 } 194 case DebugItemType.END_LOCAL: { 195 int register = reader.readSmallUleb128(); 196 LocalInfo localInfo = locals[register]; 197 boolean replaceLocalInTable = true; 198 if (localInfo instanceof EndLocal) { 199 localInfo = EMPTY_LOCAL_INFO; 200 // don't replace the local info in locals. The new EndLocal won't have any info at all, 201 // and we dont want to wipe out what's there, so that it is available for a subsequent 202 // RestartLocal 203 replaceLocalInTable = false; 204 } 205 ImmutableEndLocal endLocal = 206 new ImmutableEndLocal(codeAddress, register, localInfo.getName(), 207 localInfo.getType(), localInfo.getSignature()); 208 if (replaceLocalInTable) { 209 locals[register] = endLocal; 210 } 211 return endLocal; 212 } 213 case DebugItemType.RESTART_LOCAL: { 214 int register = reader.readSmallUleb128(); 215 LocalInfo localInfo = locals[register]; 216 ImmutableRestartLocal restartLocal = 217 new ImmutableRestartLocal(codeAddress, register, localInfo.getName(), 218 localInfo.getType(), localInfo.getSignature()); 219 locals[register] = restartLocal; 220 return restartLocal; 221 } 222 case DebugItemType.PROLOGUE_END: { 223 return new ImmutablePrologueEnd(codeAddress); 224 } 225 case DebugItemType.EPILOGUE_BEGIN: { 226 return new ImmutableEpilogueBegin(codeAddress); 227 } 228 case DebugItemType.SET_SOURCE_FILE: { 229 String sourceFile = dexFile.getOptionalString(reader.readSmallUleb128() - 1); 230 return new ImmutableSetSourceFile(codeAddress, sourceFile); 231 } 232 default: { 233 int adjusted = next - 0x0A; 234 codeAddress += adjusted / 15; 235 lineNumber += (adjusted % 15) - 4; 236 return new ImmutableLineNumber(codeAddress, lineNumber); 237 } 238 } 239 } 240 } 241 }; 242 } 243 244 @Nonnull 245 @Override 246 public VariableSizeIterator<String> getParameterNames(@Nullable DexReader reader) { 247 if (reader == null) { 248 reader = dexFile.readerAt(debugInfoOffset); 249 reader.skipUleb128(); 250 } 251 //TODO: make sure dalvik doesn't allow more parameter names than we have parameters 252 final int parameterNameCount = reader.readSmallUleb128(); 253 return new VariableSizeIterator<String>(reader, parameterNameCount) { 254 @Override protected String readNextItem(@Nonnull DexReader reader, int index) { 255 return dexFile.getOptionalString(reader.readSmallUleb128() - 1); 256 } 257 }; 258 } 259 } 260} 261