]>
Commit | Line | Data |
---|---|---|
58eb6e7c AG |
1 | // resolve.cc - Code for linking and resolving classes and pool entries. |
2 | ||
3 | /* Copyright (C) 1999 Cygnus Solutions | |
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 | /* Author: Kresten Krab Thorup <krab@gnu.org> */ | |
12 | ||
13 | #include <java-interp.h> | |
14 | ||
15 | #ifdef INTERPRETER | |
16 | ||
17 | #include <cni.h> | |
18 | #include <jvm.h> | |
19 | #include <string.h> | |
20 | #include <java-cpool.h> | |
21 | #include <java/lang/Class.h> | |
22 | #include <java/lang/String.h> | |
23 | #include <java/lang/Thread.h> | |
24 | #include <java/lang/InternalError.h> | |
25 | #include <java/lang/VirtualMachineError.h> | |
26 | #include <java/lang/NoSuchFieldError.h> | |
27 | #include <java/lang/ClassFormatError.h> | |
28 | #include <java/lang/IllegalAccessError.h> | |
29 | #include <java/lang/AbstractMethodError.h> | |
30 | #include <java/lang/ClassNotFoundException.h> | |
31 | #include <java/lang/IncompatibleClassChangeError.h> | |
32 | ||
33 | static void throw_internal_error (char *msg) | |
34 | __attribute__ ((__noreturn__)); | |
35 | static void throw_class_format_error (jstring msg) | |
36 | __attribute__ ((__noreturn__)); | |
37 | static void throw_class_format_error (char *msg) | |
38 | __attribute__ ((__noreturn__)); | |
39 | ||
40 | #define StringClass _CL_Q34java4lang6String | |
41 | extern java::lang::Class StringClass; | |
42 | #define ClassObject _CL_Q34java4lang6Object | |
43 | extern java::lang::Class ClassObject; | |
44 | #define ObjectClass _CL_Q34java4lang6Object | |
45 | extern java::lang::Class ObjectClass; | |
46 | ||
47 | ||
48 | static int get_alignment_from_class (jclass); | |
49 | ||
50 | static _Jv_ResolvedMethod* | |
51 | _Jv_BuildResolvedMethod (_Jv_Method*, | |
52 | jclass, | |
53 | jboolean, | |
54 | jint); | |
55 | ||
56 | ||
57 | static const int PUBLIC = 0x001; | |
58 | static const int PRIVATE = 0x002; | |
59 | static const int PROTECTED = 0x004; | |
60 | static const int STATIC = 0x008; | |
61 | static const int FINAL = 0x010; | |
62 | static const int SYNCHRONIZED = 0x020; | |
63 | static const int VOLATILE = 0x040; | |
64 | static const int TRANSIENT = 0x080; | |
65 | static const int NATIVE = 0x100; | |
66 | static const int INTERFACE = 0x200; | |
67 | static const int ABSTRACT = 0x400; | |
68 | static const int ALL_FLAGS = 0x7FF; | |
69 | ||
70 | // We need to know the name of a constructor. | |
71 | static _Jv_Utf8Const *init_name = _Jv_makeUtf8Const ("<init>", 6); | |
72 | ||
73 | static void throw_incompatible_class_change_error (jstring msg) | |
74 | { | |
75 | JvThrow (new java::lang::IncompatibleClassChangeError (msg)); | |
76 | } | |
77 | ||
78 | void* | |
79 | _Jv_ResolvePoolEntry (jclass klass, int index) | |
80 | { | |
81 | _Jv_Constants *pool = &klass->constants; | |
82 | ||
83 | if ((pool->tags[index] & JV_CONSTANT_ResolvedFlag) != 0) | |
84 | return pool->data[index]; | |
85 | ||
86 | switch (pool->tags[index]) { | |
87 | case JV_CONSTANT_Class: | |
88 | { | |
89 | _Jv_Utf8Const *name = (_Jv_Utf8Const *) pool->data[index]; | |
90 | ||
91 | jclass found; | |
92 | if (name->data[0] == '[') | |
93 | found = _Jv_FindClassFromSignature (&name->data[0], | |
94 | klass->loader); | |
95 | else | |
96 | found = _Jv_FindClass (name, klass->loader); | |
97 | ||
98 | if (! found) | |
99 | { | |
100 | jstring str = _Jv_NewStringUTF (name->data); | |
101 | JvThrow (new java::lang::ClassNotFoundException (str)); | |
102 | } | |
103 | ||
104 | if ((found->accflags & PUBLIC) == PUBLIC | |
105 | || (_Jv_ClassNameSamePackage (found->name, | |
106 | klass->name))) | |
107 | { | |
108 | pool->data[index] = (void *) found; | |
109 | pool->tags[index] |= JV_CONSTANT_ResolvedFlag; | |
110 | } | |
111 | else | |
112 | { | |
113 | JvThrow (new java::lang::IllegalAccessError (found->getName())); | |
114 | } | |
115 | } | |
116 | break; | |
117 | ||
118 | case JV_CONSTANT_String: | |
119 | { | |
120 | jstring str; | |
121 | str = _Jv_NewStringUtf8Const ((_Jv_Utf8Const *) pool->data[index]); | |
122 | pool->data[index] = (void *) str; | |
123 | pool->tags[index] |= JV_CONSTANT_ResolvedFlag; | |
124 | } | |
125 | break; | |
126 | ||
127 | case JV_CONSTANT_Fieldref: | |
128 | { | |
129 | _Jv_ushort class_index, name_and_type_index; | |
130 | _Jv_loadIndexes ((const void**) &pool->data[index], | |
131 | class_index, | |
132 | name_and_type_index); | |
133 | jclass owner = (jclass) _Jv_ResolvePoolEntry (klass, class_index); | |
134 | ||
135 | if (owner != klass) | |
136 | _Jv_InitClass (owner); | |
137 | ||
138 | _Jv_ushort name_index, type_index; | |
139 | _Jv_loadIndexes ((const void**) &pool->data[name_and_type_index], | |
140 | name_index, | |
141 | type_index); | |
142 | ||
143 | _Jv_Utf8Const *field_name = (_Jv_Utf8Const*) pool->data[name_index]; | |
144 | _Jv_Utf8Const *field_type_name = | |
145 | (_Jv_Utf8Const*) pool->data[type_index]; | |
146 | ||
147 | // FIXME: The implementation of this function | |
148 | // (_Jv_FindClassFromSignature) will generate an instance of | |
149 | // _Jv_Utf8Const for each call if the field type is a class name | |
150 | // (Lxx.yy.Z;). This may be too expensive to do for each and | |
151 | // every fieldref being resolved. For now, we fix the problem by | |
152 | // only doing it when we have a loader different from the class | |
153 | // declaring the field. | |
154 | ||
155 | jclass field_type = 0; | |
156 | ||
157 | if (owner->loader != klass->loader) | |
158 | field_type = _Jv_FindClassFromSignature (field_type_name->data, | |
159 | klass->loader); | |
160 | ||
161 | _Jv_Field* the_field = 0; | |
162 | ||
163 | for (jclass cls = owner; cls != 0; cls = cls->getSuperclass ()) | |
164 | { | |
165 | for (int i = 0; i < cls->field_count; i++) | |
166 | { | |
167 | _Jv_Field *field = &cls->fields[i]; | |
168 | if (! _Jv_equalUtf8Consts (field->name, field_name)) | |
169 | continue; | |
170 | ||
171 | // now, check field access. | |
172 | ||
173 | if ( (cls == klass) | |
174 | || ((field->flags & PUBLIC) != 0) | |
175 | || (((field->flags & PROTECTED) != 0) | |
176 | && cls->isAssignableFrom (klass)) | |
177 | || (((field->flags & PRIVATE) == 0) | |
178 | && _Jv_ClassNameSamePackage (cls->name, | |
179 | klass->name))) | |
180 | { | |
181 | /* resove the field using the class' own loader | |
182 | if necessary */ | |
183 | ||
184 | if (!field->isResolved ()) | |
185 | _Jv_ResolveField (field, cls->loader); | |
186 | ||
187 | if (field_type != 0 && field->type != field_type) | |
188 | JvThrow | |
189 | (new java::lang::LinkageError | |
190 | (JvNewStringLatin1 | |
191 | ("field type mismatch with different loaders"))); | |
192 | ||
193 | the_field = field; | |
194 | goto end_of_field_search; | |
195 | } | |
196 | else | |
197 | { | |
198 | JvThrow (new java::lang::IllegalAccessError); | |
199 | } | |
200 | } | |
201 | } | |
202 | ||
203 | end_of_field_search: | |
204 | if (the_field == 0) | |
205 | { | |
206 | jstring msg = JvNewStringLatin1 ("field "); | |
207 | msg = msg->concat (owner->getName ()); | |
208 | msg = msg->concat (JvNewStringLatin1(".")); | |
209 | msg = msg->concat (_Jv_NewStringUTF (field_name->data)); | |
210 | msg = msg->concat (JvNewStringLatin1(" was not found.")); | |
211 | throw_incompatible_class_change_error (msg); | |
212 | } | |
213 | ||
214 | pool->data[index] = (void*)the_field; | |
215 | pool->tags[index] |= JV_CONSTANT_ResolvedFlag; | |
216 | } | |
217 | break; | |
218 | ||
219 | case JV_CONSTANT_Methodref: | |
220 | case JV_CONSTANT_InterfaceMethodref: | |
221 | { | |
222 | _Jv_ushort class_index, name_and_type_index; | |
223 | _Jv_loadIndexes ((const void**) &pool->data[index], | |
224 | class_index, | |
225 | name_and_type_index); | |
226 | jclass owner = (jclass) _Jv_ResolvePoolEntry (klass, class_index); | |
227 | ||
228 | if (owner != klass) | |
229 | _Jv_InitClass (owner); | |
230 | ||
231 | _Jv_ushort name_index, type_index; | |
232 | _Jv_loadIndexes ((const void**) &pool->data[name_and_type_index], | |
233 | name_index, | |
234 | type_index); | |
235 | ||
236 | _Jv_Utf8Const *method_name = (_Jv_Utf8Const*) pool->data[name_index]; | |
237 | _Jv_Utf8Const *method_signature = | |
238 | (_Jv_Utf8Const*) pool->data[type_index]; | |
239 | ||
240 | int vtable_index = -1; | |
241 | _Jv_Method *the_method = 0; | |
242 | jclass found_class = 0; | |
243 | ||
244 | // we make a loop here, because methods are allowed to be moved to | |
245 | // a super class, and still be visible.. (binary compatibility). | |
246 | ||
247 | for (jclass cls = owner; cls != 0; cls = cls->getSuperclass ()) | |
248 | { | |
249 | for (int i = 0; i < cls->method_count; i++) | |
250 | { | |
251 | _Jv_Method *method = &cls->methods[i]; | |
252 | if ( (!_Jv_equalUtf8Consts (method->name, | |
253 | method_name)) | |
254 | || (!_Jv_equalUtf8Consts (method->signature, | |
255 | method_signature))) | |
256 | continue; | |
257 | ||
258 | if (cls == klass | |
259 | || ((method->accflags & PUBLIC) != 0) | |
260 | || (((method->accflags & PROTECTED) != 0) | |
261 | && cls->isAssignableFrom (klass)) | |
262 | || (((method->accflags & PRIVATE) == 0) | |
263 | && _Jv_ClassNameSamePackage (cls->name, | |
264 | klass->name))) | |
265 | { | |
266 | // FIXME: if (cls->loader != klass->loader), then we | |
267 | // must actually check that the types of arguments | |
268 | // correspond. That is, for each argument type, and | |
269 | // the return type, doing _Jv_FindClassFromSignature | |
270 | // with either loader should produce the same result, | |
271 | // i.e., exactly the same jclass object. JVMS 5.4.3.3 | |
272 | ||
273 | the_method = method; | |
274 | found_class = cls; | |
275 | ||
276 | ||
277 | if (pool->tags[index] == JV_CONSTANT_InterfaceMethodref) | |
278 | vtable_index = -1; | |
279 | else | |
280 | vtable_index = _Jv_DetermineVTableIndex | |
281 | (cls, method_name, method_signature); | |
282 | ||
283 | if (vtable_index == 0) | |
284 | throw_incompatible_class_change_error | |
285 | (JvNewStringLatin1 ("method not found")); | |
286 | ||
287 | goto end_of_method_search; | |
288 | } | |
289 | else | |
290 | { | |
291 | JvThrow (new java::lang::IllegalAccessError); | |
292 | } | |
293 | } | |
294 | } | |
295 | ||
296 | end_of_method_search: | |
297 | if (the_method == 0) | |
298 | { | |
299 | jstring msg = JvNewStringLatin1 ("method "); | |
300 | msg = msg->concat (owner->getName ()); | |
301 | msg = msg->concat (JvNewStringLatin1(".")); | |
302 | msg = msg->concat (_Jv_NewStringUTF (method_name->data)); | |
303 | msg = msg->concat (JvNewStringLatin1(" was not found.")); | |
304 | JvThrow(new java::lang::NoSuchFieldError (msg)); | |
305 | } | |
306 | ||
307 | pool->data[index] = (void*) | |
308 | _Jv_BuildResolvedMethod(the_method, | |
309 | found_class, | |
310 | ((the_method->accflags & STATIC) != 0), | |
311 | vtable_index); | |
312 | pool->tags[index] |= JV_CONSTANT_ResolvedFlag; | |
313 | } | |
314 | break; | |
315 | ||
316 | } | |
317 | ||
318 | return pool->data[index]; | |
319 | } | |
320 | ||
321 | void | |
322 | _Jv_ResolveField (_Jv_Field *field, java::lang::ClassLoader *loader) | |
323 | { | |
324 | if (! field->isResolved ()) | |
325 | { | |
326 | _Jv_Utf8Const *sig = (_Jv_Utf8Const*)field->type; | |
327 | field->type = _Jv_FindClassFromSignature (sig->data, loader); | |
328 | field->flags &= ~_Jv_FIELD_UNRESOLVED_FLAG; | |
329 | } | |
330 | } | |
331 | ||
332 | _Jv_Method* | |
333 | _Jv_LookupDeclaredMethod (jclass klass, _Jv_Utf8Const *name, | |
334 | _Jv_Utf8Const *signature) | |
335 | { | |
336 | for (; klass; klass = klass->getSuperclass()) | |
337 | { | |
338 | _Jv_Method *meth = _Jv_GetMethodLocal (klass, name, signature); | |
339 | ||
340 | if (meth) | |
341 | return meth; | |
342 | } | |
343 | ||
344 | return NULL; | |
345 | } | |
346 | ||
347 | /** FIXME: this is a terribly inefficient algorithm! It would improve | |
348 | things if compiled classes to know vtable offset, and _Jv_Method had | |
349 | a field for this. | |
350 | ||
351 | Returns 0 if this class does not declare the given method. | |
352 | Returns -1 if the given method does not appear in the vtable. | |
353 | i.e., it is static, private, final or a constructor. | |
354 | Otherwise, returns the vtable index. */ | |
355 | int | |
356 | _Jv_DetermineVTableIndex (jclass klass, | |
357 | _Jv_Utf8Const *name, | |
358 | _Jv_Utf8Const *signature) | |
359 | { | |
360 | jclass super_class = klass->getSuperclass (); | |
361 | ||
362 | if (super_class != NULL) | |
363 | { | |
364 | int prev = _Jv_DetermineVTableIndex (super_class, | |
365 | name, | |
366 | signature); | |
367 | if (prev != 0) | |
368 | return prev; | |
369 | } | |
370 | ||
371 | /* at this point, we know that the super-class does not declare | |
372 | * the method. Otherwise, the above call would have found it, and | |
373 | * determined the result of this function (-1 or some positive | |
374 | * number). | |
375 | */ | |
376 | ||
377 | _Jv_Method *meth = _Jv_GetMethodLocal (klass, name, signature); | |
378 | ||
379 | /* now, if we do not declare this method, return zero */ | |
380 | if (meth == NULL) | |
381 | return 0; | |
382 | ||
383 | /* so now, we know not only that the super class does not declare the | |
384 | * method, but we do! So, this is a first declaration of the method. */ | |
385 | ||
386 | /* now, the checks for things that are declared in this class, but do | |
387 | * not go into the vtable. There are three cases. | |
388 | * 1) the method is static, private or final | |
389 | * 2) the class itself is final, or | |
390 | * 3) it is the method <init> | |
391 | */ | |
392 | ||
393 | if ( (meth->accflags & (STATIC|PRIVATE|FINAL)) != 0 | |
394 | || (klass->accflags & FINAL) != 0 | |
395 | || _Jv_equalUtf8Consts (name, init_name)) | |
396 | return -1; | |
397 | ||
398 | /* reaching this point, we know for sure, that the method in question | |
399 | * will be in the vtable. The question is where. */ | |
400 | ||
401 | /* the base offset, is where we will start assigning vtable | |
402 | * indexes for this class. It is 1 for base classes | |
403 | * (vtable->method[0] is unused), and for non-base classes it is the | |
404 | * number of entries in the super class' vtable plus 1. */ | |
405 | ||
406 | int base_offset; | |
407 | if (super_class == 0) | |
408 | base_offset = 1; | |
409 | else | |
410 | base_offset = super_class->vtable_method_count+1; | |
411 | ||
412 | /* we will consider methods 0..this_method_index-1. And for each one, | |
413 | * determine if it is new (i.e., if it appears in the super class), | |
414 | * and if it should go in the vtable. If so, increment base_offset */ | |
415 | ||
416 | int this_method_index = meth - (&klass->methods[0]); | |
417 | ||
418 | for (int i = 0; i < this_method_index; i++) | |
419 | { | |
420 | _Jv_Method *m = &klass->methods[i]; | |
421 | ||
422 | /* fist some checks for things that surely do not go in the | |
423 | * vtable */ | |
424 | ||
425 | if ((m->accflags & (STATIC|PRIVATE)) != 0) | |
426 | continue; | |
427 | if (_Jv_equalUtf8Consts (m->name, init_name)) | |
428 | continue; | |
429 | ||
430 | /* Then, we need to know if this method appears in the | |
431 | superclass. (This is where this function gets expensive) */ | |
432 | _Jv_Method *sm = _Jv_LookupDeclaredMethod (super_class, | |
433 | m->name, | |
434 | m->signature); | |
435 | ||
436 | /* if it was somehow declared in the superclass, skip this */ | |
437 | if (sm != NULL) | |
438 | continue; | |
439 | ||
440 | /* but if it is final, and not declared in the super class, | |
441 | * then we also skip it */ | |
442 | if ((m->accflags & FINAL) != 0) | |
443 | continue; | |
444 | ||
445 | /* finally, we can assign the index of this method */ | |
446 | /* m->vtable_index = base_offset */ | |
447 | base_offset += 1; | |
448 | } | |
449 | ||
450 | return base_offset; | |
451 | } | |
452 | ||
453 | /* this is installed in place of abstract methods */ | |
454 | static void | |
455 | _Jv_abstractMethodError () | |
456 | { | |
457 | JvThrow (new java::lang::AbstractMethodError); | |
458 | } | |
459 | ||
460 | void | |
461 | _Jv_PrepareClass(jclass klass) | |
462 | { | |
463 | /* | |
464 | * The job of this function is to: 1) assign storage to fields, and 2) | |
465 | * build the vtable. static fields are assigned real memory, instance | |
466 | * fields are assigned offsets. | |
467 | * | |
468 | * NOTE: we have a contract with the garbage collector here. Static | |
469 | * reference fields must not be resolved, until after they have storage | |
470 | * assigned which is the check used by the collector to see if it | |
471 | * should indirect the static field reference and mark the object | |
472 | * pointed to. | |
473 | * | |
474 | * Most fields are resolved lazily (i.e. have their class-type | |
475 | * assigned) when they are accessed the first time by calling as part | |
476 | * of _Jv_ResolveField, which is allways called after _Jv_PrepareClass. | |
477 | * Static fields with initializers are resolved as part of this | |
478 | * function, as are fields with primitive types. | |
479 | */ | |
480 | ||
481 | if (! _Jv_IsInterpretedClass (klass)) | |
482 | return; | |
483 | ||
484 | if (klass->state >= JV_STATE_PREPARED) | |
485 | return; | |
486 | ||
487 | // make sure super-class is linked. This involves taking a lock on | |
488 | // the super class, so we use the Java method resolveClass, which will | |
489 | // unlock it properly, should an exception happen. | |
490 | ||
491 | java::lang::ClassLoader::resolveClass (klass->superclass); | |
492 | ||
493 | _Jv_InterpClass *clz = (_Jv_InterpClass*)klass; | |
494 | ||
495 | /************ PART ONE: OBJECT LAYOUT ***************/ | |
496 | ||
497 | int instance_size; | |
498 | int static_size; | |
499 | ||
500 | // java.lang.Object is never interpreted! | |
501 | instance_size = clz->superclass->size (); | |
502 | static_size = 0; | |
503 | ||
504 | for (int i = 0; i < clz->field_count; i++) | |
505 | { | |
506 | int field_size; | |
507 | int field_align; | |
508 | ||
509 | _Jv_Field *field = &clz->fields[i]; | |
510 | ||
511 | if (! field->isRef ()) | |
512 | { | |
513 | // it's safe to resolve the field here, since it's | |
514 | // a primitive class, which does not cause loading to happen. | |
515 | _Jv_ResolveField (field, clz->loader); | |
516 | ||
517 | field_size = field->type->size (); | |
518 | field_align = get_alignment_from_class (field->type); | |
519 | } | |
520 | else | |
521 | { | |
522 | field_size = sizeof (jobject); | |
523 | field_align = __alignof__ (jobject); | |
524 | } | |
525 | ||
526 | #ifndef COMPACT_FIELDS | |
527 | field->bsize = field_size; | |
528 | #endif | |
529 | ||
530 | if (field->flags & STATIC) | |
531 | { | |
532 | /* this computes an offset into a region we'll allocate | |
533 | shortly, and then add this offset to the start address */ | |
534 | ||
535 | static_size = ROUND (static_size, field_align); | |
536 | field->u.boffset = static_size; | |
537 | static_size += field_size; | |
538 | } | |
539 | else | |
540 | { | |
541 | instance_size = ROUND (instance_size, field_align); | |
542 | field->u.boffset = instance_size; | |
543 | instance_size += field_size; | |
544 | } | |
545 | } | |
546 | ||
547 | // set the instance size for the class | |
548 | clz->size_in_bytes = instance_size; | |
549 | ||
550 | // allocate static memory | |
551 | if (static_size != 0) | |
552 | { | |
553 | char *static_data = (char*)_Jv_AllocBytesChecked (static_size); | |
554 | ||
555 | memset (static_data, 0, static_size); | |
556 | ||
557 | for (int i = 0; i < clz->field_count; i++) | |
558 | { | |
559 | _Jv_Field *field = &clz->fields[i]; | |
560 | ||
561 | if ((field->flags & STATIC) != 0) | |
562 | { | |
563 | field->u.addr = static_data + field->u.boffset; | |
564 | ||
565 | if (clz->field_initializers[i] != 0) | |
566 | { | |
567 | _Jv_ResolveField (field, clz->loader); | |
568 | _Jv_InitField (0, clz, i); | |
569 | } | |
570 | } | |
571 | } | |
572 | ||
573 | // now we don't need the field_initializers anymore, so let the | |
574 | // collector get rid of it! | |
575 | ||
576 | clz->field_initializers = 0; | |
577 | } | |
578 | ||
579 | /************ PART TWO: VTABLE LAYOUT ***************/ | |
580 | ||
581 | /* preparation: build the vtable stubs (even interfaces can) | |
582 | have code -- for static constructors. */ | |
583 | for (int i = 0; i < clz->method_count; i++) | |
584 | { | |
585 | _Jv_InterpMethod *imeth = clz->interpreted_methods[i]; | |
586 | ||
587 | if (imeth != 0) // it could be abstract or native | |
588 | { | |
589 | clz->methods[i].ncode = imeth->ncode (); | |
590 | } | |
591 | else | |
592 | { | |
593 | if ((clz->methods[i].accflags & NATIVE) != 0) | |
594 | { | |
595 | JvThrow | |
596 | (new java::lang::VirtualMachineError | |
597 | (JvNewStringLatin1 | |
598 | ("the interpreter does not support native methods"))); | |
599 | } | |
600 | } | |
601 | } | |
602 | ||
603 | if (clz->accflags & INTERFACE) | |
604 | { | |
605 | clz->state = JV_STATE_PREPARED; | |
606 | clz->notifyAll (); | |
607 | return; | |
608 | } | |
609 | ||
610 | /* FIXME: native methods for interpreted classes should be handled, I | |
611 | * dunno exactly how, but it seems that we should try to find them at | |
612 | * this point, and if we fail, try again after <clinit>, since it | |
613 | * could have caused additional code to be loaded. Interfaces cannot | |
614 | * have native methods (not even for static initialization). */ | |
615 | ||
616 | ||
617 | /* Now onto the actual job: vtable layout. First, count how many new | |
618 | methods we have */ | |
619 | int new_method_count = 0; | |
620 | ||
621 | jclass super_class = clz->getSuperclass (); | |
622 | ||
623 | if (super_class == 0) | |
624 | throw_internal_error ("cannot handle interpreted base classes"); | |
625 | ||
626 | for (int i = 0; i < clz->method_count; i++) | |
627 | { | |
628 | _Jv_Method *this_meth = &clz->methods[i]; | |
629 | ||
630 | if ((this_meth->accflags & (STATIC|PRIVATE)) != 0 | |
631 | || _Jv_equalUtf8Consts (this_meth->name, init_name)) | |
632 | { | |
633 | /* skip this, it doesn't go in the vtable */ | |
634 | continue; | |
635 | } | |
636 | ||
637 | _Jv_Method *orig_meth = _Jv_LookupDeclaredMethod (super_class, | |
638 | this_meth->name, | |
639 | this_meth->signature); | |
640 | ||
641 | if (orig_meth == 0) | |
642 | { | |
643 | // new methods that are final, also don't go in the vtable | |
644 | if ((this_meth->accflags & FINAL) != 0) | |
645 | continue; | |
646 | ||
647 | new_method_count += 1; | |
648 | continue; | |
649 | } | |
650 | ||
651 | if ((orig_meth->accflags & (STATIC|PRIVATE|FINAL)) != 0 | |
652 | || ((orig_meth->accflags & ABSTRACT) == 0 | |
653 | && (this_meth->accflags & ABSTRACT) != 0 | |
654 | && (klass->accflags & ABSTRACT) == 0)) | |
655 | { | |
656 | clz->state = JV_STATE_ERROR; | |
657 | clz->notifyAll (); | |
658 | JvThrow (new java::lang::IncompatibleClassChangeError | |
659 | (clz->getName ())); | |
660 | } | |
661 | ||
662 | /* FIXME: At this point, if (loader != super_class->loader), we | |
663 | * need to "impose class loader constraints" for the types | |
664 | * involved in the signature of this method */ | |
665 | } | |
666 | ||
667 | /* determine size */ | |
668 | int vtable_count = (super_class->vtable_method_count) + new_method_count; | |
669 | clz->vtable_method_count = vtable_count; | |
670 | ||
671 | /* allocate vtable structure */ | |
672 | _Jv_VTable *vtable = (_Jv_VTable*) | |
673 | _Jv_AllocBytesChecked (sizeof (_Jv_VTable) | |
674 | + (sizeof (void*) * (vtable_count))); | |
675 | vtable->clas = clz; | |
676 | ||
677 | /* copy super class' vtable entries (index 0 goes unused). */ | |
678 | memcpy ((void*)&vtable->method[1], | |
679 | (void*)&super_class->vtable->method[1], | |
680 | sizeof (void*) * super_class->vtable_method_count); | |
681 | ||
682 | /* now, install our own vtable entries, reprise... */ | |
683 | for (int i = 0; i < clz->method_count; i++) | |
684 | { | |
685 | _Jv_Method *this_meth = &clz->methods[i]; | |
686 | ||
687 | int index = _Jv_DetermineVTableIndex (clz, | |
688 | this_meth->name, | |
689 | this_meth->signature); | |
690 | ||
691 | if (index == 0) | |
692 | throw_internal_error ("method now found in own class"); | |
693 | ||
694 | if (index != -1) | |
695 | { | |
696 | if (index > clz->vtable_method_count+1) | |
697 | throw_internal_error ("vtable problem..."); | |
698 | ||
699 | if (clz->interpreted_methods[i] == 0) | |
700 | vtable->method[index] = (void*)&_Jv_abstractMethodError; | |
701 | else | |
702 | vtable->method[index] = this_meth->ncode; | |
703 | } | |
704 | } | |
705 | ||
706 | /* finally, assign the vtable! */ | |
707 | clz->vtable = vtable; | |
708 | ||
709 | /* wooha! we're done. */ | |
710 | clz->state = JV_STATE_PREPARED; | |
711 | clz->notifyAll (); | |
712 | } | |
713 | ||
714 | /** Do static initialization for fields with a constant initializer */ | |
715 | void | |
716 | _Jv_InitField (jobject obj, jclass klass, int index) | |
717 | { | |
718 | if (obj != 0 && klass == 0) | |
719 | klass = obj->getClass (); | |
720 | ||
721 | if (!_Jv_IsInterpretedClass (klass)) | |
722 | return; | |
723 | ||
724 | _Jv_InterpClass *clz = (_Jv_InterpClass*)klass; | |
725 | ||
726 | _Jv_Field * field = (&clz->fields[0]) + index; | |
727 | ||
728 | if (index > clz->field_count) | |
729 | throw_internal_error ("field out of range"); | |
730 | ||
731 | int init = clz->field_initializers[index]; | |
732 | if (init == 0) | |
733 | return; | |
734 | ||
735 | _Jv_Constants *pool = &clz->constants; | |
736 | int tag = pool->tags[init]; | |
737 | ||
738 | if (! field->isResolved ()) | |
739 | throw_internal_error ("initializing unresolved field"); | |
740 | ||
741 | if (obj==0 && ((field->flags & STATIC) == 0)) | |
742 | throw_internal_error ("initializing non-static field with no object"); | |
743 | ||
744 | void *addr = 0; | |
745 | ||
746 | if ((field->flags & STATIC) != 0) | |
747 | addr = (void*) field->u.addr; | |
748 | else | |
749 | addr = (void*) (((char*)obj) + field->u.boffset); | |
750 | ||
751 | switch (tag) | |
752 | { | |
753 | case JV_CONSTANT_String: | |
754 | { | |
755 | _Jv_MonitorEnter (clz); | |
756 | jstring str; | |
757 | str = _Jv_NewStringUtf8Const ((_Jv_Utf8Const *) pool->data[init]); | |
758 | pool->data[init] = (void *) str; | |
759 | pool->tags[init] = JV_CONSTANT_ResolvedString; | |
760 | _Jv_MonitorExit (clz); | |
761 | } | |
762 | /* fall through */ | |
763 | ||
764 | case JV_CONSTANT_ResolvedString: | |
765 | if (! (field->type == &StringClass || field->type == &ObjectClass)) | |
766 | throw_class_format_error ("string initialiser to non-string field"); | |
767 | ||
768 | *(jstring*)addr = *(jstring*) (pool->data + init); | |
769 | break; | |
770 | ||
771 | case JV_CONSTANT_Integer: | |
772 | { | |
773 | int value = *(jint*)(pool->data + init); | |
774 | ||
775 | if (field->type == JvPrimClass (boolean)) | |
776 | *(jboolean*)addr = (jboolean)value; | |
777 | ||
778 | else if (field->type == JvPrimClass (byte)) | |
779 | *(jbyte*)addr = (jbyte)value; | |
780 | ||
781 | else if (field->type == JvPrimClass (char)) | |
782 | *(jchar*)addr = (jchar)value; | |
783 | ||
784 | else if (field->type == JvPrimClass (short)) | |
785 | *(jshort*)addr = (jshort)value; | |
786 | ||
787 | else if (field->type == JvPrimClass (int)) | |
788 | *(jint*)addr = (jint)value; | |
789 | ||
790 | else | |
791 | throw_class_format_error ("erroneous field initializer"); | |
792 | } | |
793 | break; | |
794 | ||
795 | case JV_CONSTANT_Long: | |
796 | if (field->type != JvPrimClass (long)) | |
797 | throw_class_format_error ("erroneous field initializer"); | |
798 | ||
799 | memcpy (addr, pool->data+init, 8); | |
800 | break; | |
801 | ||
802 | case JV_CONSTANT_Float: | |
803 | if (field->type != JvPrimClass (float)) | |
804 | throw_class_format_error ("erroneous field initializer"); | |
805 | ||
806 | memcpy (addr, pool->data+init, 4); | |
807 | break; | |
808 | ||
809 | case JV_CONSTANT_Double: | |
810 | if (field->type != JvPrimClass (double)) | |
811 | throw_class_format_error ("erroneous field initializer"); | |
812 | ||
813 | memcpy (addr, pool->data+init, 8); | |
814 | break; | |
815 | ||
816 | default: | |
817 | throw_class_format_error ("erroneous field initializer"); | |
818 | } | |
819 | } | |
820 | ||
821 | static int | |
822 | get_alignment_from_class (jclass klass) | |
823 | { | |
824 | if (klass == JvPrimClass (byte)) | |
825 | return __alignof__ (jbyte); | |
826 | else if (klass == JvPrimClass (short)) | |
827 | return __alignof__ (jshort); | |
828 | else if (klass == JvPrimClass (int)) | |
829 | return __alignof__ (jint); | |
830 | else if (klass == JvPrimClass (long)) | |
831 | return __alignof__ (jlong); | |
832 | else if (klass == JvPrimClass (boolean)) | |
833 | return __alignof__ (jboolean); | |
834 | else if (klass == JvPrimClass (char)) | |
835 | return __alignof__ (jchar); | |
836 | else if (klass == JvPrimClass (float)) | |
837 | return __alignof__ (jfloat); | |
838 | else if (klass == JvPrimClass (double)) | |
839 | return __alignof__ (jdouble); | |
840 | else | |
841 | return __alignof__ (jobject); | |
842 | } | |
843 | ||
844 | ||
845 | inline static unsigned char* | |
846 | skip_one_type (unsigned char* ptr) | |
847 | { | |
848 | int ch = *ptr++; | |
849 | ||
850 | while (ch == '[') | |
851 | { | |
852 | ch = *ptr++; | |
853 | } | |
854 | ||
855 | if (ch == 'L') | |
856 | { | |
857 | do { ch = *ptr++; } while (ch != ';'); | |
858 | } | |
859 | ||
860 | return ptr; | |
861 | } | |
862 | ||
863 | static ffi_type* | |
864 | get_ffi_type_from_signature (unsigned char* ptr) | |
865 | { | |
866 | switch (*ptr) | |
867 | { | |
868 | case 'L': | |
869 | case '[': | |
870 | return &ffi_type_pointer; | |
871 | break; | |
872 | ||
873 | case 'Z': | |
874 | case 'B': | |
875 | return &ffi_type_sint8; | |
876 | break; | |
877 | ||
878 | case 'C': | |
879 | return &ffi_type_uint16; | |
880 | break; | |
881 | ||
882 | case 'S': | |
883 | return &ffi_type_sint16; | |
884 | break; | |
885 | ||
886 | case 'I': | |
887 | return &ffi_type_sint32; | |
888 | break; | |
889 | ||
890 | case 'J': | |
891 | return &ffi_type_sint64; | |
892 | break; | |
893 | ||
894 | case 'F': | |
895 | return &ffi_type_float; | |
896 | break; | |
897 | ||
898 | case 'D': | |
899 | return &ffi_type_double; | |
900 | break; | |
901 | ||
902 | case 'V': | |
903 | return &ffi_type_void; | |
904 | break; | |
905 | } | |
906 | ||
907 | throw_internal_error ("unknown type in signature"); | |
908 | } | |
909 | ||
910 | /* this function yields the number of actual arguments, that is, if the | |
911 | * function is non-static, then one is added to the number of elements | |
912 | * found in the signature */ | |
913 | ||
914 | static int | |
915 | count_arguments (_Jv_Utf8Const *signature, | |
916 | jboolean staticp) | |
917 | { | |
918 | unsigned char *ptr = (unsigned char*) signature->data; | |
919 | int arg_count = staticp ? 0 : 1; | |
920 | ||
921 | /* first, count number of arguments */ | |
922 | ||
923 | // skip '(' | |
924 | ptr++; | |
925 | ||
926 | // count args | |
927 | while (*ptr != ')') | |
928 | { | |
929 | ptr = skip_one_type (ptr); | |
930 | arg_count += 1; | |
931 | } | |
932 | ||
933 | return arg_count; | |
934 | } | |
935 | ||
936 | /* This beast will build a cif, given the signature. Memory for | |
937 | * the cif itself and for the argument types must be allocated by the | |
938 | * caller. | |
939 | */ | |
940 | ||
941 | static int | |
942 | init_cif (_Jv_Utf8Const* signature, | |
943 | int arg_count, | |
944 | jboolean staticp, | |
945 | ffi_cif *cif, | |
946 | ffi_type **arg_types) | |
947 | { | |
948 | unsigned char *ptr = (unsigned char*) signature->data; | |
949 | ||
950 | int arg_index = 0; // arg number | |
951 | int item_count = 0; // stack-item count | |
952 | ||
953 | // setup receiver | |
954 | if (!staticp) | |
955 | { | |
956 | arg_types[arg_index++] = &ffi_type_pointer; | |
957 | item_count += 1; | |
958 | } | |
959 | ||
960 | // skip '(' | |
961 | ptr++; | |
962 | ||
963 | // assign arg types | |
964 | while (*ptr != ')') | |
965 | { | |
966 | arg_types[arg_index++] = get_ffi_type_from_signature (ptr); | |
967 | ||
968 | if (*ptr == 'J' || *ptr == 'D') | |
969 | item_count += 2; | |
970 | else | |
971 | item_count += 1; | |
972 | ||
973 | ptr = skip_one_type (ptr); | |
974 | } | |
975 | ||
976 | // skip ')' | |
977 | ptr++; | |
978 | ffi_type *rtype = get_ffi_type_from_signature (ptr); | |
979 | ||
980 | ptr = skip_one_type (ptr); | |
981 | if (ptr != (unsigned char*)signature->data + signature->length) | |
982 | throw_internal_error ("did not find end of signature"); | |
983 | ||
984 | if (ffi_prep_cif (cif, FFI_DEFAULT_ABI, | |
985 | arg_count, rtype, arg_types) != FFI_OK) | |
986 | throw_internal_error ("ffi_prep_cif failed"); | |
987 | ||
988 | return item_count; | |
989 | } | |
990 | ||
991 | ||
992 | /* we put this one here, and not in interpret.cc because it | |
993 | * calls the utility routines count_arguments | |
994 | * which are static to this module. The following struct defines the | |
995 | * layout we use for the stubs, it's only used in the ncode method. */ | |
996 | ||
997 | typedef struct { | |
998 | ffi_raw_closure closure; | |
999 | ffi_cif cif; | |
1000 | ffi_type *arg_types[0]; | |
1001 | } ncode_closure; | |
1002 | ||
1003 | typedef void (*ffi_closure_fun) (ffi_cif*,void*,ffi_raw*,void*); | |
1004 | ||
1005 | void* _Jv_InterpMethod::ncode () | |
1006 | { | |
1007 | if (self->ncode != 0) | |
1008 | return self->ncode; | |
1009 | ||
1010 | jboolean staticp = (self->accflags & STATIC) != 0; | |
1011 | int arg_count = count_arguments (self->signature, staticp); | |
1012 | ||
1013 | ncode_closure *closure = | |
1014 | (ncode_closure*)_Jv_AllocBytesChecked (sizeof (ncode_closure) | |
1015 | + arg_count * sizeof (ffi_type*)); | |
1016 | ||
1017 | init_cif (self->signature, | |
1018 | arg_count, | |
1019 | staticp, | |
1020 | &closure->cif, | |
1021 | &closure->arg_types[0]); | |
1022 | ||
1023 | ffi_closure_fun fun; | |
1024 | ||
1025 | args_raw_size = ffi_raw_size (&closure->cif); | |
1026 | ||
1027 | if ((self->accflags & SYNCHRONIZED) != 0) | |
1028 | { | |
1029 | if (staticp) | |
1030 | fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_class; | |
1031 | else | |
1032 | fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_object; | |
1033 | } | |
1034 | else | |
1035 | { | |
1036 | fun = (ffi_closure_fun)&_Jv_InterpMethod::run_normal; | |
1037 | } | |
1038 | ||
1039 | ffi_prep_raw_closure (&closure->closure, | |
1040 | &closure->cif, | |
1041 | fun, | |
1042 | (void*)this); | |
1043 | ||
1044 | self->ncode = (void*)closure; | |
1045 | return self->ncode; | |
1046 | } | |
1047 | ||
1048 | ||
1049 | /* A _Jv_ResolvedMethod is what is put in the constant pool for a | |
1050 | * MethodRef or InterfacemethodRef. */ | |
1051 | static _Jv_ResolvedMethod* | |
1052 | _Jv_BuildResolvedMethod (_Jv_Method* method, | |
1053 | jclass klass, | |
1054 | jboolean staticp, | |
1055 | jint vtable_index) | |
1056 | { | |
1057 | int arg_count = count_arguments (method->signature, staticp); | |
1058 | ||
1059 | _Jv_ResolvedMethod* result = (_Jv_ResolvedMethod*) | |
1060 | _Jv_AllocBytesChecked (sizeof (_Jv_ResolvedMethod) | |
1061 | + arg_count*sizeof (ffi_type*)); | |
1062 | ||
1063 | result->stack_item_count | |
1064 | = init_cif (method->signature, | |
1065 | arg_count, | |
1066 | staticp, | |
1067 | &result->cif, | |
1068 | &result->arg_types[0]); | |
1069 | ||
1070 | result->vtable_index = vtable_index; | |
1071 | result->method = method; | |
1072 | result->klass = klass; | |
1073 | ||
1074 | return result; | |
1075 | } | |
1076 | ||
1077 | ||
1078 | static void | |
1079 | throw_class_format_error (jstring msg) | |
1080 | { | |
1081 | if (msg == 0) | |
1082 | JvThrow (new java::lang::ClassFormatError); | |
1083 | else | |
1084 | JvThrow (new java::lang::ClassFormatError (msg)); | |
1085 | } | |
1086 | ||
1087 | static void | |
1088 | throw_class_format_error (char *msg) | |
1089 | { | |
1090 | throw_class_format_error (JvNewStringLatin1 (msg)); | |
1091 | } | |
1092 | ||
1093 | static void | |
1094 | throw_internal_error (char *msg) | |
1095 | { | |
1096 | JvThrow | |
1097 | (new java::lang::InternalError (JvNewStringLatin1 (msg))); | |
1098 | } | |
1099 | ||
1100 | ||
1101 | #endif |