1 // ASM: a very small and fast Java bytecode manipulation framework 2 // Copyright (c) 2000-2011 INRIA, France Telecom 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 7 // are met: 8 // 1. Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // 2. Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // 3. Neither the name of the copyright holders nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 // THE POSSIBILITY OF SUCH DAMAGE. 28 package org.objectweb.asm.tree.analysis; 29 30 import java.util.List; 31 import org.objectweb.asm.Opcodes; 32 import org.objectweb.asm.Type; 33 34 /** 35 * An extended {@link BasicVerifier} that performs more precise verifications. This verifier 36 * computes exact class types, instead of using a single "object reference" type (as done in {@link 37 * BasicVerifier}). 38 * 39 * @author Eric Bruneton 40 * @author Bing Ran 41 */ 42 public class SimpleVerifier extends BasicVerifier { 43 44 /** The type of the class that is verified. */ 45 private final Type currentClass; 46 47 /** The type of the super class of the class that is verified. */ 48 private final Type currentSuperClass; 49 50 /** The types of the interfaces directly implemented by the class that is verified. */ 51 private final List<Type> currentClassInterfaces; 52 53 /** Whether the class that is verified is an interface. */ 54 private final boolean isInterface; 55 56 /** The loader to use to load the referenced classes. */ 57 private ClassLoader loader = getClass().getClassLoader(); 58 59 /** 60 * Constructs a new {@link SimpleVerifier}. <i>Subclasses must not use this constructor</i>. 61 * Instead, they must use the {@link #SimpleVerifier(int, Type, Type, List, boolean)} version. 62 */ SimpleVerifier()63 public SimpleVerifier() { 64 this(null, null, false); 65 } 66 67 /** 68 * Constructs a new {@link SimpleVerifier} to verify a specific class. This class will not be 69 * loaded into the JVM since it may be incorrect. <i>Subclasses must not use this constructor</i>. 70 * Instead, they must use the {@link #SimpleVerifier(int, Type, Type, List, boolean)} version. 71 * 72 * @param currentClass the type of the class to be verified. 73 * @param currentSuperClass the type of the super class of the class to be verified. 74 * @param isInterface whether the class to be verifier is an interface. 75 */ SimpleVerifier( final Type currentClass, final Type currentSuperClass, final boolean isInterface)76 public SimpleVerifier( 77 final Type currentClass, final Type currentSuperClass, final boolean isInterface) { 78 this(currentClass, currentSuperClass, null, isInterface); 79 } 80 81 /** 82 * Constructs a new {@link SimpleVerifier} to verify a specific class. This class will not be 83 * loaded into the JVM since it may be incorrect. <i>Subclasses must not use this constructor</i>. 84 * Instead, they must use the {@link #SimpleVerifier(int, Type, Type, List, boolean)} version. 85 * 86 * @param currentClass the type of the class to be verified. 87 * @param currentSuperClass the type of the super class of the class to be verified. 88 * @param currentClassInterfaces the types of the interfaces directly implemented by the class to 89 * be verified. 90 * @param isInterface whether the class to be verifier is an interface. 91 */ SimpleVerifier( final Type currentClass, final Type currentSuperClass, final List<Type> currentClassInterfaces, final boolean isInterface)92 public SimpleVerifier( 93 final Type currentClass, 94 final Type currentSuperClass, 95 final List<Type> currentClassInterfaces, 96 final boolean isInterface) { 97 this( 98 /* latest api = */ ASM9, 99 currentClass, 100 currentSuperClass, 101 currentClassInterfaces, 102 isInterface); 103 if (getClass() != SimpleVerifier.class) { 104 throw new IllegalStateException(); 105 } 106 } 107 108 /** 109 * Constructs a new {@link SimpleVerifier} to verify a specific class. This class will not be 110 * loaded into the JVM since it may be incorrect. 111 * 112 * @param api the ASM API version supported by this verifier. Must be one of the {@code 113 * ASM}<i>x</i> values in {@link Opcodes}. 114 * @param currentClass the type of the class to be verified. 115 * @param currentSuperClass the type of the super class of the class to be verified. 116 * @param currentClassInterfaces the types of the interfaces directly implemented by the class to 117 * be verified. 118 * @param isInterface whether the class to be verifier is an interface. 119 */ SimpleVerifier( final int api, final Type currentClass, final Type currentSuperClass, final List<Type> currentClassInterfaces, final boolean isInterface)120 protected SimpleVerifier( 121 final int api, 122 final Type currentClass, 123 final Type currentSuperClass, 124 final List<Type> currentClassInterfaces, 125 final boolean isInterface) { 126 super(api); 127 this.currentClass = currentClass; 128 this.currentSuperClass = currentSuperClass; 129 this.currentClassInterfaces = currentClassInterfaces; 130 this.isInterface = isInterface; 131 } 132 133 /** 134 * Sets the <code>ClassLoader</code> to be used in {@link #getClass}. 135 * 136 * @param loader the <code>ClassLoader</code> to use. 137 */ setClassLoader(final ClassLoader loader)138 public void setClassLoader(final ClassLoader loader) { 139 this.loader = loader; 140 } 141 142 @Override newValue(final Type type)143 public BasicValue newValue(final Type type) { 144 if (type == null) { 145 return BasicValue.UNINITIALIZED_VALUE; 146 } 147 148 boolean isArray = type.getSort() == Type.ARRAY; 149 if (isArray) { 150 switch (type.getElementType().getSort()) { 151 case Type.BOOLEAN: 152 case Type.CHAR: 153 case Type.BYTE: 154 case Type.SHORT: 155 return new BasicValue(type); 156 default: 157 break; 158 } 159 } 160 161 BasicValue value = super.newValue(type); 162 if (BasicValue.REFERENCE_VALUE.equals(value)) { 163 if (isArray) { 164 value = newValue(type.getElementType()); 165 StringBuilder descriptor = new StringBuilder(); 166 for (int i = 0; i < type.getDimensions(); ++i) { 167 descriptor.append('['); 168 } 169 descriptor.append(value.getType().getDescriptor()); 170 value = new BasicValue(Type.getType(descriptor.toString())); 171 } else { 172 value = new BasicValue(type); 173 } 174 } 175 return value; 176 } 177 178 @Override isArrayValue(final BasicValue value)179 protected boolean isArrayValue(final BasicValue value) { 180 Type type = value.getType(); 181 return type != null && (type.getSort() == Type.ARRAY || type.equals(NULL_TYPE)); 182 } 183 184 @Override getElementValue(final BasicValue objectArrayValue)185 protected BasicValue getElementValue(final BasicValue objectArrayValue) throws AnalyzerException { 186 Type arrayType = objectArrayValue.getType(); 187 if (arrayType != null) { 188 if (arrayType.getSort() == Type.ARRAY) { 189 return newValue(Type.getType(arrayType.getDescriptor().substring(1))); 190 } else if (arrayType.equals(NULL_TYPE)) { 191 return objectArrayValue; 192 } 193 } 194 throw new AssertionError(); 195 } 196 197 @Override isSubTypeOf(final BasicValue value, final BasicValue expected)198 protected boolean isSubTypeOf(final BasicValue value, final BasicValue expected) { 199 Type expectedType = expected.getType(); 200 Type type = value.getType(); 201 switch (expectedType.getSort()) { 202 case Type.INT: 203 case Type.FLOAT: 204 case Type.LONG: 205 case Type.DOUBLE: 206 return type.equals(expectedType); 207 case Type.ARRAY: 208 case Type.OBJECT: 209 if (type.equals(NULL_TYPE)) { 210 return true; 211 } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { 212 if (isAssignableFrom(expectedType, type)) { 213 return true; 214 } else if (getClass(expectedType).isInterface()) { 215 // The merge of class or interface types can only yield class types (because it is not 216 // possible in general to find an unambiguous common super interface, due to multiple 217 // inheritance). Because of this limitation, we need to relax the subtyping check here 218 // if 'value' is an interface. 219 return Object.class.isAssignableFrom(getClass(type)); 220 } else { 221 return false; 222 } 223 } else { 224 return false; 225 } 226 default: 227 throw new AssertionError(); 228 } 229 } 230 231 @Override merge(final BasicValue value1, final BasicValue value2)232 public BasicValue merge(final BasicValue value1, final BasicValue value2) { 233 if (!value1.equals(value2)) { 234 Type type1 = value1.getType(); 235 Type type2 = value2.getType(); 236 if (type1 != null 237 && (type1.getSort() == Type.OBJECT || type1.getSort() == Type.ARRAY) 238 && type2 != null 239 && (type2.getSort() == Type.OBJECT || type2.getSort() == Type.ARRAY)) { 240 if (type1.equals(NULL_TYPE)) { 241 return value2; 242 } 243 if (type2.equals(NULL_TYPE)) { 244 return value1; 245 } 246 if (isAssignableFrom(type1, type2)) { 247 return value1; 248 } 249 if (isAssignableFrom(type2, type1)) { 250 return value2; 251 } 252 int numDimensions = 0; 253 if (type1.getSort() == Type.ARRAY 254 && type2.getSort() == Type.ARRAY 255 && type1.getDimensions() == type2.getDimensions() 256 && type1.getElementType().getSort() == Type.OBJECT 257 && type2.getElementType().getSort() == Type.OBJECT) { 258 numDimensions = type1.getDimensions(); 259 type1 = type1.getElementType(); 260 type2 = type2.getElementType(); 261 } 262 while (true) { 263 if (type1 == null || isInterface(type1)) { 264 return newArrayValue(Type.getObjectType("java/lang/Object"), numDimensions); 265 } 266 type1 = getSuperClass(type1); 267 if (isAssignableFrom(type1, type2)) { 268 return newArrayValue(type1, numDimensions); 269 } 270 } 271 } 272 return BasicValue.UNINITIALIZED_VALUE; 273 } 274 return value1; 275 } 276 newArrayValue(final Type type, final int dimensions)277 private BasicValue newArrayValue(final Type type, final int dimensions) { 278 if (dimensions == 0) { 279 return newValue(type); 280 } else { 281 StringBuilder descriptor = new StringBuilder(); 282 for (int i = 0; i < dimensions; ++i) { 283 descriptor.append('['); 284 } 285 descriptor.append(type.getDescriptor()); 286 return newValue(Type.getType(descriptor.toString())); 287 } 288 } 289 290 /** 291 * Returns whether the given type corresponds to the type of an interface. The default 292 * implementation of this method loads the class and uses the reflection API to return its result 293 * (unless the given type corresponds to the class being verified). 294 * 295 * @param type a type. 296 * @return whether 'type' corresponds to an interface. 297 */ isInterface(final Type type)298 protected boolean isInterface(final Type type) { 299 if (currentClass != null && currentClass.equals(type)) { 300 return isInterface; 301 } 302 return getClass(type).isInterface(); 303 } 304 305 /** 306 * Returns the type corresponding to the super class of the given type. The default implementation 307 * of this method loads the class and uses the reflection API to return its result (unless the 308 * given type corresponds to the class being verified). 309 * 310 * @param type a type. 311 * @return the type corresponding to the super class of 'type'. 312 */ getSuperClass(final Type type)313 protected Type getSuperClass(final Type type) { 314 if (currentClass != null && currentClass.equals(type)) { 315 return currentSuperClass; 316 } 317 Class<?> superClass = getClass(type).getSuperclass(); 318 return superClass == null ? null : Type.getType(superClass); 319 } 320 321 /** 322 * Returns whether the class corresponding to the first argument is either the same as, or is a 323 * superclass or superinterface of the class corresponding to the second argument. The default 324 * implementation of this method loads the classes and uses the reflection API to return its 325 * result (unless the result can be computed from the class being verified, and the types of its 326 * super classes and implemented interfaces). 327 * 328 * @param type1 a type. 329 * @param type2 another type. 330 * @return whether the class corresponding to 'type1' is either the same as, or is a superclass or 331 * superinterface of the class corresponding to 'type2'. 332 */ isAssignableFrom(final Type type1, final Type type2)333 protected boolean isAssignableFrom(final Type type1, final Type type2) { 334 if (type1.equals(type2)) { 335 return true; 336 } 337 if (currentClass != null && currentClass.equals(type1)) { 338 if (getSuperClass(type2) == null) { 339 return false; 340 } else { 341 if (isInterface) { 342 return type2.getSort() == Type.OBJECT || type2.getSort() == Type.ARRAY; 343 } 344 return isAssignableFrom(type1, getSuperClass(type2)); 345 } 346 } 347 if (currentClass != null && currentClass.equals(type2)) { 348 if (isAssignableFrom(type1, currentSuperClass)) { 349 return true; 350 } 351 if (currentClassInterfaces != null) { 352 for (Type currentClassInterface : currentClassInterfaces) { 353 if (isAssignableFrom(type1, currentClassInterface)) { 354 return true; 355 } 356 } 357 } 358 return false; 359 } 360 return getClass(type1).isAssignableFrom(getClass(type2)); 361 } 362 363 /** 364 * Loads the class corresponding to the given type. The class is loaded with the class loader 365 * specified with {@link #setClassLoader}, or with the class loader of this class if no class 366 * loader was specified. 367 * 368 * @param type a type. 369 * @return the class corresponding to 'type'. 370 */ getClass(final Type type)371 protected Class<?> getClass(final Type type) { 372 try { 373 if (type.getSort() == Type.ARRAY) { 374 return Class.forName(type.getDescriptor().replace('/', '.'), false, loader); 375 } 376 return Class.forName(type.getClassName(), false, loader); 377 } catch (ClassNotFoundException e) { 378 throw new TypeNotPresentException(e.toString(), e); 379 } 380 } 381 } 382