• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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