View Javadoc

1   // TypeCheck.java, created Mon Feb  5 23:23:21 2001 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   package joeq.Runtime;
5   
6   import java.util.Collection;
7   import java.util.Stack;
8   import joeq.Class.PrimordialClassLoader;
9   import joeq.Class.jq_Array;
10  import joeq.Class.jq_Class;
11  import joeq.Class.jq_ClassFileConstants;
12  import joeq.Class.jq_Primitive;
13  import joeq.Class.jq_Reference;
14  import joeq.Class.jq_StaticMethod;
15  import joeq.Class.jq_Type;
16  import joeq.Compiler.CompilationConstants;
17  import joeq.Memory.Address;
18  import joeq.Memory.HeapAddress;
19  import jwutil.util.Assert;
20  
21  /***
22   * Implements Java type checking.
23   * 
24   * @author  John Whaley <jwhaley@alum.mit.edu>
25   * @version $Id: TypeCheck.java 1931 2004-09-22 22:17:47Z joewhaley $
26   */
27  public abstract class TypeCheck implements jq_ClassFileConstants, CompilationConstants {
28      
29      public static final boolean VerifyAssertions = true;
30      
31      /*** Performs a checkcast operation. */
32      public static Object checkcast(Object k, jq_Type t) {
33          if (k != null) {
34              jq_Type t2 = jq_Reference.getTypeOf(k);
35              if (!TypeCheck.isAssignable(t2, t)) 
36                  throw new ClassCastException(t2+" cannot be cast into "+t);
37          }
38          return k;
39      }
40      
41      /*** Performs an instanceof operation. */
42      public static boolean instance_of(Object k, jq_Type t) {
43          if (k == null)
44              return false;
45          jq_Type t2 = jq_Reference.getTypeOf(k);
46          if (!TypeCheck.isAssignable(t2, t)) 
47              return false;
48          return true;
49      }
50      
51      /*** Performs an arrayStoreCheck operation. */
52      public static void arrayStoreCheck(HeapAddress value, Object[] arrayref) 
53      throws ArrayStoreException {
54          if (value.isNull()) return;
55          jq_Array a = (jq_Array)jq_Reference.getTypeOf(arrayref);
56          jq_Type t2 = a.getElementType();
57          if (t2.isAddressType()) return;
58          Object v = value.asObject();
59          jq_Type t = jq_Reference.getTypeOf(v);
60          if (!isAssignable(t, t2))
61              throw new ArrayStoreException(t+" into array "+a);
62      }
63      
64      /***
65       * Returns true if "T = S;" would be legal. (T is same or supertype of S)
66       * From the algorithm in vm spec under "checkcast"
67       * 
68       * @param S subtype
69       * @param T type
70       * @return true iff "T = S;" would be legal.
71       */
72      public static boolean isAssignable(jq_Type S, jq_Type T) {
73          if (S == T)
74              return true;
75          S.prepare(); T.prepare();
76          return S.isSubtypeOf(T);
77      }
78      
79      /***
80       * Uses (old) graph traversal algorithm for type check.
81       * 
82       * @param S subtype
83       * @param T type
84       * @return true iff "T = S;" would be legal.
85       */
86      public static boolean isAssignable_graph(jq_Type S, jq_Type T) {
87          if (S == T)
88              return true;
89          jq_Type s2 = S, t2 = T;
90          if (false) {
91              if (t2 == Address._class)
92                  return s2.isAddressType();
93              if (t2.isAddressType() || s2.isAddressType())
94                  return false;
95          }
96          while (t2.isArrayType()) {
97              if (!s2.isArrayType()) {
98                  return false;
99              }
100             s2 = ((jq_Array)s2).getElementType(); s2.prepare();
101             t2 = ((jq_Array)t2).getElementType(); t2.prepare();
102         }
103         if (s2.isPrimitiveType() || t2.isPrimitiveType()) {
104             return false;
105         }
106         // t2 is a class
107         boolean is_t2_loaded = t2.isLoaded();
108         t2.load();
109         if (s2.isArrayType()) {
110             ((jq_Array)s2).chkState(STATE_PREPARED);
111             if (((jq_Class)t2).isInterface()) {
112                 //s2.prepare();
113                 return ((jq_Array)s2).implementsInterface((jq_Class)t2);
114             }
115             return t2 == PrimordialClassLoader.getJavaLangObject();
116         }
117         // both are classes
118         ((jq_Class)s2).chkState(STATE_PREPARED);
119         if (((jq_Class)t2).isInterface()) {
120             /*
121             if (((jq_Class)s2).isInterface()) {
122                 if (!is_t2_loaded) return false;
123                 return isSuperclassOf((jq_Class)t2, (jq_Class)s2);
124             }
125             */
126             return ((jq_Class)s2).implementsInterface((jq_Class)t2);
127         }
128         // t2 is not an interface
129         if (!is_t2_loaded) return false;
130         return isSuperclassOf((jq_Class)t2, (jq_Class)s2, true) == YES;
131     }
132     
133     /*** Returns YES iff t1 is a superclass of t2. */
134     public static byte isSuperclassOf(jq_Class t1, jq_Class t2, boolean loadClasses) {
135         // doesn't do equality test.
136         for (;;) {
137             if (!t2.isLoaded() && !loadClasses) return MAYBE;
138             t2.load();
139             t2 = t2.getSuperclass();
140             if (t2 == null) return NO;
141             if (t1 == t2) return YES;
142         }
143     }
144     
145     /*** Returns YES iff "T = S;" would be legal. (T is same or supertype of S) */
146     public static byte isAssignable_noload(jq_Type S, jq_Type T) {
147         if (S == jq_Reference.jq_NullType.NULL_TYPE) {
148             if (T.isReferenceType()) return YES;
149             else return NO;
150         }
151         if (T == jq_Reference.jq_NullType.NULL_TYPE) return NO;
152         if (T == S) return YES;
153         if (T.isIntLike() && S.isIntLike()) return YES;
154         if (T == PrimordialClassLoader.getJavaLangObject() && S.isReferenceType()) return YES;
155         if (!T.isPrepared() || !S.isPrepared()) return MAYBE;
156         if (T.isArrayType()) {
157             jq_Type elemType = ((jq_Array) T).getInnermostElementType();
158             if (!elemType.isPrepared()) return MAYBE;
159         }
160         if (S.isArrayType()) {
161             jq_Type elemType = ((jq_Array) S).getInnermostElementType();
162             if (!elemType.isPrepared()) return MAYBE;
163         }
164         if (S.isSubtypeOf(T)) return YES;
165         else return NO;
166     }
167     
168     /*** Returns YES iff T declares one of the given interfaces. */
169     public static byte declaresInterface(jq_Class T, Collection interfaces, boolean loadClasses) {
170         if (VerifyAssertions)
171             Assert._assert(T.isLoaded());
172         jq_Class[] klass_interfaces = T.getDeclaredInterfaces();
173         for (int i=0; i<klass_interfaces.length; ++i) {
174             if (!loadClasses && !klass_interfaces[i].isLoaded()) return MAYBE;
175             if (interfaces.contains(klass_interfaces[i])) return YES;
176         }
177         return NO;
178     }
179     
180     /*** Returns YES iff T implements the given interface. */
181     public static byte implementsInterface_noload(jq_Class klass, jq_Class inter) {
182         byte res = NO; jq_Class k = klass;
183         if (!klass.isLoaded()) return MAYBE;
184         for (;;) {
185             if (k.getDeclaredInterface(inter.getDesc()) == inter) return YES;
186             k = k.getSuperclass();
187             if (k == null) break;
188             if (!k.isLoaded()) {
189                 res = MAYBE; break;
190             }
191         }
192         jq_Class[] interfaces = klass.getDeclaredInterfaces();
193         for (int i=0; i<interfaces.length; ++i) {
194             jq_Class k2 = interfaces[i];
195             byte res2 = implementsInterface_noload(k2, inter);
196             if (res2 == YES) return YES;
197             if (res2 == MAYBE) res = MAYBE;
198         }
199         return res;
200     }
201     
202     public static jq_Type findCommonSuperclass(jq_Type t1, jq_Type t2, boolean load) {
203         if (t1 == t2) return t1;
204         if (t1.isPrimitiveType() && t2.isPrimitiveType()) {
205             if (t1.isIntLike() && t2.isIntLike()) {
206                 if (t1 == jq_Primitive.INT || t2 == jq_Primitive.INT) return jq_Primitive.INT;
207                 if (t1 == jq_Primitive.CHAR) {
208                     if (t2 == jq_Primitive.SHORT) return jq_Primitive.INT;
209                     return jq_Primitive.CHAR;
210                 }
211                 if (t2 == jq_Primitive.CHAR) return jq_Primitive.CHAR;
212                 if (t1 == jq_Primitive.SHORT) {
213                     if (t2 == jq_Primitive.CHAR) return jq_Primitive.INT;
214                     return jq_Primitive.SHORT;
215                 }
216                 if (t2 == jq_Primitive.SHORT) return jq_Primitive.SHORT;
217                 if (t1 == jq_Primitive.BYTE || t2 == jq_Primitive.BYTE) return jq_Primitive.BYTE;
218                 if (t1 == jq_Primitive.BOOLEAN || t2 == jq_Primitive.BOOLEAN) return jq_Primitive.BOOLEAN;
219             }
220             return null;
221         }
222         if (!t1.isReferenceType() || !t2.isReferenceType()) return null;
223         if (t1 == jq_Reference.jq_NullType.NULL_TYPE) return t2;
224         if (t2 == jq_Reference.jq_NullType.NULL_TYPE) return t1;
225         int dim = 0;
226         while (t1.isArrayType() && t2.isArrayType()) {
227             ++dim;
228             t1 = ((jq_Array)t1).getElementType();
229             t2 = ((jq_Array)t2).getElementType();
230         }
231         if (t1.isPrimitiveType() || t2.isPrimitiveType()) {
232             jq_Reference result = PrimordialClassLoader.getJavaLangObject();
233             --dim;
234             while (--dim >= 0) result = result.getArrayTypeForElementType();
235             return result;
236         }
237         if (!t1.isClassType() || !t2.isClassType()) {
238             jq_Reference result = PrimordialClassLoader.getJavaLangObject();
239             while (--dim >= 0) result = result.getArrayTypeForElementType();
240             return result;
241         }
242         jq_Class c1 = (jq_Class)t1;
243         jq_Class c2 = (jq_Class)t2;
244         Stack s1 = new Stack();
245         do {
246             if (!c1.isLoaded()) {
247                 if (load) c1.load();
248                 else c1 = PrimordialClassLoader.getJavaLangObject();
249             }
250             s1.push(c1);
251             if (c1.isLoaded()) c1 = c1.getSuperclass();
252             else break;
253         } while (c1 != null);
254         Stack s2 = new Stack();
255         do {
256             if (!c2.isLoaded()) {
257                 if (load) c2.load();
258                 else c2 = PrimordialClassLoader.getJavaLangObject();
259             }
260             s2.push(c2);
261             if (c2.isLoaded()) c2 = c2.getSuperclass();
262             else break;
263         } while (c2 != null);
264         jq_Class result = PrimordialClassLoader.getJavaLangObject();
265         while (!s1.empty() && !s2.empty()) {
266             jq_Class temp = (jq_Class)s1.pop();
267             if (temp == s2.pop()) result = temp;
268             else break;
269         }
270         jq_Reference result2 = result;
271         while (--dim >= 0) result2 = result2.getArrayTypeForElementType();
272         return result2;
273     }
274     
275     public static final jq_StaticMethod _checkcast;
276     public static final jq_StaticMethod _instance_of;
277     public static final jq_StaticMethod _arrayStoreCheck;
278     static {
279         jq_Class k = (jq_Class)PrimordialClassLoader.loader.getOrCreateBSType("Ljoeq/Runtime/TypeCheck;");
280         _checkcast = k.getOrCreateStaticMethod("checkcast", "(Ljava/lang/Object;Ljoeq/Class/jq_Type;)Ljava/lang/Object;");
281         _instance_of = k.getOrCreateStaticMethod("instance_of", "(Ljava/lang/Object;Ljoeq/Class/jq_Type;)Z");
282         _arrayStoreCheck = k.getOrCreateStaticMethod("arrayStoreCheck", "(Ljoeq/Memory/HeapAddress;[Ljava/lang/Object;)V");
283     }
284 }