X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libjava%2Fprims.cc;h=a0ad08e738fb52985226b1a4cabc9b8fc4eb076f;hb=e755efce8f87ba0b194aab239570f6a0690dc88a;hp=4d11cd8d7bbe3cbe4b068ce7d436e3226779941c;hpb=95ef33d1772a9cc5a602b9a671ed84e45558eb3c;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libjava/prims.cc b/libjava/prims.cc index 4d11cd8d7bb..a0ad08e738f 100644 --- a/libjava/prims.cc +++ b/libjava/prims.cc @@ -1,6 +1,6 @@ // prims.cc - Code for core of runtime environment. -/* Copyright (C) 1998, 1999 Cygnus Solutions +/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation This file is part of libgcj. @@ -9,74 +9,169 @@ Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ #include +#include #include #include #include #include +#include -#pragma implementation "java-array.h" +#ifdef HAVE_UNISTD_H +#include +#endif -#include +#include #include #include +#include +#include + +#ifdef ENABLE_JVMPI +#include +#include +#endif + +#ifndef DISABLE_GETENV_PROPERTIES +#include +#include +#define PROCESS_GCJ_PROPERTIES process_gcj_properties() +#else +#define PROCESS_GCJ_PROPERTIES +#endif // DISABLE_GETENV_PROPERTIES #include +#include #include #include #include #include -#include #include #include #include -#include +#include #include #include #include -#include #include +#include #include #include - - -#define ObjectClass _CL_Q34java4lang6Object -extern java::lang::Class ObjectClass; +#include +#include +#include +#include +#include + +#ifdef USE_LTDL +#include +#endif // We allocate a single OutOfMemoryError exception which we keep // around for use if we run out of memory. static java::lang::OutOfMemoryError *no_memory; -// Largest representable size_t. -#define SIZE_T_MAX ((size_t) (~ (size_t) 0)) +// Number of bytes in largest array object we create. This could be +// increased to the largest size_t value, so long as the appropriate +// functions are changed to take a size_t argument instead of jint. +#define MAX_OBJECT_SIZE ((1<<31) - 1) + +static const char *no_properties[] = { NULL }; + +// Properties set at compile time. +const char **_Jv_Compiler_Properties = no_properties; + +// The JAR file to add to the beginning of java.class.path. +const char *_Jv_Jar_Class_Path; + +#ifndef DISABLE_GETENV_PROPERTIES +// Property key/value pairs. +property_pair *_Jv_Environment_Properties; +#endif + +// Stash the argv pointer to benefit native libraries that need it. +const char **_Jv_argv; +int _Jv_argc; + +// Argument support. +int +_Jv_GetNbArgs (void) +{ + // _Jv_argc is 0 if not explicitly initialized. + return _Jv_argc; +} +const char * +_Jv_GetSafeArg (int index) +{ + if (index >=0 && index < _Jv_GetNbArgs ()) + return _Jv_argv[index]; + else + return ""; +} + +void +_Jv_SetArgs (int argc, const char **argv) +{ + _Jv_argc = argc; + _Jv_argv = argv; +} + +#ifdef ENABLE_JVMPI +// Pointer to JVMPI notification functions. +void (*_Jv_JVMPI_Notify_OBJECT_ALLOC) (JVMPI_Event *event); +void (*_Jv_JVMPI_Notify_THREAD_START) (JVMPI_Event *event); +void (*_Jv_JVMPI_Notify_THREAD_END) (JVMPI_Event *event); +#endif +/* Unblock a signal. Unless we do this, the signal may only be sent + once. */ +static void +unblock_signal (int signum) +{ +#ifdef _POSIX_VERSION + sigset_t sigs; + + sigemptyset (&sigs); + sigaddset (&sigs, signum); + sigprocmask (SIG_UNBLOCK, &sigs, NULL); +#endif +} + #ifdef HANDLE_SEGV -static java::lang::NullPointerException *nullp; SIGNAL_HANDLER (catch_segv) { - MAKE_THROW_FRAME; - _Jv_Throw (nullp); + java::lang::NullPointerException *nullp + = new java::lang::NullPointerException; + unblock_signal (SIGSEGV); + MAKE_THROW_FRAME (nullp); + throw nullp; } #endif #ifdef HANDLE_FPE -static java::lang::ArithmeticException *arithexception; SIGNAL_HANDLER (catch_fpe) { - MAKE_THROW_FRAME; - _Jv_Throw (arithexception); + java::lang::ArithmeticException *arithexception + = new java::lang::ArithmeticException (JvNewStringLatin1 ("/ by zero")); + unblock_signal (SIGFPE); +#ifdef HANDLE_DIVIDE_OVERFLOW + HANDLE_DIVIDE_OVERFLOW; +#else + MAKE_THROW_FRAME (arithexception); +#endif + throw arithexception; } #endif jboolean -_Jv_equalUtf8Consts (Utf8Const* a, Utf8Const *b) +_Jv_equalUtf8Consts (const Utf8Const* a, const Utf8Const *b) { - register int len; - register _Jv_ushort *aptr, *bptr; + int len; + const _Jv_ushort *aptr, *bptr; if (a == b) return true; if (a->hash != b->hash) @@ -84,8 +179,8 @@ _Jv_equalUtf8Consts (Utf8Const* a, Utf8Const *b) len = a->length; if (b->length != len) return false; - aptr = (_Jv_ushort *)a->data; - bptr = (_Jv_ushort *)b->data; + aptr = (const _Jv_ushort *)a->data; + bptr = (const _Jv_ushort *)b->data; len = (len + 1) >> 1; while (--len >= 0) if (*aptr++ != *bptr++) @@ -94,7 +189,7 @@ _Jv_equalUtf8Consts (Utf8Const* a, Utf8Const *b) } /* True iff A is equal to STR. - HASH is STR->hashCode(). + HASH is STR->hashCode(). */ jboolean @@ -105,8 +200,8 @@ _Jv_equal (Utf8Const* a, jstring str, jint hash) jint len = str->length(); jint i = 0; jchar *sptr = _Jv_GetStringChars (str); - register unsigned char* ptr = (unsigned char*) a->data; - register unsigned char* limit = ptr + a->length; + unsigned char* ptr = (unsigned char*) a->data; + unsigned char* limit = ptr + a->length; for (;; i++, sptr++) { int ch = UTF8_GET (ptr, limit); @@ -118,22 +213,42 @@ _Jv_equal (Utf8Const* a, jstring str, jint hash) return true; } +/* Like _Jv_equal, but stop after N characters. */ +jboolean +_Jv_equaln (Utf8Const *a, jstring str, jint n) +{ + jint len = str->length(); + jint i = 0; + jchar *sptr = _Jv_GetStringChars (str); + unsigned char* ptr = (unsigned char*) a->data; + unsigned char* limit = ptr + a->length; + for (; n-- > 0; i++, sptr++) + { + int ch = UTF8_GET (ptr, limit); + if (i == len) + return ch < 0; + if (ch != *sptr) + return false; + } + return true; +} + /* Count the number of Unicode chars encoded in a given Ut8 string. */ int _Jv_strLengthUtf8(char* str, int len) { - register unsigned char* ptr; - register unsigned char* limit; + unsigned char* ptr; + unsigned char* limit; int str_length; ptr = (unsigned char*) str; limit = ptr + len; str_length = 0; - for (; ptr < limit; str_length++) { - if (UTF8_GET (ptr, limit) < 0) { - return (-1); + for (; ptr < limit; str_length++) + { + if (UTF8_GET (ptr, limit) < 0) + return (-1); } - } return (str_length); } @@ -143,8 +258,8 @@ _Jv_strLengthUtf8(char* str, int len) static jint hashUtf8String (char* str, int len) { - register unsigned char* ptr = (unsigned char*) str; - register unsigned char* limit = ptr + len; + unsigned char* ptr = (unsigned char*) str; + unsigned char* limit = ptr + len; jint hash = 0; for (; ptr < limit;) @@ -163,8 +278,6 @@ _Jv_makeUtf8Const (char* s, int len) if (len < 0) len = strlen (s); Utf8Const* m = (Utf8Const*) _Jv_AllocBytes (sizeof(Utf8Const) + len + 1); - if (! m) - JvThrow (no_memory); memcpy (m->data, s, len); m->data[len] = 0; m->length = len; @@ -172,6 +285,24 @@ _Jv_makeUtf8Const (char* s, int len) return (m); } +_Jv_Utf8Const * +_Jv_makeUtf8Const (jstring string) +{ + jint hash = string->hashCode (); + jint len = _Jv_GetStringUTFLength (string); + + Utf8Const* m = (Utf8Const*) + _Jv_AllocBytes (sizeof(Utf8Const) + len + 1); + + m->hash = hash; + m->length = len; + + _Jv_GetStringUTFRegion (string, 0, string->length (), m->data); + m->data[len] = 0; + + return m; +} + #ifdef DEBUG @@ -188,10 +319,7 @@ _Jv_Abort (const char *, const char *, int, const char *message) "libgcj failure: %s\n in function %s, file %s, line %d\n", message, function, file, line); #else - java::io::PrintStream *err = java::lang::System::err; - err->print(JvNewStringLatin1 ("libgcj failure: ")); - err->println(JvNewStringLatin1 (message)); - err->flush(); + fprintf (stderr, "libgcj failure: %s\n", message); #endif abort (); } @@ -211,102 +339,221 @@ _Jv_GCWatch (jobject obj) void _Jv_ThrowBadArrayIndex(jint bad_index) { - JvThrow (new java::lang::ArrayIndexOutOfBoundsException - (java::lang::String::valueOf(bad_index))); + throw new java::lang::ArrayIndexOutOfBoundsException + (java::lang::String::valueOf (bad_index)); } -void* -_Jv_CheckCast (jclass c, jobject obj) +void +_Jv_ThrowNullPointerException () { - if (obj != NULL && ! c->isAssignableFrom(obj->getClass())) - JvThrow (new java::lang::ClassCastException); - return obj; + throw new java::lang::NullPointerException; } -void -_Jv_CheckArrayStore (jobject arr, jobject obj) +// Explicitly throw a no memory exception. +// The collector calls this when it encounters an out-of-memory condition. +void _Jv_ThrowNoMemory() +{ + throw no_memory; +} + +#ifdef ENABLE_JVMPI +static void +jvmpi_notify_alloc(jclass klass, jint size, jobject obj) { - if (obj) + // Service JVMPI allocation request. + if (__builtin_expect (_Jv_JVMPI_Notify_OBJECT_ALLOC != 0, false)) { - JvAssert (arr != NULL); - jclass arr_class = arr->getClass(); - JvAssert (arr_class->isArray()); - jclass elt_class = arr_class->getComponentType(); - jclass obj_class = obj->getClass(); - if (! elt_class->isAssignableFrom(obj_class)) - JvThrow (new java::lang::ArrayStoreException); + JVMPI_Event event; + + event.event_type = JVMPI_EVENT_OBJECT_ALLOC; + event.env_id = NULL; + event.u.obj_alloc.arena_id = 0; + event.u.obj_alloc.class_id = (jobjectID) klass; + event.u.obj_alloc.is_array = 0; + event.u.obj_alloc.size = size; + event.u.obj_alloc.obj_id = (jobjectID) obj; + + // FIXME: This doesn't look right for the Boehm GC. A GC may + // already be in progress. _Jv_DisableGC () doesn't wait for it. + // More importantly, I don't see the need for disabling GC, since we + // blatantly have a pointer to obj on our stack, ensuring that the + // object can't be collected. Even for a nonconservative collector, + // it appears to me that this must be true, since we are about to + // return obj. Isn't this whole approach way too intrusive for + // a useful profiling interface? - HB + _Jv_DisableGC (); + (*_Jv_JVMPI_Notify_OBJECT_ALLOC) (&event); + _Jv_EnableGC (); } } +#else /* !ENABLE_JVMPI */ +# define jvmpi_notify_alloc(klass,size,obj) /* do nothing */ +#endif - +// Allocate a new object of class KLASS. +// First a version that assumes that we have no finalizer, and that +// the class is already initialized. +// If we know that JVMPI is disabled, this can be replaced by a direct call +// to the allocator for the appropriate GC. +jobject +_Jv_AllocObjectNoInitNoFinalizer (jclass klass) +{ + jint size = klass->size (); + jobject obj = (jobject) _Jv_AllocObj (size, klass); + jvmpi_notify_alloc (klass, size, obj); + return obj; +} -// Allocate some unscanned memory and throw an exception if no memory. -void * -_Jv_AllocBytesChecked (jsize size) +// And now a version that initializes if necessary. +jobject +_Jv_AllocObjectNoFinalizer (jclass klass) { - void *r = _Jv_AllocBytes (size); - if (! r) - _Jv_Throw (no_memory); - return r; + _Jv_InitClass (klass); + jint size = klass->size (); + jobject obj = (jobject) _Jv_AllocObj (size, klass); + jvmpi_notify_alloc (klass, size, obj); + return obj; } -// Allocate a new object of class C. SIZE is the size of the object -// to allocate. You might think this is redundant, but it isn't; some -// classes, such as String, aren't of fixed size. +// And now the general version that registers a finalizer if necessary. jobject -_Jv_AllocObject (jclass c, jint size) -{ - _Jv_InitClass (c); - - jobject obj = (jobject) _Jv_AllocObj (size); - if (! obj) - JvThrow (no_memory); - *((_Jv_VTable **) obj) = c->vtable; - - // If this class has inherited finalize from Object, then don't - // bother registering a finalizer. We know that finalize() is the - // very first method after the dummy entry. If this turns out to be - // unreliable, a more robust implementation can be written. Such an - // implementation would look for Object.finalize in Object's method - // table at startup, and then use that information to find the - // appropriate index in the method vector. - if (c->vtable->method[1] != ObjectClass.vtable->method[1]) +_Jv_AllocObject (jclass klass) +{ + jobject obj = _Jv_AllocObjectNoFinalizer (klass); + + // We assume that the compiler only generates calls to this routine + // if there really is an interesting finalizer. + // Unfortunately, we still have to the dynamic test, since there may + // be cni calls to this routine. + // Note that on IA64 get_finalizer() returns the starting address of the + // function, not a function pointer. Thus this still works. + if (klass->vtable->get_finalizer () + != java::lang::Object::class$.vtable->get_finalizer ()) _Jv_RegisterFinalizer (obj, _Jv_FinalizeObject); + return obj; +} + +// Allocate a String, including variable length storage. +jstring +_Jv_AllocString(jsize len) +{ + using namespace java::lang; + + jsize sz = sizeof(java::lang::String) + len * sizeof(jchar); + + // We assert that for strings allocated this way, the data field + // will always point to the object itself. Thus there is no reason + // for the garbage collector to scan any of it. + // Furthermore, we're about to overwrite the string data, so + // initialization of the object is not an issue. + // String needs no initialization, and there is no finalizer, so + // we can go directly to the collector's allocator interface. + jstring obj = (jstring) _Jv_AllocPtrFreeObj(sz, &String::class$); + + obj->data = obj; + obj->boffset = sizeof(java::lang::String); + obj->count = len; + obj->cachedHashCode = 0; + +#ifdef ENABLE_JVMPI + // Service JVMPI request. + + if (__builtin_expect (_Jv_JVMPI_Notify_OBJECT_ALLOC != 0, false)) + { + JVMPI_Event event; + + event.event_type = JVMPI_EVENT_OBJECT_ALLOC; + event.env_id = NULL; + event.u.obj_alloc.arena_id = 0; + event.u.obj_alloc.class_id = (jobjectID) &String::class$; + event.u.obj_alloc.is_array = 0; + event.u.obj_alloc.size = sz; + event.u.obj_alloc.obj_id = (jobjectID) obj; + + _Jv_DisableGC (); + (*_Jv_JVMPI_Notify_OBJECT_ALLOC) (&event); + _Jv_EnableGC (); + } +#endif + return obj; } +// A version of the above that assumes the object contains no pointers, +// and requires no finalization. This can't happen if we need pointers +// to locks. +#ifdef JV_HASH_SYNCHRONIZATION +jobject +_Jv_AllocPtrFreeObject (jclass klass) +{ + _Jv_InitClass (klass); + jint size = klass->size (); + + jobject obj = (jobject) _Jv_AllocPtrFreeObj (size, klass); + +#ifdef ENABLE_JVMPI + // Service JVMPI request. + + if (__builtin_expect (_Jv_JVMPI_Notify_OBJECT_ALLOC != 0, false)) + { + JVMPI_Event event; + + event.event_type = JVMPI_EVENT_OBJECT_ALLOC; + event.env_id = NULL; + event.u.obj_alloc.arena_id = 0; + event.u.obj_alloc.class_id = (jobjectID) klass; + event.u.obj_alloc.is_array = 0; + event.u.obj_alloc.size = size; + event.u.obj_alloc.obj_id = (jobjectID) obj; + + _Jv_DisableGC (); + (*_Jv_JVMPI_Notify_OBJECT_ALLOC) (&event); + _Jv_EnableGC (); + } +#endif + + return obj; +} +#endif /* JV_HASH_SYNCHRONIZATION */ + + // Allocate a new array of Java objects. Each object is of type // `elementClass'. `init' is used to initialize each slot in the // array. jobjectArray _Jv_NewObjectArray (jsize count, jclass elementClass, jobject init) { - if (count < 0) - JvThrow (new java::lang::NegativeArraySizeException); + if (__builtin_expect (count < 0, false)) + throw new java::lang::NegativeArraySizeException; + + JvAssert (! elementClass->isPrimitive ()); + // Ensure that elements pointer is properly aligned. + jobjectArray obj = NULL; + size_t size = (size_t) elements (obj); // Check for overflow. - if ((size_t) count > (SIZE_T_MAX - sizeof (__JArray)) / sizeof (jobject)) - JvThrow (no_memory); - - size_t size = count * sizeof (jobject) + sizeof (__JArray); - jclass clas = _Jv_FindArrayClass (elementClass); - jobjectArray obj = (jobjectArray) _Jv_AllocArray (size); - if (! obj) - JvThrow (no_memory); - obj->length = count; - jobject* ptr = elements(obj); + if (__builtin_expect ((size_t) count > + (MAX_OBJECT_SIZE - 1 - size) / sizeof (jobject), false)) + throw no_memory; + + size += count * sizeof (jobject); + + jclass klass = _Jv_GetArrayClass (elementClass, + elementClass->getClassLoaderInternal()); + + obj = (jobjectArray) _Jv_AllocArray (size, klass); + // Cast away const. + jsize *lp = const_cast (&obj->length); + *lp = count; // We know the allocator returns zeroed memory. So don't bother // zeroing it again. if (init) { + jobject *ptr = elements(obj); while (--count >= 0) *ptr++ = init; } - // Set the vtbl last to avoid problems if the GC happens during the - // window in this function between the allocation and this - // assignment. - *((_Jv_VTable **) obj) = clas->vtable; return obj; } @@ -316,76 +563,37 @@ jobject _Jv_NewPrimArray (jclass eltype, jint count) { int elsize = eltype->size(); - if (count < 0) - JvThrow (new java::lang::NegativeArraySizeException ()); + if (__builtin_expect (count < 0, false)) + throw new java::lang::NegativeArraySizeException; + + JvAssert (eltype->isPrimitive ()); + jobject dummy = NULL; + size_t size = (size_t) _Jv_GetArrayElementFromElementType (dummy, eltype); // Check for overflow. - if ((size_t) count > (SIZE_T_MAX - sizeof (__JArray)) / elsize) - JvThrow (no_memory); - - __JArray *arr = (__JArray*) _Jv_AllocObj (sizeof (__JArray) - + elsize * count); - if (! arr) - JvThrow (no_memory); - arr->length = count; + if (__builtin_expect ((size_t) count > + (MAX_OBJECT_SIZE - size) / elsize, false)) + throw no_memory; + + jclass klass = _Jv_GetArrayClass (eltype, 0); + +# ifdef JV_HASH_SYNCHRONIZATION + // Since the vtable is always statically allocated, + // these are completely pointerfree! Make sure the GC doesn't touch them. + __JArray *arr = + (__JArray*) _Jv_AllocPtrFreeObj (size + elsize * count, klass); + memset((char *)arr + size, 0, elsize * count); +# else + __JArray *arr = (__JArray*) _Jv_AllocObj (size + elsize * count, klass); // Note that we assume we are given zeroed memory by the allocator. +# endif + // Cast away const. + jsize *lp = const_cast (&arr->length); + *lp = count; - jclass klass = _Jv_FindArrayClass (eltype); - // Set the vtbl last to avoid problems if the GC happens during the - // window in this function between the allocation and this - // assignment. - *((_Jv_VTable **) arr) = klass->vtable; return arr; } -jcharArray -JvNewCharArray (jint length) -{ - return (jcharArray) _Jv_NewPrimArray (JvPrimClass (char), length); -} - -jbooleanArray -JvNewBooleanArray (jint length) -{ - return (jbooleanArray) _Jv_NewPrimArray (JvPrimClass (boolean), length); -} - -jbyteArray -JvNewByteArray (jint length) -{ - return (jbyteArray) _Jv_NewPrimArray (JvPrimClass (byte), length); -} - -jshortArray -JvNewShortArray (jint length) -{ - return (jshortArray) _Jv_NewPrimArray (JvPrimClass (short), length); -} - -jintArray -JvNewIntArray (jint length) -{ - return (jintArray) _Jv_NewPrimArray (JvPrimClass (int), length); -} - -jlongArray -JvNewLongArray (jint length) -{ - return (jlongArray) _Jv_NewPrimArray (JvPrimClass (long), length); -} - -jfloatArray -JvNewFloatArray (jint length) -{ - return (jfloatArray) _Jv_NewPrimArray (JvPrimClass (float), length); -} - -jdoubleArray -JvNewDoubleArray (jint length) -{ - return (jdoubleArray) _Jv_NewPrimArray (JvPrimClass (double), length); -} - jobject _Jv_NewArray (jint type, jint size) { @@ -400,12 +608,14 @@ _Jv_NewArray (jint type, jint size) case 10: return JvNewIntArray (size); case 11: return JvNewLongArray (size); } - JvFail ("newarray - bad type code"); - return NULL; // Placate compiler. + throw new java::lang::InternalError + (JvNewStringLatin1 ("invalid type code in _Jv_NewArray")); } -jobject -_Jv_NewMultiArray (jclass type, jint dimensions, jint *sizes) +// Allocate a possibly multi-dimensional array but don't check that +// any array length is <0. +static jobject +_Jv_NewMultiArrayUnchecked (jclass type, jint dimensions, jint *sizes) { JvAssert (type->isArray()); jclass element_type = type->getComponentType(); @@ -421,14 +631,24 @@ _Jv_NewMultiArray (jclass type, jint dimensions, jint *sizes) JvAssert (element_type->isArray()); jobject *contents = elements ((jobjectArray) result); for (int i = 0; i < sizes[0]; ++i) - contents[i] = _Jv_NewMultiArray (element_type, dimensions - 1, - sizes + 1); + contents[i] = _Jv_NewMultiArrayUnchecked (element_type, dimensions - 1, + sizes + 1); } return result; } jobject +_Jv_NewMultiArray (jclass type, jint dimensions, jint *sizes) +{ + for (int i = 0; i < dimensions; ++i) + if (sizes[i] < 0) + throw new java::lang::NegativeArraySizeException; + + return _Jv_NewMultiArrayUnchecked (type, dimensions, sizes); +} + +jobject _Jv_NewMultiArray (jclass array_type, jint dimensions, ...) { va_list args; @@ -437,61 +657,54 @@ _Jv_NewMultiArray (jclass array_type, jint dimensions, ...) for (int i = 0; i < dimensions; ++i) { jint size = va_arg (args, jint); + if (size < 0) + throw new java::lang::NegativeArraySizeException; sizes[i] = size; } va_end (args); - return _Jv_NewMultiArray (array_type, dimensions, sizes); + return _Jv_NewMultiArrayUnchecked (array_type, dimensions, sizes); } -class _Jv_PrimClass : public java::lang::Class -{ -public: - // FIXME: calling convention is weird. If we use the natural types - // then the compiler will complain because they aren't Java types. - _Jv_PrimClass (jobject cname, jbyte sig, jint len) - { - using namespace java::lang::reflect; - - // We must initialize every field of the class. We do this in - // the same order they are declared in Class.h. - next = NULL; - name = _Jv_makeUtf8Const ((char *) cname, -1); - accflags = Modifier::PUBLIC | Modifier::FINAL; - superclass = NULL; - constants.size = 0; - constants.tags = NULL; - constants.data = NULL; - methods = NULL; - method_count = sig; - vtable_method_count = 0; - fields = NULL; - size_in_bytes = len; - field_count = 0; - static_field_count = 0; - vtable = JV_PRIMITIVE_VTABLE; - interfaces = NULL; - loader = NULL; - interface_count = 0; - state = 0; // FIXME. - thread = NULL; - } -}; +// Ensure 8-byte alignment, for hash synchronization. +#define DECLARE_PRIM_TYPE(NAME) \ + _Jv_ArrayVTable _Jv_##NAME##VTable; \ + java::lang::Class _Jv_##NAME##Class __attribute__ ((aligned (8))); + +DECLARE_PRIM_TYPE(byte) +DECLARE_PRIM_TYPE(short) +DECLARE_PRIM_TYPE(int) +DECLARE_PRIM_TYPE(long) +DECLARE_PRIM_TYPE(boolean) +DECLARE_PRIM_TYPE(char) +DECLARE_PRIM_TYPE(float) +DECLARE_PRIM_TYPE(double) +DECLARE_PRIM_TYPE(void) -#define DECLARE_PRIM_TYPE(NAME, SIG, LEN) \ - _Jv_PrimClass _Jv_##NAME##Class((jobject) #NAME, (jbyte) SIG, (jint) LEN) - -DECLARE_PRIM_TYPE(byte, 'B', 1); -DECLARE_PRIM_TYPE(short, 'S', 2); -DECLARE_PRIM_TYPE(int, 'I', 4); -DECLARE_PRIM_TYPE(long, 'J', 8); -DECLARE_PRIM_TYPE(boolean, 'Z', 1); -DECLARE_PRIM_TYPE(char, 'C', 2); -DECLARE_PRIM_TYPE(float, 'F', 4); -DECLARE_PRIM_TYPE(double, 'D', 8); -DECLARE_PRIM_TYPE(void, 'V', 0); +void +_Jv_InitPrimClass (jclass cl, char *cname, char sig, int len, + _Jv_ArrayVTable *array_vtable) +{ + using namespace java::lang::reflect; + + // We must set the vtable for the class; the Java constructor + // doesn't do this. + (*(_Jv_VTable **) cl) = java::lang::Class::class$.vtable; + + // Initialize the fields we care about. We do this in the same + // order they are declared in Class.h. + cl->name = _Jv_makeUtf8Const ((char *) cname, -1); + cl->accflags = Modifier::PUBLIC | Modifier::FINAL | Modifier::ABSTRACT; + cl->method_count = sig; + cl->size_in_bytes = len; + cl->vtable = JV_PRIMITIVE_VTABLE; + cl->state = JV_STATE_DONE; + cl->depth = -1; + if (sig != 'V') + _Jv_NewArrayClass (cl, NULL, (_Jv_VTable *) array_vtable); +} jclass _Jv_FindClassFromSignature (char *sig, java::lang::ClassLoader *loader) @@ -525,9 +738,14 @@ _Jv_FindClassFromSignature (char *sig, java::lang::ClassLoader *loader) return _Jv_FindClass (name, loader); } case '[': - return _Jv_FindArrayClass (_Jv_FindClassFromSignature (&sig[1], loader)); + { + jclass klass = _Jv_FindClassFromSignature (&sig[1], loader); + if (! klass) + return NULL; + return _Jv_GetArrayClass (klass, loader); + } } - JvFail ("couldn't understand class signature"); + return NULL; // Placate compiler. } @@ -539,12 +757,20 @@ JvConvertArgv (int argc, const char **argv) if (argc < 0) argc = 0; jobjectArray ar = JvNewObjectArray(argc, &StringClass, NULL); - jobject* ptr = elements(ar); + jobject *ptr = elements(ar); + jbyteArray bytes = NULL; for (int i = 0; i < argc; i++) { const char *arg = argv[i]; - // FIXME - should probably use JvNewStringUTF. - *ptr++ = JvNewStringLatin1(arg, strlen(arg)); + int len = strlen (arg); + if (bytes == NULL || bytes->length < len) + bytes = JvNewByteArray (len); + jbyte *bytePtr = elements (bytes); + // We assume jbyte == char. + memcpy (bytePtr, arg, len); + + // Now convert using the default encoding. + *ptr++ = new java::lang::String (bytes, 0, len); } return (JArray*) ar; } @@ -555,30 +781,342 @@ JvConvertArgv (int argc, const char **argv) // it will only scan the qthreads stacks. // Command line arguments. -static jobject arg_vec; - -// The primary threadgroup. -static java::lang::ThreadGroup *main_group; +static JArray *arg_vec; // The primary thread. static java::lang::Thread *main_thread; -void -JvRunMain (jclass klass, int argc, const char **argv) +#ifndef DISABLE_GETENV_PROPERTIES + +static char * +next_property_key (char *s, size_t *length) { + size_t l = 0; + + JvAssert (s); + + // Skip over whitespace + while (isspace (*s)) + s++; + + // If we've reached the end, return NULL. Also return NULL if for + // some reason we've come across a malformed property string. + if (*s == 0 + || *s == ':' + || *s == '=') + return NULL; + + // Determine the length of the property key. + while (s[l] != 0 + && ! isspace (s[l]) + && s[l] != ':' + && s[l] != '=') + { + if (s[l] == '\\' + && s[l+1] != 0) + l++; + l++; + } + + *length = l; + + return s; +} + +static char * +next_property_value (char *s, size_t *length) +{ + size_t l = 0; + + JvAssert (s); + + while (isspace (*s)) + s++; + + if (*s == ':' + || *s == '=') + s++; + + while (isspace (*s)) + s++; + + // If we've reached the end, return NULL. + if (*s == 0) + return NULL; + + // Determine the length of the property value. + while (s[l] != 0 + && ! isspace (s[l]) + && s[l] != ':' + && s[l] != '=') + { + if (s[l] == '\\' + && s[l+1] != 0) + l += 2; + else + l++; + } + + *length = l; + + return s; +} + +static void +process_gcj_properties () +{ + char *props = getenv("GCJ_PROPERTIES"); + char *p = props; + size_t length; + size_t property_count = 0; + + if (NULL == props) + return; + + // Whip through props quickly in order to count the number of + // property values. + while (p && (p = next_property_key (p, &length))) + { + // Skip to the end of the key + p += length; + + p = next_property_value (p, &length); + if (p) + p += length; + + property_count++; + } + + // Allocate an array of property value/key pairs. + _Jv_Environment_Properties = + (property_pair *) malloc (sizeof(property_pair) + * (property_count + 1)); + + // Go through the properties again, initializing _Jv_Properties + // along the way. + p = props; + property_count = 0; + while (p && (p = next_property_key (p, &length))) + { + _Jv_Environment_Properties[property_count].key = p; + _Jv_Environment_Properties[property_count].key_length = length; + + // Skip to the end of the key + p += length; + + p = next_property_value (p, &length); + + _Jv_Environment_Properties[property_count].value = p; + _Jv_Environment_Properties[property_count].value_length = length; + + if (p) + p += length; + + property_count++; + } + memset ((void *) &_Jv_Environment_Properties[property_count], + 0, sizeof (property_pair)); + { + size_t i = 0; + + // Null terminate the strings. + while (_Jv_Environment_Properties[i].key) + { + property_pair *prop = &_Jv_Environment_Properties[i]; + prop->key[prop->key_length] = 0; + prop->value[prop->value_length] = 0; + i++; + } + } +} +#endif // DISABLE_GETENV_PROPERTIES + +namespace gcj +{ + _Jv_Utf8Const *void_signature; + _Jv_Utf8Const *clinit_name; + _Jv_Utf8Const *init_name; + _Jv_Utf8Const *finit_name; + + bool runtimeInitialized = false; +} + +jint +_Jv_CreateJavaVM (void* /*vm_args*/) +{ + using namespace gcj; + + if (runtimeInitialized) + return -1; + + runtimeInitialized = true; + + PROCESS_GCJ_PROPERTIES; + + _Jv_InitThreads (); + _Jv_InitGC (); + _Jv_InitializeSyncMutex (); + +#ifdef INTERPRETER + _Jv_InitInterpreter (); +#endif + +#ifdef HANDLE_SEGV INIT_SEGV; +#endif + +#ifdef HANDLE_FPE INIT_FPE; +#endif + + /* Initialize Utf8 constants declared in jvm.h. */ + void_signature = _Jv_makeUtf8Const ("()V", 3); + clinit_name = _Jv_makeUtf8Const ("", 8); + init_name = _Jv_makeUtf8Const ("", 6); + finit_name = _Jv_makeUtf8Const ("finit$", 6); + + /* Initialize built-in classes to represent primitive TYPEs. */ + _Jv_InitPrimClass (&_Jv_byteClass, "byte", 'B', 1, &_Jv_byteVTable); + _Jv_InitPrimClass (&_Jv_shortClass, "short", 'S', 2, &_Jv_shortVTable); + _Jv_InitPrimClass (&_Jv_intClass, "int", 'I', 4, &_Jv_intVTable); + _Jv_InitPrimClass (&_Jv_longClass, "long", 'J', 8, &_Jv_longVTable); + _Jv_InitPrimClass (&_Jv_booleanClass, "boolean", 'Z', 1, &_Jv_booleanVTable); + _Jv_InitPrimClass (&_Jv_charClass, "char", 'C', 2, &_Jv_charVTable); + _Jv_InitPrimClass (&_Jv_floatClass, "float", 'F', 4, &_Jv_floatVTable); + _Jv_InitPrimClass (&_Jv_doubleClass, "double", 'D', 8, &_Jv_doubleVTable); + _Jv_InitPrimClass (&_Jv_voidClass, "void", 'V', 0, &_Jv_voidVTable); + + // Turn stack trace generation off while creating exception objects. + _Jv_InitClass (&java::lang::VMThrowable::class$); + java::lang::VMThrowable::trace_enabled = 0; + + // We have to initialize this fairly early, to avoid circular class + // initialization. In particular we want to start the + // initialization of ClassLoader before we start the initialization + // of VMClassLoader. + _Jv_InitClass (&java::lang::ClassLoader::class$); + + // Once the bootstrap loader is in place, change it into a kind of + // system loader, by having it read the class path. + gnu::gcj::runtime::VMClassLoader::initialize(); no_memory = new java::lang::OutOfMemoryError; - arg_vec = JvConvertArgv (argc - 1, argv + 1); - main_group = new java::lang::ThreadGroup (23); - main_thread = new java::lang::FirstThread (main_group, klass, arg_vec); + java::lang::VMThrowable::trace_enabled = 1; + +#ifdef USE_LTDL + LTDL_SET_PRELOADED_SYMBOLS (); +#endif + + _Jv_platform_initialize (); + + _Jv_JNI_Init (); + + _Jv_GCInitializeFinalizers (&::gnu::gcj::runtime::FinalizerThread::finalizerReady); + + // Start the GC finalizer thread. A VirtualMachineError can be + // thrown by the runtime if, say, threads aren't available. + try + { + using namespace gnu::gcj::runtime; + FinalizerThread *ft = new FinalizerThread (); + ft->start (); + } + catch (java::lang::VirtualMachineError *ignore) + { + } + + return 0; +} + +void +_Jv_RunMain (jclass klass, const char *name, int argc, const char **argv, + bool is_jar) +{ +#ifndef DISABLE_MAIN_ARGS + _Jv_SetArgs (argc, argv); +#endif + + java::lang::Runtime *runtime = NULL; + + try + { + // Set this very early so that it is seen when java.lang.System + // is initialized. + if (is_jar) + _Jv_Jar_Class_Path = strdup (name); + _Jv_CreateJavaVM (NULL); + + // Get the Runtime here. We want to initialize it before searching + // for `main'; that way it will be set up if `main' is a JNI method. + runtime = java::lang::Runtime::getRuntime (); + +#ifdef DISABLE_MAIN_ARGS + arg_vec = JvConvertArgv (0, 0); +#else + arg_vec = JvConvertArgv (argc - 1, argv + 1); +#endif + + using namespace gnu::java::lang; + if (klass) + main_thread = new MainThread (klass, arg_vec); + else + main_thread = new MainThread (JvNewStringLatin1 (name), + arg_vec, is_jar); + } + catch (java::lang::Throwable *t) + { + java::lang::System::err->println (JvNewStringLatin1 + ("Exception during runtime initialization")); + t->printStackTrace(); + runtime->exit (1); + } - main_thread->start(); + _Jv_AttachCurrentThread (main_thread); + _Jv_ThreadRun (main_thread); _Jv_ThreadWait (); - java::lang::Runtime::getRuntime ()->exit (0); + int status = (int) java::lang::ThreadGroup::had_uncaught_exception; + runtime->exit (status); +} + +void +JvRunMain (jclass klass, int argc, const char **argv) +{ + _Jv_RunMain (klass, NULL, argc, argv, false); +} + + + +// Parse a string and return a heap size. +static size_t +parse_heap_size (const char *spec) +{ + char *end; + unsigned long val = strtoul (spec, &end, 10); + if (*end == 'k' || *end == 'K') + val *= 1024; + else if (*end == 'm' || *end == 'M') + val *= 1048576; + return (size_t) val; +} + +// Set the initial heap size. This might be ignored by the GC layer. +// This must be called before _Jv_RunMain. +void +_Jv_SetInitialHeapSize (const char *arg) +{ + size_t size = parse_heap_size (arg); + _Jv_GCSetInitialHeapSize (size); +} + +// Set the maximum heap size. This might be ignored by the GC layer. +// This must be called before _Jv_RunMain. +void +_Jv_SetMaximumHeapSize (const char *arg) +{ + size_t size = parse_heap_size (arg); + _Jv_GCSetMaximumHeapSize (size); } @@ -586,16 +1124,124 @@ JvRunMain (jclass klass, int argc, const char **argv) void * _Jv_Malloc (jsize size) { - if (size == 0) + if (__builtin_expect (size == 0, false)) size = 1; void *ptr = malloc ((size_t) size); - if (ptr == NULL) - JvThrow (no_memory); + if (__builtin_expect (ptr == NULL, false)) + throw no_memory; return ptr; } +void * +_Jv_Realloc (void *ptr, jsize size) +{ + if (__builtin_expect (size == 0, false)) + size = 1; + ptr = realloc (ptr, (size_t) size); + if (__builtin_expect (ptr == NULL, false)) + throw no_memory; + return ptr; +} + +void * +_Jv_MallocUnchecked (jsize size) +{ + if (__builtin_expect (size == 0, false)) + size = 1; + return malloc ((size_t) size); +} + void _Jv_Free (void* ptr) { return free (ptr); } + + + +// In theory, these routines can be #ifdef'd away on machines which +// support divide overflow signals. However, we never know if some +// code might have been compiled with "-fuse-divide-subroutine", so we +// always include them in libgcj. + +jint +_Jv_divI (jint dividend, jint divisor) +{ + if (__builtin_expect (divisor == 0, false)) + { + java::lang::ArithmeticException *arithexception + = new java::lang::ArithmeticException (JvNewStringLatin1 ("/ by zero")); + throw arithexception; + } + + if (dividend == (jint) 0x80000000L && divisor == -1) + return dividend; + + return dividend / divisor; +} + +jint +_Jv_remI (jint dividend, jint divisor) +{ + if (__builtin_expect (divisor == 0, false)) + { + java::lang::ArithmeticException *arithexception + = new java::lang::ArithmeticException (JvNewStringLatin1 ("/ by zero")); + throw arithexception; + } + + if (dividend == (jint) 0x80000000L && divisor == -1) + return 0; + + return dividend % divisor; +} + +jlong +_Jv_divJ (jlong dividend, jlong divisor) +{ + if (__builtin_expect (divisor == 0, false)) + { + java::lang::ArithmeticException *arithexception + = new java::lang::ArithmeticException (JvNewStringLatin1 ("/ by zero")); + throw arithexception; + } + + if (dividend == (jlong) 0x8000000000000000LL && divisor == -1) + return dividend; + + return dividend / divisor; +} + +jlong +_Jv_remJ (jlong dividend, jlong divisor) +{ + if (__builtin_expect (divisor == 0, false)) + { + java::lang::ArithmeticException *arithexception + = new java::lang::ArithmeticException (JvNewStringLatin1 ("/ by zero")); + throw arithexception; + } + + if (dividend == (jlong) 0x8000000000000000LL && divisor == -1) + return 0; + + return dividend % divisor; +} + + + +// Return true if SELF_KLASS can access a field or method in +// OTHER_KLASS. The field or method's access flags are specified in +// FLAGS. +jboolean +_Jv_CheckAccess (jclass self_klass, jclass other_klass, jint flags) +{ + using namespace java::lang::reflect; + return ((self_klass == other_klass) + || ((flags & Modifier::PUBLIC) != 0) + || (((flags & Modifier::PROTECTED) != 0) + && other_klass->isAssignableFrom (self_klass)) + || (((flags & Modifier::PRIVATE) == 0) + && _Jv_ClassNameSamePackage (self_klass->name, + other_klass->name))); +}