ClassProto.java revision 9d8cf0d67c2d9cce3e8d4cf59f78e0475241ce23
1bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com/* 2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2013, Google Inc. 3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * All rights reserved. 4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * 5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Redistribution and use in source and binary forms, with or without 6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * modification, are permitted provided that the following conditions are 7bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com * met: 8bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com * 9b58772f86659cfe0e8d9247fcee878dddd8fdad9epoger@google.com * * Redistributions of source code must retain the above copyright 101f2f338e23789f3eef168dcbd8171a28820ba6c1robertphillips@google.com * notice, this list of conditions and the following disclaimer. 1110dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com * * Redistributions in binary form must reproduce the above 12bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com * copyright notice, this list of conditions and the following disclaimer 1310dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com * in the documentation and/or other materials provided with the 148a85d0c4938173476d037d7af0ee3b9436a1234ereed@google.com * distribution. 1510dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com * * Neither the name of Google Inc. nor the names of its 16acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com * contributors may be used to endorse or promote products derived from 17bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com * this software without specific prior written permission. 1816d1d0b39a78fa4ab4fbd6ed3296cf010ea9a61cscroggo@google.com * 1910dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 208b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2110dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2210dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2310dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2410dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 254ed0fb768409bf97b79899c3990d8c15f5e9d784reed@google.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2610dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com */ 31b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com 324ed0fb768409bf97b79899c3990d8c15f5e9d784reed@google.compackage org.jf.dexlib2.analysis; 334ed0fb768409bf97b79899c3990d8c15f5e9d784reed@google.com 344ed0fb768409bf97b79899c3990d8c15f5e9d784reed@google.comimport com.google.common.collect.Iterables; 354ed0fb768409bf97b79899c3990d8c15f5e9d784reed@google.comimport com.google.common.collect.Lists; 360c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.comimport com.google.common.collect.Maps; 370c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.comimport org.jf.dexlib2.AccessFlags; 380c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.comimport org.jf.dexlib2.analysis.util.TypeProtoUtils; 390c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.comimport org.jf.dexlib2.iface.ClassDef; 40b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.comimport org.jf.dexlib2.iface.Field; 41b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.comimport org.jf.dexlib2.iface.Method; 42b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.comimport org.jf.dexlib2.iface.reference.FieldReference; 43b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.comimport org.jf.dexlib2.iface.reference.MethodReference; 440faac1e8579088a39f38d02ff675f14d7deb608dreed@google.comimport org.jf.dexlib2.util.FieldUtil; 45b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.comimport org.jf.util.ExceptionWithContext; 46b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.comimport org.jf.util.SparseArray; 47b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com 48b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.comimport javax.annotation.Nonnull; 4916d1d0b39a78fa4ab4fbd6ed3296cf010ea9a61cscroggo@google.comimport javax.annotation.Nullable; 50b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.comimport java.util.*; 51b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com 520c00f21fee3f5cfa3aa7e5d46ff94cb8cf340451tomhudson@google.com/** 53b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com * A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields 54b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com * and their offsets. 55bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com */ 56f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.compublic class ClassProto implements TypeProto { 57f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com @Nonnull protected final ClassPath classPath; 58f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com @Nonnull protected final String type; 59f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com @Nullable protected ClassDef classDef; 60f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com @Nullable protected LinkedHashMap<String, ClassDef> interfaces; 61f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com @Nullable protected Method[] vtable; 62f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com @Nullable protected SparseArray<FieldReference> instanceFields; 638a85d0c4938173476d037d7af0ee3b9436a1234ereed@google.com protected boolean interfacesFullyResolved = true; 6459f46b81f8bdd1b524f5cc43bc27603f9604c71arobertphillips@google.com 65f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com public ClassProto(@Nonnull ClassPath classPath, @Nonnull String type) { 665af9b2032b552516c9223d9fb22185b022b13c62scroggo@google.com if (type.charAt(0) != 'L') { 67f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com throw new ExceptionWithContext("Cannot construct ClassProto for non reference type: %s", type); 68f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com } 69bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com this.classPath = classPath; 70bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com this.type = type; 714dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 724dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com 73664fab1b3454faea01cbae2f1dc2777c5afb9998scroggo@google.com @Override public String toString() { return type; } 74a2bd2d12ad9504583e9311404fcd82b40df49d30commit-bot@chromium.org @Nonnull @Override public ClassPath getClassPath() { return classPath; } 75a2bd2d12ad9504583e9311404fcd82b40df49d30commit-bot@chromium.org @Nonnull @Override public String getType() { return type; } 76664fab1b3454faea01cbae2f1dc2777c5afb9998scroggo@google.com 77664fab1b3454faea01cbae2f1dc2777c5afb9998scroggo@google.com @Nonnull 78664fab1b3454faea01cbae2f1dc2777c5afb9998scroggo@google.com public ClassDef getClassDef() { 79664fab1b3454faea01cbae2f1dc2777c5afb9998scroggo@google.com if (classDef == null) { 800c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com classDef = classPath.getClassDef(type); 814dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 824dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com return classDef; 834dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 844dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com 854dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com @Nonnull 864dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com Method[] getVtable() { 874dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com if (vtable == null) { 884dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com vtable = loadVtable(); 894dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 907ca24437c71af06fc06ab5f6f261b185882fa440scroggo@google.com return vtable; 917ca24437c71af06fc06ab5f6f261b185882fa440scroggo@google.com } 927ca24437c71af06fc06ab5f6f261b185882fa440scroggo@google.com 93d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com @Nonnull 944dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com SparseArray<FieldReference> getInstanceFields() { 954dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com if (instanceFields == null) { 964dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com instanceFields = loadFields(); 974dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 984dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com return instanceFields; 994dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1004dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com 1014dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com /** 1024dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com * Returns true if this class is an interface. 1034dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com * 1044dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com * If this class is not defined, then this will throw an UnresolvedClassException 1054dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com * 1064dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com * @return True if this class is an interface 1074dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com */ 1084dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com public boolean isInterface() { 1094dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com ClassDef classDef = getClassDef(); 1104dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com return (classDef.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0; 1114dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 112a2bd2d12ad9504583e9311404fcd82b40df49d30commit-bot@chromium.org 113a2bd2d12ad9504583e9311404fcd82b40df49d30commit-bot@chromium.org @Nonnull 1144dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com protected LinkedHashMap<String, ClassDef> getInterfaces() { 1154dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com if (interfaces != null) { 1164dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com return interfaces; 1174dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1184dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com 1194dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com interfaces = Maps.newLinkedHashMap(); 1204dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com 1214dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com try { 1224dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com for (String interfaceType: getClassDef().getInterfaces()) { 1234dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com if (!interfaces.containsKey(interfaceType)) { 1244dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com ClassDef interfaceDef; 1254dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com try { 1264dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com interfaceDef = classPath.getClassDef(interfaceType); 1274dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com interfaces.put(interfaceType, interfaceDef); 1284dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } catch (UnresolvedClassException ex) { 1294dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com interfaces.put(interfaceType, null); 1304dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com interfacesFullyResolved = false; 1314dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1324dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com 1334dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com ClassProto interfaceProto = (ClassProto) classPath.getClass(interfaceType); 1344dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com for (String superInterface: interfaceProto.getInterfaces().keySet()) { 1354dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com if (!interfaces.containsKey(superInterface)) { 1364dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com interfaces.put(superInterface, interfaceProto.getInterfaces().get(superInterface)); 1374dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1384dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1394dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com if (!interfaceProto.interfacesFullyResolved) { 1404dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com interfacesFullyResolved = false; 1414dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1424dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1434dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1444dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } catch (UnresolvedClassException ex) { 1454dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com interfacesFullyResolved = false; 1464dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1474dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com 1484dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com // now add self and super class interfaces, required for common super class lookup 1494dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com // we don't really need ClassDef's for that, so let's just use null 1504dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com 1514dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com if (isInterface() && !interfaces.containsKey(getType())) { 1524dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com interfaces.put(getType(), null); 1534dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1544dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com 15507adb6359fd137ccb633b2c64ee2287c8edfd701commit-bot@chromium.org try { 156186c0ccac25229534ec6fb84726043083304d4d1commit-bot@chromium.org String superclass = getSuperclass(); 15707adb6359fd137ccb633b2c64ee2287c8edfd701commit-bot@chromium.org if (superclass != null) { 1584dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com ClassProto superclassProto = (ClassProto) classPath.getClass(superclass); 15907adb6359fd137ccb633b2c64ee2287c8edfd701commit-bot@chromium.org for (String superclassInterface: superclassProto.getInterfaces().keySet()) { 1604dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com if (!interfaces.containsKey(superclassInterface)) { 16107adb6359fd137ccb633b2c64ee2287c8edfd701commit-bot@chromium.org interfaces.put(superclassInterface, null); 1624dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1634dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 1644dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 16559c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com } catch (UnresolvedClassException ex) { 16659c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com // TODO: not sure if this is necessary 16759c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com interfacesFullyResolved = false; 16859c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com } 16959c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com 17059c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com return interfaces; 17159c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com } 17259c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com 17359c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com @Nonnull 17459c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com protected LinkedHashMap<String, ClassDef> getInterfacesFull() { 17559c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com LinkedHashMap<String, ClassDef> interfaces = getInterfaces(); 17659c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com 17759c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com if (!interfacesFullyResolved) { 17859c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType()); 17959c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com } 18059c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com return interfaces; 18159c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com } 18259c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com 18359c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com /** 18459c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * Checks if this class implements the given interface. 18559c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * 18659c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * If the interfaces of this class cannot be fully resolved then this 18759c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * method will either return true or throw an UnresolvedClassException 18859c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * 189bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com * @param iface The interface to check for 190bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com * @return true if this class implements the given interface, otherwise false 191a8db8fe39a640bda4b85b9342c3b6b2525142afajunov@chromium.org */ 192a8db8fe39a640bda4b85b9342c3b6b2525142afajunov@chromium.org @Override 193bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com public boolean implementsInterface(@Nonnull String iface) { 194bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com if (getInterfaces().containsKey(iface)) { 19559c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com return true; 19659c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com } 19759c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com if (!interfacesFullyResolved) { 19859c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType()); 19959c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com } 20059c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com return false; 20159c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com } 20259c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com 20359c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com @Nullable @Override 20459c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com public String getSuperclass() { 20559c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com return getClassDef().getSuperclass(); 20659c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com } 20759c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com 20859c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com /** 20959c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * This is a helper method for getCommonSuperclass 21059c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * 211bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com * It checks if this class is an interface, and if so, if other implements it. 21259c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * 21359c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * If this class is undefined, we go ahead and check if it is listed in other's interfaces. If not, we throw an 21459c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * UndefinedClassException 21559c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * 21659c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * If the interfaces of other cannot be fully resolved, we check the interfaces that can be resolved. If not found, 21759c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * we throw an UndefinedClassException 21859c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * 21959c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * @param other The class to check the interfaces of 22059c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * @return true if this class is an interface (or is undefined) other implements this class 22159c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com * 222bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com */ 223bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com private boolean checkInterface(@Nonnull ClassProto other) { 22477eec248cbd5a0c2f5f8595e62e3bff5ea363f17junov@chromium.org boolean isResolved = true; 2252e14ba8ceb41c68042ff133fecf0561a2c22efcajunov@chromium.org boolean isInterface = true; 22677eec248cbd5a0c2f5f8595e62e3bff5ea363f17junov@chromium.org try { 22715011ee5e4068ab6523e432e435473a822ee7d80scroggo@google.com isInterface = isInterface(); 228d5d158b325f05902ac845f2f7c8c65ffe6074257scroggo@google.com } catch (UnresolvedClassException ex) { 22915011ee5e4068ab6523e432e435473a822ee7d80scroggo@google.com isResolved = false; 23015011ee5e4068ab6523e432e435473a822ee7d80scroggo@google.com // if we don't know if this class is an interface or not, 231bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com // we can still try to call other.implementsInterface(this) 232fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org } 2333b45cd53f0c4c9b810b6383f529a72ecfa4e6e7fscroggo@google.com if (isInterface) { 2343b45cd53f0c4c9b810b6383f529a72ecfa4e6e7fscroggo@google.com try { 235bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com if (other.implementsInterface(getType())) { 2363b45cd53f0c4c9b810b6383f529a72ecfa4e6e7fscroggo@google.com return true; 2374ed0fb768409bf97b79899c3990d8c15f5e9d784reed@google.com } 2387ce564cccb246ec56427085872b2e1458fe74bd1bsalomon@google.com } catch (UnresolvedClassException ex) { 2394ed0fb768409bf97b79899c3990d8c15f5e9d784reed@google.com // There are 2 possibilities here, depending on whether we were able to resolve this class. 2407ce564cccb246ec56427085872b2e1458fe74bd1bsalomon@google.com // 1. If this class is resolved, then we know it is an interface class. The other class either 241bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com // isn't defined, or its interfaces couldn't be fully resolved. 2423b45cd53f0c4c9b810b6383f529a72ecfa4e6e7fscroggo@google.com // In this case, we throw an UnresolvedClassException 2437112173c3c4cd1b1e7da8cdf971d71f01dd91299reed@google.com // 2. If this class is not resolved, we had tried to call implementsInterface anyway. We don't 2447475811143e190e172bf83d13c4bdba85704b604skia.committer@gmail.com // know for sure if this class is an interface or not. We return false, and let processing 245eed779d866e1e239bfb9ebc6a225b7345a41adf9commit-bot@chromium.org // continue in getCommonSuperclass 246bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com if (isResolved) { 2473b45cd53f0c4c9b810b6383f529a72ecfa4e6e7fscroggo@google.com throw ex; 2485a2e879ef8a3720ac3f06fbc13dcdaeb179f30c3scroggo@google.com } 2495a2e879ef8a3720ac3f06fbc13dcdaeb179f30c3scroggo@google.com } 250bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 2513b45cd53f0c4c9b810b6383f529a72ecfa4e6e7fscroggo@google.com return false; 2523b45cd53f0c4c9b810b6383f529a72ecfa4e6e7fscroggo@google.com } 253bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com 254bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com @Override @Nonnull 255bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com public TypeProto getCommonSuperclass(@Nonnull TypeProto other) { 256bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com // use the other type's more specific implementation 2573b45cd53f0c4c9b810b6383f529a72ecfa4e6e7fscroggo@google.com if (!(other instanceof ClassProto)) { 2583b45cd53f0c4c9b810b6383f529a72ecfa4e6e7fscroggo@google.com return other.getCommonSuperclass(this); 2590a4805e33f8ddb445a2fd061462e715e1707f049robertphillips@google.com } 2600a4805e33f8ddb445a2fd061462e715e1707f049robertphillips@google.com 2610a4805e33f8ddb445a2fd061462e715e1707f049robertphillips@google.com if (this == other || getType().equals(other.getType())) { 262bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com return this; 2633e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com } 2643e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com 2653e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com if (this.getType().equals("Ljava/lang/Object;")) { 2663e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com return this; 2673e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com } 268ed9806f5c972513d4141c9d1b5a04ab78b3af4cbcommit-bot@chromium.org 269ed9806f5c972513d4141c9d1b5a04ab78b3af4cbcommit-bot@chromium.org if (other.getType().equals("Ljava/lang/Object;")) { 270e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org return other; 271e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org } 272e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org 273e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org boolean gotException = false; 27444c48d062f7996b5b46917e1b312a32ad101f326commit-bot@chromium.org try { 27544c48d062f7996b5b46917e1b312a32ad101f326commit-bot@chromium.org if (checkInterface((ClassProto)other)) { 27644c48d062f7996b5b46917e1b312a32ad101f326commit-bot@chromium.org return this; 277ed9806f5c972513d4141c9d1b5a04ab78b3af4cbcommit-bot@chromium.org } 278e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com } catch (UnresolvedClassException ex) { 279e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com gotException = true; 280e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com } 281e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com 282e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com try { 283e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com if (((ClassProto)other).checkInterface(this)) { 284e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com return other; 285e0d9ce890e67d02727ac2811bb456ddb64f827d4reed@google.com } 286ed9806f5c972513d4141c9d1b5a04ab78b3af4cbcommit-bot@chromium.org } catch (UnresolvedClassException ex) { 2878f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com gotException = true; 2888f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com } 2898f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com if (gotException) { 2908f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com return classPath.getUnknownClass(); 2918f90a892c5130d4d26b5588e1ff151d01a40688arobertphillips@google.com } 292bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com 293d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org List<TypeProto> thisChain = Lists.<TypeProto>newArrayList(this); 294d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org Iterables.addAll(thisChain, TypeProtoUtils.getSuperclassChain(this)); 295d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org 296d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org List<TypeProto> otherChain = Lists.newArrayList(other); 297fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org Iterables.addAll(otherChain, TypeProtoUtils.getSuperclassChain(other)); 298fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org 299fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org // reverse them, so that the first entry is either Ljava/lang/Object; or Ujava/lang/Object; 3000c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com thisChain = Lists.reverse(thisChain); 3010c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com otherChain = Lists.reverse(otherChain); 302d9d2967ce1cca98c7992ac034a6b6e0707c56051scroggo@google.com 303acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com for (int i=Math.min(thisChain.size(), otherChain.size())-1; i>=0; i--) { 3040c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com TypeProto typeProto = thisChain.get(i); 3050c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com if (typeProto.getType().equals(otherChain.get(i).getType())) { 3060c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com return typeProto; 3070c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com } 3083e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com } 309bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com 3100c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com return classPath.getUnknownClass(); 311f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com } 312f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com 313f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com @Override 314acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com @Nullable 315bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com public FieldReference getFieldByOffset(int fieldOffset) { 316bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com if (getInstanceFields().size() == 0) { 31774b461961607fa57a150a9282c410ef0cab38764vandebo@chromium.org return null; 318acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 319bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com return getInstanceFields().get(fieldOffset); 320bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 321acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com 322acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com @Override 323acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com @Nullable 324acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com public MethodReference getMethodByVtableIndex(int vtableIndex) { 325acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com if (vtableIndex < 0 || vtableIndex >= getVtable().length) { 3264469938e92d779dff05e745559e67907bbf21e78reed@google.com return null; 32716d1d0b39a78fa4ab4fbd6ed3296cf010ea9a61cscroggo@google.com } 32816d1d0b39a78fa4ab4fbd6ed3296cf010ea9a61cscroggo@google.com return getVtable()[vtableIndex]; 32916d1d0b39a78fa4ab4fbd6ed3296cf010ea9a61cscroggo@google.com } 33016d1d0b39a78fa4ab4fbd6ed3296cf010ea9a61cscroggo@google.com 331acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com @Nonnull 332acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com private SparseArray<FieldReference> loadFields() { 333b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com //This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to 3340c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com //arrange fields, so that we end up with the same field offsets (which is needed for deodexing). 3350c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com //See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets() 3360c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com 3370c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com final byte REFERENCE = 0; 33859c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com final byte WIDE = 1; 33959c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com final byte OTHER = 2; 34059c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com 34159c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com ArrayList<Field> loadedFields = getInstanceFields(getClassDef()); 34259c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com Field[] fields = new Field[loadedFields.size()]; 343b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com //the "type" for each field in fields. 0=reference,1=wide,2=other 344b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com byte[] fieldTypes = new byte[fields.length]; 3453e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com for (int i=0;i<fields.length;i++) { 3463e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com fields[i] = loadedFields.get(i); 3473e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com fieldTypes[i] = getFieldType(fields[i].getType()); 3483e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com } 34958be682c77c76d9a0caf23337f9b357798179b6cscroggo@google.com 35031891584fef10c88b39f6bf19ac5cde0a862b8c4reed@google.com //The first operation is to move all of the reference fields to the front. To do this, find the first 35131891584fef10c88b39f6bf19ac5cde0a862b8c4reed@google.com //non-reference field, then find the last reference field, swap them and repeat 352bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com int back = fields.length - 1; 353acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com int front; 354acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com for (front = 0; front<fields.length; front++) { 355acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com if (fieldTypes[front] != REFERENCE) { 356acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com while (back > front) { 357acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com if (fieldTypes[back] == REFERENCE) { 358acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com swap(fieldTypes, fields, front, back--); 359acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com break; 360acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 361acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com back--; 362bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 363bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 364bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com 3650c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com if (fieldTypes[front] != REFERENCE) { 3660c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com break; 3670c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com } 3680c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com } 3690c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com 3700c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com int startFieldOffset = 8; 3710c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com String superclassType = getSuperclass(); 3720c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com ClassProto superclass = null; 3730c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com if (superclassType != null) { 3740c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com superclass = (ClassProto) classPath.getClass(superclassType); 3750c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com if (superclass != null) { 3763e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com startFieldOffset = superclass.getNextFieldOffset(); 377565254bc9343d0befdfbbb97a3dc6d44c6e18658scroggo@google.com } 3788b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org } 3793e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com 38074b7ffda687c66d46ac3cfa4f2baedd4c62e3fbescroggo@google.com int fieldIndexMod; 3813e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com if ((startFieldOffset % 8) == 0) { 3828b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org fieldIndexMod = 0; 3833e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com } else { 3843e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com fieldIndexMod = 1; 3853e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com } 3863e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com 3873e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com //next, we need to group all the wide fields after the reference fields. But the wide fields have to be 3884dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com //8-byte aligned. If we're on an odd field index, we need to insert a 32-bit field. If the next field 3893e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com //is already a 32-bit field, use that. Otherwise, find the first 32-bit field from the end and swap it in. 390d3ba5cc85e24896f980ed1ba6e3f4495973baeb3scroggo@google.com //If there are no 32-bit fields, do nothing for now. We'll add padding when calculating the field offsets 391d3ba5cc85e24896f980ed1ba6e3f4495973baeb3scroggo@google.com if (front < fields.length && (front % 2) != fieldIndexMod) { 392b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com if (fieldTypes[front] == WIDE) { 393d3ba5cc85e24896f980ed1ba6e3f4495973baeb3scroggo@google.com //we need to swap in a 32-bit field, so the wide fields will be correctly aligned 394b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com back = fields.length - 1; 395d5d158b325f05902ac845f2f7c8c65ffe6074257scroggo@google.com while (back > front) { 396b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com if (fieldTypes[back] == OTHER) { 397b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com swap(fieldTypes, fields, front++, back); 398b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com break; 399b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com } 400d9d2967ce1cca98c7992ac034a6b6e0707c56051scroggo@google.com back--; 4014dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 402664fab1b3454faea01cbae2f1dc2777c5afb9998scroggo@google.com } else { 403664fab1b3454faea01cbae2f1dc2777c5afb9998scroggo@google.com //there's already a 32-bit field here that we can use 404d9d2967ce1cca98c7992ac034a6b6e0707c56051scroggo@google.com front++; 4054dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 4060c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com } 4070c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com 4080c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com //do the swap thing for wide fields 4090c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com back = fields.length - 1; 4100c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com for (; front<fields.length; front++) { 4110c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com if (fieldTypes[front] != WIDE) { 4120c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com while (back > front) { 4130c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com if (fieldTypes[back] == WIDE) { 4140c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com swap(fieldTypes, fields, front, back--); 415b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com break; 4164dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 4174dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com back--; 418d3ba5cc85e24896f980ed1ba6e3f4495973baeb3scroggo@google.com } 4194dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com } 420b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com 421b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com if (fieldTypes[front] != WIDE) { 422bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com break; 423bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 424acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 4254dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com 4264dffc596aa9fabd3104e66bc1f9957e8de4cb65dscroggo@google.com int superFieldCount = 0; 427bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com if (superclass != null) { 428acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com superFieldCount = superclass.instanceFields.size(); 429d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com } 430a8db8fe39a640bda4b85b9342c3b6b2525142afajunov@chromium.org 431403f8d7a052269583175e945689824838e5e0ef4commit-bot@chromium.org //now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets 432403f8d7a052269583175e945689824838e5e0ef4commit-bot@chromium.org int totalFieldCount = superFieldCount + fields.length; 433403f8d7a052269583175e945689824838e5e0ef4commit-bot@chromium.org SparseArray<FieldReference> instanceFields = new SparseArray<FieldReference>(totalFieldCount); 434403f8d7a052269583175e945689824838e5e0ef4commit-bot@chromium.org 435403f8d7a052269583175e945689824838e5e0ef4commit-bot@chromium.org int fieldOffset; 436403f8d7a052269583175e945689824838e5e0ef4commit-bot@chromium.org 437403f8d7a052269583175e945689824838e5e0ef4commit-bot@chromium.org if (superclass != null && superFieldCount > 0) { 438acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com for (int i=0; i<superFieldCount; i++) { 439bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com instanceFields.append(superclass.instanceFields.keyAt(i), superclass.instanceFields.valueAt(i)); 440acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 4415a2e879ef8a3720ac3f06fbc13dcdaeb179f30c3scroggo@google.com 442fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org fieldOffset = instanceFields.keyAt(superFieldCount-1); 443b55d118e32062b1ddd88e7fcf8fa86303f887d8freed@google.com 444acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com FieldReference lastSuperField = superclass.instanceFields.valueAt(superFieldCount-1); 445565254bc9343d0befdfbbb97a3dc6d44c6e18658scroggo@google.com char fieldType = lastSuperField.getType().charAt(0); 446565254bc9343d0befdfbbb97a3dc6d44c6e18658scroggo@google.com if (fieldType == 'J' || fieldType == 'D') { 447565254bc9343d0befdfbbb97a3dc6d44c6e18658scroggo@google.com fieldOffset += 8; 448565254bc9343d0befdfbbb97a3dc6d44c6e18658scroggo@google.com } else { 449d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com fieldOffset += 4; 45010dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com } 45159c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com } else { 45259c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com //the field values start at 8 bytes into the DataObject dalvik structure 45310dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com fieldOffset = 8; 454d9d2967ce1cca98c7992ac034a6b6e0707c56051scroggo@google.com } 4553e26bd0c357d849ff40b092decd7a5c46ec2ada4scroggo@google.com 45610dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com boolean gotDouble = false; 457d9d2967ce1cca98c7992ac034a6b6e0707c56051scroggo@google.com for (int i=0; i<fields.length; i++) { 458d9d2967ce1cca98c7992ac034a6b6e0707c56051scroggo@google.com FieldReference field = fields[i]; 45910dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com 46010dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com //add padding to align the wide fields, if needed 461d9d2967ce1cca98c7992ac034a6b6e0707c56051scroggo@google.com if (fieldTypes[i] == WIDE && !gotDouble) { 46210dccde54a769b8d472bccf8c1993034b93ef58dscroggo@google.com if (!gotDouble) { 463bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com if (fieldOffset % 8 != 0) { 464bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com assert fieldOffset % 8 == 4; 465bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com fieldOffset += 4; 46659c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com } 4670c3e5fe728ce4b8606819ee919a4b82f4d9efc85scroggo@google.com gotDouble = true; 468d9d2967ce1cca98c7992ac034a6b6e0707c56051scroggo@google.com } 469bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 470bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com 471acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com instanceFields.append(fieldOffset, field); 472acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com if (fieldTypes[i] == WIDE) { 473acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com fieldOffset += 8; 474acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } else { 475acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com fieldOffset += 4; 476acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 4774469938e92d779dff05e745559e67907bbf21e78reed@google.com } 4785a2e879ef8a3720ac3f06fbc13dcdaeb179f30c3scroggo@google.com 4795a2e879ef8a3720ac3f06fbc13dcdaeb179f30c3scroggo@google.com return instanceFields; 4805a2e879ef8a3720ac3f06fbc13dcdaeb179f30c3scroggo@google.com } 4815a2e879ef8a3720ac3f06fbc13dcdaeb179f30c3scroggo@google.com 4825a2e879ef8a3720ac3f06fbc13dcdaeb179f30c3scroggo@google.com private ArrayList<Field> getInstanceFields(ClassDef classDef) { 483acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com ArrayList<Field> instanceFields = Lists.newArrayList(); 48459c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com for (Field field: classDef.getFields()) { 48559c3ab637b7cb23c1afc098a2967518788a671eascroggo@google.com if (!FieldUtil.isStatic(field)) { 486acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com instanceFields.add(field); 487acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 488460a23e6fd6b71c80d5515300c6b989cd3383029scroggo@google.com } 489acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com return instanceFields; 490acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 491acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com 492acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com private byte getFieldType(String fieldType) { 493acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com switch (fieldType.charAt(0)) { 494acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com case '[': 495f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com case 'L': 496f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com return 0; //REFERENCE 497f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com case 'J': 498f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com case 'D': 499f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com return 1; //WIDE 500f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com default: 501f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com return 2; //OTHER 502f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com } 503bb6793bd7751f7a4e48c942567cd6c5270661a2freed@google.com } 504f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com 505f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com private void swap(byte[] fieldTypes, FieldReference[] fields, int position1, int position2) { 506f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com byte tempType = fieldTypes[position1]; 507f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com fieldTypes[position1] = fieldTypes[position2]; 508f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com fieldTypes[position2] = tempType; 509f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com 510f5842f773b7e8612a52784b3c35c7455e67cb90areed@google.com FieldReference tempField = fields[position1]; 511bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com fields[position1] = fields[position2]; 512bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com fields[position2] = tempField; 513acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 514acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com 515acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com private int getNextFieldOffset() { 516e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org SparseArray<FieldReference> instanceFields = getInstanceFields(); 517acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com if (instanceFields.size() == 0) { 518acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com return 8; 519acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 520acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com 521e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org int lastItemIndex = instanceFields.size()-1; 522e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org int fieldOffset = instanceFields.keyAt(lastItemIndex); 523bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com FieldReference lastField = instanceFields.valueAt(lastItemIndex); 524bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com 525e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org switch (lastField.getType().charAt(0)) { 526e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org case 'J': 527acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com case 'D': 528acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com return fieldOffset + 8; 529bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com default: 53074b461961607fa57a150a9282c410ef0cab38764vandebo@chromium.org return fieldOffset + 4; 531bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 532bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 533acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com 534bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com //TODO: check the case when we have a package private method that overrides an interface method 535bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com @Nonnull 536bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com private Method[] loadVtable() { 53731891584fef10c88b39f6bf19ac5cde0a862b8c4reed@google.com //TODO: it might be useful to keep track of which class's implementation is used for each virtual method. In other words, associate the implementing class type with each vtable entry 538bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com List<Method> virtualMethodList = Lists.newLinkedList(); 539bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com 540acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com //copy the virtual methods from the superclass 541acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com String superclassType = getSuperclass(); 542acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com if (superclassType != null) { 543acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com ClassProto superclass = (ClassProto) classPath.getClass(superclassType); 544acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com for (int i=0; i<superclass.getVtable().length; i++) { 545bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com virtualMethodList.add(superclass.getVtable()[i]); 54674b461961607fa57a150a9282c410ef0cab38764vandebo@chromium.org } 547fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org } 548fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org 549fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org //iterate over the virtual methods in the current class, and only add them when we don't already have the 550e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org //method (i.e. if it was implemented by the superclass) 551e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org if (!isInterface()) { 552e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org addToVtable(getClassDef().getVirtualMethods(), virtualMethodList); 553e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org 554bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com for (ClassDef interfaceDef: getInterfacesFull().values()) { 555bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com if (interfaceDef != null) { 556e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org addToVtable(interfaceDef.getVirtualMethods(), virtualMethodList); 557acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 558acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 559acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com } 560acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com 561fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org Method[] vtable = new Method[virtualMethodList.size()]; 562e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org for (int i=0; i<virtualMethodList.size(); i++) { 563fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org vtable[i] = virtualMethodList.get(i); 564fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org } 565e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org 566e54a23fcfa42b2fc9d320650de72bcb2d9566b2dcommit-bot@chromium.org return vtable; 567fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org } 568fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org 569fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org private void addToVtable(@Nonnull Iterable<? extends Method> localMethods, @Nonnull List<Method> vtable) { 570fbe9c8fbb8e74ad55694f8bb7fa5463708e94a6djunov@chromium.org List<? extends Method> methods = Lists.newArrayList(localMethods); 571bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com Collections.sort(methods); 572bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com 573d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org for (Method virtualMethod: methods) { 574d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org boolean found = false; 575d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org for (int i=0; i<vtable.size(); i++) { 576d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org Method superMethod = vtable.get(i); 577d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org if (methodSignaturesMatch(superMethod, virtualMethod)) { 578bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com if (classPath.getApi() < 17 || canAccess(superMethod)) { 579bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com found = true; 580bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com vtable.set(i, virtualMethod); 581d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org break; 582d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org } 583d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org } 584d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org } 585d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org if (!found) { 586bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com vtable.add(virtualMethod); 587bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 588bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 589d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org } 590d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org 591d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org private boolean methodSignaturesMatch(Method a, Method b) { 592d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org return (a.getName().equals(b.getName()) 593bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com && a.getReturnType().equals(b.getReturnType()) 594bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com && a.getParameters().equals(b.getParameters())); 595bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 59644c48d062f7996b5b46917e1b312a32ad101f326commit-bot@chromium.org 597bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com private boolean canAccess(Method virtualMethod) { 598acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com if (!methodIsPackagePrivate(virtualMethod.getAccessFlags())) { 599d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org return true; 600d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org } 601d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org 602d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org String otherPackage = getPackage(virtualMethod.getDefiningClass()); 603d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org String ourPackage = getPackage(getClassDef().getType()); 604d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org return otherPackage.equals(ourPackage); 605d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org } 606d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org 607d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org private String getPackage(String classType) { 608d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org int lastSlash = classType.lastIndexOf('/'); 609acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com if (lastSlash < 0) { 610bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com return ""; 611d9ea09e1f29b303e6fa36079e99729d2951925b9commit-bot@chromium.org } 61244c48d062f7996b5b46917e1b312a32ad101f326commit-bot@chromium.org return classType.substring(1, lastSlash); 613bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com } 614bb6992a9d6e21b3f28068765de0a41c6f2508dfdreed@google.com 61544c48d062f7996b5b46917e1b312a32ad101f326commit-bot@chromium.org private static boolean methodIsPackagePrivate(int accessFlags) { 616acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com return (accessFlags & (AccessFlags.PRIVATE.getValue() | 61794e75ee46a569cbcdf61fb7f04ee3a69d3ca0896djsollen@google.com AccessFlags.PROTECTED.getValue() | 618acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com AccessFlags.PUBLIC.getValue())) == 0; 6192b2ede3e713065e1bac461787b0aafb03eaf871fdjsollen@google.com } 620acd471f47ccfb97cf2f2f00dc01cd1fd45bc1ef2reed@google.com} 62144c48d062f7996b5b46917e1b312a32ad101f326commit-bot@chromium.org