CodeItem.java revision 5240d96f410fb6c15e715211592316cec93a2b5b
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 % 4 != 0) { 173 offset+=2; 174 } 175 176 offset += tries.length * 8; 177 int encodedCatchHandlerBaseOffset = offset; 178 offset += Leb128Utils.unsignedLeb128Size(encodedCatchHandlers.length); 179 for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { 180 offset = encodedCatchHandler.place(offset, encodedCatchHandlerBaseOffset); 181 } 182 } 183 return offset; 184 } 185 186 /** {@inheritDoc} */ 187 protected void writeItem(final AnnotatedOutput out) { 188 if (out.annotates()) { 189 out.annotate(2, "registers_size"); 190 out.annotate(2, "ins_size"); 191 out.annotate(2, "outs_size"); 192 out.annotate(2, "tries_size"); 193 out.annotate(4, "debug_info_off"); 194 out.annotate(4, "insns_size"); 195 InstructionIterator.IterateInstructions(encodedInstructions, 196 new InstructionIterator.ProcessRawInstructionDelegate() { 197 198 public void ProcessNormalInstruction(Opcode opcode, int index) { 199 out.annotate(opcode.format.size, opcode.name + " instruction"); 200 } 201 202 public void ProcessReferenceInstruction(Opcode opcode, int index) { 203 out.annotate(opcode.format.size, opcode.name + " instruction"); 204 } 205 206 public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) { 207 out.annotate(instructionLength, "packed_switch instruction"); 208 } 209 210 public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) { 211 out.annotate(instructionLength, "sparse_switch instruction"); 212 } 213 214 public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount, int instructionLength) { 215 out.annotate(instructionLength, "fill_array_data instruction"); 216 } 217 }); 218 if (tries != null && (tries.length % 2 == 1)) { 219 out.annotate(2, "padding"); 220 } 221 } 222 223 out.writeShort(registerCount); 224 out.writeShort(inWords); 225 out.writeShort(outWords); 226 if (tries == null) { 227 out.writeShort(0); 228 } else { 229 out.writeShort(tries.length); 230 } 231 if (debugInfo == null) { 232 out.writeInt(0); 233 } else { 234 out.writeInt(debugInfo.getIndex()); 235 } 236 out.writeInt(encodedInstructions.length / 2); 237 InstructionWriter.writeInstructions(encodedInstructions, referencedItems, out); 238 239 if (tries != null && tries.length > 0) { 240 if ((encodedInstructions.length % 4) != 0) { 241 out.writeShort(0); 242 } 243 244 for (TryItem tryItem: tries) { 245 tryItem.writeTo(out); 246 } 247 248 out.writeUnsignedLeb128(encodedCatchHandlers.length); 249 250 for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { 251 encodedCatchHandler.writeTo(out); 252 } 253 } 254 } 255 256 /** {@inheritDoc} */ 257 public ItemType getItemType() { 258 return ItemType.TYPE_CODE_ITEM; 259 } 260 261 /** {@inheritDoc} */ 262 public String getConciseIdentity() { 263 //TODO: should mention the method name here 264 return "code_item @0x" + Integer.toHexString(getOffset()); 265 } 266 267 /** {@inheritDoc} */ 268 public int compareTo(CodeItem other) { 269 if (parent == null) { 270 if (other.parent == null) { 271 return 0; 272 } 273 return -1; 274 } 275 if (other.parent == null) { 276 return 1; 277 } 278 return parent.method.compareTo(other.parent.method); 279 } 280 281 /** 282 * @return the register count 283 */ 284 public int getRegisterCount() { 285 return registerCount; 286 } 287 288 /** 289 * @return a byte array containing the encoded instructions 290 */ 291 public byte[] getEncodedInstructions() { 292 return encodedInstructions; 293 } 294 295 /** 296 * @return an array of the <code>TryItem</code> objects in this <code>CodeItem</code> 297 */ 298 public TryItem[] getTries() { 299 return tries; 300 } 301 302 /** 303 * @return the <code>DebugInfoItem</code> associated with this <code>CodeItem</code> 304 */ 305 public DebugInfoItem getDebugInfo() { 306 return debugInfo; 307 } 308 309 /** 310 * Sets the <code>MethodIdItem</code> of the method that this <code>CodeItem</code> is associated with 311 * @param encodedMethod the <code>EncodedMethod</code> of the method that this <code>CodeItem</code> is associated 312 * with 313 */ 314 protected void setParent(ClassDataItem.EncodedMethod encodedMethod) { 315 this.parent = encodedMethod; 316 } 317 318 /** 319 * @return the MethodIdItem of the method that this CodeItem belongs to 320 */ 321 public ClassDataItem.EncodedMethod getParent() { 322 return parent; 323 } 324 325 public static class TryItem { 326 /** 327 * The address (in 2-byte words) within the code where the try block starts 328 */ 329 public final int startAddress; 330 331 /** 332 * The number of 2-byte words that the try block covers 333 */ 334 public final int instructionCount; 335 336 /** 337 * The associated exception handler 338 */ 339 public final EncodedCatchHandler encodedCatchHandler; 340 341 /** 342 * Construct a new <code>TryItem</code> with the given values 343 * @param startAddress the address (in 2-byte words) within the code where the try block starts 344 * @param instructionCount the number of 2-byte words that the try block covers 345 * @param encodedCatchHandler the associated exception handler 346 */ 347 public TryItem(int startAddress, int instructionCount, EncodedCatchHandler encodedCatchHandler) { 348 this.startAddress = startAddress; 349 this.instructionCount = instructionCount; 350 this.encodedCatchHandler = encodedCatchHandler; 351 } 352 353 /** 354 * This is used internally to construct a new <code>TryItem</code> while reading in a <code>DexFile</code> 355 * @param in the Input object to read the <code>TryItem</code> from 356 * @param encodedCatchHandlers a SparseArray of the EncodedCatchHandlers for this <code>CodeItem</code>. The 357 * key should be the offset of the EncodedCatchHandler from the beginning of the encoded_catch_handler_list 358 * structure. 359 */ 360 private TryItem(Input in, SparseArray<EncodedCatchHandler> encodedCatchHandlers) { 361 startAddress = in.readInt(); 362 instructionCount = in.readShort(); 363 364 encodedCatchHandler = encodedCatchHandlers.get(in.readShort()); 365 if (encodedCatchHandler == null) { 366 throw new RuntimeException("Could not find the EncodedCatchHandler referenced by this TryItem"); 367 } 368 } 369 370 /** 371 * Writes the <code>TryItem</code> to the given <code>AnnotatedOutput</code> object 372 * @param out the <code>AnnotatedOutput</code> object to write to 373 */ 374 private void writeTo(AnnotatedOutput out) { 375 if (out.annotates()) { 376 out.annotate(4, "start_addr"); 377 out.annotate(2, "insn_count"); 378 out.annotate(2, "handler_off"); 379 } 380 381 out.writeInt(startAddress); 382 out.writeShort(instructionCount); 383 out.writeShort(encodedCatchHandler.getOffsetInList()); 384 } 385 } 386 387 public static class EncodedCatchHandler { 388 /** 389 * An array of the individual exception handlers 390 */ 391 public final EncodedTypeAddrPair[] handlers; 392 393 /** 394 * The address within the code (in 2-byte words) for the catch all handler, or -1 if there is no catch all 395 * handler 396 */ 397 public final int catchAllHandlerAddress; 398 399 //TODO: would it be possible to get away without having these? and generate/create these values while writing? 400 private int baseOffset; 401 private int offset; 402 403 /** 404 * Constructs a new <code>EncodedCatchHandler</code> with the given values 405 * @param handlers an array of the individual exception handlers 406 * @param catchAllHandlerAddress The address within the code (in 2-byte words) for the catch all handler, or -1 407 * if there is no catch all handler 408 */ 409 public EncodedCatchHandler(EncodedTypeAddrPair[] handlers, int catchAllHandlerAddress) { 410 this.handlers = handlers; 411 this.catchAllHandlerAddress = catchAllHandlerAddress; 412 } 413 414 /** 415 * This is used internally to construct a new <code>EncodedCatchHandler</code> while reading in a 416 * <code>DexFile</code> 417 * @param dexFile the <code>DexFile</code> that is being read in 418 * @param in the Input object to read the <code>EncodedCatchHandler</code> from 419 */ 420 private EncodedCatchHandler(DexFile dexFile, Input in) { 421 int handlerCount = in.readSignedLeb128(); 422 423 if (handlerCount < 0) { 424 handlers = new EncodedTypeAddrPair[-1 * handlerCount]; 425 } else { 426 handlers = new EncodedTypeAddrPair[handlerCount]; 427 } 428 429 for (int i=0; i<handlers.length; i++) { 430 handlers[i] = new EncodedTypeAddrPair(dexFile, in); 431 } 432 433 if (handlerCount <= 0) { 434 catchAllHandlerAddress = in.readUnsignedLeb128(); 435 } else { 436 catchAllHandlerAddress = -1; 437 } 438 } 439 440 /** 441 * @return the offset of this <code>EncodedCatchHandler</code> from the beginning of the 442 * encoded_catch_handler_list structure 443 */ 444 private int getOffsetInList() { 445 return offset-baseOffset; 446 } 447 448 /** 449 * Places the <code>EncodedCatchHandler</code>, storing the offset and baseOffset, and returning the offset 450 * immediately following this <code>EncodedCatchHandler</code> 451 * @param offset the offset of this <code>EncodedCatchHandler</code> in the <code>DexFile</code> 452 * @param baseOffset the offset of the beginning of the encoded_catch_handler_list structure in the 453 * <code>DexFile</code> 454 * @return the offset immediately following this <code>EncodedCatchHandler</code> 455 */ 456 private int place(int offset, int baseOffset) { 457 this.offset = offset; 458 this.baseOffset = baseOffset; 459 460 int size = handlers.length; 461 if (catchAllHandlerAddress > -1) { 462 size *= -1; 463 offset += Leb128Utils.unsignedLeb128Size(catchAllHandlerAddress); 464 } 465 offset += Leb128Utils.signedLeb128Size(size); 466 467 for (EncodedTypeAddrPair handler: handlers) { 468 offset += handler.getSize(); 469 } 470 return offset; 471 } 472 473 /** 474 * Writes the <code>EncodedCatchHandler</code> to the given <code>AnnotatedOutput</code> object 475 * @param out the <code>AnnotatedOutput</code> object to write to 476 */ 477 private void writeTo(AnnotatedOutput out) { 478 if (out.annotates()) { 479 out.annotate("size"); 480 481 int size = handlers.length; 482 if (catchAllHandlerAddress < 0) { 483 size = size * -1; 484 } 485 out.writeSignedLeb128(size); 486 487 for (EncodedTypeAddrPair handler: handlers) { 488 handler.writeTo(out); 489 } 490 491 if (catchAllHandlerAddress > -1) { 492 out.annotate("catch_all_addr"); 493 out.writeUnsignedLeb128(catchAllHandlerAddress); 494 } 495 } else { 496 int size = handlers.length; 497 if (catchAllHandlerAddress < 0) { 498 size = size * -1; 499 } 500 out.writeSignedLeb128(size); 501 502 for (EncodedTypeAddrPair handler: handlers) { 503 handler.writeTo(out); 504 } 505 506 if (catchAllHandlerAddress > -1) { 507 out.writeUnsignedLeb128(catchAllHandlerAddress); 508 } 509 } 510 } 511 } 512 513 public static class EncodedTypeAddrPair { 514 /** 515 * The type of the <code>Exception</code> that this handler handles 516 */ 517 public final TypeIdItem exceptionType; 518 519 /** 520 * The address (in 2-byte words) in the code of the handler 521 */ 522 public final int handlerAddress; 523 524 /** 525 * Constructs a new <code>EncodedTypeAddrPair</code> with the given values 526 * @param exceptionType the type of the <code>Exception</code> that this handler handles 527 * @param handlerAddress the address (in 2-byte words) in the code of the handler 528 */ 529 public EncodedTypeAddrPair(TypeIdItem exceptionType, int handlerAddress) { 530 this.exceptionType = exceptionType; 531 this.handlerAddress = handlerAddress; 532 } 533 534 /** 535 * This is used internally to construct a new <code>EncodedTypeAddrPair</code> while reading in a 536 * <code>DexFile</code> 537 * @param dexFile the <code>DexFile</code> that is being read in 538 * @param in the Input object to read the <code>EncodedCatchHandler</code> from 539 */ 540 private EncodedTypeAddrPair(DexFile dexFile, Input in) { 541 exceptionType = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128()); 542 handlerAddress = in.readUnsignedLeb128(); 543 } 544 545 /** 546 * @return the size of this <code>EncodedTypeAddrPair</code> 547 */ 548 private int getSize() { 549 return Leb128Utils.unsignedLeb128Size(exceptionType.getIndex()) + 550 Leb128Utils.unsignedLeb128Size(handlerAddress); 551 } 552 553 /** 554 * Writes the <code>EncodedTypeAddrPair</code> to the given <code>AnnotatedOutput</code> object 555 * @param out the <code>AnnotatedOutput</code> object to write to 556 */ 557 private void writeTo(AnnotatedOutput out) { 558 if (out.annotates()) { 559 out.annotate("type_idx"); 560 out.writeUnsignedLeb128(exceptionType.getIndex()); 561 562 out.annotate("addr"); 563 out.writeUnsignedLeb128(handlerAddress); 564 } else { 565 out.writeUnsignedLeb128(exceptionType.getIndex()); 566 out.writeUnsignedLeb128(handlerAddress); 567 } 568 } 569 } 570} 571