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