1 /* 2 * Copyright 2013, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package org.jf.dexlib2.analysis; 33 34 import com.google.common.base.Predicates; 35 import com.google.common.base.Supplier; 36 import com.google.common.base.Suppliers; 37 import com.google.common.collect.Iterables; 38 import com.google.common.collect.Lists; 39 import com.google.common.collect.Maps; 40 import com.google.common.primitives.Ints; 41 import org.jf.dexlib2.AccessFlags; 42 import org.jf.dexlib2.analysis.util.TypeProtoUtils; 43 import org.jf.dexlib2.iface.ClassDef; 44 import org.jf.dexlib2.iface.Field; 45 import org.jf.dexlib2.iface.Method; 46 import org.jf.dexlib2.iface.reference.FieldReference; 47 import org.jf.dexlib2.iface.reference.MethodReference; 48 import org.jf.dexlib2.immutable.ImmutableMethod; 49 import org.jf.dexlib2.util.MethodUtil; 50 import org.jf.util.AlignmentUtils; 51 import org.jf.util.ExceptionWithContext; 52 import org.jf.util.SparseArray; 53 54 import javax.annotation.Nonnull; 55 import javax.annotation.Nullable; 56 import java.util.*; 57 58 /** 59 * A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields 60 * and their offsets. 61 */ 62 public class ClassProto implements TypeProto { 63 private static final byte REFERENCE = 0; 64 private static final byte WIDE = 1; 65 private static final byte OTHER = 2; 66 67 @Nonnull protected final ClassPath classPath; 68 @Nonnull protected final String type; 69 70 protected boolean vtableFullyResolved = true; 71 protected boolean interfacesFullyResolved = true; 72 73 public ClassProto(@Nonnull ClassPath classPath, @Nonnull String type) { 74 if (type.charAt(0) != 'L') { 75 throw new ExceptionWithContext("Cannot construct ClassProto for non reference type: %s", type); 76 } 77 this.classPath = classPath; 78 this.type = type; 79 } 80 81 @Override public String toString() { return type; } 82 @Nonnull @Override public ClassPath getClassPath() { return classPath; } 83 @Nonnull @Override public String getType() { return type; } 84 85 @Nonnull 86 public ClassDef getClassDef() { 87 return classDefSupplier.get(); 88 } 89 90 91 @Nonnull private final Supplier<ClassDef> classDefSupplier = Suppliers.memoize(new Supplier<ClassDef>() { 92 @Override public ClassDef get() { 93 return classPath.getClassDef(type); 94 } 95 }); 96 97 /** 98 * Returns true if this class is an interface. 99 * 100 * If this class is not defined, then this will throw an UnresolvedClassException 101 * 102 * @return True if this class is an interface 103 */ 104 public boolean isInterface() { 105 ClassDef classDef = getClassDef(); 106 return (classDef.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0; 107 } 108 109 /** 110 * Returns the set of interfaces that this class implements as a Map<String, ClassDef>. 111 * 112 * The ClassDef value will be present only for the interfaces that this class directly implements (including any 113 * interfaces transitively implemented), but not for any interfaces that are only implemented by a superclass of 114 * this class 115 * 116 * For any interfaces that are only implemented by a superclass (or the class itself, if the class is an interface), 117 * the value will be null. 118 * 119 * If any interface couldn't be resolved, then the interfacesFullyResolved field will be set to false upon return. 120 * 121 * @return the set of interfaces that this class implements as a Map<String, ClassDef>. 122 */ 123 @Nonnull 124 protected LinkedHashMap<String, ClassDef> getInterfaces() { 125 return interfacesSupplier.get(); 126 } 127 128 @Nonnull 129 private final Supplier<LinkedHashMap<String, ClassDef>> interfacesSupplier = 130 Suppliers.memoize(new Supplier<LinkedHashMap<String, ClassDef>>() { 131 @Override public LinkedHashMap<String, ClassDef> get() { 132 LinkedHashMap<String, ClassDef> interfaces = Maps.newLinkedHashMap(); 133 134 try { 135 for (String interfaceType: getClassDef().getInterfaces()) { 136 if (!interfaces.containsKey(interfaceType)) { 137 ClassDef interfaceDef; 138 try { 139 interfaceDef = classPath.getClassDef(interfaceType); 140 interfaces.put(interfaceType, interfaceDef); 141 } catch (UnresolvedClassException ex) { 142 interfaces.put(interfaceType, null); 143 interfacesFullyResolved = false; 144 } 145 146 ClassProto interfaceProto = (ClassProto) classPath.getClass(interfaceType); 147 for (String superInterface: interfaceProto.getInterfaces().keySet()) { 148 if (!interfaces.containsKey(superInterface)) { 149 interfaces.put(superInterface, interfaceProto.getInterfaces().get(superInterface)); 150 } 151 } 152 if (!interfaceProto.interfacesFullyResolved) { 153 interfacesFullyResolved = false; 154 } 155 } 156 } 157 } catch (UnresolvedClassException ex) { 158 interfacesFullyResolved = false; 159 } 160 161 // now add self and super class interfaces, required for common super class lookup 162 // we don't really need ClassDef's for that, so let's just use null 163 164 if (isInterface() && !interfaces.containsKey(getType())) { 165 interfaces.put(getType(), null); 166 } 167 168 try { 169 String superclass = getSuperclass(); 170 if (superclass != null) { 171 ClassProto superclassProto = (ClassProto) classPath.getClass(superclass); 172 for (String superclassInterface: superclassProto.getInterfaces().keySet()) { 173 if (!interfaces.containsKey(superclassInterface)) { 174 interfaces.put(superclassInterface, null); 175 } 176 } 177 if (!superclassProto.interfacesFullyResolved) { 178 interfacesFullyResolved = false; 179 } 180 } 181 } catch (UnresolvedClassException ex) { 182 interfacesFullyResolved = false; 183 } 184 185 return interfaces; 186 } 187 }); 188 189 /** 190 * Gets the interfaces directly implemented by this class, or the interfaces they transitively implement. 191 * 192 * This does not include any interfaces that are only implemented by a superclass 193 * 194 * @return An iterables of ClassDefs representing the directly or transitively implemented interfaces 195 * @throws UnresolvedClassException if interfaces could not be fully resolved 196 */ 197 @Nonnull 198 protected Iterable<ClassDef> getDirectInterfaces() { 199 Iterable<ClassDef> directInterfaces = 200 Iterables.filter(getInterfaces().values(), Predicates.notNull()); 201 202 if (!interfacesFullyResolved) { 203 throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType()); 204 } 205 206 return directInterfaces; 207 } 208 209 /** 210 * Checks if this class implements the given interface. 211 * 212 * If the interfaces of this class cannot be fully resolved then this 213 * method will either return true or throw an UnresolvedClassException 214 * 215 * @param iface The interface to check for 216 * @return true if this class implements the given interface, otherwise false 217 * @throws UnresolvedClassException if the interfaces for this class could not be fully resolved, and the interface 218 * is not one of the interfaces that were successfully resolved 219 */ 220 @Override 221 public boolean implementsInterface(@Nonnull String iface) { 222 if (getInterfaces().containsKey(iface)) { 223 return true; 224 } 225 if (!interfacesFullyResolved) { 226 throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType()); 227 } 228 return false; 229 } 230 231 @Nullable @Override 232 public String getSuperclass() { 233 return getClassDef().getSuperclass(); 234 } 235 236 /** 237 * This is a helper method for getCommonSuperclass 238 * 239 * It checks if this class is an interface, and if so, if other implements it. 240 * 241 * If this class is undefined, we go ahead and check if it is listed in other's interfaces. If not, we throw an 242 * UndefinedClassException 243 * 244 * If the interfaces of other cannot be fully resolved, we check the interfaces that can be resolved. If not found, 245 * we throw an UndefinedClassException 246 * 247 * @param other The class to check the interfaces of 248 * @return true if this class is an interface (or is undefined) other implements this class 249 * 250 */ 251 private boolean checkInterface(@Nonnull ClassProto other) { 252 boolean isResolved = true; 253 boolean isInterface = true; 254 try { 255 isInterface = isInterface(); 256 } catch (UnresolvedClassException ex) { 257 isResolved = false; 258 // if we don't know if this class is an interface or not, 259 // we can still try to call other.implementsInterface(this) 260 } 261 if (isInterface) { 262 try { 263 if (other.implementsInterface(getType())) { 264 return true; 265 } 266 } catch (UnresolvedClassException ex) { 267 // There are 2 possibilities here, depending on whether we were able to resolve this class. 268 // 1. If this class is resolved, then we know it is an interface class. The other class either 269 // isn't defined, or its interfaces couldn't be fully resolved. 270 // In this case, we throw an UnresolvedClassException 271 // 2. If this class is not resolved, we had tried to call implementsInterface anyway. We don't 272 // know for sure if this class is an interface or not. We return false, and let processing 273 // continue in getCommonSuperclass 274 if (isResolved) { 275 throw ex; 276 } 277 } 278 } 279 return false; 280 } 281 282 @Override @Nonnull 283 public TypeProto getCommonSuperclass(@Nonnull TypeProto other) { 284 // use the other type's more specific implementation 285 if (!(other instanceof ClassProto)) { 286 return other.getCommonSuperclass(this); 287 } 288 289 if (this == other || getType().equals(other.getType())) { 290 return this; 291 } 292 293 if (this.getType().equals("Ljava/lang/Object;")) { 294 return this; 295 } 296 297 if (other.getType().equals("Ljava/lang/Object;")) { 298 return other; 299 } 300 301 boolean gotException = false; 302 try { 303 if (checkInterface((ClassProto)other)) { 304 return this; 305 } 306 } catch (UnresolvedClassException ex) { 307 gotException = true; 308 } 309 310 try { 311 if (((ClassProto)other).checkInterface(this)) { 312 return other; 313 } 314 } catch (UnresolvedClassException ex) { 315 gotException = true; 316 } 317 if (gotException) { 318 return classPath.getUnknownClass(); 319 } 320 321 List<TypeProto> thisChain = Lists.<TypeProto>newArrayList(this); 322 Iterables.addAll(thisChain, TypeProtoUtils.getSuperclassChain(this)); 323 324 List<TypeProto> otherChain = Lists.newArrayList(other); 325 Iterables.addAll(otherChain, TypeProtoUtils.getSuperclassChain(other)); 326 327 // reverse them, so that the first entry is either Ljava/lang/Object; or Ujava/lang/Object; 328 thisChain = Lists.reverse(thisChain); 329 otherChain = Lists.reverse(otherChain); 330 331 for (int i=Math.min(thisChain.size(), otherChain.size())-1; i>=0; i--) { 332 TypeProto typeProto = thisChain.get(i); 333 if (typeProto.getType().equals(otherChain.get(i).getType())) { 334 return typeProto; 335 } 336 } 337 338 return classPath.getUnknownClass(); 339 } 340 341 @Override 342 @Nullable 343 public FieldReference getFieldByOffset(int fieldOffset) { 344 if (getInstanceFields().size() == 0) { 345 return null; 346 } 347 return getInstanceFields().get(fieldOffset); 348 } 349 350 @Override 351 @Nullable 352 public Method getMethodByVtableIndex(int vtableIndex) { 353 List<Method> vtable = getVtable(); 354 if (vtableIndex < 0 || vtableIndex >= vtable.size()) { 355 return null; 356 } 357 358 return vtable.get(vtableIndex); 359 } 360 361 public int findMethodIndexInVtable(@Nonnull MethodReference method) { 362 List<Method> vtable = getVtable(); 363 for (int i=0; i<vtable.size(); i++) { 364 Method candidate = vtable.get(i); 365 if (MethodUtil.methodSignaturesMatch(candidate, method)) { 366 if (!classPath.shouldCheckPackagePrivateAccess() || 367 AnalyzedMethodUtil.canAccess(this, candidate, true, false, false)) { 368 return i; 369 } 370 } 371 } 372 return -1; 373 } 374 375 @Nonnull SparseArray<FieldReference> getInstanceFields() { 376 if (classPath.isArt()) { 377 return artInstanceFieldsSupplier.get(); 378 } else { 379 return dalvikInstanceFieldsSupplier.get(); 380 } 381 } 382 383 @Nonnull private final Supplier<SparseArray<FieldReference>> dalvikInstanceFieldsSupplier = 384 Suppliers.memoize(new Supplier<SparseArray<FieldReference>>() { 385 @Override public SparseArray<FieldReference> get() { 386 //This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to 387 //arrange fields, so that we end up with the same field offsets (which is needed for deodexing). 388 //See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets() 389 390 ArrayList<Field> fields = getSortedInstanceFields(getClassDef()); 391 final int fieldCount = fields.size(); 392 //the "type" for each field in fields. 0=reference,1=wide,2=other 393 byte[] fieldTypes = new byte[fields.size()]; 394 for (int i=0; i<fieldCount; i++) { 395 fieldTypes[i] = getFieldType(fields.get(i)); 396 } 397 398 //The first operation is to move all of the reference fields to the front. To do this, find the first 399 //non-reference field, then find the last reference field, swap them and repeat 400 int back = fields.size() - 1; 401 int front; 402 for (front = 0; front<fieldCount; front++) { 403 if (fieldTypes[front] != REFERENCE) { 404 while (back > front) { 405 if (fieldTypes[back] == REFERENCE) { 406 swap(fieldTypes, fields, front, back--); 407 break; 408 } 409 back--; 410 } 411 } 412 413 if (fieldTypes[front] != REFERENCE) { 414 break; 415 } 416 } 417 418 int startFieldOffset = 8; 419 String superclassType = getSuperclass(); 420 ClassProto superclass = null; 421 if (superclassType != null) { 422 superclass = (ClassProto) classPath.getClass(superclassType); 423 if (superclass != null) { 424 startFieldOffset = superclass.getNextFieldOffset(); 425 } 426 } 427 428 int fieldIndexMod; 429 if ((startFieldOffset % 8) == 0) { 430 fieldIndexMod = 0; 431 } else { 432 fieldIndexMod = 1; 433 } 434 435 //next, we need to group all the wide fields after the reference fields. But the wide fields have to be 436 //8-byte aligned. If we're on an odd field index, we need to insert a 32-bit field. If the next field 437 //is already a 32-bit field, use that. Otherwise, find the first 32-bit field from the end and swap it in. 438 //If there are no 32-bit fields, do nothing for now. We'll add padding when calculating the field offsets 439 if (front < fieldCount && (front % 2) != fieldIndexMod) { 440 if (fieldTypes[front] == WIDE) { 441 //we need to swap in a 32-bit field, so the wide fields will be correctly aligned 442 back = fieldCount - 1; 443 while (back > front) { 444 if (fieldTypes[back] == OTHER) { 445 swap(fieldTypes, fields, front++, back); 446 break; 447 } 448 back--; 449 } 450 } else { 451 //there's already a 32-bit field here that we can use 452 front++; 453 } 454 } 455 456 //do the swap thing for wide fields 457 back = fieldCount - 1; 458 for (; front<fieldCount; front++) { 459 if (fieldTypes[front] != WIDE) { 460 while (back > front) { 461 if (fieldTypes[back] == WIDE) { 462 swap(fieldTypes, fields, front, back--); 463 break; 464 } 465 back--; 466 } 467 } 468 469 if (fieldTypes[front] != WIDE) { 470 break; 471 } 472 } 473 474 SparseArray<FieldReference> superFields; 475 if (superclass != null) { 476 superFields = superclass.getInstanceFields(); 477 } else { 478 superFields = new SparseArray<FieldReference>(); 479 } 480 int superFieldCount = superFields.size(); 481 482 //now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets 483 int totalFieldCount = superFieldCount + fieldCount; 484 SparseArray<FieldReference> instanceFields = new SparseArray<FieldReference>(totalFieldCount); 485 486 int fieldOffset; 487 488 if (superclass != null && superFieldCount > 0) { 489 for (int i=0; i<superFieldCount; i++) { 490 instanceFields.append(superFields.keyAt(i), superFields.valueAt(i)); 491 } 492 493 fieldOffset = instanceFields.keyAt(superFieldCount-1); 494 495 FieldReference lastSuperField = superFields.valueAt(superFieldCount-1); 496 char fieldType = lastSuperField.getType().charAt(0); 497 if (fieldType == 'J' || fieldType == 'D') { 498 fieldOffset += 8; 499 } else { 500 fieldOffset += 4; 501 } 502 } else { 503 //the field values start at 8 bytes into the DataObject dalvik structure 504 fieldOffset = 8; 505 } 506 507 boolean gotDouble = false; 508 for (int i=0; i<fieldCount; i++) { 509 FieldReference field = fields.get(i); 510 511 //add padding to align the wide fields, if needed 512 if (fieldTypes[i] == WIDE && !gotDouble) { 513 if (!gotDouble) { 514 if (fieldOffset % 8 != 0) { 515 assert fieldOffset % 8 == 4; 516 fieldOffset += 4; 517 } 518 gotDouble = true; 519 } 520 } 521 522 instanceFields.append(fieldOffset, field); 523 if (fieldTypes[i] == WIDE) { 524 fieldOffset += 8; 525 } else { 526 fieldOffset += 4; 527 } 528 } 529 530 return instanceFields; 531 } 532 533 @Nonnull 534 private ArrayList<Field> getSortedInstanceFields(@Nonnull ClassDef classDef) { 535 ArrayList<Field> fields = Lists.newArrayList(classDef.getInstanceFields()); 536 Collections.sort(fields); 537 return fields; 538 } 539 540 private void swap(byte[] fieldTypes, List<Field> fields, int position1, int position2) { 541 byte tempType = fieldTypes[position1]; 542 fieldTypes[position1] = fieldTypes[position2]; 543 fieldTypes[position2] = tempType; 544 545 Field tempField = fields.set(position1, fields.get(position2)); 546 fields.set(position2, tempField); 547 } 548 }); 549 550 private static abstract class FieldGap implements Comparable<FieldGap> { 551 public final int offset; 552 public final int size; 553 554 public static FieldGap newFieldGap(int offset, int size, int oatVersion) { 555 if (oatVersion >= 67) { 556 return new FieldGap(offset, size) { 557 @Override public int compareTo(FieldGap o) { 558 int result = Ints.compare(o.size, size); 559 if (result != 0) { 560 return result; 561 } 562 return Ints.compare(offset, o.offset); 563 } 564 }; 565 } else { 566 return new FieldGap(offset, size) { 567 @Override public int compareTo(FieldGap o) { 568 int result = Ints.compare(size, o.size); 569 if (result != 0) { 570 return result; 571 } 572 return Ints.compare(o.offset, offset); 573 } 574 }; 575 } 576 } 577 578 private FieldGap(int offset, int size) { 579 this.offset = offset; 580 this.size = size; 581 } 582 } 583 584 @Nonnull private final Supplier<SparseArray<FieldReference>> artInstanceFieldsSupplier = 585 Suppliers.memoize(new Supplier<SparseArray<FieldReference>>() { 586 587 @Override public SparseArray<FieldReference> get() { 588 // We need to follow the same algorithm that art uses to arrange fields, so that we end up with the 589 // same field offsets, which is needed for deodexing. 590 // See LinkFields() in art/runtime/class_linker.cc 591 592 PriorityQueue<FieldGap> gaps = new PriorityQueue<FieldGap>(); 593 594 SparseArray<FieldReference> linkedFields = new SparseArray<FieldReference>(); 595 ArrayList<Field> fields = getSortedInstanceFields(getClassDef()); 596 597 int fieldOffset = 0; 598 String superclassType = getSuperclass(); 599 if (superclassType != null) { 600 // TODO: what to do if superclass doesn't exist? 601 ClassProto superclass = (ClassProto) classPath.getClass(superclassType); 602 SparseArray<FieldReference> superFields = superclass.getInstanceFields(); 603 FieldReference field = null; 604 int lastOffset = 0; 605 for (int i=0; i<superFields.size(); i++) { 606 int offset = superFields.keyAt(i); 607 field = superFields.valueAt(i); 608 linkedFields.put(offset, field); 609 lastOffset = offset; 610 } 611 if (field != null) { 612 fieldOffset = lastOffset + getFieldSize(field); 613 } 614 } 615 616 for (Field field: fields) { 617 int fieldSize = getFieldSize(field); 618 619 if (!AlignmentUtils.isAligned(fieldOffset, fieldSize)) { 620 int oldOffset = fieldOffset; 621 fieldOffset = AlignmentUtils.alignOffset(fieldOffset, fieldSize); 622 addFieldGap(oldOffset, fieldOffset, gaps); 623 } 624 625 FieldGap gap = gaps.peek(); 626 if (gap != null && gap.size >= fieldSize) { 627 gaps.poll(); 628 linkedFields.put(gap.offset, field); 629 if (gap.size > fieldSize) { 630 addFieldGap(gap.offset + fieldSize, gap.offset + gap.size, gaps); 631 } 632 } else { 633 linkedFields.append(fieldOffset, field); 634 fieldOffset += fieldSize; 635 } 636 } 637 638 return linkedFields; 639 } 640 641 private void addFieldGap(int gapStart, int gapEnd, @Nonnull PriorityQueue<FieldGap> gaps) { 642 int offset = gapStart; 643 644 while (offset < gapEnd) { 645 int remaining = gapEnd - offset; 646 647 if ((remaining >= 4) && (offset % 4 == 0)) { 648 gaps.add(FieldGap.newFieldGap(offset, 4, classPath.oatVersion)); 649 offset += 4; 650 } else if (remaining >= 2 && (offset % 2 == 0)) { 651 gaps.add(FieldGap.newFieldGap(offset, 2, classPath.oatVersion)); 652 offset += 2; 653 } else { 654 gaps.add(FieldGap.newFieldGap(offset, 1, classPath.oatVersion)); 655 offset += 1; 656 } 657 } 658 } 659 660 @Nonnull 661 private ArrayList<Field> getSortedInstanceFields(@Nonnull ClassDef classDef) { 662 ArrayList<Field> fields = Lists.newArrayList(classDef.getInstanceFields()); 663 Collections.sort(fields, new Comparator<Field>() { 664 @Override public int compare(Field field1, Field field2) { 665 int result = Ints.compare(getFieldSortOrder(field1), getFieldSortOrder(field2)); 666 if (result != 0) { 667 return result; 668 } 669 670 result = field1.getName().compareTo(field2.getName()); 671 if (result != 0) { 672 return result; 673 } 674 return field1.getType().compareTo(field2.getType()); 675 } 676 }); 677 return fields; 678 } 679 680 private int getFieldSortOrder(@Nonnull FieldReference field) { 681 // The sort order is based on type size (except references are first), and then based on the 682 // enum value of the primitive type for types of equal size. See: Primitive::Type enum 683 // in art/runtime/primitive.h 684 switch (field.getType().charAt(0)) { 685 /* reference */ 686 case '[': 687 case 'L': 688 return 0; 689 /* 64 bit */ 690 case 'J': 691 return 1; 692 case 'D': 693 return 2; 694 /* 32 bit */ 695 case 'I': 696 return 3; 697 case 'F': 698 return 4; 699 /* 16 bit */ 700 case 'C': 701 return 5; 702 case 'S': 703 return 6; 704 /* 8 bit */ 705 case 'Z': 706 return 7; 707 case 'B': 708 return 8; 709 } 710 throw new ExceptionWithContext("Invalid field type: %s", field.getType()); 711 } 712 713 private int getFieldSize(@Nonnull FieldReference field) { 714 return getTypeSize(field.getType().charAt(0)); 715 } 716 }); 717 718 private int getNextFieldOffset() { 719 SparseArray<FieldReference> instanceFields = getInstanceFields(); 720 if (instanceFields.size() == 0) { 721 return classPath.isArt() ? 0 : 8; 722 } 723 724 int lastItemIndex = instanceFields.size()-1; 725 int fieldOffset = instanceFields.keyAt(lastItemIndex); 726 FieldReference lastField = instanceFields.valueAt(lastItemIndex); 727 728 if (classPath.isArt()) { 729 return fieldOffset + getTypeSize(lastField.getType().charAt(0)); 730 } else { 731 switch (lastField.getType().charAt(0)) { 732 case 'J': 733 case 'D': 734 return fieldOffset + 8; 735 default: 736 return fieldOffset + 4; 737 } 738 } 739 } 740 741 private static int getTypeSize(char type) { 742 switch (type) { 743 case 'J': 744 case 'D': 745 return 8; 746 case '[': 747 case 'L': 748 case 'I': 749 case 'F': 750 return 4; 751 case 'C': 752 case 'S': 753 return 2; 754 case 'B': 755 case 'Z': 756 return 1; 757 } 758 throw new ExceptionWithContext("Invalid type: %s", type); 759 } 760 761 @Nonnull List<Method> getVtable() { 762 return vtableSupplier.get(); 763 } 764 765 //TODO: check the case when we have a package private method that overrides an interface method 766 @Nonnull private final Supplier<List<Method>> vtableSupplier = Suppliers.memoize(new Supplier<List<Method>>() { 767 @Override public List<Method> get() { 768 List<Method> vtable = Lists.newArrayList(); 769 770 //copy the virtual methods from the superclass 771 String superclassType; 772 try { 773 superclassType = getSuperclass(); 774 } catch (UnresolvedClassException ex) { 775 vtable.addAll(((ClassProto)classPath.getClass("Ljava/lang/Object;")).getVtable()); 776 vtableFullyResolved = false; 777 return vtable; 778 } 779 780 if (superclassType != null) { 781 ClassProto superclass = (ClassProto) classPath.getClass(superclassType); 782 vtable.addAll(superclass.getVtable()); 783 784 // if the superclass's vtable wasn't fully resolved, then we can't know where the new methods added by this 785 // class should start, so we just propagate what we can from the parent and hope for the best. 786 if (!superclass.vtableFullyResolved) { 787 vtableFullyResolved = false; 788 return vtable; 789 } 790 } 791 792 //iterate over the virtual methods in the current class, and only add them when we don't already have the 793 //method (i.e. if it was implemented by the superclass) 794 if (!isInterface()) { 795 addToVtable(getClassDef().getVirtualMethods(), vtable, true); 796 797 // assume that interface method is implemented in the current class, when adding it to vtable 798 // otherwise it looks like that method is invoked on an interface, which fails Dalvik's optimization checks 799 for (ClassDef interfaceDef: getDirectInterfaces()) { 800 List<Method> interfaceMethods = Lists.newArrayList(); 801 for (Method interfaceMethod: interfaceDef.getVirtualMethods()) { 802 ImmutableMethod method = new ImmutableMethod( 803 type, 804 interfaceMethod.getName(), 805 interfaceMethod.getParameters(), 806 interfaceMethod.getReturnType(), 807 interfaceMethod.getAccessFlags(), 808 interfaceMethod.getAnnotations(), 809 interfaceMethod.getImplementation()); 810 interfaceMethods.add(method); 811 } 812 addToVtable(interfaceMethods, vtable, false); 813 } 814 } 815 return vtable; 816 } 817 818 private void addToVtable(@Nonnull Iterable<? extends Method> localMethods, 819 @Nonnull List<Method> vtable, boolean replaceExisting) { 820 List<? extends Method> methods = Lists.newArrayList(localMethods); 821 Collections.sort(methods); 822 823 outer: for (Method virtualMethod: methods) { 824 for (int i=0; i<vtable.size(); i++) { 825 Method superMethod = vtable.get(i); 826 if (MethodUtil.methodSignaturesMatch(superMethod, virtualMethod)) { 827 if (!classPath.shouldCheckPackagePrivateAccess() || 828 AnalyzedMethodUtil.canAccess(ClassProto.this, superMethod, true, false, false)) { 829 if (replaceExisting) { 830 vtable.set(i, virtualMethod); 831 } 832 continue outer; 833 } 834 } 835 } 836 // we didn't find an equivalent method, so add it as a new entry 837 vtable.add(virtualMethod); 838 } 839 } 840 }); 841 842 private static byte getFieldType(@Nonnull FieldReference field) { 843 switch (field.getType().charAt(0)) { 844 case '[': 845 case 'L': 846 return 0; //REFERENCE 847 case 'J': 848 case 'D': 849 return 1; //WIDE 850 default: 851 return 2; //OTHER 852 } 853 } 854 } 855