CodeItem.java revision 281b510a9c2b4ae914ab28b9a4f4d622e5861da6
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.InstructionField; 32import org.jf.dexlib.Code.Opcode; 33import org.jf.dexlib.Util.AnnotatedOutput; 34import org.jf.dexlib.Util.Input; 35 36import java.util.ArrayList; 37import java.util.HashMap; 38import java.util.List; 39import java.util.Collections; 40 41public class CodeItem extends OffsettedItem<CodeItem> { 42 private final ArrayList<InstructionField> instructionList; 43 private final ArrayList<TryItem> tryItems = new ArrayList<TryItem>(); 44 private final ArrayList<EncodedCatchHandler> catchHandlerList = new ArrayList<EncodedCatchHandler>(); 45 46 private final ShortIntegerField registersCountField; 47 private final ShortIntegerField inArgumentCountField; 48 private final ShortIntegerField outArgumentCountField; 49 private final ListSizeField triesCountField; 50 private final OffsettedItemReference<DebugInfoItem> debugInfoReferenceField; 51 private final IntegerField instructionsSizeField; 52 private final InstructionListField instructionListField; 53 private final PaddingField paddingField; 54 private final FieldListField<TryItem> triesListField; 55 private final EncodedCatchHandlerList catchHandlersListField; 56 57 private MethodIdItem parent = null; 58 59 public CodeItem(final DexFile dexFile, int offset) { 60 super(dexFile, offset); 61 62 instructionList = new ArrayList<InstructionField>(); 63 64 fields = new Field[] { 65 registersCountField = new ShortIntegerField("registers_size"), 66 inArgumentCountField = new ShortIntegerField("ins_size"), 67 outArgumentCountField = new ShortIntegerField("outs_size"), 68 triesCountField = new ListSizeField(tryItems, new ShortIntegerField("tries_size")), 69 debugInfoReferenceField = new OffsettedItemReference<DebugInfoItem>(dexFile.DebugInfoItemsSection, 70 new IntegerField(null), "debug_off"), 71 instructionsSizeField = new IntegerField("insns_size"), 72 instructionListField = new InstructionListField(dexFile), 73 paddingField = new PaddingField(), 74 triesListField = new FieldListField<TryItem>(tryItems, "try_item") { 75 protected TryItem make() { 76 return new TryItem(catchHandlersListField); 77 } 78 }, 79 80 catchHandlersListField = new EncodedCatchHandlerList(dexFile) 81 }; 82 } 83 84 public CodeItem(final DexFile dexFile, 85 int registersCount, 86 int inArguments, 87 List<InstructionField> instructions, 88 DebugInfoItem debugInfo, 89 List<TryItem> tries, 90 List<EncodedCatchHandler> handlers) { 91 this(dexFile, 0); 92 93 instructionList.addAll(instructions); 94 instructionsSizeField.cacheValue(instructionListField.getInstructionWordCount()); 95 96 if (tries != null) { 97 tryItems.addAll(tries); 98 if (handlers == null) { 99 throw new RuntimeException("The handlers parameter cannot be null if tries parameter is not null"); 100 } 101 catchHandlerList.addAll(handlers); 102 } else if (handlers != null) { 103 throw new RuntimeException("The handlers parameter must be null if the tries parameter is null"); 104 } 105 106 registersCountField.cacheValue(registersCount); 107 inArgumentCountField.cacheValue(inArguments); 108 outArgumentCountField.cacheValue(instructionListField.getOutArguments()); 109 debugInfoReferenceField.setReference(debugInfo); 110 111 if (debugInfo != null) { 112 debugInfo.setParent(this); 113 } 114 } 115 116 protected int getAlignment() { 117 return 4; 118 } 119 120 public ItemType getItemType() { 121 return ItemType.TYPE_CODE_ITEM; 122 } 123 124 public int getRegisterCount() { 125 return registersCountField.getCachedValue(); 126 } 127 128 public List<InstructionField> getInstructions() { 129 return Collections.unmodifiableList(instructionList); 130 } 131 132 public List<TryItem> getTries() { 133 return Collections.unmodifiableList(tryItems); 134 } 135 136 public DebugInfoItem getDebugInfo() { 137 return debugInfoReferenceField.getReference(); 138 } 139 140 protected void setParent(MethodIdItem methodIdItem) { 141 this.parent = methodIdItem; 142 } 143 144 public void copyTo(DexFile dexFile, CodeItem copy) 145 { 146 for (int i = 0; i < fields.length-2; i++) { 147 fields[i].copyTo(dexFile, copy.fields[i]); 148 } 149 //we need to do this in reverse order, so when the tries are copied, 150 //the catchHandler copies will already exist 151 catchHandlersListField.copyTo(dexFile, copy.catchHandlersListField); 152 triesListField.copyTo(dexFile, copy.triesListField); 153 154 DebugInfoItem copyDebugInfo = copy.getDebugInfo(); 155 if (copy != null) { 156 copyDebugInfo.setParent(copy); 157 } 158 } 159 160 public void readFrom(Input in, int index) { 161 super.readFrom(in, index); 162 163 DebugInfoItem debugInfoItem = debugInfoReferenceField.getReference(); 164 if (debugInfoItem != null) { 165 debugInfoItem.setParent(this); 166 } 167 } 168 169 public String getConciseIdentity() { 170 //TODO: should mention the method name here 171 return "code_item @0x" + Integer.toHexString(getOffset()); 172 } 173 174 public int compareTo(CodeItem other) { 175 if (parent == null) { 176 if (other.parent == null) { 177 return 0; 178 } 179 return -1; 180 } 181 if (other.parent == null) { 182 return 1; 183 } 184 return parent.compareTo(other.parent); 185 } 186 187 public static class TryItem extends CompositeField<TryItem> { 188 private final IntegerField startAddr; 189 private final ShortIntegerField insnCount; 190 private final EncodedCatchHandlerReference encodedCatchHandlerReference; 191 192 public TryItem(EncodedCatchHandlerList encodedCatchHandlerList) { 193 super("try_item"); 194 fields = new Field[] { 195 startAddr = new IntegerField("start_addr"), 196 insnCount = new ShortIntegerField("insn_count"), 197 encodedCatchHandlerReference = new EncodedCatchHandlerReference(encodedCatchHandlerList) 198 }; 199 } 200 201 public TryItem(int startAddr, int insnCount, EncodedCatchHandler encodedCatchHandler) { 202 super("try_item"); 203 fields = new Field[] { 204 this.startAddr = new IntegerField(startAddr, "start_addr"), 205 this.insnCount = new ShortIntegerField(insnCount, "insn_count"), 206 this.encodedCatchHandlerReference = new EncodedCatchHandlerReference(encodedCatchHandler) 207 }; 208 } 209 210 public int getStartAddress() { 211 return startAddr.getCachedValue(); 212 } 213 214 public int getEndAddress() { 215 return startAddr.getCachedValue() + insnCount.getCachedValue(); 216 } 217 218 public EncodedCatchHandler getHandler() { 219 return encodedCatchHandlerReference.getReference(); 220 } 221 } 222 223 public static class EncodedCatchHandlerReference extends ShortIntegerField { 224 private final EncodedCatchHandlerList encodedCatchHandlerList; 225 private EncodedCatchHandler encodedCatchHandler; 226 227 public EncodedCatchHandlerReference(EncodedCatchHandlerList encodedCatchHandlerList) { 228 super("encoded_catch_handler"); 229 this.encodedCatchHandlerList = encodedCatchHandlerList; 230 } 231 232 public EncodedCatchHandlerReference(EncodedCatchHandler encodedCatchHandler) { 233 super("encoded_catch_handler"); 234 this.encodedCatchHandlerList = null; 235 this.encodedCatchHandler = encodedCatchHandler; 236 } 237 238 public EncodedCatchHandlerList getEncodedCatchHandlerList() { 239 return encodedCatchHandlerList; 240 } 241 242 private void setReference(EncodedCatchHandler encodedCatchHandler) { 243 this.encodedCatchHandler = encodedCatchHandler; 244 } 245 246 public EncodedCatchHandler getReference() { 247 return encodedCatchHandler; 248 } 249 250 public void copyTo(DexFile dexFile, CachedIntegerValueField _copy) { 251 EncodedCatchHandlerReference copy = (EncodedCatchHandlerReference)_copy; 252 EncodedCatchHandler copiedItem = copy.getEncodedCatchHandlerList().intern(encodedCatchHandler); 253 copy.setReference(copiedItem); 254 } 255 256 public void writeTo(AnnotatedOutput out) { 257 cacheValue(encodedCatchHandler.getOffsetInList()); 258 259 super.writeTo(out); 260 } 261 262 public void readFrom(Input in) { 263 super.readFrom(in); 264 265 encodedCatchHandler = encodedCatchHandlerList.getByOffset(getCachedValue()); 266 } 267 268 public int place(int offset) { 269 cacheValue(encodedCatchHandler.getOffsetInList()); 270 return super.place(offset); 271 } 272 } 273 274 public class EncodedCatchHandlerList extends CompositeField<EncodedCatchHandlerList> { 275 private boolean fieldPresent = false; 276 //this field is only valid when reading a dex file in 277 protected HashMap<Integer, EncodedCatchHandler> itemsByOffset = 278 new HashMap<Integer, EncodedCatchHandler>(); 279 280 protected HashMap<EncodedCatchHandler, EncodedCatchHandler> uniqueItems = null; 281 282 private final DexFile dexFile; 283 284 public EncodedCatchHandler getByOffset(int offset) { 285 EncodedCatchHandler encodedCatchHandler = itemsByOffset.get(offset); 286 if (encodedCatchHandler == null) { 287 encodedCatchHandler = new EncodedCatchHandler(dexFile, offset); 288 itemsByOffset.put(offset, encodedCatchHandler); 289 } 290 return encodedCatchHandler; 291 } 292 293 public EncodedCatchHandler intern(EncodedCatchHandler item) { 294 if (uniqueItems == null) { 295 buildInternedItemMap(); 296 } 297 EncodedCatchHandler encodedCatchHandler = uniqueItems.get(item); 298 if (encodedCatchHandler == null) { 299 encodedCatchHandler = new EncodedCatchHandler(dexFile, -1); 300 catchHandlerList.add(encodedCatchHandler); 301 item.copyTo(dexFile, encodedCatchHandler); 302 uniqueItems.put(encodedCatchHandler, encodedCatchHandler); 303 } 304 return encodedCatchHandler; 305 } 306 307 private void buildInternedItemMap() { 308 uniqueItems = new HashMap<EncodedCatchHandler, EncodedCatchHandler>(); 309 for (EncodedCatchHandler item: catchHandlerList) { 310 uniqueItems.put(item, item); 311 } 312 } 313 314 public EncodedCatchHandlerList(final DexFile dexFile) { 315 super("encoded_catch_handler_list"); 316 this.dexFile = dexFile; 317 318 fields = new Field[] { 319 sizeField = new ListSizeField(catchHandlerList, new Leb128Field("size")), 320 listField = new FieldListField<EncodedCatchHandler>(catchHandlerList, "encoded_catch_handler") { 321 protected EncodedCatchHandler make() { 322 return new EncodedCatchHandler(dexFile, 0); 323 } 324 325 public void readFrom(Input in) { 326 int currentOffset = sizeField.place(0); 327 328 for (int i = 0; i < list.size(); i++) { 329 EncodedCatchHandler field = list.get(i); 330 331 if (field == null) { 332 field = itemsByOffset.get(currentOffset); 333 if (field == null) { 334 field = new EncodedCatchHandler(dexFile, currentOffset); 335 } 336 list.set(i, field); 337 } 338 int savedOffset = in.getCursor(); 339 field.readFrom(in); 340 currentOffset += in.getCursor() - savedOffset; 341 } 342 } 343 } 344 }; 345 } 346 347 private final ListSizeField sizeField; 348 private final FieldListField<EncodedCatchHandler> listField; 349 350 public void readFrom(Input in) { 351 if (tryItems.size() > 0) { 352 fieldPresent = true; 353 super.readFrom(in); 354 } 355 } 356 357 public void writeTo(AnnotatedOutput out) { 358 if (fieldPresent) { 359 super.writeTo(out); 360 } 361 } 362 363 public int place(int offset) { 364 for (EncodedCatchHandler encodedCatchHandler: listField.list) { 365 encodedCatchHandler.setBaseOffset(offset); 366 } 367 if (tryItems.size() > 0) { 368 fieldPresent = true; 369 return super.place(offset); 370 } else { 371 return offset; 372 } 373 } 374 375 public void copyTo(DexFile dexFile, EncodedCatchHandlerList copy) { 376 super.copyTo(dexFile, copy); 377 copy.fieldPresent = fieldPresent; 378 copy.itemsByOffset.clear(); 379 int offset = 0; 380 for (EncodedCatchHandler encodedCatchHandler: copy.listField.list) { 381 copy.itemsByOffset.put(encodedCatchHandler.offset, encodedCatchHandler); 382 } 383 } 384 } 385 386 public static class EncodedCatchHandler extends CompositeField<EncodedCatchHandler> { 387 private ArrayList<EncodedTypeAddrPair> list; 388 boolean hasCatchAll = false; 389 private int baseOffset = 0; 390 391 private final ListSizeField size; 392 private final FieldListField<EncodedTypeAddrPair> handlers; 393 private final Leb128Field catchAllAddress; 394 395 private int offset; 396 397 public EncodedCatchHandler(final DexFile dexFile, int offset) { 398 super("encoded_catch_handler"); 399 this.offset = offset; 400 401 list = new ArrayList<EncodedTypeAddrPair>(); 402 fields = new Field[] { 403 size = new ListSizeField(list, new SignedLeb128Field("size") { 404 public void readFrom(Input in) { 405 super.readFrom(in); 406 hasCatchAll = (getCachedValue() <= 0); 407 } 408 409 public void cacheValue(int value) { 410 super.cacheValue(value * (hasCatchAll?-1:1)); 411 }}) 412 , 413 handlers = new FieldListField<EncodedTypeAddrPair>(list, "encoded_type_addr_pair") { 414 protected EncodedTypeAddrPair make() { 415 return new EncodedTypeAddrPair(dexFile); 416 } 417 }, 418 catchAllAddress = new Leb128Field("catch_all_addr") { 419 public void readFrom(Input in) { 420 if (hasCatchAll) { 421 super.readFrom(in); 422 } 423 } 424 425 public void writeTo(AnnotatedOutput out) { 426 if (hasCatchAll) { 427 super.writeTo(out); 428 } 429 } 430 431 public int place(int offset) { 432 if (hasCatchAll) { 433 return super.place(offset); 434 } 435 return offset; 436 } 437 } 438 }; 439 } 440 441 public EncodedCatchHandler(final DexFile dexFile, List<EncodedTypeAddrPair> handlers, int catchAllHandler) { 442 this(dexFile, 0); 443 444 list.addAll(handlers); 445 if (catchAllHandler >= 0) { 446 hasCatchAll = true; 447 catchAllAddress.cacheValue(catchAllHandler); 448 } 449 } 450 451 public int getOffsetInList() { 452 return offset-baseOffset; 453 } 454 455 public void setBaseOffset(int baseOffset) { 456 this.baseOffset = baseOffset; 457 } 458 459 public void copyTo(DexFile dexFile, EncodedCatchHandler copy) { 460 super.copyTo(dexFile, copy); 461 copy.hasCatchAll = hasCatchAll; 462 copy.offset = offset; 463 } 464 465 public int place(int offset) { 466 this.offset = offset; 467 return super.place(offset); 468 } 469 470 public int getCatchAllAddress() { 471 if (hasCatchAll) { 472 return catchAllAddress.getCachedValue(); 473 } else { 474 return -1; 475 } 476 } 477 478 public List<EncodedTypeAddrPair> getHandlers() { 479 return Collections.unmodifiableList(list); 480 } 481 } 482 483 public static class EncodedTypeAddrPair extends CompositeField<EncodedTypeAddrPair> { 484 public final IndexedItemReference<TypeIdItem> typeReferenceField; 485 public final Leb128Field handlerAddressField; 486 487 public EncodedTypeAddrPair(DexFile dexFile) { 488 super("encoded_type_addr_pair"); 489 fields = new Field[] { 490 typeReferenceField = new IndexedItemReference<TypeIdItem>(dexFile.TypeIdsSection, 491 new Leb128Field(null), "type_idx"), 492 handlerAddressField = new Leb128Field("addr") 493 }; 494 } 495 496 public EncodedTypeAddrPair(DexFile dexFile, TypeIdItem type, int handlerOffset) { 497 this(dexFile); 498 typeReferenceField.setReference(type); 499 handlerAddressField.cacheValue(handlerOffset); 500 } 501 502 public TypeIdItem getTypeReferenceField() { 503 return typeReferenceField.getReference(); 504 } 505 506 public int getHandlerAddress() { 507 return handlerAddressField.getCachedValue(); 508 } 509 } 510 511 private class InstructionListField implements Field<InstructionListField> { 512 private final DexFile dexFile; 513 514 public InstructionListField(DexFile dexFile) { 515 this.dexFile = dexFile; 516 } 517 518 public void writeTo(AnnotatedOutput out) { 519 int startPosition = out.getCursor(); 520 for (InstructionField instruction: instructionList) { 521 instruction.writeTo(out); 522 } 523 if ((out.getCursor() - startPosition) != (instructionsSizeField.getCachedValue() * 2)) { 524 throw new RuntimeException("Did not write the expected amount of bytes"); 525 } 526 } 527 528 public void readFrom(Input in) { 529 int numBytes = instructionsSizeField.getCachedValue() * 2; 530 int startPosition = in.getCursor(); 531 532 do { 533 InstructionField instruction = new InstructionField(dexFile); 534 instruction.readFrom(in); 535 instructionList.add(instruction); 536 } while (in.getCursor() - startPosition < numBytes); 537 538 if (in.getCursor() - startPosition != numBytes) { 539 throw new RuntimeException("Read past the end of the code section"); 540 } 541 } 542 543 public int place(int offset) { 544 return offset + (instructionsSizeField.getCachedValue() * 2); 545 } 546 547 public void copyTo(DexFile dexFile, InstructionListField copy) { 548 ArrayList<InstructionField> copyInstructionList = copy.getInstructionList(); 549 copyInstructionList.clear(); 550 for (InstructionField instruction: instructionList) { 551 InstructionField instructionCopy = new InstructionField(dexFile); 552 instruction.copyTo(dexFile, instructionCopy); 553 copyInstructionList.add(instructionCopy); 554 } 555 } 556 557 private ArrayList<InstructionField> getInstructionList() { 558 return instructionList; 559 } 560 561 //return the word size of the instruction list 562 public int getInstructionWordCount() { 563 int bytes = 0; 564 for (InstructionField instruction: instructionList) { 565 bytes += instruction.getSize(bytes); 566 } 567 return bytes/2; 568 } 569 570 //return the highest parameter word count of any method invokation 571 public int getOutArguments() { 572 int maxParamWordCount = 0; 573 for (InstructionField instruction: instructionList) { 574 IndexedItem item = instruction.getInstruction().getReferencedItem(); 575 if (item instanceof MethodIdItem) { 576 MethodIdItem methodIdItem = (MethodIdItem)item; 577 Opcode opcode = instruction.getInstruction().getOpcode(); 578 579 boolean isStatic = false; 580 if (opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE) { 581 isStatic = true; 582 } 583 int paramWordCount = methodIdItem.getParameterRegisterCount(isStatic); 584 585 if (maxParamWordCount < paramWordCount) { 586 maxParamWordCount = paramWordCount; 587 } 588 } 589 } 590 return maxParamWordCount; 591 } 592 } 593 594 private class PaddingField implements Field { 595 596 public PaddingField() { 597 } 598 599 private boolean needsAlign() { 600 return (triesCountField.getCachedValue() > 0) && (instructionsSizeField.getCachedValue() % 2 == 1); 601 } 602 603 public void writeTo(AnnotatedOutput out) { 604 if (needsAlign()) { 605 out.writeShort(0); 606 } 607 } 608 609 public void readFrom(Input in) { 610 if (needsAlign()) { 611 in.skipBytes(2); 612 } 613 } 614 615 public int place(int offset) { 616 if (needsAlign()) { 617 return offset + 2; 618 } else { 619 return offset; 620 } 621 } 622 623 public int hashCode() { 624 return 0; 625 } 626 627 public boolean equals(Object o) { 628 return getClass() == o.getClass(); 629 } 630 631 public void copyTo(DexFile dexFile, Field field) { 632 } 633 } 634} 635