]>
Commit | Line | Data |
---|---|---|
ee9dd372 TT |
1 | // prims.cc - Code for core of runtime environment. |
2 | ||
3 | /* Copyright (C) 1998, 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 | #include <config.h> | |
12 | ||
13 | #include <stdlib.h> | |
14 | #include <stdarg.h> | |
15 | #include <stdio.h> | |
16 | #include <string.h> | |
17 | #ifdef HANDLE_SEGV | |
18 | #include <signal.h> | |
19 | #endif | |
20 | ||
21 | #pragma implementation "java-array.h" | |
22 | ||
23 | #include <cni.h> | |
24 | #include <jvm.h> | |
25 | ||
26 | #include <java/lang/Class.h> | |
27 | #include <java/lang/Runtime.h> | |
28 | #include <java/lang/String.h> | |
29 | #include <java/lang/Thread.h> | |
30 | #include <java/lang/ThreadGroup.h> | |
31 | #include <java/lang/FirstThread.h> | |
32 | #include <java/lang/ArrayIndexOutOfBoundsException.h> | |
33 | #include <java/lang/ClassFormatError.h> | |
34 | #include <java/lang/ClassCastException.h> | |
35 | #include <java/lang/NegativeArraySizeException.h> | |
36 | #include <java/lang/NullPointerException.h> | |
37 | #include <java/lang/OutOfMemoryError.h> | |
38 | #include <java/lang/ArrayStoreException.h> | |
39 | #include <java/lang/System.h> | |
40 | #include <java/lang/reflect/Modifier.h> | |
41 | #include <java/io/PrintStream.h> | |
42 | ||
43 | #define ObjectClass _CL_Q34java4lang6Object | |
44 | extern java::lang::Class ObjectClass; | |
45 | ||
46 | // We allocate a single OutOfMemoryError exception which we keep | |
47 | // around for use if we run out of memory. | |
48 | static java::lang::OutOfMemoryError *no_memory; | |
49 | ||
50 | // Largest representable size_t. | |
51 | #define SIZE_T_MAX ((size_t) (~ (size_t) 0)) | |
52 | ||
53 | \f | |
54 | ||
55 | jboolean | |
56 | _Jv_equalUtf8Consts (Utf8Const* a, Utf8Const *b) | |
57 | { | |
58 | register int len; | |
59 | register _Jv_ushort *aptr, *bptr; | |
60 | if (a == b) | |
61 | return true; | |
62 | if (a->hash != b->hash) | |
63 | return false; | |
64 | len = a->length; | |
65 | if (b->length != len) | |
66 | return false; | |
67 | aptr = (_Jv_ushort *)a->data; | |
68 | bptr = (_Jv_ushort *)b->data; | |
69 | len = (len + 1) >> 1; | |
70 | while (--len >= 0) | |
71 | if (*aptr++ != *bptr++) | |
72 | return false; | |
73 | return true; | |
74 | } | |
75 | ||
76 | /* True iff A is equal to STR. | |
77 | HASH is STR->hashCode(). | |
78 | */ | |
79 | ||
80 | jboolean | |
81 | _Jv_equal (Utf8Const* a, jstring str, jint hash) | |
82 | { | |
83 | if (a->hash != (_Jv_ushort) hash) | |
84 | return false; | |
85 | jint len = str->length(); | |
86 | jint i = 0; | |
87 | jchar *sptr = _Jv_GetStringChars (str); | |
88 | register unsigned char* ptr = (unsigned char*) a->data; | |
89 | register unsigned char* limit = ptr + a->length; | |
90 | for (;; i++, sptr++) | |
91 | { | |
92 | int ch = UTF8_GET (ptr, limit); | |
93 | if (i == len) | |
94 | return ch < 0; | |
95 | if (ch != *sptr) | |
96 | return false; | |
97 | } | |
98 | return true; | |
99 | } | |
100 | ||
101 | /* Count the number of Unicode chars encoded in a given Ut8 string. */ | |
102 | int | |
103 | _Jv_strLengthUtf8(char* str, int len) | |
104 | { | |
105 | register unsigned char* ptr; | |
106 | register unsigned char* limit; | |
107 | int str_length; | |
108 | ||
109 | ptr = (unsigned char*) str; | |
110 | limit = ptr + len; | |
111 | str_length = 0; | |
112 | for (; ptr < limit; str_length++) { | |
113 | if (UTF8_GET (ptr, limit) < 0) { | |
114 | return (-1); | |
115 | } | |
116 | } | |
117 | return (str_length); | |
118 | } | |
119 | ||
120 | /* Calculate a hash value for a string encoded in Utf8 format. | |
121 | * This returns the same hash value as specified or java.lang.String.hashCode. | |
122 | */ | |
123 | static jint | |
124 | hashUtf8String (char* str, int len) | |
125 | { | |
126 | register unsigned char* ptr = (unsigned char*) str; | |
127 | register unsigned char* limit = ptr + len; | |
128 | jint hash = 0; | |
129 | ||
130 | for (; ptr < limit;) | |
131 | { | |
132 | int ch = UTF8_GET (ptr, limit); | |
133 | /* Updated specification from | |
134 | http://www.javasoft.com/docs/books/jls/clarify.html. */ | |
135 | hash = (31 * hash) + ch; | |
136 | } | |
137 | return hash; | |
138 | } | |
139 | ||
140 | _Jv_Utf8Const * | |
141 | _Jv_makeUtf8Const (char* s, int len) | |
142 | { | |
143 | if (len < 0) | |
144 | len = strlen (s); | |
145 | Utf8Const* m = (Utf8Const*) _Jv_AllocBytes (sizeof(Utf8Const) + len + 1); | |
146 | if (! m) | |
147 | JvThrow (no_memory); | |
148 | memcpy (m->data, s, len); | |
149 | m->data[len] = 0; | |
150 | m->length = len; | |
151 | m->hash = hashUtf8String (s, len) & 0xFFFF; | |
152 | return (m); | |
153 | } | |
154 | ||
155 | \f | |
156 | ||
157 | #ifdef DEBUG | |
158 | void | |
159 | _Jv_Abort (const char *function, const char *file, int line, | |
160 | const char *message) | |
161 | #else | |
162 | void | |
163 | _Jv_Abort (const char *, const char *, int, const char *message) | |
164 | #endif | |
165 | { | |
166 | #ifdef DEBUG | |
167 | fprintf (stderr, | |
168 | "libgcj failure: %s\n in function %s, file %s, line %d\n", | |
169 | message, function, file, line); | |
170 | #else | |
171 | java::io::PrintStream *err = java::lang::System::err; | |
172 | err->print(JvNewStringLatin1 ("libgcj failure: ")); | |
173 | err->println(JvNewStringLatin1 (message)); | |
174 | err->flush(); | |
175 | #endif | |
176 | abort (); | |
177 | } | |
178 | ||
179 | static void | |
180 | fail_on_finalization (jobject) | |
181 | { | |
182 | JvFail ("object was finalized"); | |
183 | } | |
184 | ||
185 | void | |
186 | _Jv_GCWatch (jobject obj) | |
187 | { | |
188 | _Jv_RegisterFinalizer (obj, fail_on_finalization); | |
189 | } | |
190 | ||
191 | void | |
192 | _Jv_ThrowBadArrayIndex(jint bad_index) | |
193 | { | |
194 | JvThrow (new java::lang::ArrayIndexOutOfBoundsException | |
195 | (java::lang::String::valueOf(bad_index))); | |
196 | } | |
197 | ||
198 | void* | |
199 | _Jv_CheckCast (jclass c, jobject obj) | |
200 | { | |
201 | if (obj != NULL && ! c->isAssignableFrom(obj->getClass())) | |
202 | JvThrow (new java::lang::ClassCastException); | |
203 | return obj; | |
204 | } | |
205 | ||
206 | void | |
207 | _Jv_CheckArrayStore (jobject arr, jobject obj) | |
208 | { | |
209 | if (obj) | |
210 | { | |
211 | JvAssert (arr != NULL); | |
212 | jclass arr_class = arr->getClass(); | |
213 | JvAssert (arr_class->isArray()); | |
214 | jclass elt_class = arr_class->getComponentType(); | |
215 | jclass obj_class = obj->getClass(); | |
216 | if (! elt_class->isAssignableFrom(obj_class)) | |
217 | JvThrow (new java::lang::ArrayStoreException); | |
218 | } | |
219 | } | |
220 | ||
221 | \f | |
222 | ||
223 | // Allocate some unscanned memory and throw an exception if no memory. | |
224 | void * | |
225 | _Jv_AllocBytesChecked (jsize size) | |
226 | { | |
227 | void *r = _Jv_AllocBytes (size); | |
228 | if (! r) | |
229 | _Jv_Throw (no_memory); | |
230 | return r; | |
231 | } | |
232 | ||
233 | // Allocate a new object of class C. SIZE is the size of the object | |
234 | // to allocate. You might think this is redundant, but it isn't; some | |
235 | // classes, such as String, aren't of fixed size. | |
236 | jobject | |
237 | _Jv_AllocObject (jclass c, jint size) | |
238 | { | |
239 | _Jv_InitClass (c); | |
240 | ||
241 | jobject obj = (jobject) _Jv_AllocObj (size); | |
242 | if (! obj) | |
243 | JvThrow (no_memory); | |
244 | *((_Jv_VTable **) obj) = c->vtable; | |
245 | ||
246 | // If this class has inherited finalize from Object, then don't | |
247 | // bother registering a finalizer. We know that finalize() is the | |
248 | // very first method after the dummy entry. If this turns out to be | |
249 | // unreliable, a more robust implementation can be written. Such an | |
250 | // implementation would look for Object.finalize in Object's method | |
251 | // table at startup, and then use that information to find the | |
252 | // appropriate index in the method vector. | |
253 | if (c->vtable->method[1] != ObjectClass.vtable->method[1]) | |
254 | _Jv_RegisterFinalizer (obj, _Jv_FinalizeObject); | |
255 | ||
256 | return obj; | |
257 | } | |
258 | ||
259 | // Allocate a new array of Java objects. Each object is of type | |
260 | // `elementClass'. `init' is used to initialize each slot in the | |
261 | // array. | |
262 | jobjectArray | |
263 | _Jv_NewObjectArray (jsize count, jclass elementClass, jobject init) | |
264 | { | |
265 | if (count < 0) | |
266 | JvThrow (new java::lang::NegativeArraySizeException); | |
267 | ||
268 | // Check for overflow. | |
269 | if ((size_t) count > (SIZE_T_MAX - sizeof (__JArray)) / sizeof (jobject)) | |
270 | JvThrow (no_memory); | |
271 | ||
272 | size_t size = count * sizeof (jobject) + sizeof (__JArray); | |
273 | jclass clas = _Jv_FindArrayClass (elementClass); | |
274 | jobjectArray obj = (jobjectArray) _Jv_AllocArray (size); | |
275 | if (! obj) | |
276 | JvThrow (no_memory); | |
277 | obj->length = count; | |
278 | jobject* ptr = elements(obj); | |
279 | // We know the allocator returns zeroed memory. So don't bother | |
280 | // zeroing it again. | |
281 | if (init) | |
282 | { | |
283 | while (--count >= 0) | |
284 | *ptr++ = init; | |
285 | } | |
286 | // Set the vtbl last to avoid problems if the GC happens during the | |
287 | // window in this function between the allocation and this | |
288 | // assignment. | |
289 | *((_Jv_VTable **) obj) = clas->vtable; | |
290 | return obj; | |
291 | } | |
292 | ||
293 | // Allocate a new array of primitives. ELTYPE is the type of the | |
294 | // element, COUNT is the size of the array. | |
295 | jobject | |
296 | _Jv_NewPrimArray (jclass eltype, jint count) | |
297 | { | |
298 | int elsize = eltype->size(); | |
299 | if (count < 0) | |
300 | JvThrow (new java::lang::NegativeArraySizeException ()); | |
301 | ||
302 | // Check for overflow. | |
303 | if ((size_t) count > (SIZE_T_MAX - sizeof (__JArray)) / elsize) | |
304 | JvThrow (no_memory); | |
305 | ||
306 | __JArray *arr = (__JArray*) _Jv_AllocObj (sizeof (__JArray) | |
307 | + elsize * count); | |
308 | if (! arr) | |
309 | JvThrow (no_memory); | |
310 | arr->length = count; | |
311 | // Note that we assume we are given zeroed memory by the allocator. | |
312 | ||
313 | jclass klass = _Jv_FindArrayClass (eltype); | |
314 | // Set the vtbl last to avoid problems if the GC happens during the | |
315 | // window in this function between the allocation and this | |
316 | // assignment. | |
317 | *((_Jv_VTable **) arr) = klass->vtable; | |
318 | return arr; | |
319 | } | |
320 | ||
321 | jcharArray | |
322 | JvNewCharArray (jint length) | |
323 | { | |
324 | return (jcharArray) _Jv_NewPrimArray (JvPrimClass (char), length); | |
325 | } | |
326 | ||
327 | jbooleanArray | |
328 | JvNewBooleanArray (jint length) | |
329 | { | |
330 | return (jbooleanArray) _Jv_NewPrimArray (JvPrimClass (boolean), length); | |
331 | } | |
332 | ||
333 | jbyteArray | |
334 | JvNewByteArray (jint length) | |
335 | { | |
336 | return (jbyteArray) _Jv_NewPrimArray (JvPrimClass (byte), length); | |
337 | } | |
338 | ||
339 | jshortArray | |
340 | JvNewShortArray (jint length) | |
341 | { | |
342 | return (jshortArray) _Jv_NewPrimArray (JvPrimClass (short), length); | |
343 | } | |
344 | ||
345 | jintArray | |
346 | JvNewIntArray (jint length) | |
347 | { | |
348 | return (jintArray) _Jv_NewPrimArray (JvPrimClass (int), length); | |
349 | } | |
350 | ||
351 | jlongArray | |
352 | JvNewLongArray (jint length) | |
353 | { | |
354 | return (jlongArray) _Jv_NewPrimArray (JvPrimClass (long), length); | |
355 | } | |
356 | ||
357 | jfloatArray | |
358 | JvNewFloatArray (jint length) | |
359 | { | |
360 | return (jfloatArray) _Jv_NewPrimArray (JvPrimClass (float), length); | |
361 | } | |
362 | ||
363 | jdoubleArray | |
364 | JvNewDoubleArray (jint length) | |
365 | { | |
366 | return (jdoubleArray) _Jv_NewPrimArray (JvPrimClass (double), length); | |
367 | } | |
368 | ||
369 | jobject | |
370 | _Jv_NewArray (jint type, jint size) | |
371 | { | |
372 | switch (type) | |
373 | { | |
374 | case 4: return JvNewBooleanArray (size); | |
375 | case 5: return JvNewCharArray (size); | |
376 | case 6: return JvNewFloatArray (size); | |
377 | case 7: return JvNewDoubleArray (size); | |
378 | case 8: return JvNewByteArray (size); | |
379 | case 9: return JvNewShortArray (size); | |
380 | case 10: return JvNewIntArray (size); | |
381 | case 11: return JvNewLongArray (size); | |
382 | } | |
383 | JvFail ("newarray - bad type code"); | |
384 | return NULL; // Placate compiler. | |
385 | } | |
386 | ||
387 | jobject | |
388 | _Jv_NewMultiArray (jclass type, jint dimensions, jint *sizes) | |
389 | { | |
390 | JvAssert (type->isArray()); | |
391 | jclass element_type = type->getComponentType(); | |
392 | jobject result; | |
393 | if (element_type->isPrimitive()) | |
394 | result = _Jv_NewPrimArray (element_type, sizes[0]); | |
395 | else | |
396 | result = _Jv_NewObjectArray (sizes[0], element_type, NULL); | |
397 | ||
398 | if (dimensions > 1) | |
399 | { | |
400 | JvAssert (! element_type->isPrimitive()); | |
401 | JvAssert (element_type->isArray()); | |
402 | jobject *contents = elements ((jobjectArray) result); | |
403 | for (int i = 0; i < sizes[0]; ++i) | |
404 | contents[i] = _Jv_NewMultiArray (element_type, dimensions - 1, | |
405 | sizes + 1); | |
406 | } | |
407 | ||
408 | return result; | |
409 | } | |
410 | ||
411 | jobject | |
412 | _Jv_NewMultiArray (jclass array_type, jint dimensions, ...) | |
413 | { | |
414 | va_list args; | |
415 | jint sizes[dimensions]; | |
416 | va_start (args, dimensions); | |
417 | for (int i = 0; i < dimensions; ++i) | |
418 | { | |
419 | jint size = va_arg (args, jint); | |
420 | sizes[i] = size; | |
421 | } | |
422 | va_end (args); | |
423 | ||
424 | return _Jv_NewMultiArray (array_type, dimensions, sizes); | |
425 | } | |
426 | ||
427 | \f | |
428 | ||
429 | #ifdef HANDLE_SEGV | |
430 | ||
431 | static java::lang::NullPointerException *nullp; | |
432 | ||
433 | static void | |
434 | catch_segv (int) | |
435 | { | |
436 | // Don't run `new' in a signal handler, so we always throw the same | |
437 | // null pointer exception. | |
438 | _Jv_Throw (nullp); | |
439 | } | |
440 | ||
441 | #endif /* HANDLE_SEGV */ | |
442 | ||
443 | class _Jv_PrimClass : public java::lang::Class | |
444 | { | |
445 | public: | |
446 | // FIXME: calling convention is weird. If we use the natural types | |
447 | // then the compiler will complain because they aren't Java types. | |
448 | _Jv_PrimClass (jobject cname, jbyte sig, jint len) | |
449 | { | |
450 | using namespace java::lang::reflect; | |
451 | ||
452 | // We must initialize every field of the class. We do this in | |
453 | // the same order they are declared in Class.h. | |
454 | next = NULL; | |
455 | name = _Jv_makeUtf8Const ((char *) cname, -1); | |
456 | accflags = Modifier::PUBLIC | Modifier::FINAL; | |
457 | superclass = NULL; | |
458 | constants.size = 0; | |
459 | constants.tags = NULL; | |
460 | constants.data = NULL; | |
461 | methods = NULL; | |
462 | method_count = sig; | |
463 | vtable_method_count = 0; | |
464 | fields = NULL; | |
465 | size_in_bytes = len; | |
466 | field_count = 0; | |
467 | static_field_count = 0; | |
468 | vtable = JV_PRIMITIVE_VTABLE; | |
469 | interfaces = NULL; | |
470 | loader = NULL; | |
471 | interface_count = 0; | |
472 | state = 0; // FIXME. | |
473 | thread = NULL; | |
474 | } | |
475 | }; | |
476 | ||
477 | #define DECLARE_PRIM_TYPE(NAME, SIG, LEN) \ | |
478 | _Jv_PrimClass _Jv_##NAME##Class((jobject) #NAME, (jbyte) SIG, (jint) LEN) | |
479 | ||
480 | DECLARE_PRIM_TYPE(byte, 'B', 1); | |
481 | DECLARE_PRIM_TYPE(short, 'S', 2); | |
482 | DECLARE_PRIM_TYPE(int, 'I', 4); | |
483 | DECLARE_PRIM_TYPE(long, 'J', 8); | |
484 | DECLARE_PRIM_TYPE(boolean, 'Z', 1); | |
485 | DECLARE_PRIM_TYPE(char, 'C', 2); | |
486 | DECLARE_PRIM_TYPE(float, 'F', 4); | |
487 | DECLARE_PRIM_TYPE(double, 'D', 8); | |
488 | DECLARE_PRIM_TYPE(void, 'V', 0); | |
489 | ||
490 | jclass | |
491 | _Jv_FindClassFromSignature (char *sig, java::lang::ClassLoader *loader) | |
492 | { | |
493 | switch (*sig) | |
494 | { | |
495 | case 'B': | |
496 | return JvPrimClass (byte); | |
497 | case 'S': | |
498 | return JvPrimClass (short); | |
499 | case 'I': | |
500 | return JvPrimClass (int); | |
501 | case 'J': | |
502 | return JvPrimClass (long); | |
503 | case 'Z': | |
504 | return JvPrimClass (boolean); | |
505 | case 'C': | |
506 | return JvPrimClass (char); | |
507 | case 'F': | |
508 | return JvPrimClass (float); | |
509 | case 'D': | |
510 | return JvPrimClass (double); | |
511 | case 'V': | |
512 | return JvPrimClass (void); | |
513 | case 'L': | |
514 | { | |
515 | int i; | |
516 | for (i = 1; sig[i] && sig[i] != ';'; ++i) | |
517 | ; | |
518 | _Jv_Utf8Const *name = _Jv_makeUtf8Const (&sig[1], i - 1); | |
519 | return _Jv_FindClass (name, loader); | |
520 | } | |
521 | case '[': | |
522 | return _Jv_FindArrayClass (_Jv_FindClassFromSignature (&sig[1], loader)); | |
523 | } | |
524 | JvFail ("couldn't understand class signature"); | |
525 | return NULL; // Placate compiler. | |
526 | } | |
527 | ||
528 | \f | |
529 | ||
530 | JArray<jstring> * | |
531 | JvConvertArgv (int argc, const char **argv) | |
532 | { | |
533 | if (argc < 0) | |
534 | argc = 0; | |
535 | jobjectArray ar = JvNewObjectArray(argc, &StringClass, NULL); | |
536 | jobject* ptr = elements(ar); | |
537 | for (int i = 0; i < argc; i++) | |
538 | { | |
539 | const char *arg = argv[i]; | |
540 | // FIXME - should probably use JvNewStringUTF. | |
541 | *ptr++ = JvNewStringLatin1(arg, strlen(arg)); | |
542 | } | |
543 | return (JArray<jstring>*) ar; | |
544 | } | |
545 | ||
546 | // FIXME: These variables are static so that they will be | |
547 | // automatically scanned by the Boehm collector. This is needed | |
548 | // because with qthreads the collector won't scan the initial stack -- | |
549 | // it will only scan the qthreads stacks. | |
550 | ||
551 | // Command line arguments. | |
552 | static jobject arg_vec; | |
553 | ||
554 | // The primary threadgroup. | |
555 | static java::lang::ThreadGroup *main_group; | |
556 | ||
557 | // The primary thread. | |
558 | static java::lang::Thread *main_thread; | |
559 | ||
560 | void | |
561 | JvRunMain (jclass klass, int argc, const char **argv) | |
562 | { | |
563 | #ifdef HANDLE_SEGV | |
564 | nullp = new java::lang::NullPointerException (); | |
565 | ||
566 | struct sigaction act; | |
567 | act.sa_handler = catch_segv; | |
568 | sigemptyset (&act.sa_mask); | |
569 | act.sa_flags = 0; | |
570 | sigaction (SIGSEGV, &act, NULL); | |
571 | #endif | |
572 | ||
573 | no_memory = new java::lang::OutOfMemoryError; | |
574 | ||
575 | arg_vec = JvConvertArgv (argc - 1, argv + 1); | |
576 | main_group = new java::lang::ThreadGroup (23); | |
577 | main_thread = new java::lang::FirstThread (main_group, klass, arg_vec); | |
578 | ||
579 | main_thread->start(); | |
580 | _Jv_ThreadWait (); | |
581 | ||
582 | java::lang::Runtime::getRuntime ()->exit (0); | |
583 | } | |
584 | ||
585 | \f | |
586 | ||
587 | void * | |
588 | _Jv_Malloc (jsize size) | |
589 | { | |
590 | if (size == 0) | |
591 | size = 1; | |
592 | void *ptr = malloc ((size_t) size); | |
593 | if (ptr == NULL) | |
594 | JvThrow (no_memory); | |
595 | return ptr; | |
596 | } | |
597 | ||
598 | void | |
599 | _Jv_Free (void* ptr) | |
600 | { | |
601 | return free (ptr); | |
602 | } |