DebugInfoBuilder.java revision aaf4c4062adff8a83b2d590a5fd162e5e8ab7b26
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.Util; 30 31import org.jf.dexlib.DebugInfoItem; 32import org.jf.dexlib.DexFile; 33import org.jf.dexlib.StringIdItem; 34import org.jf.dexlib.TypeIdItem; 35import org.jf.dexlib.Debug.*; 36 37import java.util.ArrayList; 38import java.util.List; 39 40/** 41 * This class is intended to provide an easy to use container to build up a method's debug info. You can easily add 42 * an "event" at a specific address, where an event is something like a line number, start/end local, etc. 43 * The events must be added such that the code addresses increase monotonically. This matches how a parser would 44 * generally behave, and is intended to increase performance. 45 */ 46public class DebugInfoBuilder 47{ 48 private static final int LINE_BASE = -4; 49 private static final int LINE_RANGE = 15; 50 private static final int FIRST_SPECIAL = 0x0a; 51 52 private int lineStart = 0; 53 private ArrayList<String> parameterNames = new ArrayList<String>(); 54 private ArrayList<Event> events = new ArrayList<Event>(); 55 private int lastAddress = 0; 56 57 private boolean hasData; 58 59 private int currentAddress; 60 private int currentLine; 61 62 public DebugInfoBuilder() { 63 } 64 65 private void checkAddress(int address) { 66 if (lastAddress > address) { 67 throw new RuntimeException("Cannot add an event with an address before the address of the prior event"); 68 } 69 } 70 71 public void addParameterName(String parameterName) { 72 if (parameterName != null) { 73 hasData = true; 74 } 75 76 parameterNames.add(parameterName); 77 } 78 79 public void addLine(int address, int line) { 80 hasData = true; 81 82 checkAddress(address); 83 84 if (lineStart == 0) { 85 lineStart = line; 86 } 87 88 events.add(new LineEvent(address, line)); 89 } 90 91 public void addLocal(int address, int registerNumber, String localName, String localType) { 92 hasData = true; 93 94 checkAddress(address); 95 96 events.add(new StartLocalEvent(address, registerNumber, localName, localType)); 97 } 98 99 public void addLocalExtended(int address, int registerNumber, String localName, String localType, 100 String signature) { 101 hasData = true; 102 103 checkAddress(address); 104 105 events.add(new StartLocalExtendedEvent(address, registerNumber, localName, localType, signature)); 106 } 107 108 public void addEndLocal(int address, int registerNumber) { 109 hasData = true; 110 111 checkAddress(address); 112 113 events.add(new EndLocalEvent(address, registerNumber)); 114 } 115 116 public void addRestartLocal(int address, int registerNumber) { 117 hasData = true; 118 119 checkAddress(address); 120 121 events.add(new RestartLocalEvent(address, registerNumber)); 122 } 123 124 public void addPrologue(int address) { 125 hasData = true; 126 127 checkAddress(address); 128 129 events.add(new PrologueEvent(address)); 130 } 131 132 public void addEpilogue(int address) { 133 hasData = true; 134 135 checkAddress(address); 136 137 events.add(new EpilogueEvent(address)); 138 } 139 140 public void addSetFile(int address, String fileName) { 141 hasData = true; 142 143 checkAddress(address); 144 145 events.add(new SetFileEvent(address, fileName)); 146 } 147 148 public int getParameterNameCount() { 149 return parameterNames.size(); 150 } 151 152 public DebugInfoItem encodeDebugInfo(DexFile dexFile) { 153 if (!hasData) { 154 return null; 155 } 156 157 ArrayList<DebugInstruction> debugInstructions = new ArrayList<DebugInstruction>(); 158 ArrayList<StringIdItem> parameterNameReferences = new ArrayList<StringIdItem>(); 159 160 if (lineStart == 0) { 161 lineStart = 1; 162 } 163 164 currentLine = lineStart; 165 166 for (Event event: events) { 167 event.emit(dexFile, debugInstructions); 168 } 169 debugInstructions.add(new EndSequence()); 170 171 for (String parameterName: parameterNames) { 172 if (parameterName == null) { 173 parameterNameReferences.add(null); 174 } else { 175 parameterNameReferences.add(new StringIdItem(dexFile, parameterName)); 176 } 177 } 178 179 return new DebugInfoItem(dexFile, lineStart, parameterNameReferences, debugInstructions); 180 } 181 182 private interface Event 183 { 184 int getAddress(); 185 void emit(DexFile dexFile, List<DebugInstruction> debugInstructions); 186 } 187 188 private void emitAdvancePC(int address, List<DebugInstruction> debugInstructions) { 189 int addressDelta = address-currentAddress; 190 191 if (addressDelta > 0) { 192 debugInstructions.add(new AdvancePC(addressDelta)); 193 currentAddress = address; 194 } 195 } 196 197 private class LineEvent implements Event 198 { 199 private final int address; 200 private final int line; 201 202 public LineEvent(int address, int line) { 203 this.address = address; 204 this.line = line; 205 } 206 207 public int getAddress() { 208 return address; 209 } 210 211 public void emit(DexFile dexFile, List<DebugInstruction> debugInstructions) { 212 int lineDelta = line - currentLine; 213 int addressDelta = address - currentAddress; 214 215 if (lineDelta < -4 || lineDelta > 10) { 216 debugInstructions.add(new AdvanceLine(lineDelta)); 217 lineDelta = 0; 218 } 219 if (lineDelta < 2 && addressDelta > 16 || lineDelta > 1 && addressDelta > 15) { 220 debugInstructions.add(new AdvancePC(addressDelta)); 221 addressDelta = 0; 222 } 223 224 //TODO: need to handle the case when the line delta is larger than a signed int 225 debugInstructions.add(new SpecialOpcode(calculateSpecialOpcode(lineDelta, addressDelta))); 226 227 228 currentAddress = address; 229 currentLine = line; 230 } 231 232 private byte calculateSpecialOpcode(int lineDelta, int addressDelta) { 233 return (byte)(FIRST_SPECIAL + (addressDelta * LINE_RANGE) + (lineDelta - LINE_BASE)); 234 } 235 } 236 237 private class StartLocalEvent implements Event 238 { 239 private final int address; 240 private final int registerNum; 241 private final String localName; 242 private final String localType; 243 244 public StartLocalEvent(int address, int registerNum, String localName, String localType) { 245 this.address = address; 246 this.registerNum = registerNum; 247 this.localName = localName; 248 this.localType = localType; 249 } 250 251 public int getAddress() { 252 return address; 253 } 254 255 public void emit(DexFile dexFile, List<DebugInstruction> debugInstructions) { 256 emitAdvancePC(address, debugInstructions); 257 258 debugInstructions.add(new StartLocal(dexFile, registerNum, new StringIdItem(dexFile, localName), 259 new TypeIdItem(dexFile, localType))); 260 } 261 } 262 263 private class StartLocalExtendedEvent implements Event 264 { 265 private final int address; 266 private final int registerNum; 267 private final String localName; 268 private final String localType; 269 private final String signature; 270 271 public StartLocalExtendedEvent(int address, int registerNum, String localName, String localType, 272 String signature) { 273 this.address = address; 274 this.registerNum = registerNum; 275 this.localName = localName; 276 this.localType = localType; 277 this.signature = signature; 278 } 279 280 public int getAddress() { 281 return address; 282 } 283 284 public void emit(DexFile dexFile, List<DebugInstruction> debugInstructions) { 285 emitAdvancePC(address, debugInstructions); 286 287 debugInstructions.add(new StartLocalExtended(dexFile, registerNum, new StringIdItem(dexFile, localName), 288 new TypeIdItem(dexFile, localType), new StringIdItem(dexFile, signature))); 289 } 290 } 291 292 private class EndLocalEvent implements Event 293 { 294 private final int address; 295 private final int registerNum; 296 297 public EndLocalEvent(int address, int registerNum) { 298 this.address = address; 299 this.registerNum = registerNum; 300 } 301 302 public int getAddress() { 303 return address; 304 } 305 306 public void emit(DexFile dexFile, List<DebugInstruction> debugInstructions) { 307 emitAdvancePC(address, debugInstructions); 308 309 debugInstructions.add(new EndLocal(registerNum)); 310 } 311 } 312 313 private class RestartLocalEvent implements Event 314 { 315 private final int address; 316 private final int registerNum; 317 318 public RestartLocalEvent(int address, int registerNum) { 319 this.address = address; 320 this.registerNum = registerNum; 321 } 322 323 public int getAddress() { 324 return address; 325 } 326 327 public void emit(DexFile dexFile, List<DebugInstruction> debugInstructions) { 328 emitAdvancePC(address, debugInstructions); 329 330 debugInstructions.add(new RestartLocal(registerNum)); 331 } 332 } 333 334 private class PrologueEvent implements Event 335 { 336 private final int address; 337 338 public PrologueEvent(int address) { 339 this.address = address; 340 } 341 342 public int getAddress() { 343 return address; 344 } 345 346 public void emit(DexFile dexFile, List<DebugInstruction> debugInstructions) { 347 emitAdvancePC(address, debugInstructions); 348 349 debugInstructions.add(new SetPrologueEnd()); 350 } 351 } 352 353 private class EpilogueEvent implements Event 354 { 355 private final int address; 356 357 public EpilogueEvent(int address) { 358 this.address = address; 359 } 360 361 public int getAddress() { 362 return address; 363 } 364 365 public void emit(DexFile dexFile, List<DebugInstruction> debugInstructions) { 366 emitAdvancePC(address, debugInstructions); 367 368 debugInstructions.add(new SetEpilogueBegin()); 369 } 370 } 371 372 private class SetFileEvent implements Event 373 { 374 private final int address; 375 private final String fileName; 376 377 public SetFileEvent(int address, String fileName) { 378 this.address = address; 379 this.fileName = fileName; 380 } 381 382 public int getAddress() { 383 return address; 384 } 385 386 public void emit(DexFile dexFile, List<DebugInstruction> debugInstructions) { 387 emitAdvancePC(address, debugInstructions); 388 389 debugInstructions.add(new SetFile(dexFile, new StringIdItem(dexFile, fileName))); 390 } 391 } 392} 393