CodeItem.java revision b7399b7fb3e86ff596c19731f9ed99c29c885e57
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; 30 31import org.jf.dexlib.Code.InstructionReader; 32import org.jf.dexlib.Code.InstructionIterator; 33import org.jf.dexlib.Code.Opcode; 34import org.jf.dexlib.Code.InstructionWriter; 35import org.jf.dexlib.Util.AnnotatedOutput; 36import org.jf.dexlib.Util.Input; 37import org.jf.dexlib.Util.SparseArray; 38import org.jf.dexlib.Util.Leb128Utils; 39 40public class CodeItem extends Item<CodeItem> { 41 private int registerCount; 42 private int inWords; 43 private int outWords; 44 private DebugInfoItem debugInfo; 45 private byte[] encodedInstructions; 46 private Item[] referencedItems; 47 private TryItem[] tries; 48 private EncodedCatchHandler[] encodedCatchHandlers; 49 50 private ClassDataItem.EncodedMethod parent; 51 52 /** 53 * Creates a new uninitialized <code>CodeItem</code> 54 * @param dexFile The <code>DexFile</code> that this item belongs to 55 */ 56 public CodeItem(DexFile dexFile) { 57 super(dexFile); 58 } 59 60 /** 61 * Creates a new <code>CodeItem</code> with the given values. 62 * @param dexFile The <code>DexFile</code> that this item belongs to 63 * @param registerCount the number of registers that the method containing this code uses 64 * @param inWords the number of 2-byte words that the parameters to the method containing this code take 65 * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code 66 * @param debugInfo the debug information for this code/method 67 * @param encodedInstructions the instructions, encoded as a byte array 68 * @param referencedItems an array of the items referenced by instructions, in order of occurance in the code 69 * @param tries an array of the tries defined for this code/method 70 * @param encodedCatchHandlers an array of the exception handlers defined for this code/method 71 */ 72 private CodeItem(DexFile dexFile, 73 int registerCount, 74 int inWords, 75 int outWords, 76 DebugInfoItem debugInfo, 77 byte[] encodedInstructions, 78 Item[] referencedItems, 79 TryItem[] tries, 80 EncodedCatchHandler[] encodedCatchHandlers) { 81 super(dexFile); 82 83 this.registerCount = registerCount; 84 this.inWords = inWords; 85 this.outWords = outWords; 86 this.debugInfo = debugInfo; 87 if (debugInfo != null) { 88 debugInfo.setParent(this); 89 } 90 this.encodedInstructions = encodedInstructions; 91 this.referencedItems = referencedItems; 92 this.tries = tries; 93 this.encodedCatchHandlers = encodedCatchHandlers; 94 } 95 96 /** 97 * Returns a new <code>CodeItem</code> with the given values. 98 * @param dexFile The <code>DexFile</code> that this item belongs to 99 * @param registerCount the number of registers that the method containing this code uses 100 * @param inWords the number of 2-byte words that the parameters to the method containing this code take 101 * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code 102 * @param debugInfo the debug information for this code/method 103 * @param encodedInstructions the instructions, encoded as a byte array 104 * @param referencedItems an array of the items referenced by instructions, in order of occurance in the code 105 * @param tries an array of the tries defined for this code/method 106 * @param encodedCatchHandlers an array of the exception handlers defined for this code/method 107 * @return a new <code>CodeItem</code> with the given values. 108 */ 109 public static CodeItem getInternedCodeItem(DexFile dexFile, 110 int registerCount, 111 int inWords, 112 int outWords, 113 DebugInfoItem debugInfo, 114 byte[] encodedInstructions, 115 Item[] referencedItems, 116 TryItem[] tries, 117 EncodedCatchHandler[] encodedCatchHandlers) { 118 CodeItem codeItem = new CodeItem(dexFile, registerCount, inWords, outWords, debugInfo, encodedInstructions, 119 referencedItems, tries, encodedCatchHandlers); 120 return dexFile.CodeItemsSection.intern(codeItem); 121 } 122 123 /** {@inheritDoc} */ 124 protected void readItem(Input in, ReadContext readContext) { 125 this.registerCount = in.readShort(); 126 this.inWords = in.readShort(); 127 this.outWords = in.readShort(); 128 int triesCount = in.readShort(); 129 this.debugInfo = (DebugInfoItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_DEBUG_INFO_ITEM, 130 in.readInt()); 131 if (this.debugInfo != null) { 132 this.debugInfo.setParent(this); 133 } 134 int instructionCount = in.readInt(); 135 this.encodedInstructions = in.readBytes(instructionCount * 2); 136 this.referencedItems = InstructionReader.getReferencedItems(encodedInstructions, dexFile); 137 if (triesCount > 0) { 138 in.alignTo(4); 139 140 //we need to read in the catch handlers first, so save the offset to the try items for future reference 141 int triesOffset = in.getCursor(); 142 in.setCursor(triesOffset + 8 * triesCount); 143 144 //read in the encoded catch handlers 145 int encodedHandlerStart = in.getCursor(); 146 int handlerCount = in.readUnsignedLeb128(); 147 SparseArray<EncodedCatchHandler> handlerMap = new SparseArray<EncodedCatchHandler>(handlerCount); 148 encodedCatchHandlers = new EncodedCatchHandler[handlerCount]; 149 for (int i=0; i<handlerCount; i++) { 150 int position = in.getCursor() - encodedHandlerStart; 151 encodedCatchHandlers[i] = new EncodedCatchHandler(dexFile, in); 152 handlerMap.append(position, encodedCatchHandlers[i]); 153 } 154 int codeItemEnd = in.getCursor(); 155 156 //now go back and read the tries 157 in.setCursor(triesOffset); 158 tries = new TryItem[triesCount]; 159 for (int i=0; i<triesCount; i++) { 160 tries[i] = new TryItem(in, handlerMap); 161 } 162 163 //and now back to the end of the code item 164 in.setCursor(codeItemEnd); 165 } 166 } 167 168 /** {@inheritDoc} */ 169 protected int placeItem(int offset) { 170 offset += 16 + encodedInstructions.length; 171 if (tries != null && tries.length > 0) { 172 if (encodedInstructions.length % 2 == 1) { 173 offset++; 174 } 175 176 offset += tries.length * 8; 177 int encodedCatchHandlerBaseOffset = offset; 178 for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { 179 offset += encodedCatchHandler.place(offset, encodedCatchHandlerBaseOffset); 180 } 181 } 182 return offset; 183 } 184 185 /** {@inheritDoc} */ 186 protected void writeItem(final AnnotatedOutput out) { 187 if (out.annotates()) { 188 out.annotate(2, "registers_size"); 189 out.annotate(2, "ins_size"); 190 out.annotate(2, "outs_size"); 191 out.annotate(2, "tries_size"); 192 out.annotate(4, "debug_info_off"); 193 out.annotate(4, "insns_size"); 194 InstructionIterator.IterateInstructions(encodedInstructions, 195 new InstructionIterator.ProcessRawInstructionDelegate() { 196 197 public void ProcessNormalInstruction(Opcode opcode, int index) { 198 out.annotate(opcode.format.size, opcode.name + " instruction"); 199 } 200 201 public void ProcessReferenceInstruction(Opcode opcode, int index) { 202 out.annotate(opcode.format.size, opcode.name + " instruction"); 203 } 204 205 public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) { 206 out.annotate(instructionLength, "packed_switch instruction"); 207 } 208 209 public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) { 210 out.annotate(instructionLength, "sparse_switch instruction"); 211 } 212 213 public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount, int instructionLength) { 214 out.annotate(instructionLength, "fill_array_data instruction"); 215 } 216 }); 217 if (tries != null && (tries.length % 2 == 1)) { 218 out.annotate(2, "padding"); 219 } 220 } 221 222 out.writeShort(registerCount); 223 out.writeShort(inWords); 224 out.writeShort(outWords); 225 if (tries == null) { 226 out.writeShort(0); 227 } else { 228 out.writeShort(tries.length); 229 } 230 if (debugInfo == null) { 231 out.writeInt(0); 232 } else { 233 out.writeInt(debugInfo.getIndex()); 234 } 235 out.writeInt(encodedInstructions.length / 2); 236 InstructionWriter.writeInstructions(encodedInstructions, referencedItems, out); 237 238 if (tries != null && tries.length > 0) { 239 if ((tries.length % 2) == 1) { 240 out.writeShort(0); 241 } 242 243 for (TryItem tryItem: tries) { 244 tryItem.writeTo(out); 245 } 246 247 out.writeUnsignedLeb128(encodedCatchHandlers.length); 248 249 for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { 250 encodedCatchHandler.writeTo(out); 251 } 252 } 253 } 254 255 /** {@inheritDoc} */ 256 public ItemType getItemType() { 257 return ItemType.TYPE_CODE_ITEM; 258 } 259 260 /** {@inheritDoc} */ 261 public String getConciseIdentity() { 262 //TODO: should mention the method name here 263 return "code_item @0x" + Integer.toHexString(getOffset()); 264 } 265 266 /** {@inheritDoc} */ 267 public int compareTo(CodeItem other) { 268 if (parent == null) { 269 if (other.parent == null) { 270 return 0; 271 } 272 return -1; 273 } 274 if (other.parent == null) { 275 return 1; 276 } 277 return parent.method.compareTo(other.parent.method); 278 } 279 280 /** 281 * @return the register count 282 */ 283 public int getRegisterCount() { 284 return registerCount; 285 } 286 287 /** 288 * @return a byte array containing the encoded instructions 289 */ 290 public byte[] getEncodedInstructions() { 291 return encodedInstructions; 292 } 293 294 /** 295 * @return an array of the <code>TryItem</code> objects in this <code>CodeItem</code> 296 */ 297 public TryItem[] getTries() { 298 return tries; 299 } 300 301 /** 302 * @return the <code>DebugInfoItem</code> associated with this <code>CodeItem</code> 303 */ 304 public DebugInfoItem getDebugInfo() { 305 return debugInfo; 306 } 307 308 /** 309 * Sets the <code>MethodIdItem</code> of the method that this <code>CodeItem</code> is associated with 310 * @param encodedMethod the <code>EncodedMethod</code> of the method that this <code>CodeItem</code> is associated 311 * with 312 */ 313 protected void setParent(ClassDataItem.EncodedMethod encodedMethod) { 314 this.parent = encodedMethod; 315 } 316 317 /** 318 * @return the MethodIdItem of the method that this CodeItem belongs to 319 */ 320 public ClassDataItem.EncodedMethod getParent() { 321 return parent; 322 } 323 324 public static class TryItem { 325 /** 326 * The address (in 2-byte words) within the code where the try block starts 327 */ 328 public final int startAddress; 329 330 /** 331 * The number of 2-byte words that the try block covers 332 */ 333 public final int instructionCount; 334 335 /** 336 * The associated exception handler 337 */ 338 public final EncodedCatchHandler encodedCatchHandler; 339 340 /** 341 * Construct a new <code>TryItem</code> with the given values 342 * @param startAddress the address (in 2-byte words) within the code where the try block starts 343 * @param instructionCount the number of 2-byte words that the try block covers 344 * @param encodedCatchHandler the associated exception handler 345 */ 346 public TryItem(int startAddress, int instructionCount, EncodedCatchHandler encodedCatchHandler) { 347 this.startAddress = startAddress; 348 this.instructionCount = instructionCount; 349 this.encodedCatchHandler = encodedCatchHandler; 350 } 351 352 /** 353 * This is used internally to construct a new <code>TryItem</code> while reading in a <code>DexFile</code> 354 * @param in the Input object to read the <code>TryItem</code> from 355 * @param encodedCatchHandlers a SparseArray of the EncodedCatchHandlers for this <code>CodeItem</code>. The 356 * key should be the offset of the EncodedCatchHandler from the beginning of the encoded_catch_handler_list 357 * structure. 358 */ 359 private TryItem(Input in, SparseArray<EncodedCatchHandler> encodedCatchHandlers) { 360 startAddress = in.readInt(); 361 instructionCount = in.readShort(); 362 363 encodedCatchHandler = encodedCatchHandlers.get(in.readShort()); 364 if (encodedCatchHandler == null) { 365 throw new RuntimeException("Could not find the EncodedCatchHandler referenced by this TryItem"); 366 } 367 } 368 369 /** 370 * Writes the <code>TryItem</code> to the given <code>AnnotatedOutput</code> object 371 * @param out the <code>AnnotatedOutput</code> object to write to 372 */ 373 private void writeTo(AnnotatedOutput out) { 374 if (out.annotates()) { 375 out.annotate(4, "start_addr"); 376 out.annotate(2, "insn_count"); 377 out.annotate(2, "handler_off"); 378 } 379 380 out.writeInt(startAddress); 381 out.writeShort(instructionCount); 382 out.writeShort(encodedCatchHandler.getOffsetInList()); 383 } 384 } 385 386 public static class EncodedCatchHandler { 387 /** 388 * An array of the individual exception handlers 389 */ 390 public final EncodedTypeAddrPair[] handlers; 391 392 /** 393 * The address within the code (in 2-byte words) for the catch all handler, or -1 if there is no catch all 394 * handler 395 */ 396 public final int catchAllHandlerAddress; 397 398 //TODO: would it be possible to get away without having these? and generate/create these values while writing? 399 private int baseOffset; 400 private int offset; 401 402 /** 403 * Constructs a new <code>EncodedCatchHandler</code> with the given values 404 * @param handlers an array of the individual exception handlers 405 * @param catchAllHandlerAddress The address within the code (in 2-byte words) for the catch all handler, or -1 406 * if there is no catch all handler 407 */ 408 public EncodedCatchHandler(EncodedTypeAddrPair[] handlers, int catchAllHandlerAddress) { 409 this.handlers = handlers; 410 this.catchAllHandlerAddress = catchAllHandlerAddress; 411 } 412 413 /** 414 * This is used internally to construct a new <code>EncodedCatchHandler</code> while reading in a 415 * <code>DexFile</code> 416 * @param dexFile the <code>DexFile</code> that is being read in 417 * @param in the Input object to read the <code>EncodedCatchHandler</code> from 418 */ 419 private EncodedCatchHandler(DexFile dexFile, Input in) { 420 int handlerCount = in.readSignedLeb128(); 421 422 if (handlerCount < 0) { 423 handlers = new EncodedTypeAddrPair[-1 * handlerCount]; 424 } else { 425 handlers = new EncodedTypeAddrPair[handlerCount]; 426 } 427 428 for (int i=0; i<handlers.length; i++) { 429 handlers[i] = new EncodedTypeAddrPair(dexFile, in); 430 } 431 432 if (handlerCount <= 0) { 433 catchAllHandlerAddress = in.readUnsignedLeb128(); 434 } else { 435 catchAllHandlerAddress = -1; 436 } 437 } 438 439 /** 440 * @return the offset of this <code>EncodedCatchHandler</code> from the beginning of the 441 * encoded_catch_handler_list structure 442 */ 443 private int getOffsetInList() { 444 return offset-baseOffset; 445 } 446 447 /** 448 * Places the <code>EncodedCatchHandler</code>, storing the offset and baseOffset, and returning the offset 449 * immediately following this <code>EncodedCatchHandler</code> 450 * @param offset the offset of this <code>EncodedCatchHandler</code> in the <code>DexFile</code> 451 * @param baseOffset the offset of the beginning of the encoded_catch_handler_list structure in the 452 * <code>DexFile</code> 453 * @return the offset immediately following this <code>EncodedCatchHandler</code> 454 */ 455 private int place(int offset, int baseOffset) { 456 this.offset = offset; 457 this.baseOffset = baseOffset; 458 459 int size = handlers.length; 460 if (catchAllHandlerAddress > -1) { 461 size *= -1; 462 offset += Leb128Utils.unsignedLeb128Size(catchAllHandlerAddress); 463 } 464 offset += Leb128Utils.signedLeb128Size(size); 465 466 for (EncodedTypeAddrPair handler: handlers) { 467 offset += handler.getSize(); 468 } 469 return offset; 470 } 471 472 /** 473 * Writes the <code>EncodedCatchHandler</code> to the given <code>AnnotatedOutput</code> object 474 * @param out the <code>AnnotatedOutput</code> object to write to 475 */ 476 private void writeTo(AnnotatedOutput out) { 477 if (out.annotates()) { 478 out.annotate("size"); 479 480 int size = handlers.length; 481 if (catchAllHandlerAddress < 0) { 482 size = size * -1; 483 } 484 out.writeSignedLeb128(size); 485 486 for (EncodedTypeAddrPair handler: handlers) { 487 handler.writeTo(out); 488 } 489 490 if (catchAllHandlerAddress > -1) { 491 out.annotate("catch_all_addr"); 492 out.writeUnsignedLeb128(catchAllHandlerAddress); 493 } 494 } else { 495 int size = handlers.length; 496 if (catchAllHandlerAddress < 0) { 497 size = size * -1; 498 } 499 out.writeSignedLeb128(size); 500 501 for (EncodedTypeAddrPair handler: handlers) { 502 handler.writeTo(out); 503 } 504 505 if (catchAllHandlerAddress > -1) { 506 out.writeUnsignedLeb128(catchAllHandlerAddress); 507 } 508 } 509 } 510 } 511 512 public static class EncodedTypeAddrPair { 513 /** 514 * The type of the <code>Exception</code> that this handler handles 515 */ 516 public final TypeIdItem exceptionType; 517 518 /** 519 * The address (in 2-byte words) in the code of the handler 520 */ 521 public final int handlerAddress; 522 523 /** 524 * Constructs a new <code>EncodedTypeAddrPair</code> with the given values 525 * @param exceptionType the type of the <code>Exception</code> that this handler handles 526 * @param handlerAddress the address (in 2-byte words) in the code of the handler 527 */ 528 public EncodedTypeAddrPair(TypeIdItem exceptionType, int handlerAddress) { 529 this.exceptionType = exceptionType; 530 this.handlerAddress = handlerAddress; 531 } 532 533 /** 534 * This is used internally to construct a new <code>EncodedTypeAddrPair</code> while reading in a 535 * <code>DexFile</code> 536 * @param dexFile the <code>DexFile</code> that is being read in 537 * @param in the Input object to read the <code>EncodedCatchHandler</code> from 538 */ 539 private EncodedTypeAddrPair(DexFile dexFile, Input in) { 540 exceptionType = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128()); 541 handlerAddress = in.readUnsignedLeb128(); 542 } 543 544 /** 545 * @return the size of this <code>EncodedTypeAddrPair</code> 546 */ 547 private int getSize() { 548 return Leb128Utils.unsignedLeb128Size(exceptionType.getIndex()) + 549 Leb128Utils.unsignedLeb128Size(handlerAddress); 550 } 551 552 /** 553 * Writes the <code>EncodedTypeAddrPair</code> to the given <code>AnnotatedOutput</code> object 554 * @param out the <code>AnnotatedOutput</code> object to write to 555 */ 556 private void writeTo(AnnotatedOutput out) { 557 if (out.annotates()) { 558 out.annotate("type_idx"); 559 out.writeUnsignedLeb128(exceptionType.getIndex()); 560 561 out.annotate("addr"); 562 out.writeUnsignedLeb128(handlerAddress); 563 } else { 564 out.writeUnsignedLeb128(exceptionType.getIndex()); 565 out.writeUnsignedLeb128(handlerAddress); 566 } 567 } 568 } 569} 570