ClassProto.java revision 8f383501c16660dbce78d6bdbd2e3c6985f9483f
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 32package org.jf.dexlib2.analysis; 33 34import com.google.common.collect.Lists; 35import org.jf.dexlib2.iface.ClassDef; 36import org.jf.util.ExceptionWithContext; 37 38import javax.annotation.Nonnull; 39import javax.annotation.Nullable; 40import java.util.Iterator; 41import java.util.List; 42import java.util.NoSuchElementException; 43 44/** 45 * A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields 46 * and their offsets. 47 */ 48public class ClassProto { 49 @Nonnull public final ClassPath classPath; 50 @Nonnull public final String type; 51 @Nullable private ClassDef classDef; 52 53 public ClassProto(@Nonnull ClassPath classPath, @Nonnull String type) { 54 this.classPath = classPath; 55 this.type = type; 56 } 57 58 @Nonnull 59 public ClassDef getClassDef() { 60 if (classDef == null) { 61 classDef = classPath.getClassDef(type); 62 } 63 return classDef; 64 } 65 66 @Nonnull 67 public String getType() { 68 return type; 69 } 70 71 public boolean isInterface() { 72 // TODO: implement 73 return false; 74 } 75 76 public boolean implementsInterface(String iface) { 77 // TODO: implement 78 return false; 79 } 80 81 /** 82 * Get the chain of superclasses of this class. The first element will be the immediate superclass followed by 83 * it's superclass, etc. up to java.lang.Object. 84 * 85 * Returns an empty iterable if called on java.lang.Object. 86 * 87 * @return An iterable containing the superclasses of this class. 88 * @throws UnresolvedClassException if any class in the chain can't be resolved 89 */ 90 @Nonnull 91 public Iterable<String> getSuperclassChain() { 92 final ClassDef topClassDef = this.getClassDef(); 93 94 return new Iterable<String>() { 95 private ClassDef classDef = topClassDef; 96 97 @Override public Iterator<String> iterator() { 98 return new Iterator<String>() { 99 @Override public boolean hasNext() { 100 return classDef == null || classDef.getSuperclass() == null; 101 } 102 103 @Override public String next() { 104 if (classDef == null) { 105 throw new NoSuchElementException(); 106 } 107 108 String next = classDef.getSuperclass(); 109 if (next == null) { 110 throw new NoSuchElementException(); 111 } 112 113 classDef = classPath.getClassDef(next); 114 return next; 115 } 116 117 @Override public void remove() { 118 throw new UnsupportedOperationException(); 119 } 120 }; 121 } 122 }; 123 } 124 125 @Nonnull public ClassProto getCommonSuperclass(@Nonnull ClassProto other) { 126 if (this == other || getType().equals(other.getType())) { 127 return this; 128 } 129 130 if (isInterface()) { 131 if (other.implementsInterface(getType())) { 132 return this; 133 } 134 return classPath.getClass("Ljava/lang/Object;"); 135 } 136 137 if (other.isInterface()) { 138 if (implementsInterface(other.getType())) { 139 return other; 140 } 141 return classPath.getClass("Ljava/lang/Object;"); 142 } 143 144 boolean thisResolved = true; 145 boolean otherResolved = true; 146 List<String> thisChain = Lists.newArrayList(getType()); 147 List<String> otherChain = Lists.newArrayList(other.getType()); 148 149 // grab as much of the superclass chain as we can for both types, 150 // and keep track of whether we were able to get all of it 151 try { 152 for (String type: getSuperclassChain()) { 153 thisChain.add(type); 154 } 155 } catch (UnresolvedClassException ex) { 156 thisResolved = false; 157 } 158 159 try { 160 for (String type: other.getSuperclassChain()) { 161 otherChain.add(type); 162 } 163 } catch (UnresolvedClassException ex) { 164 otherResolved = false; 165 } 166 167 // if both were resolved, then we start looking backwards from the end of the shorter chain, until 168 // we find a pair of entries in the chains that match 169 if (thisResolved && otherResolved) { 170 for (int i=Math.min(thisChain.size(), otherChain.size()); i>=0; i--) { 171 String type = thisChain.get(i); 172 if (type.equals(otherChain.get(i))) { 173 return classPath.getClass(type); 174 } 175 } 176 // "This should never happen" 177 throw new ExceptionWithContext("Wasn't able to find a common superclass for %s and %s", this.getType(), 178 other.getType()); 179 } 180 181 // we weren't able to fully resolve both classes. Let's see if we can find a common superclass in what we 182 // were able to resolve 183 for (String thisType: thisChain) { 184 for (String otherType: otherChain) { 185 if (thisType.equals(otherType)) { 186 return classPath.getClass(thisType); 187 } 188 } 189 } 190 191 // Nope. We'll throw an UnresolvedClassException. The caller can catch the exception and use java.lang.Object 192 // as the superclass, if it is appropriate to do so 193 if (!thisResolved) { 194 if (!otherResolved) { 195 throw new UnresolvedClassException( 196 "Could not fully resolve %s or %s while getting their common superclass", 197 getType(), other.getType()); 198 } else { 199 throw new UnresolvedClassException( 200 "Could not fully resolve %s while getting common superclass with %s", 201 getType(), other.getType()); 202 } 203 } 204 throw new UnresolvedClassException( 205 "Could not fully resolve %s while getting common superclass with %s", other.getType(), getType()); 206 } 207} 208