1
2
3
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
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
113 return ((jq_Array)s2).implementsInterface((jq_Class)t2);
114 }
115 return t2 == PrimordialClassLoader.getJavaLangObject();
116 }
117
118 ((jq_Class)s2).chkState(STATE_PREPARED);
119 if (((jq_Class)t2).isInterface()) {
120
121
122
123
124
125
126 return ((jq_Class)s2).implementsInterface((jq_Class)t2);
127 }
128
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
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 }