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