]>
Commit | Line | Data |
---|---|---|
ee9dd372 TT |
1 | // natClass.cc - Implementation of java.lang.Class native methods. |
2 | ||
2ba5f774 | 3 | /* Copyright (C) 1998, 1999, 2000 Free Software Foundation |
ee9dd372 TT |
4 | |
5 | This file is part of libgcj. | |
6 | ||
7 | This software is copyrighted work licensed under the terms of the | |
8 | Libgcj License. Please consult the file "LIBGCJ_LICENSE" for | |
9 | details. */ | |
10 | ||
11 | #include <config.h> | |
12 | ||
ddf0fc6c | 13 | #include <limits.h> |
ee9dd372 TT |
14 | #include <string.h> |
15 | ||
16 | #pragma implementation "Class.h" | |
17 | ||
27e934d8 | 18 | #include <gcj/cni.h> |
ee9dd372 | 19 | #include <jvm.h> |
ddf0fc6c BM |
20 | #include <java-threads.h> |
21 | ||
ee9dd372 TT |
22 | #include <java/lang/Class.h> |
23 | #include <java/lang/ClassLoader.h> | |
24 | #include <java/lang/String.h> | |
25 | #include <java/lang/reflect/Modifier.h> | |
26 | #include <java/lang/reflect/Member.h> | |
27 | #include <java/lang/reflect/Method.h> | |
28 | #include <java/lang/reflect/Field.h> | |
29 | #include <java/lang/reflect/Constructor.h> | |
30 | #include <java/lang/AbstractMethodError.h> | |
ddf0fc6c BM |
31 | #include <java/lang/ArrayStoreException.h> |
32 | #include <java/lang/ClassCastException.h> | |
ee9dd372 | 33 | #include <java/lang/ClassNotFoundException.h> |
b099f07d | 34 | #include <java/lang/ExceptionInInitializerError.h> |
ee9dd372 TT |
35 | #include <java/lang/IllegalAccessException.h> |
36 | #include <java/lang/IllegalAccessError.h> | |
37 | #include <java/lang/IncompatibleClassChangeError.h> | |
38 | #include <java/lang/InstantiationException.h> | |
39 | #include <java/lang/NoClassDefFoundError.h> | |
40 | #include <java/lang/NoSuchFieldException.h> | |
ddf0fc6c | 41 | #include <java/lang/NoSuchMethodError.h> |
ee9dd372 TT |
42 | #include <java/lang/NoSuchMethodException.h> |
43 | #include <java/lang/Thread.h> | |
44 | #include <java/lang/NullPointerException.h> | |
45 | #include <java/lang/System.h> | |
46 | #include <java/lang/SecurityManager.h> | |
0f918fea | 47 | #include <java/lang/StringBuffer.h> |
d6ab24c5 | 48 | #include <gcj/method.h> |
ee9dd372 | 49 | |
58eb6e7c AG |
50 | #include <java-cpool.h> |
51 | ||
ee9dd372 TT |
52 | \f |
53 | ||
54 | #define CloneableClass _CL_Q34java4lang9Cloneable | |
55 | extern java::lang::Class CloneableClass; | |
56 | #define ObjectClass _CL_Q34java4lang6Object | |
57 | extern java::lang::Class ObjectClass; | |
58 | #define ErrorClass _CL_Q34java4lang5Error | |
59 | extern java::lang::Class ErrorClass; | |
60 | #define ClassClass _CL_Q34java4lang5Class | |
61 | extern java::lang::Class ClassClass; | |
62 | #define MethodClass _CL_Q44java4lang7reflect6Method | |
63 | extern java::lang::Class MethodClass; | |
64 | #define FieldClass _CL_Q44java4lang7reflect5Field | |
65 | extern java::lang::Class FieldClass; | |
0f918fea TT |
66 | #define ConstructorClass _CL_Q44java4lang7reflect11Constructor |
67 | extern java::lang::Class ConstructorClass; | |
ee9dd372 TT |
68 | |
69 | // Some constants we use to look up the class initializer. | |
70 | static _Jv_Utf8Const *void_signature = _Jv_makeUtf8Const ("()V", 3); | |
71 | static _Jv_Utf8Const *clinit_name = _Jv_makeUtf8Const ("<clinit>", 8); | |
72 | static _Jv_Utf8Const *init_name = _Jv_makeUtf8Const ("<init>", 6); | |
6f2b93eb | 73 | static _Jv_Utf8Const *finit_name = _Jv_makeUtf8Const ("$finit$", 7); |
ee9dd372 | 74 | |
ee9dd372 TT |
75 | \f |
76 | ||
77 | jclass | |
78 | java::lang::Class::forName (jstring className) | |
79 | { | |
80 | if (! className) | |
81 | JvThrow (new java::lang::NullPointerException); | |
82 | ||
83 | #if 0 | |
84 | // FIXME: should check syntax of CLASSNAME and throw | |
85 | // IllegalArgumentException on failure. | |
86 | ||
87 | // FIXME: should use class loader from calling method. | |
88 | jclass klass = _Jv_FindClass (className, NULL); | |
89 | #else | |
90 | jsize length = _Jv_GetStringUTFLength (className); | |
91 | char buffer[length]; | |
92 | _Jv_GetStringUTFRegion (className, 0, length, buffer); | |
93 | ||
94 | // FIXME: should check syntax of CLASSNAME and throw | |
95 | // IllegalArgumentException on failure. | |
96 | _Jv_Utf8Const *name = _Jv_makeUtf8Const (buffer, length); | |
97 | ||
98 | // FIXME: should use class loader from calling method. | |
99 | jclass klass = (buffer[0] == '[' | |
100 | ? _Jv_FindClassFromSignature (name->data, NULL) | |
101 | : _Jv_FindClass (name, NULL)); | |
102 | #endif | |
103 | if (! klass) | |
104 | JvThrow (new java::lang::ClassNotFoundException (className)); | |
58eb6e7c | 105 | |
ee9dd372 TT |
106 | return klass; |
107 | } | |
108 | ||
109 | java::lang::reflect::Constructor * | |
0f918fea | 110 | java::lang::Class::getConstructor (JArray<jclass> *param_types) |
ee9dd372 | 111 | { |
95c6cc0a | 112 | jstring partial_sig = getSignature (param_types, true); |
0f918fea TT |
113 | jint hash = partial_sig->hashCode (); |
114 | ||
115 | int i = isPrimitive () ? 0 : method_count; | |
116 | while (--i >= 0) | |
117 | { | |
118 | // FIXME: access checks. | |
119 | if (_Jv_equalUtf8Consts (methods[i].name, init_name) | |
120 | && _Jv_equal (methods[i].signature, partial_sig, hash)) | |
121 | { | |
122 | // Found it. For getConstructor, the constructor must be | |
123 | // public. | |
124 | using namespace java::lang::reflect; | |
95c6cc0a | 125 | if (! Modifier::isPublic(methods[i].accflags)) |
0f918fea TT |
126 | break; |
127 | Constructor *cons = new Constructor (); | |
128 | cons->offset = (char *) (&methods[i]) - (char *) methods; | |
129 | cons->declaringClass = this; | |
130 | return cons; | |
131 | } | |
132 | } | |
133 | JvThrow (new java::lang::NoSuchMethodException); | |
ee9dd372 TT |
134 | } |
135 | ||
136 | JArray<java::lang::reflect::Constructor *> * | |
0f918fea | 137 | java::lang::Class::_getConstructors (jboolean declared) |
ee9dd372 | 138 | { |
0f918fea TT |
139 | // FIXME: this method needs access checks. |
140 | ||
141 | int numConstructors = 0; | |
142 | int max = isPrimitive () ? 0 : method_count; | |
143 | int i; | |
144 | for (i = max; --i >= 0; ) | |
145 | { | |
146 | _Jv_Method *method = &methods[i]; | |
147 | if (method->name == NULL | |
55ff3de7 | 148 | || ! _Jv_equalUtf8Consts (method->name, init_name)) |
0f918fea | 149 | continue; |
95c6cc0a | 150 | if (! declared |
0f918fea TT |
151 | && ! java::lang::reflect::Modifier::isPublic(method->accflags)) |
152 | continue; | |
153 | numConstructors++; | |
154 | } | |
155 | JArray<java::lang::reflect::Constructor *> *result | |
156 | = (JArray<java::lang::reflect::Constructor *> *) | |
157 | JvNewObjectArray (numConstructors, &ConstructorClass, NULL); | |
158 | java::lang::reflect::Constructor** cptr = elements (result); | |
159 | for (i = 0; i < max; i++) | |
160 | { | |
161 | _Jv_Method *method = &methods[i]; | |
162 | if (method->name == NULL | |
55ff3de7 | 163 | || ! _Jv_equalUtf8Consts (method->name, init_name)) |
0f918fea | 164 | continue; |
95c6cc0a | 165 | if (! declared |
0f918fea TT |
166 | && ! java::lang::reflect::Modifier::isPublic(method->accflags)) |
167 | continue; | |
168 | java::lang::reflect::Constructor *cons | |
169 | = new java::lang::reflect::Constructor (); | |
170 | cons->offset = (char *) method - (char *) methods; | |
171 | cons->declaringClass = this; | |
172 | *cptr++ = cons; | |
173 | } | |
174 | return result; | |
ee9dd372 TT |
175 | } |
176 | ||
177 | java::lang::reflect::Constructor * | |
0f918fea | 178 | java::lang::Class::getDeclaredConstructor (JArray<jclass> *param_types) |
ee9dd372 | 179 | { |
95c6cc0a | 180 | jstring partial_sig = getSignature (param_types, true); |
0f918fea | 181 | jint hash = partial_sig->hashCode (); |
ee9dd372 | 182 | |
0f918fea TT |
183 | int i = isPrimitive () ? 0 : method_count; |
184 | while (--i >= 0) | |
185 | { | |
186 | // FIXME: access checks. | |
187 | if (_Jv_equalUtf8Consts (methods[i].name, init_name) | |
188 | && _Jv_equal (methods[i].signature, partial_sig, hash)) | |
189 | { | |
190 | // Found it. | |
191 | using namespace java::lang::reflect; | |
192 | Constructor *cons = new Constructor (); | |
193 | cons->offset = (char *) (&methods[i]) - (char *) methods; | |
194 | cons->declaringClass = this; | |
195 | return cons; | |
196 | } | |
197 | } | |
198 | JvThrow (new java::lang::NoSuchMethodException); | |
ee9dd372 TT |
199 | } |
200 | ||
201 | java::lang::reflect::Field * | |
202 | java::lang::Class::getField (jstring name, jint hash) | |
203 | { | |
204 | java::lang::reflect::Field* rfield; | |
205 | for (int i = 0; i < field_count; i++) | |
206 | { | |
207 | _Jv_Field *field = &fields[i]; | |
208 | if (! _Jv_equal (field->name, name, hash)) | |
209 | continue; | |
210 | if (! (field->getModifiers() & java::lang::reflect::Modifier::PUBLIC)) | |
211 | continue; | |
212 | rfield = new java::lang::reflect::Field (); | |
213 | rfield->offset = (char*) field - (char*) fields; | |
214 | rfield->declaringClass = this; | |
215 | rfield->name = name; | |
216 | return rfield; | |
217 | } | |
218 | jclass superclass = getSuperclass(); | |
219 | if (superclass == NULL) | |
220 | return NULL; | |
221 | rfield = superclass->getField(name, hash); | |
222 | for (int i = 0; i < interface_count && rfield == NULL; ++i) | |
223 | rfield = interfaces[i]->getField (name, hash); | |
224 | return rfield; | |
225 | } | |
226 | ||
227 | java::lang::reflect::Field * | |
228 | java::lang::Class::getDeclaredField (jstring name) | |
229 | { | |
230 | java::lang::SecurityManager *s = java::lang::System::getSecurityManager(); | |
231 | if (s != NULL) | |
232 | s->checkMemberAccess (this, java::lang::reflect::Member::DECLARED); | |
233 | int hash = name->hashCode(); | |
234 | for (int i = 0; i < field_count; i++) | |
235 | { | |
236 | _Jv_Field *field = &fields[i]; | |
237 | if (! _Jv_equal (field->name, name, hash)) | |
238 | continue; | |
239 | java::lang::reflect::Field* rfield = new java::lang::reflect::Field (); | |
240 | rfield->offset = (char*) field - (char*) fields; | |
241 | rfield->declaringClass = this; | |
242 | rfield->name = name; | |
243 | return rfield; | |
244 | } | |
245 | JvThrow (new java::lang::NoSuchFieldException (name)); | |
246 | } | |
247 | ||
248 | JArray<java::lang::reflect::Field *> * | |
249 | java::lang::Class::getDeclaredFields (void) | |
250 | { | |
251 | java::lang::SecurityManager *s = java::lang::System::getSecurityManager(); | |
252 | if (s != NULL) | |
253 | s->checkMemberAccess (this, java::lang::reflect::Member::DECLARED); | |
254 | JArray<java::lang::reflect::Field *> *result | |
255 | = (JArray<java::lang::reflect::Field *> *) | |
256 | JvNewObjectArray (field_count, &FieldClass, NULL); | |
257 | java::lang::reflect::Field** fptr = elements (result); | |
258 | for (int i = 0; i < field_count; i++) | |
259 | { | |
260 | _Jv_Field *field = &fields[i]; | |
261 | java::lang::reflect::Field* rfield = new java::lang::reflect::Field (); | |
262 | rfield->offset = (char*) field - (char*) fields; | |
263 | rfield->declaringClass = this; | |
264 | *fptr++ = rfield; | |
265 | } | |
266 | return result; | |
267 | } | |
268 | ||
0f918fea TT |
269 | void |
270 | java::lang::Class::getSignature (java::lang::StringBuffer *buffer) | |
ee9dd372 | 271 | { |
0f918fea TT |
272 | if (isPrimitive()) |
273 | buffer->append((jchar) method_count); | |
274 | else | |
275 | { | |
276 | jstring name = getName(); | |
277 | if (name->charAt(0) != '[') | |
278 | buffer->append((jchar) 'L'); | |
279 | buffer->append(name); | |
280 | if (name->charAt(0) != '[') | |
281 | buffer->append((jchar) ';'); | |
282 | } | |
283 | } | |
284 | ||
285 | // This doesn't have to be native. It is an implementation detail | |
286 | // only called from the C++ code, though, so maybe this is clearer. | |
287 | jstring | |
95c6cc0a TT |
288 | java::lang::Class::getSignature (JArray<jclass> *param_types, |
289 | jboolean is_constructor) | |
0f918fea TT |
290 | { |
291 | java::lang::StringBuffer *buf = new java::lang::StringBuffer (); | |
292 | buf->append((jchar) '('); | |
293 | jclass *v = elements (param_types); | |
294 | for (int i = 0; i < param_types->length; ++i) | |
295 | v[i]->getSignature(buf); | |
296 | buf->append((jchar) ')'); | |
95c6cc0a TT |
297 | if (is_constructor) |
298 | buf->append((jchar) 'V'); | |
0f918fea TT |
299 | return buf->toString(); |
300 | } | |
301 | ||
302 | java::lang::reflect::Method * | |
303 | java::lang::Class::getDeclaredMethod (jstring name, | |
304 | JArray<jclass> *param_types) | |
305 | { | |
95c6cc0a | 306 | jstring partial_sig = getSignature (param_types, false); |
0f918fea TT |
307 | jint p_len = partial_sig->length(); |
308 | _Jv_Utf8Const *utf_name = _Jv_makeUtf8Const (name); | |
309 | int i = isPrimitive () ? 0 : method_count; | |
310 | while (--i >= 0) | |
311 | { | |
312 | // FIXME: access checks. | |
313 | if (_Jv_equalUtf8Consts (methods[i].name, utf_name) | |
314 | && _Jv_equaln (methods[i].signature, partial_sig, p_len)) | |
315 | { | |
316 | // Found it. | |
317 | using namespace java::lang::reflect; | |
318 | Method *rmethod = new Method (); | |
319 | rmethod->offset = (char*) (&methods[i]) - (char*) methods; | |
320 | rmethod->declaringClass = this; | |
6f2b93eb | 321 | return rmethod; |
0f918fea TT |
322 | } |
323 | } | |
324 | JvThrow (new java::lang::NoSuchMethodException); | |
ee9dd372 TT |
325 | } |
326 | ||
327 | JArray<java::lang::reflect::Method *> * | |
328 | java::lang::Class::getDeclaredMethods (void) | |
329 | { | |
330 | int numMethods = 0; | |
0f918fea | 331 | int max = isPrimitive () ? 0 : method_count; |
ee9dd372 | 332 | int i; |
0f918fea | 333 | for (i = max; --i >= 0; ) |
ee9dd372 TT |
334 | { |
335 | _Jv_Method *method = &methods[i]; | |
336 | if (method->name == NULL | |
337 | || _Jv_equalUtf8Consts (method->name, clinit_name) | |
6f2b93eb TT |
338 | || _Jv_equalUtf8Consts (method->name, init_name) |
339 | || _Jv_equalUtf8Consts (method->name, finit_name)) | |
ee9dd372 TT |
340 | continue; |
341 | numMethods++; | |
342 | } | |
343 | JArray<java::lang::reflect::Method *> *result | |
344 | = (JArray<java::lang::reflect::Method *> *) | |
345 | JvNewObjectArray (numMethods, &MethodClass, NULL); | |
346 | java::lang::reflect::Method** mptr = elements (result); | |
0f918fea | 347 | for (i = 0; i < max; i++) |
ee9dd372 TT |
348 | { |
349 | _Jv_Method *method = &methods[i]; | |
350 | if (method->name == NULL | |
351 | || _Jv_equalUtf8Consts (method->name, clinit_name) | |
6f2b93eb TT |
352 | || _Jv_equalUtf8Consts (method->name, init_name) |
353 | || _Jv_equalUtf8Consts (method->name, finit_name)) | |
ee9dd372 | 354 | continue; |
0f918fea TT |
355 | java::lang::reflect::Method* rmethod |
356 | = new java::lang::reflect::Method (); | |
357 | rmethod->offset = (char*) method - (char*) methods; | |
ee9dd372 TT |
358 | rmethod->declaringClass = this; |
359 | *mptr++ = rmethod; | |
360 | } | |
361 | return result; | |
362 | } | |
363 | ||
364 | jstring | |
365 | java::lang::Class::getName (void) | |
366 | { | |
367 | char buffer[name->length + 1]; | |
368 | memcpy (buffer, name->data, name->length); | |
369 | buffer[name->length] = '\0'; | |
370 | return _Jv_NewStringUTF (buffer); | |
371 | } | |
372 | ||
373 | JArray<jclass> * | |
374 | java::lang::Class::getClasses (void) | |
375 | { | |
bd3a924b TT |
376 | // Until we have inner classes, it always makes sense to return an |
377 | // empty array. | |
378 | JArray<jclass> *result | |
379 | = (JArray<jclass> *) JvNewObjectArray (0, &ClassClass, NULL); | |
380 | return result; | |
ee9dd372 TT |
381 | } |
382 | ||
383 | JArray<jclass> * | |
384 | java::lang::Class::getDeclaredClasses (void) | |
385 | { | |
386 | checkMemberAccess (java::lang::reflect::Member::DECLARED); | |
6f2b93eb TT |
387 | // Until we have inner classes, it always makes sense to return an |
388 | // empty array. | |
389 | JArray<jclass> *result | |
390 | = (JArray<jclass> *) JvNewObjectArray (0, &ClassClass, NULL); | |
391 | return result; | |
ee9dd372 TT |
392 | } |
393 | ||
ee9dd372 TT |
394 | jclass |
395 | java::lang::Class::getDeclaringClass (void) | |
396 | { | |
6f2b93eb TT |
397 | // Until we have inner classes, it makes sense to always return |
398 | // NULL. | |
399 | return NULL; | |
ee9dd372 TT |
400 | } |
401 | ||
0f918fea TT |
402 | jint |
403 | java::lang::Class::_getFields (JArray<java::lang::reflect::Field *> *result, | |
404 | jint offset) | |
405 | { | |
406 | int count = 0; | |
407 | for (int i = 0; i < field_count; i++) | |
408 | { | |
409 | _Jv_Field *field = &fields[i]; | |
410 | if (! (field->getModifiers() & java::lang::reflect::Modifier::PUBLIC)) | |
411 | continue; | |
412 | ++count; | |
413 | ||
414 | if (result != NULL) | |
415 | { | |
416 | java::lang::reflect::Field *rfield | |
417 | = new java::lang::reflect::Field (); | |
418 | rfield->offset = (char *) field - (char *) fields; | |
419 | rfield->declaringClass = this; | |
420 | rfield->name = _Jv_NewStringUtf8Const (field->name); | |
421 | (elements (result))[offset + i] = rfield; | |
422 | } | |
423 | } | |
424 | jclass superclass = getSuperclass(); | |
425 | if (superclass != NULL) | |
426 | { | |
427 | int s_count = superclass->_getFields (result, offset); | |
428 | count += s_count; | |
429 | offset += s_count; | |
430 | } | |
431 | for (int i = 0; i < interface_count; ++i) | |
432 | { | |
433 | int f_count = interfaces[i]->_getFields (result, offset); | |
434 | count += f_count; | |
435 | offset += f_count; | |
436 | } | |
437 | return count; | |
438 | } | |
439 | ||
ee9dd372 TT |
440 | JArray<java::lang::reflect::Field *> * |
441 | java::lang::Class::getFields (void) | |
442 | { | |
0f918fea TT |
443 | using namespace java::lang::reflect; |
444 | ||
445 | int count = _getFields (NULL, 0); | |
446 | ||
447 | JArray<java::lang::reflect::Field *> *result | |
448 | = ((JArray<java::lang::reflect::Field *> *) | |
449 | JvNewObjectArray (count, &FieldClass, NULL)); | |
450 | ||
451 | _getFields (result, 0); | |
452 | ||
453 | return result; | |
ee9dd372 TT |
454 | } |
455 | ||
456 | JArray<jclass> * | |
457 | java::lang::Class::getInterfaces (void) | |
458 | { | |
459 | jobjectArray r = JvNewObjectArray (interface_count, getClass (), NULL); | |
460 | jobject *data = elements (r); | |
461 | for (int i = 0; i < interface_count; ++i) | |
462 | data[i] = interfaces[i]; | |
463 | return reinterpret_cast<JArray<jclass> *> (r); | |
464 | } | |
465 | ||
466 | java::lang::reflect::Method * | |
0f918fea | 467 | java::lang::Class::getMethod (jstring name, JArray<jclass> *param_types) |
ee9dd372 | 468 | { |
95c6cc0a | 469 | jstring partial_sig = getSignature (param_types, false); |
0f918fea TT |
470 | jint p_len = partial_sig->length(); |
471 | _Jv_Utf8Const *utf_name = _Jv_makeUtf8Const (name); | |
472 | for (Class *klass = this; klass; klass = klass->getSuperclass()) | |
473 | { | |
474 | int i = klass->isPrimitive () ? 0 : klass->method_count; | |
475 | while (--i >= 0) | |
476 | { | |
477 | // FIXME: access checks. | |
478 | if (_Jv_equalUtf8Consts (klass->methods[i].name, utf_name) | |
479 | && _Jv_equaln (klass->methods[i].signature, partial_sig, p_len)) | |
480 | { | |
481 | // Found it. | |
482 | using namespace java::lang::reflect; | |
6f2b93eb TT |
483 | |
484 | // Method must be public. | |
55ff3de7 | 485 | if (! Modifier::isPublic (klass->methods[i].accflags)) |
6f2b93eb TT |
486 | break; |
487 | ||
0f918fea | 488 | Method *rmethod = new Method (); |
41ecf930 TT |
489 | rmethod->offset = ((char *) (&klass->methods[i]) |
490 | - (char *) klass->methods); | |
0f918fea TT |
491 | rmethod->declaringClass = klass; |
492 | return rmethod; | |
493 | } | |
494 | } | |
495 | } | |
496 | JvThrow (new java::lang::NoSuchMethodException); | |
ee9dd372 TT |
497 | } |
498 | ||
6f2b93eb TT |
499 | // This is a very slow implementation, since it re-scans all the |
500 | // methods we've already listed to make sure we haven't duplicated a | |
501 | // method. It also over-estimates the required size, so we have to | |
502 | // shrink the result array later. | |
503 | jint | |
504 | java::lang::Class::_getMethods (JArray<java::lang::reflect::Method *> *result, | |
505 | jint offset) | |
506 | { | |
507 | jint count = 0; | |
508 | ||
509 | // First examine all local methods | |
510 | for (int i = isPrimitive () ? 0 : method_count; --i >= 0; ) | |
511 | { | |
512 | _Jv_Method *method = &methods[i]; | |
513 | if (method->name == NULL | |
514 | || _Jv_equalUtf8Consts (method->name, clinit_name) | |
515 | || _Jv_equalUtf8Consts (method->name, init_name) | |
516 | || _Jv_equalUtf8Consts (method->name, finit_name)) | |
517 | continue; | |
518 | // Only want public methods. | |
519 | if (! java::lang::reflect::Modifier::isPublic (method->accflags)) | |
520 | continue; | |
521 | ||
522 | // This is where we over-count the slots required if we aren't | |
523 | // filling the result for real. | |
524 | if (result != NULL) | |
525 | { | |
526 | jboolean add = true; | |
527 | java::lang::reflect::Method **mp = elements (result); | |
528 | // If we already have a method with this name and signature, | |
529 | // then ignore this one. This can happen with virtual | |
530 | // methods. | |
531 | for (int j = 0; j < offset; ++j) | |
532 | { | |
533 | _Jv_Method *meth_2 = _Jv_FromReflectedMethod (mp[j]); | |
534 | if (_Jv_equalUtf8Consts (method->name, meth_2->name) | |
535 | && _Jv_equalUtf8Consts (method->signature, | |
536 | meth_2->signature)) | |
537 | { | |
538 | add = false; | |
539 | break; | |
540 | } | |
541 | } | |
542 | if (! add) | |
543 | continue; | |
544 | } | |
545 | ||
546 | if (result != NULL) | |
547 | { | |
548 | using namespace java::lang::reflect; | |
549 | Method *rmethod = new Method (); | |
550 | rmethod->offset = (char *) method - (char *) methods; | |
551 | rmethod->declaringClass = this; | |
552 | Method **mp = elements (result); | |
553 | mp[offset + count] = rmethod; | |
554 | } | |
555 | ++count; | |
556 | } | |
557 | offset += count; | |
558 | ||
559 | // Now examine superclasses. | |
560 | if (getSuperclass () != NULL) | |
561 | { | |
562 | jint s_count = getSuperclass()->_getMethods (result, offset); | |
563 | offset += s_count; | |
564 | count += s_count; | |
565 | } | |
566 | ||
567 | // Finally, examine interfaces. | |
568 | for (int i = 0; i < interface_count; ++i) | |
569 | { | |
570 | int f_count = interfaces[i]->_getMethods (result, offset); | |
571 | count += f_count; | |
572 | offset += f_count; | |
573 | } | |
574 | ||
575 | return count; | |
576 | } | |
577 | ||
ee9dd372 TT |
578 | JArray<java::lang::reflect::Method *> * |
579 | java::lang::Class::getMethods (void) | |
580 | { | |
6f2b93eb TT |
581 | using namespace java::lang::reflect; |
582 | ||
583 | // FIXME: security checks. | |
584 | ||
585 | // This will overestimate the size we need. | |
586 | jint count = _getMethods (NULL, 0); | |
587 | ||
588 | JArray<Method *> *result | |
589 | = ((JArray<Method *> *) JvNewObjectArray (count, &MethodClass, NULL)); | |
590 | ||
591 | // When filling the array for real, we get the actual count. Then | |
592 | // we resize the array. | |
593 | jint real_count = _getMethods (result, 0); | |
594 | ||
595 | if (real_count != count) | |
596 | { | |
597 | JArray<Method *> *r2 | |
598 | = ((JArray<Method *> *) JvNewObjectArray (real_count, &MethodClass, | |
599 | NULL)); | |
600 | ||
601 | Method **destp = elements (r2); | |
602 | Method **srcp = elements (result); | |
603 | ||
604 | for (int i = 0; i < real_count; ++i) | |
605 | *destp++ = *srcp++; | |
606 | ||
607 | result = r2; | |
608 | } | |
609 | ||
610 | return result; | |
ee9dd372 TT |
611 | } |
612 | ||
613 | jboolean | |
614 | java::lang::Class::isAssignableFrom (jclass klass) | |
615 | { | |
ddf0fc6c | 616 | return _Jv_IsAssignableFrom (this, klass); |
ee9dd372 TT |
617 | } |
618 | ||
ddf0fc6c | 619 | inline jboolean |
ee9dd372 TT |
620 | java::lang::Class::isInstance (jobject obj) |
621 | { | |
622 | if (! obj || isPrimitive ()) | |
623 | return false; | |
624 | return isAssignableFrom (obj->getClass()); | |
625 | } | |
626 | ||
ddf0fc6c | 627 | inline jboolean |
ee9dd372 TT |
628 | java::lang::Class::isInterface (void) |
629 | { | |
630 | return (accflags & java::lang::reflect::Modifier::INTERFACE) != 0; | |
631 | } | |
632 | ||
633 | jobject | |
634 | java::lang::Class::newInstance (void) | |
635 | { | |
636 | // FIXME: do accessibility checks here. There currently doesn't | |
637 | // seem to be any way to do these. | |
638 | // FIXME: we special-case one check here just to pass a Plum Hall | |
639 | // test. Once access checking is implemented, remove this. | |
640 | if (this == &ClassClass) | |
641 | JvThrow (new java::lang::IllegalAccessException); | |
642 | ||
643 | if (isPrimitive () | |
644 | || isInterface () | |
645 | || isArray () | |
646 | || java::lang::reflect::Modifier::isAbstract(accflags)) | |
647 | JvThrow (new java::lang::InstantiationException); | |
648 | ||
eb4534a6 KKT |
649 | _Jv_InitClass (this); |
650 | ||
ee9dd372 TT |
651 | _Jv_Method *meth = _Jv_GetMethodLocal (this, init_name, void_signature); |
652 | if (! meth) | |
653 | JvThrow (new java::lang::NoSuchMethodException); | |
654 | ||
655 | jobject r = JvAllocObject (this); | |
656 | ((void (*) (jobject)) meth->ncode) (r); | |
657 | return r; | |
658 | } | |
659 | ||
ee9dd372 | 660 | void |
58eb6e7c | 661 | java::lang::Class::finalize (void) |
ee9dd372 | 662 | { |
58eb6e7c AG |
663 | #ifdef INTERPRETER |
664 | JvAssert (_Jv_IsInterpretedClass (this)); | |
665 | _Jv_UnregisterClass (this); | |
666 | #endif | |
ee9dd372 TT |
667 | } |
668 | ||
ee9dd372 TT |
669 | // This implements the initialization process for a class. From Spec |
670 | // section 12.4.2. | |
671 | void | |
672 | java::lang::Class::initializeClass (void) | |
673 | { | |
ddf0fc6c | 674 | // jshort-circuit to avoid needless locking. |
58eb6e7c | 675 | if (state == JV_STATE_DONE) |
ee9dd372 TT |
676 | return; |
677 | ||
ddf0fc6c BM |
678 | // Step 1. |
679 | _Jv_MonitorEnter (this); | |
680 | ||
58eb6e7c | 681 | if (state < JV_STATE_LINKED) |
ddf0fc6c | 682 | { |
58eb6e7c AG |
683 | #ifdef INTERPRETER |
684 | if (_Jv_IsInterpretedClass (this)) | |
685 | { | |
ddf0fc6c BM |
686 | // this can throw exceptions, so exit the monitor as a precaution. |
687 | _Jv_MonitorExit (this); | |
efc3b511 | 688 | java::lang::ClassLoader::resolveClass0 (this); |
58eb6e7c AG |
689 | _Jv_MonitorEnter (this); |
690 | } | |
691 | else | |
692 | #endif | |
693 | { | |
eb4534a6 | 694 | _Jv_PrepareCompiledClass (this); |
58eb6e7c AG |
695 | } |
696 | } | |
ddf0fc6c BM |
697 | |
698 | if (state <= JV_STATE_LINKED) | |
699 | _Jv_PrepareConstantTimeTables (this); | |
ee9dd372 TT |
700 | |
701 | // Step 2. | |
702 | java::lang::Thread *self = java::lang::Thread::currentThread(); | |
703 | // FIXME: `self' can be null at startup. Hence this nasty trick. | |
704 | self = (java::lang::Thread *) ((long) self | 1); | |
58eb6e7c | 705 | while (state == JV_STATE_IN_PROGRESS && thread && thread != self) |
ee9dd372 TT |
706 | wait (); |
707 | ||
708 | // Steps 3 & 4. | |
58eb6e7c | 709 | if (state == JV_STATE_DONE || state == JV_STATE_IN_PROGRESS || thread == self) |
ee9dd372 TT |
710 | { |
711 | _Jv_MonitorExit (this); | |
712 | return; | |
713 | } | |
714 | ||
715 | // Step 5. | |
58eb6e7c | 716 | if (state == JV_STATE_ERROR) |
ee9dd372 TT |
717 | { |
718 | _Jv_MonitorExit (this); | |
719 | JvThrow (new java::lang::NoClassDefFoundError); | |
720 | } | |
721 | ||
722 | // Step 6. | |
723 | thread = self; | |
58eb6e7c | 724 | state = JV_STATE_IN_PROGRESS; |
ee9dd372 TT |
725 | _Jv_MonitorExit (this); |
726 | ||
727 | // Step 7. | |
728 | if (! isInterface () && superclass) | |
729 | { | |
b099f07d TT |
730 | try |
731 | { | |
732 | superclass->initializeClass (); | |
733 | } | |
734 | catch (java::lang::Throwable *except) | |
ee9dd372 TT |
735 | { |
736 | // Caught an exception. | |
737 | _Jv_MonitorEnter (this); | |
58eb6e7c | 738 | state = JV_STATE_ERROR; |
a41cb705 | 739 | notifyAll (); |
ee9dd372 | 740 | _Jv_MonitorExit (this); |
b099f07d | 741 | throw except; |
ee9dd372 TT |
742 | } |
743 | } | |
744 | ||
b099f07d TT |
745 | // Steps 8, 9, 10, 11. |
746 | try | |
ee9dd372 | 747 | { |
b099f07d TT |
748 | _Jv_Method *meth = _Jv_GetMethodLocal (this, clinit_name, |
749 | void_signature); | |
750 | if (meth) | |
751 | ((void (*) (void)) meth->ncode) (); | |
ee9dd372 | 752 | } |
b099f07d | 753 | catch (java::lang::Throwable *except) |
ee9dd372 TT |
754 | { |
755 | if (! ErrorClass.isInstance(except)) | |
756 | { | |
b099f07d TT |
757 | try |
758 | { | |
759 | except = new ExceptionInInitializerError (except); | |
760 | } | |
761 | catch (java::lang::Throwable *t) | |
762 | { | |
763 | except = t; | |
764 | } | |
ee9dd372 TT |
765 | } |
766 | _Jv_MonitorEnter (this); | |
58eb6e7c | 767 | state = JV_STATE_ERROR; |
b099f07d TT |
768 | notifyAll (); |
769 | _Jv_MonitorExit (this); | |
770 | JvThrow (except); | |
ee9dd372 | 771 | } |
b099f07d TT |
772 | |
773 | _Jv_MonitorEnter (this); | |
774 | state = JV_STATE_DONE; | |
a41cb705 | 775 | notifyAll (); |
ee9dd372 | 776 | _Jv_MonitorExit (this); |
ee9dd372 TT |
777 | } |
778 | ||
779 | \f | |
780 | ||
781 | // | |
782 | // Some class-related convenience functions. | |
783 | // | |
784 | ||
0f918fea TT |
785 | // Find a method declared in the class. If it is not declared locally |
786 | // (or if it is inherited), return NULL. | |
ee9dd372 TT |
787 | _Jv_Method * |
788 | _Jv_GetMethodLocal (jclass klass, _Jv_Utf8Const *name, | |
789 | _Jv_Utf8Const *signature) | |
790 | { | |
791 | for (int i = 0; i < klass->method_count; ++i) | |
792 | { | |
793 | if (_Jv_equalUtf8Consts (name, klass->methods[i].name) | |
794 | && _Jv_equalUtf8Consts (signature, klass->methods[i].signature)) | |
795 | return &klass->methods[i]; | |
796 | } | |
797 | return NULL; | |
798 | } | |
799 | ||
0f918fea TT |
800 | _Jv_Method * |
801 | _Jv_LookupDeclaredMethod (jclass klass, _Jv_Utf8Const *name, | |
ddf0fc6c | 802 | _Jv_Utf8Const *signature) |
0f918fea TT |
803 | { |
804 | for (; klass; klass = klass->getSuperclass()) | |
805 | { | |
806 | _Jv_Method *meth = _Jv_GetMethodLocal (klass, name, signature); | |
807 | ||
808 | if (meth) | |
ddf0fc6c | 809 | return meth; |
0f918fea TT |
810 | } |
811 | ||
812 | return NULL; | |
813 | } | |
814 | ||
5b8c39e0 TT |
815 | // NOTE: MCACHE_SIZE should be a power of 2 minus one. |
816 | #define MCACHE_SIZE 1023 | |
58eb6e7c | 817 | |
5b8c39e0 TT |
818 | struct _Jv_mcache |
819 | { | |
58eb6e7c AG |
820 | jclass klass; |
821 | _Jv_Method *method; | |
822 | }; | |
823 | ||
5b8c39e0 | 824 | static _Jv_mcache method_cache[MCACHE_SIZE + 1]; |
58eb6e7c | 825 | |
397f674e | 826 | static void * |
58eb6e7c | 827 | _Jv_FindMethodInCache (jclass klass, |
ddf0fc6c BM |
828 | _Jv_Utf8Const *name, |
829 | _Jv_Utf8Const *signature) | |
58eb6e7c | 830 | { |
397f674e TT |
831 | int index = name->hash & MCACHE_SIZE; |
832 | _Jv_mcache *mc = method_cache + index; | |
833 | _Jv_Method *m = mc->method; | |
834 | ||
835 | if (mc->klass == klass | |
ddf0fc6c | 836 | && m != NULL // thread safe check |
397f674e TT |
837 | && _Jv_equalUtf8Consts (m->name, name) |
838 | && _Jv_equalUtf8Consts (m->signature, signature)) | |
839 | return mc->method->ncode; | |
58eb6e7c AG |
840 | return NULL; |
841 | } | |
842 | ||
843 | static void | |
844 | _Jv_AddMethodToCache (jclass klass, | |
ddf0fc6c | 845 | _Jv_Method *method) |
58eb6e7c AG |
846 | { |
847 | _Jv_MonitorEnter (&ClassClass); | |
848 | ||
397f674e | 849 | int index = method->name->hash & MCACHE_SIZE; |
58eb6e7c | 850 | |
397f674e TT |
851 | method_cache[index].method = method; |
852 | method_cache[index].klass = klass; | |
58eb6e7c | 853 | |
58eb6e7c AG |
854 | _Jv_MonitorExit (&ClassClass); |
855 | } | |
856 | ||
ee9dd372 TT |
857 | void * |
858 | _Jv_LookupInterfaceMethod (jclass klass, _Jv_Utf8Const *name, | |
ddf0fc6c | 859 | _Jv_Utf8Const *signature) |
ee9dd372 | 860 | { |
ddf0fc6c BM |
861 | using namespace java::lang::reflect; |
862 | ||
58eb6e7c AG |
863 | void *ncode = _Jv_FindMethodInCache (klass, name, signature); |
864 | if (ncode != 0) | |
865 | return ncode; | |
ee9dd372 TT |
866 | |
867 | for (; klass; klass = klass->getSuperclass()) | |
868 | { | |
869 | _Jv_Method *meth = _Jv_GetMethodLocal (klass, name, signature); | |
870 | if (! meth) | |
ddf0fc6c BM |
871 | continue; |
872 | ||
873 | if (Modifier::isStatic(meth->accflags)) | |
874 | JvThrow (new java::lang::IncompatibleClassChangeError | |
875 | (_Jv_GetMethodString (klass, meth->name))); | |
876 | if (Modifier::isAbstract(meth->accflags)) | |
877 | JvThrow (new java::lang::AbstractMethodError | |
878 | (_Jv_GetMethodString (klass, meth->name))); | |
879 | if (! Modifier::isPublic(meth->accflags)) | |
880 | JvThrow (new java::lang::IllegalAccessError | |
881 | (_Jv_GetMethodString (klass, meth->name))); | |
ee9dd372 | 882 | |
58eb6e7c AG |
883 | _Jv_AddMethodToCache (klass, meth); |
884 | ||
ee9dd372 TT |
885 | return meth->ncode; |
886 | } | |
887 | JvThrow (new java::lang::IncompatibleClassChangeError); | |
ddf0fc6c | 888 | return NULL; // Placate compiler. |
ee9dd372 TT |
889 | } |
890 | ||
ddf0fc6c BM |
891 | // Fast interface method lookup by index. |
892 | void * | |
893 | _Jv_LookupInterfaceMethodIdx (jclass klass, jclass iface, int method_idx) | |
894 | { | |
895 | _Jv_IDispatchTable *cldt = klass->idt; | |
896 | int idx = iface->idt->iface.ioffsets[cldt->cls.iindex] + method_idx; | |
897 | return cldt->cls.itable[idx]; | |
898 | } | |
899 | ||
900 | inline jboolean | |
901 | _Jv_IsAssignableFrom (jclass target, jclass source) | |
ee9dd372 | 902 | { |
ddf0fc6c BM |
903 | if (target == &ObjectClass |
904 | || source == target | |
905 | || (source->ancestors != NULL | |
906 | && source->ancestors[source->depth - target->depth] == target)) | |
907 | return true; | |
908 | ||
909 | // If target is array, so must source be. | |
910 | if (target->isArray ()) | |
911 | { | |
912 | if (! source->isArray()) | |
913 | return false; | |
914 | return _Jv_IsAssignableFrom(target->getComponentType(), | |
915 | source->getComponentType()); | |
916 | } | |
917 | ||
918 | if (target->isInterface()) | |
919 | { | |
920 | _Jv_IDispatchTable *cl_idt = source->idt; | |
921 | _Jv_IDispatchTable *if_idt = target->idt; | |
922 | jshort cl_iindex = cl_idt->cls.iindex; | |
923 | if (cl_iindex <= if_idt->iface.ioffsets[0]) | |
924 | { | |
925 | jshort offset = if_idt->iface.ioffsets[cl_iindex]; | |
926 | if (offset < cl_idt->cls.itable_length | |
927 | && cl_idt->cls.itable[offset] == target) | |
928 | return true; | |
929 | } | |
930 | return false; | |
931 | } | |
932 | ||
933 | return false; | |
ee9dd372 TT |
934 | } |
935 | ||
ee9dd372 TT |
936 | jboolean |
937 | _Jv_IsInstanceOf(jobject obj, jclass cl) | |
938 | { | |
ddf0fc6c BM |
939 | return (obj ? _Jv_IsAssignableFrom (cl, JV_CLASS (obj)) : false); |
940 | } | |
941 | ||
942 | void * | |
943 | _Jv_CheckCast (jclass c, jobject obj) | |
944 | { | |
945 | if (obj != NULL && ! _Jv_IsAssignableFrom(c, JV_CLASS (obj))) | |
946 | JvThrow (new java::lang::ClassCastException); | |
947 | return obj; | |
948 | } | |
949 | ||
950 | void | |
951 | _Jv_CheckArrayStore (jobject arr, jobject obj) | |
952 | { | |
953 | if (obj) | |
954 | { | |
955 | JvAssert (arr != NULL); | |
956 | jclass elt_class = (JV_CLASS (arr))->getComponentType(); | |
957 | jclass obj_class = JV_CLASS (obj); | |
958 | if (! _Jv_IsAssignableFrom (elt_class, obj_class)) | |
959 | JvThrow (new java::lang::ArrayStoreException); | |
960 | } | |
961 | } | |
962 | ||
963 | #define INITIAL_IOFFSETS_LEN 4 | |
964 | #define INITIAL_IFACES_LEN 4 | |
965 | ||
966 | // Generate tables for constant-time assignment testing and interface | |
967 | // method lookup. This implements the technique described by Per Bothner | |
968 | // <per@bothner.com> on the java-discuss mailing list on 1999-09-02: | |
969 | // http://sourceware.cygnus.com/ml/java-discuss/1999-q3/msg00377.html | |
970 | void | |
971 | _Jv_PrepareConstantTimeTables (jclass klass) | |
972 | { | |
973 | if (klass->isPrimitive () || klass->isInterface ()) | |
974 | return; | |
975 | ||
976 | // Short-circuit in case we've been called already. | |
977 | if ((klass->idt != NULL) || klass->depth != 0) | |
978 | return; | |
979 | ||
980 | // Calculate the class depth and ancestor table. The depth of a class | |
981 | // is how many "extends" it is removed from Object. Thus the depth of | |
982 | // java.lang.Object is 0, but the depth of java.io.FilterOutputStream | |
983 | // is 2. Depth is defined for all regular and array classes, but not | |
984 | // interfaces or primitive types. | |
985 | ||
986 | jclass klass0 = klass; | |
987 | while (klass0 != &ObjectClass) | |
988 | { | |
989 | klass0 = klass0->superclass; | |
990 | klass->depth++; | |
991 | } | |
992 | ||
993 | // We do class member testing in constant time by using a small table | |
994 | // of all the ancestor classes within each class. The first element is | |
995 | // a pointer to the current class, and the rest are pointers to the | |
996 | // classes ancestors, ordered from the current class down by decreasing | |
997 | // depth. We do not include java.lang.Object in the table of ancestors, | |
998 | // since it is redundant. | |
999 | ||
1000 | klass->ancestors = (jclass *) _Jv_Malloc (klass->depth * sizeof (jclass)); | |
1001 | klass0 = klass; | |
1002 | for (int index = 0; index < klass->depth; index++) | |
1003 | { | |
1004 | klass->ancestors[index] = klass0; | |
1005 | klass0 = klass0->superclass; | |
1006 | } | |
1007 | ||
1008 | if (klass->isArray () | |
1009 | || java::lang::reflect::Modifier::isAbstract (klass->accflags)) | |
1010 | return; | |
1011 | ||
1012 | klass->idt = | |
1013 | (_Jv_IDispatchTable *) _Jv_Malloc (sizeof (_Jv_IDispatchTable)); | |
1014 | ||
1015 | _Jv_ifaces ifaces; | |
1016 | ||
1017 | ifaces.count = 0; | |
1018 | ifaces.len = INITIAL_IFACES_LEN; | |
1019 | ifaces.list = (jclass *) _Jv_Malloc (ifaces.len * sizeof (jclass *)); | |
1020 | ||
1021 | int itable_size = _Jv_GetInterfaces (klass, &ifaces); | |
1022 | ||
1023 | if (ifaces.count > 0) | |
1024 | { | |
1025 | klass->idt->cls.itable = | |
1026 | (void **) _Jv_Malloc (itable_size * sizeof (void *)); | |
1027 | klass->idt->cls.itable_length = itable_size; | |
1028 | ||
1029 | jshort *itable_offsets = | |
1030 | (jshort *) _Jv_Malloc (ifaces.count * sizeof (jshort)); | |
1031 | ||
1032 | _Jv_GenerateITable (klass, &ifaces, itable_offsets); | |
1033 | ||
1034 | jshort cls_iindex = | |
1035 | _Jv_FindIIndex (ifaces.list, itable_offsets, ifaces.count); | |
1036 | ||
1037 | for (int i=0; i < ifaces.count; i++) | |
1038 | { | |
1039 | ifaces.list[i]->idt->iface.ioffsets[cls_iindex] = | |
1040 | itable_offsets[i]; | |
1041 | } | |
1042 | ||
1043 | klass->idt->cls.iindex = cls_iindex; | |
1044 | ||
1045 | _Jv_Free (ifaces.list); | |
1046 | _Jv_Free (itable_offsets); | |
1047 | } | |
1048 | else | |
1049 | { | |
1050 | klass->idt->cls.iindex = SHRT_MAX; | |
1051 | } | |
1052 | } | |
1053 | ||
1054 | // Return index of item in list, or -1 if item is not present. | |
1055 | jshort | |
1056 | _Jv_IndexOf (void *item, void **list, jshort list_len) | |
1057 | { | |
1058 | for (int i=0; i < list_len; i++) | |
1059 | { | |
1060 | if (list[i] == item) | |
1061 | return i; | |
1062 | } | |
1063 | return -1; | |
1064 | } | |
1065 | ||
1066 | // Find all unique interfaces directly or indirectly implemented by klass. | |
1067 | // Returns the size of the interface dispatch table (itable) for klass, which | |
1068 | // is the number of unique interfaces plus the total number of methods that | |
1069 | // those interfaces declare. May extend ifaces if required. | |
1070 | jshort | |
1071 | _Jv_GetInterfaces (jclass klass, _Jv_ifaces *ifaces) | |
1072 | { | |
1073 | jshort result = 0; | |
1074 | ||
1075 | for (int i=0; i < klass->interface_count; i++) | |
1076 | { | |
1077 | jclass iface = klass->interfaces[i]; | |
1078 | if (_Jv_IndexOf (iface, (void **) ifaces->list, ifaces->count) == -1) | |
1079 | { | |
1080 | if (ifaces->count + 1 >= ifaces->len) | |
1081 | { | |
1082 | /* Resize ifaces list */ | |
1083 | ifaces->len = ifaces->len * 2; | |
1084 | ifaces->list = (jclass *) _Jv_Realloc (ifaces->list, | |
1085 | ifaces->len * sizeof(jclass)); | |
1086 | } | |
1087 | ifaces->list[ifaces->count] = iface; | |
1088 | ifaces->count++; | |
1089 | ||
1090 | result += _Jv_GetInterfaces (klass->interfaces[i], ifaces); | |
1091 | } | |
1092 | } | |
1093 | ||
1094 | if (klass->isInterface()) | |
1095 | { | |
1096 | result += klass->method_count + 1; | |
1097 | } | |
1098 | else | |
1099 | { | |
1100 | if (klass->superclass) | |
1101 | { | |
1102 | result += _Jv_GetInterfaces (klass->superclass, ifaces); | |
1103 | } | |
1104 | } | |
1105 | return result; | |
1106 | } | |
1107 | ||
1108 | // Fill out itable in klass, resolving method declarations in each ifaces. | |
1109 | // itable_offsets is filled out with the position of each iface in itable, | |
1110 | // such that itable[itable_offsets[n]] == ifaces.list[n]. | |
1111 | void | |
1112 | _Jv_GenerateITable (jclass klass, _Jv_ifaces *ifaces, jshort *itable_offsets) | |
1113 | { | |
1114 | void **itable = klass->idt->cls.itable; | |
1115 | jshort itable_pos = 0; | |
1116 | ||
1117 | for (int i=0; i < ifaces->count; i++) | |
1118 | { | |
1119 | jclass iface = ifaces->list[i]; | |
1120 | itable_offsets[i] = itable_pos; | |
1121 | itable_pos = _Jv_AppendPartialITable (klass, iface, itable, | |
1122 | itable_pos); | |
1123 | ||
1124 | /* Create interface dispatch table for iface */ | |
1125 | if (iface->idt == NULL) | |
1126 | { | |
1127 | iface->idt = | |
1128 | (_Jv_IDispatchTable *) _Jv_Malloc (sizeof (_Jv_IDispatchTable)); | |
1129 | ||
1130 | // The first element of ioffsets is its length (itself included). | |
1131 | jshort *ioffsets = | |
1132 | (jshort *) _Jv_Malloc (INITIAL_IOFFSETS_LEN * sizeof (jshort)); | |
1133 | ioffsets[0] = INITIAL_IOFFSETS_LEN; | |
1134 | for (int i=1; i < INITIAL_IOFFSETS_LEN; i++) | |
1135 | ioffsets[i] = -1; | |
1136 | ||
1137 | iface->idt->iface.ioffsets = ioffsets; | |
1138 | } | |
1139 | } | |
1140 | } | |
1141 | ||
1142 | // Format method name for use in error messages. | |
1143 | jstring | |
1144 | _Jv_GetMethodString (jclass klass, _Jv_Utf8Const *name) | |
1145 | { | |
1146 | jstring r = JvNewStringUTF (klass->name->data); | |
1147 | r = r->concat (JvNewStringUTF (".")); | |
1148 | r = r->concat (JvNewStringUTF (name->data)); | |
1149 | return r; | |
1150 | } | |
1151 | ||
1152 | void | |
1153 | _Jv_ThrowNoSuchMethodError () | |
1154 | { | |
1155 | JvThrow (new java::lang::NoSuchMethodError ()); | |
1156 | } | |
1157 | ||
1158 | // Each superinterface of a class (i.e. each interface that the class | |
1159 | // directly or indirectly implements) has a corresponding "Partial | |
1160 | // Interface Dispatch Table" whose size is (number of methods + 1) words. | |
1161 | // The first word is a pointer to the interface (i.e. the java.lang.Class | |
1162 | // instance for that interface). The remaining words are pointers to the | |
1163 | // actual methods that implement the methods declared in the interface, | |
1164 | // in order of declaration. | |
1165 | // | |
1166 | // Append partial interface dispatch table for "iface" to "itable", at | |
1167 | // position itable_pos. | |
1168 | // Returns the offset at which the next partial ITable should be appended. | |
1169 | jshort | |
1170 | _Jv_AppendPartialITable (jclass klass, jclass iface, void **itable, | |
1171 | jshort pos) | |
1172 | { | |
1173 | using namespace java::lang::reflect; | |
1174 | ||
1175 | itable[pos++] = (void *) iface; | |
1176 | _Jv_Method *meth; | |
1177 | ||
1178 | for (int j=0; j < iface->method_count; j++) | |
1179 | { | |
1180 | meth = NULL; | |
1181 | for (jclass cl = klass; cl; cl = cl->getSuperclass()) | |
1182 | { | |
1183 | meth = _Jv_GetMethodLocal (cl, iface->methods[j].name, | |
1184 | iface->methods[j].signature); | |
1185 | ||
1186 | if (meth) | |
1187 | break; | |
1188 | } | |
1189 | ||
1190 | if (meth && (meth->name->data[0] == '<')) | |
1191 | { | |
1192 | // leave a placeholder in the itable for hidden init methods. | |
1193 | itable[pos] = NULL; | |
1194 | } | |
1195 | else if (meth) | |
1196 | { | |
1197 | if (Modifier::isStatic(meth->accflags)) | |
1198 | JvThrow (new java::lang::IncompatibleClassChangeError | |
1199 | (_Jv_GetMethodString (klass, meth->name))); | |
1200 | if (Modifier::isAbstract(meth->accflags)) | |
1201 | JvThrow (new java::lang::AbstractMethodError | |
1202 | (_Jv_GetMethodString (klass, meth->name))); | |
1203 | if (! Modifier::isPublic(meth->accflags)) | |
1204 | JvThrow (new java::lang::IllegalAccessError | |
1205 | (_Jv_GetMethodString (klass, meth->name))); | |
1206 | ||
1207 | itable[pos] = meth->ncode; | |
1208 | } | |
1209 | else | |
1210 | { | |
1211 | // The method doesn't exist in klass. Binary compatibility rules | |
1212 | // permit this, so we delay the error until runtime using a pointer | |
1213 | // to a method which throws an exception. | |
1214 | itable[pos] = (void *) _Jv_ThrowNoSuchMethodError; | |
1215 | } | |
1216 | pos++; | |
1217 | } | |
1218 | ||
1219 | return pos; | |
1220 | } | |
1221 | ||
1222 | static _Jv_Mutex_t iindex_mutex; | |
1223 | bool iindex_mutex_initialized = false; | |
1224 | ||
1225 | // We need to find the correct offset in the Class Interface Dispatch | |
1226 | // Table for a given interface. Once we have that, invoking an interface | |
1227 | // method just requires combining the Method's index in the interface | |
1228 | // (known at compile time) to get the correct method. Doing a type test | |
1229 | // (cast or instanceof) is the same problem: Once we have a possible Partial | |
1230 | // Interface Dispatch Table, we just compare the first element to see if it | |
1231 | // matches the desired interface. So how can we find the correct offset? | |
1232 | // Our solution is to keep a vector of candiate offsets in each interface | |
1233 | // (idt->iface.ioffsets), and in each class we have an index | |
1234 | // (idt->cls.iindex) used to select the correct offset from ioffsets. | |
1235 | // | |
1236 | // Calculate and return iindex for a new class. | |
1237 | // ifaces is a vector of num interfaces that the class implements. | |
1238 | // offsets[j] is the offset in the interface dispatch table for the | |
1239 | // interface corresponding to ifaces[j]. | |
1240 | // May extend the interface ioffsets if required. | |
1241 | jshort | |
1242 | _Jv_FindIIndex (jclass *ifaces, jshort *offsets, jshort num) | |
1243 | { | |
1244 | int i; | |
1245 | int j; | |
1246 | ||
1247 | // Acquire a global lock to prevent itable corruption in case of multiple | |
1248 | // classes that implement an intersecting set of interfaces being linked | |
1249 | // simultaneously. We can assume that the mutex will be initialized | |
1250 | // single-threaded. | |
1251 | if (! iindex_mutex_initialized) | |
1252 | { | |
1253 | _Jv_MutexInit (&iindex_mutex); | |
1254 | iindex_mutex_initialized = true; | |
1255 | } | |
1256 | ||
1257 | _Jv_MutexLock (&iindex_mutex); | |
1258 | ||
1259 | for (i=1;; i++) /* each potential position in ioffsets */ | |
1260 | { | |
1261 | for (j=0;; j++) /* each iface */ | |
1262 | { | |
1263 | if (j >= num) | |
1264 | goto found; | |
1265 | if (i > ifaces[j]->idt->iface.ioffsets[0]) | |
1266 | continue; | |
1267 | int ioffset = ifaces[j]->idt->iface.ioffsets[i]; | |
1268 | /* We can potentially share this position with another class. */ | |
1269 | if (ioffset >= 0 && ioffset != offsets[j]) | |
1270 | break; /* Nope. Try next i. */ | |
1271 | } | |
1272 | } | |
1273 | found: | |
1274 | for (j = 0; j < num; j++) | |
1275 | { | |
1276 | int len = ifaces[j]->idt->iface.ioffsets[0]; | |
1277 | if (i >= len) | |
1278 | { | |
1279 | /* Resize ioffsets. */ | |
1280 | int newlen = 2 * len; | |
1281 | if (i >= newlen) | |
1282 | newlen = i + 3; | |
1283 | jshort *old_ioffsets = ifaces[j]->idt->iface.ioffsets; | |
1284 | jshort *new_ioffsets = (jshort *) _Jv_Realloc (old_ioffsets, | |
1285 | newlen * sizeof(jshort)); | |
1286 | new_ioffsets[0] = newlen; | |
1287 | ||
1288 | while (len < newlen) | |
1289 | new_ioffsets[len++] = -1; | |
1290 | ||
1291 | ifaces[j]->idt->iface.ioffsets = new_ioffsets; | |
1292 | } | |
1293 | ifaces[j]->idt->iface.ioffsets[i] = offsets[j]; | |
1294 | } | |
1295 | ||
1296 | _Jv_MutexUnlock (&iindex_mutex); | |
1297 | ||
1298 | return i; | |
ee9dd372 | 1299 | } |