X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libjava%2Fprims.cc;h=90f8dc5ca23cb388f21ee17dc4d3c449310d1b99;hb=839f647191892bcaf51f7aab14ee75e1b84679b8;hp=f10497e7a9dc6acc4a560fe0b4c696ca1675deed;hpb=c929f1872687081b000692f04f648ddcd7dabab0;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libjava/prims.cc b/libjava/prims.cc index f10497e7a9d..90f8dc5ca23 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, 2000, 2001 Free Software Foundation +/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -9,15 +9,7 @@ Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ #include - -#ifdef USE_WIN32_SIGNALLING -#include -#endif /* USE_WIN32_SIGNALLING */ - -#ifdef USE_WINSOCK -#undef __INSIDE_CYGWIN__ -#include -#endif /* USE_WINSOCK */ +#include #include #include @@ -33,12 +25,18 @@ details. */ #include #include #include +#include #ifdef ENABLE_JVMPI #include #include #endif +#ifdef INTERPRETER +#include +#include "jvmti-int.h" +#endif + #ifndef DISABLE_GETENV_PROPERTIES #include #include @@ -53,50 +51,104 @@ details. */ #include #include #include -#include #include #include #include +#include +#include #include +#include #include #include #include +#include #include #include #include +#include +#include +#include +#include + +#ifdef INTERPRETER +#include +#include +#endif // INTERPRETER + +#include #ifdef USE_LTDL #include #endif +// Execution engine for compiled code. +_Jv_CompiledEngine _Jv_soleCompiledEngine; + +// Execution engine for code compiled with -findirect-classes +_Jv_IndirectCompiledEngine _Jv_soleIndirectCompiledEngine; + // 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)) - -static const char *no_properties[] = { NULL }; +// 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 (((size_t)1<<31) - 1) // 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; +const char **_Jv_Compiler_Properties = NULL; +int _Jv_Properties_Count = 0; #ifndef DISABLE_GETENV_PROPERTIES // Property key/value pairs. property_pair *_Jv_Environment_Properties; #endif -// The name of this executable. -static char * _Jv_execName; - // Stash the argv pointer to benefit native libraries that need it. const char **_Jv_argv; int _Jv_argc; -typedef void main_func (jobject); +// Debugging options +static bool remoteDebug = false; +#ifdef INTERPRETER +static char defaultJdwpOptions[] = ""; +static char *jdwpOptions = defaultJdwpOptions; + +// Typedefs for JVMTI agent functions. +typedef jint jvmti_agent_onload_func (JavaVM *vm, char *options, + void *reserved); +typedef jint jvmti_agent_onunload_func (JavaVM *vm); + +// JVMTI agent function pointers. +static jvmti_agent_onload_func *jvmti_agentonload = NULL; +static jvmti_agent_onunload_func *jvmti_agentonunload = NULL; +static char *jvmti_agent_opts; +#endif // INTERPRETER + +// 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. @@ -106,49 +158,54 @@ void (*_Jv_JVMPI_Notify_THREAD_END) (JVMPI_Event *event); #endif -extern "C" void _Jv_ThrowSignal (jthrowable) __attribute ((noreturn)); - -// Just like _Jv_Throw, but fill in the stack trace first. Although -// this is declared extern in order that its name not be mangled, it -// is not intended to be used outside this file. -void -_Jv_ThrowSignal (jthrowable throwable) +#if defined (HANDLE_SEGV) || defined(HANDLE_FPE) +/* Unblock a signal. Unless we do this, the signal may only be sent + once. */ +static void +unblock_signal (int signum __attribute__ ((__unused__))) { - throwable->fillInStackTrace (); - throw throwable; +#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; +#endif +#ifdef HANDLE_SEGV SIGNAL_HANDLER (catch_segv) { + unblock_signal (SIGSEGV); MAKE_THROW_FRAME (nullp); - _Jv_ThrowSignal (nullp); + java::lang::NullPointerException *nullp + = new java::lang::NullPointerException; + throw nullp; } #endif -static java::lang::ArithmeticException *arithexception; - #ifdef HANDLE_FPE SIGNAL_HANDLER (catch_fpe) { + unblock_signal (SIGFPE); #ifdef HANDLE_DIVIDE_OVERFLOW HANDLE_DIVIDE_OVERFLOW; #else MAKE_THROW_FRAME (arithexception); #endif - _Jv_ThrowSignal (arithexception); + java::lang::ArithmeticException *arithexception + = new java::lang::ArithmeticException (JvNewStringLatin1 ("/ by zero")); + throw arithexception; } #endif - jboolean -_Jv_equalUtf8Consts (Utf8Const* a, Utf8Const *b) +_Jv_equalUtf8Consts (const Utf8Const* a, const Utf8Const *b) { int len; - _Jv_ushort *aptr, *bptr; + const _Jv_ushort *aptr, *bptr; if (a == b) return true; if (a->hash != b->hash) @@ -156,8 +213,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++) @@ -210,9 +267,123 @@ _Jv_equaln (Utf8Const *a, jstring str, jint n) return true; } +// Determines whether the given Utf8Const object contains +// a type which is primitive or some derived form of it, eg. +// an array or multi-dimensional array variant. +jboolean +_Jv_isPrimitiveOrDerived(const Utf8Const *a) +{ + unsigned char *aptr = (unsigned char *) a->data; + unsigned char *alimit = aptr + a->length; + int ac = UTF8_GET(aptr, alimit); + + // Skips any leading array marks. + while (ac == '[') + ac = UTF8_GET(aptr, alimit); + + // There should not be another character. This implies that + // the type name is only one character long. + if (UTF8_GET(aptr, alimit) == -1) + switch ( ac ) + { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + case 'J': + case 'F': + case 'D': + return true; + default: + break; + } + + return false; +} + +// Find out whether two _Jv_Utf8Const candidates contain the same +// classname. +// The method is written to handle the different formats of classnames. +// Eg. "Ljava/lang/Class;", "Ljava.lang.Class;", "java/lang/Class" and +// "java.lang.Class" will be seen as equal. +// Warning: This function is not smart enough to declare "Z" and "boolean" +// and similar cases as equal (and is not meant to be used this way)! +jboolean +_Jv_equalUtf8Classnames (const Utf8Const *a, const Utf8Const *b) +{ + // If the class name's length differs by two characters + // it is possible that we have candidates which are given + // in the two different formats ("Lp1/p2/cn;" vs. "p1/p2/cn") + switch (a->length - b->length) + { + case -2: + case 0: + case 2: + break; + default: + return false; + } + + unsigned char *aptr = (unsigned char *) a->data; + unsigned char *alimit = aptr + a->length; + unsigned char *bptr = (unsigned char *) b->data; + unsigned char *blimit = bptr + b->length; + + if (alimit[-1] == ';') + alimit--; + + if (blimit[-1] == ';') + blimit--; + + int ac = UTF8_GET(aptr, alimit); + int bc = UTF8_GET(bptr, blimit); + + // Checks whether both strings have the same amount of leading [ characters. + while (ac == '[') + { + if (bc == '[') + { + ac = UTF8_GET(aptr, alimit); + bc = UTF8_GET(bptr, blimit); + continue; + } + + return false; + } + + // Skips leading L character. + if (ac == 'L') + ac = UTF8_GET(aptr, alimit); + + if (bc == 'L') + bc = UTF8_GET(bptr, blimit); + + // Compares the remaining characters. + while (ac != -1 && bc != -1) + { + // Replaces package separating dots with slashes. + if (ac == '.') + ac = '/'; + + if (bc == '.') + bc = '/'; + + // Now classnames differ if there is at least one non-matching + // character. + if (ac != bc) + return false; + + ac = UTF8_GET(aptr, alimit); + bc = UTF8_GET(bptr, blimit); + } + + return (ac == bc); +} + /* Count the number of Unicode chars encoded in a given Ut8 string. */ int -_Jv_strLengthUtf8(char* str, int len) +_Jv_strLengthUtf8(const char* str, int len) { unsigned char* ptr; unsigned char* limit; @@ -232,8 +403,8 @@ _Jv_strLengthUtf8(char* str, int len) /* Calculate a hash value for a string encoded in Utf8 format. * This returns the same hash value as specified or java.lang.String.hashCode. */ -static jint -hashUtf8String (char* str, int len) +jint +_Jv_hashUtf8String (const char* str, int len) { unsigned char* ptr = (unsigned char*) str; unsigned char* limit = ptr + len; @@ -249,19 +420,24 @@ hashUtf8String (char* str, int len) return hash; } +void +_Jv_Utf8Const::init(const char *s, int len) +{ + ::memcpy (data, s, len); + data[len] = 0; + length = len; + hash = _Jv_hashUtf8String (s, len) & 0xFFFF; +} + _Jv_Utf8Const * -_Jv_makeUtf8Const (char* s, int len) +_Jv_makeUtf8Const (const char* s, int len) { if (len < 0) len = strlen (s); - Utf8Const* m = (Utf8Const*) _Jv_AllocBytes (sizeof(Utf8Const) + len + 1); - if (! m) - throw no_memory; - memcpy (m->data, s, len); - m->data[len] = 0; - m->length = len; - m->hash = hashUtf8String (s, len) & 0xFFFF; - return (m); + Utf8Const* m + = (Utf8Const*) _Jv_AllocBytes (_Jv_Utf8Const::space_needed(s, len)); + m->init(s, len); + return m; } _Jv_Utf8Const * @@ -284,7 +460,7 @@ _Jv_makeUtf8Const (jstring string) -#ifdef DEBUG +#ifdef __GCJ_DEBUG void _Jv_Abort (const char *function, const char *file, int line, const char *message) @@ -293,16 +469,14 @@ void _Jv_Abort (const char *, const char *, int, const char *message) #endif { -#ifdef DEBUG +#ifdef __GCJ_DEBUG fprintf (stderr, "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 + fflush (stderr); abort (); } @@ -331,63 +505,134 @@ _Jv_ThrowNullPointerException () throw new java::lang::NullPointerException; } +// Resolve an entry in the constant pool and return the target +// address. +void * +_Jv_ResolvePoolEntry (jclass this_class, jint index) +{ + _Jv_Constants *pool = &this_class->constants; + + if ((pool->tags[index] & JV_CONSTANT_ResolvedFlag) != 0) + return pool->data[index].field->u.addr; + + JvSynchronize sync (this_class); + return (_Jv_Linker::resolve_pool_entry (this_class, index)) + .field->u.addr; +} + + // Explicitly throw a no memory exception. // The collector calls this when it encounters an out-of-memory condition. void _Jv_ThrowNoMemory() { - _Jv_Throw (no_memory); + throw no_memory; } -// Allocate a new object of class KLASS. 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. +#ifdef ENABLE_JVMPI +# define JVMPI_NOTIFY_ALLOC(klass,size,obj) \ + if (__builtin_expect (_Jv_JVMPI_Notify_OBJECT_ALLOC != 0, false)) \ + jvmpi_notify_alloc(klass,size,obj); +static void +jvmpi_notify_alloc(jclass klass, jint size, jobject obj) +{ + // Service JVMPI allocation request. + 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_AllocObject (jclass klass, jint size) +_Jv_AllocObjectNoInitNoFinalizer (jclass klass) { - _Jv_InitClass (klass); + jint size = klass->size (); + jobject obj = (jobject) _Jv_AllocObj (size, klass); + JVMPI_NOTIFY_ALLOC (klass, size, obj); + return obj; +} +// And now a version that initializes if necessary. +jobject +_Jv_AllocObjectNoFinalizer (jclass klass) +{ + if (_Jv_IsPhantomClass(klass) ) + throw new java::lang::NoClassDefFoundError(klass->getName()); + + _Jv_InitClass (klass); + jint size = klass->size (); jobject obj = (jobject) _Jv_AllocObj (size, klass); + JVMPI_NOTIFY_ALLOC (klass, size, obj); + return obj; +} - // 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 (klass->vtable->get_finalizer() - != java::lang::Object::class$.vtable->get_finalizer()) +// And now the general version that registers a finalizer if necessary. +jobject +_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; +} -#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; - - // 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 (); - } -#endif +// 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; + + JVMPI_NOTIFY_ALLOC (&String::class$, sz, obj); + return obj; } @@ -396,32 +641,14 @@ _Jv_AllocObject (jclass klass, jint size) // to locks. #ifdef JV_HASH_SYNCHRONIZATION jobject -_Jv_AllocPtrFreeObject (jclass klass, jint size) +_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 + JVMPI_NOTIFY_ALLOC (klass, size, obj); return obj; } @@ -434,6 +661,11 @@ _Jv_AllocPtrFreeObject (jclass klass, jint size) jobjectArray _Jv_NewObjectArray (jsize count, jclass elementClass, jobject init) { + // Creating an array of an unresolved type is impossible. So we throw + // the NoClassDefFoundError. + if ( _Jv_IsPhantomClass(elementClass) ) + throw new java::lang::NoClassDefFoundError(elementClass->getName()); + if (__builtin_expect (count < 0, false)) throw new java::lang::NegativeArraySizeException; @@ -442,10 +674,15 @@ _Jv_NewObjectArray (jsize count, jclass elementClass, jobject init) // Ensure that elements pointer is properly aligned. jobjectArray obj = NULL; size_t size = (size_t) elements (obj); + // Check for overflow. + if (__builtin_expect ((size_t) count > + (MAX_OBJECT_SIZE - 1 - size) / sizeof (jobject), false)) + throw no_memory; + size += count * sizeof (jobject); - // FIXME: second argument should be "current loader" - jclass klass = _Jv_GetArrayClass (elementClass, 0); + jclass klass = _Jv_GetArrayClass (elementClass, + elementClass->getClassLoaderInternal()); obj = (jobjectArray) _Jv_AllocArray (size, klass); // Cast away const. @@ -477,7 +714,7 @@ _Jv_NewPrimArray (jclass eltype, jint count) // Check for overflow. if (__builtin_expect ((size_t) count > - (SIZE_T_MAX - size) / elsize, false)) + (MAX_OBJECT_SIZE - size) / elsize, false)) throw no_memory; jclass klass = _Jv_GetArrayClass (eltype, 0); @@ -513,12 +750,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(); @@ -534,105 +773,201 @@ _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, ...) { + // Creating an array of an unresolved type is impossible. So we throw + // the NoClassDefFoundError. + if (_Jv_IsPhantomClass(array_type)) + throw new java::lang::NoClassDefFoundError(array_type->getName()); + va_list args; jint sizes[dimensions]; va_start (args, 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); } -#define DECLARE_PRIM_TYPE(NAME, SIG, LEN) \ - _Jv_ArrayVTable _Jv_##NAME##VTable; \ - java::lang::Class _Jv_##NAME##Class ((jobject) #NAME, \ - (jbyte) SIG, (jint) LEN, \ - (jobject) &_Jv_##NAME##VTable); - -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); +// Ensure 8-byte alignment, for hash synchronization. +#define DECLARE_PRIM_TYPE(NAME) \ + 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) + +void +_Jv_InitPrimClass (jclass cl, const char *cname, char sig, int len) +{ + 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; +} jclass -_Jv_FindClassFromSignature (char *sig, java::lang::ClassLoader *loader) +_Jv_FindClassFromSignature (char *sig, java::lang::ClassLoader *loader, + char **endp) { + // First count arrays. + int array_count = 0; + while (*sig == '[') + { + ++sig; + ++array_count; + } + + jclass result = NULL; switch (*sig) { case 'B': - return JvPrimClass (byte); + result = JvPrimClass (byte); + break; case 'S': - return JvPrimClass (short); + result = JvPrimClass (short); + break; case 'I': - return JvPrimClass (int); + result = JvPrimClass (int); + break; case 'J': - return JvPrimClass (long); + result = JvPrimClass (long); + break; case 'Z': - return JvPrimClass (boolean); + result = JvPrimClass (boolean); + break; case 'C': - return JvPrimClass (char); + result = JvPrimClass (char); + break; case 'F': - return JvPrimClass (float); + result = JvPrimClass (float); + break; case 'D': - return JvPrimClass (double); + result = JvPrimClass (double); + break; case 'V': - return JvPrimClass (void); + result = JvPrimClass (void); + break; case 'L': { - int i; - for (i = 1; sig[i] && sig[i] != ';'; ++i) - ; - _Jv_Utf8Const *name = _Jv_makeUtf8Const (&sig[1], i - 1); - return _Jv_FindClass (name, loader); - - } - case '[': - { - jclass klass = _Jv_FindClassFromSignature (&sig[1], loader); - if (! klass) - return NULL; - return _Jv_GetArrayClass (klass, loader); + char *save = ++sig; + while (*sig && *sig != ';') + ++sig; + // Do nothing if signature appears to be malformed. + if (*sig == ';') + { + _Jv_Utf8Const *name = _Jv_makeUtf8Const (save, sig - save); + result = _Jv_FindClass (name, loader); + } + break; } + default: + // Do nothing -- bad signature. + break; + } + + if (endp) + { + // Not really the "end", but the last valid character that we + // looked at. + *endp = sig; } - return NULL; // Placate compiler. + if (! result) + return NULL; + + // Find arrays. + while (array_count-- > 0) + result = _Jv_GetArrayClass (result, loader); + return result; } - + +jclass +_Jv_FindClassFromSignatureNoException (char *sig, java::lang::ClassLoader *loader, + char **endp) +{ + jclass klass; + + try + { + klass = _Jv_FindClassFromSignature(sig, loader, endp); + } + catch (java::lang::NoClassDefFoundError *ncdfe) + { + return NULL; + } + catch (java::lang::ClassNotFoundException *cnfe) + { + return NULL; + } + + return klass; +} JArray * JvConvertArgv (int argc, const char **argv) { if (argc < 0) argc = 0; - jobjectArray ar = JvNewObjectArray(argc, &StringClass, NULL); - jobject* ptr = elements(ar); + jobjectArray ar = JvNewObjectArray(argc, &java::lang::String::class$, NULL); + 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; } @@ -643,69 +978,11 @@ JvConvertArgv (int argc, const char **argv) // it will only scan the qthreads stacks. // Command line arguments. -static jobject arg_vec; +static JArray *arg_vec; // The primary thread. static java::lang::Thread *main_thread; -char * -_Jv_ThisExecutable (void) -{ - return _Jv_execName; -} - -void -_Jv_ThisExecutable (const char *name) -{ - if (name) - { - _Jv_execName = (char *) _Jv_Malloc (strlen (name) + 1); - strcpy (_Jv_execName, name); - } -} - -#ifdef USE_WIN32_SIGNALLING - -extern "C" int* win32_get_restart_frame (void *); - -LONG CALLBACK -win32_exception_handler (LPEXCEPTION_POINTERS e) -{ - int* setjmp_buf; - if (e->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) - setjmp_buf = win32_get_restart_frame (nullp); - else if (e->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO) - setjmp_buf = win32_get_restart_frame (arithexception); - else - return EXCEPTION_CONTINUE_SEARCH; - - e->ContextRecord->Ebp = setjmp_buf[0]; - // FIXME: Why does i386-signal.h increment the PC here, do we need to do it? - e->ContextRecord->Eip = setjmp_buf[1]; - // FIXME: Is this the stack pointer? Do we need it? - e->ContextRecord->Esp = setjmp_buf[2]; - - return EXCEPTION_CONTINUE_EXECUTION; -} - -#endif - -/* This will be different from _JNI_OnLoad if the user has preloaded a JNI - library, or linked one into the executable. */ -extern "C" -{ - /* Some systems, like Tru64 UNIX, don't support weak definitions, so use - an empty dummy function to check if the user provided his own. */ -#pragma weak JNI_OnLoad = _JNI_OnLoad - extern jint JNI_OnLoad (JavaVM *, void *) __attribute__((weak)); - - jint _JNI_OnLoad (JavaVM *vm, void *) - { - return 0; - } -} - - #ifndef DISABLE_GETENV_PROPERTIES static char * @@ -760,10 +1037,6 @@ next_property_value (char *s, size_t *length) 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]) @@ -786,13 +1059,18 @@ 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; + // Later on we will write \0s into this string. It is simplest to + // just duplicate it here. + props = strdup (props); + + char *p = props; + size_t length; + size_t property_count = 0; + // Whip through props quickly in order to count the number of // property values. while (p && (p = next_property_key (p, &length))) @@ -836,254 +1114,723 @@ process_gcj_properties () } 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) - { - _Jv_Environment_Properties[i].key[_Jv_Environment_Properties[i].key_length] = 0; - _Jv_Environment_Properties[i++].value[_Jv_Environment_Properties[i].value_length] = 0; - } - } + // Null terminate the strings. + for (property_pair *prop = &_Jv_Environment_Properties[0]; + prop->key != NULL; + prop++) + { + prop->key[prop->key_length] = 0; + prop->value[prop->value_length] = 0; + } } #endif // DISABLE_GETENV_PROPERTIES -jint -_Jv_CreateJavaVM (void* /*vm_args*/) +namespace gcj { - PROCESS_GCJ_PROPERTIES; - - // Turn stack trace generation off while creating exception objects. - _Jv_InitClass (&java::lang::Throwable::class$); - java::lang::Throwable::trace_enabled = 0; + _Jv_Utf8Const *void_signature; + _Jv_Utf8Const *clinit_name; + _Jv_Utf8Const *init_name; + _Jv_Utf8Const *finit_name; - INIT_SEGV; -#ifdef HANDLE_FPE - INIT_FPE; -#else - arithexception = new java::lang::ArithmeticException - (JvNewStringLatin1 ("/ by zero")); -#endif + bool runtimeInitialized = false; + + // When true, print debugging information about class loading. + bool verbose_class_flag; + + // When true, enable the bytecode verifier and BC-ABI type verification. + bool verifyClasses = true; - no_memory = new java::lang::OutOfMemoryError; + // Thread stack size specified by the -Xss runtime argument. + size_t stack_size = 0; - java::lang::Throwable::trace_enabled = 1; + // Start time of the VM + jlong startTime = 0; -#ifdef USE_LTDL - LTDL_SET_PRELOADED_SYMBOLS (); -#endif + // Arguments passed to the VM + JArray* vmArgs; -#ifdef USE_WINSOCK - // Initialise winsock for networking - WSADATA data; - if (WSAStartup (MAKEWORD (1, 1), &data)) - MessageBox (NULL, "Error initialising winsock library.", "Error", MB_OK | MB_ICONEXCLAMATION); -#endif /* USE_WINSOCK */ - -#ifdef USE_WIN32_SIGNALLING - // Install exception handler - SetUnhandledExceptionFilter (win32_exception_handler); -#elif defined(HAVE_SIGACTION) - // We only want this on POSIX systems. - struct sigaction act; - act.sa_handler = SIG_IGN; - sigemptyset (&act.sa_mask); - act.sa_flags = 0; - sigaction (SIGPIPE, &act, NULL); -#else - signal (SIGPIPE, SIG_IGN); -#endif + // Currently loaded classes + jint loadedClasses = 0; - _Jv_JNI_Init (); + // Unloaded classes + jlong unloadedClasses = 0; +} - /* Some systems let you preload shared libraries before running a - program. Under Linux, this is done by setting the LD_PRELOAD - environment variable. We take advatage of this here to allow for - dynamically loading a JNI library into a fully linked executable. */ +// We accept all non-standard options accepted by Sun's java command, +// for compatibility with existing application launch scripts. +static jint +parse_x_arg (char* option_string) +{ + if (strlen (option_string) <= 0) + return -1; - if (JNI_OnLoad != _JNI_OnLoad) + if (! strcmp (option_string, "int")) + { + // FIXME: this should cause the vm to never load shared objects + } + else if (! strcmp (option_string, "mixed")) { - JavaVM *vm = _Jv_GetJavaVM (); - if (vm == NULL) + // FIXME: allow interpreted and native code + } + else if (! strcmp (option_string, "batch")) + { + // FIXME: disable background JIT'ing + } + else if (! strcmp (option_string, "debug")) + { + remoteDebug = true; + } +#ifdef INTERPRETER + else if (! strncmp (option_string, "runjdwp:", 8)) + { + if (strlen (option_string) > 8) + jdwpOptions = &option_string[8]; + else { - // FIXME: what? + fprintf (stderr, + "libgcj: argument required for JDWP options"); return -1; } - jint vers = JNI_OnLoad (vm, NULL); - if (vers != JNI_VERSION_1_1 && vers != JNI_VERSION_1_2) - { - // FIXME: unload the library. - _Jv_Throw (new java::lang::UnsatisfiedLinkError (JvNewStringLatin1 ("unrecognized version from preloaded JNI_OnLoad"))); - } + } +#endif // INTERPRETER + else if (! strncmp (option_string, "bootclasspath:", 14)) + { + // FIXME: add a parse_bootclasspath_arg function + } + else if (! strncmp (option_string, "bootclasspath/a:", 16)) + { + } + else if (! strncmp (option_string, "bootclasspath/p:", 16)) + { + } + else if (! strcmp (option_string, "check:jni")) + { + // FIXME: enable strict JNI checking + } + else if (! strcmp (option_string, "future")) + { + // FIXME: enable strict class file format checks + } + else if (! strcmp (option_string, "noclassgc")) + { + // FIXME: disable garbage collection for classes + } + else if (! strcmp (option_string, "incgc")) + { + // FIXME: incremental garbage collection + } + else if (! strncmp (option_string, "loggc:", 6)) + { + if (option_string[6] == '\0') + { + fprintf (stderr, + "libgcj: filename argument expected for loggc option\n"); + return -1; + } + // FIXME: set gc logging filename + } + else if (! strncmp (option_string, "ms", 2)) + { + // FIXME: ignore this option until PR 20699 is fixed. + // _Jv_SetInitialHeapSize (option_string + 2); + } + else if (! strncmp (option_string, "mx", 2)) + _Jv_SetMaximumHeapSize (option_string + 2); + else if (! strcmp (option_string, "prof")) + { + // FIXME: enable profiling of program running in vm + } + else if (! strncmp (option_string, "runhprof:", 9)) + { + // FIXME: enable specific type of vm profiling. add a + // parse_runhprof_arg function + } + else if (! strcmp (option_string, "rs")) + { + // FIXME: reduced system signal usage. disable thread dumps, + // only terminate in response to user-initiated calls, + // e.g. System.exit() + } + else if (! strncmp (option_string, "ss", 2)) + { + _Jv_SetStackSize (option_string + 2); + } + else if (! strcmp (option_string, "X:+UseAltSigs")) + { + // FIXME: use signals other than SIGUSR1 and SIGUSR2 + } + else if (! strcmp (option_string, "share:off")) + { + // FIXME: don't share class data + } + else if (! strcmp (option_string, "share:auto")) + { + // FIXME: share class data where possible + } + else if (! strcmp (option_string, "share:on")) + { + // FIXME: fail if impossible to share class data + } + else + { + // Unrecognized. + return -1; } return 0; } -static void -runFirst (::java::lang::Class *klass, ::java::lang::Object *args) +static jint +parse_verbose_args (char* option_string, + bool ignore_unrecognized) { - Utf8Const* main_signature = _Jv_makeUtf8Const ("([Ljava.lang.String;)V", 22); - Utf8Const* main_name = _Jv_makeUtf8Const ("main", 4); + size_t len = sizeof ("-verbose") - 1; + + if (strlen (option_string) < len) + return -1; - _Jv_Method *meth = _Jv_GetMethodLocal (klass, main_name, main_signature); + if (option_string[len] == ':' + && option_string[len + 1] != '\0') + { + char* verbose_args = option_string + len + 1; - // Some checks from Java Spec section 12.1.4. - const char *msg = NULL; - if (meth == NULL) - msg = "no suitable method `main' in class"; - else if (! java::lang::reflect::Modifier::isStatic(meth->accflags)) - msg = "`main' must be static"; - else if (! java::lang::reflect::Modifier::isPublic(meth->accflags)) - msg = "`main' must be public"; - if (msg != NULL) + do + { + if (! strncmp (verbose_args, + "gc", sizeof ("gc") - 1)) + { + if (verbose_args[sizeof ("gc") - 1] == '\0' + || verbose_args[sizeof ("gc") - 1] == ',') + { + // FIXME: we should add functions to boehm-gc that + // toggle GC_print_stats, GC_PRINT_ADDRESS_MAP and + // GC_print_back_height. + verbose_args += sizeof ("gc") - 1; + } + else + { + verbose_arg_err: + fprintf (stderr, "libgcj: unknown verbose option: %s\n", + option_string); + return -1; + } + } + else if (! strncmp (verbose_args, + "class", + sizeof ("class") - 1)) + { + if (verbose_args[sizeof ("class") - 1] == '\0' + || verbose_args[sizeof ("class") - 1] == ',') + { + gcj::verbose_class_flag = true; + verbose_args += sizeof ("class") - 1; + } + else + goto verbose_arg_err; + } + else if (! strncmp (verbose_args, "jni", + sizeof ("jni") - 1)) + { + if (verbose_args[sizeof ("jni") - 1] == '\0' + || verbose_args[sizeof ("jni") - 1] == ',') + { + // FIXME: enable JNI messages. + verbose_args += sizeof ("jni") - 1; + } + else + goto verbose_arg_err; + } + else if (ignore_unrecognized + && verbose_args[0] == 'X') + { + // ignore unrecognized non-standard verbose option + while (verbose_args[0] != '\0' + && verbose_args[0] != ',') + verbose_args++; + } + else if (verbose_args[0] == ',') + { + verbose_args++; + } + else + goto verbose_arg_err; + + if (verbose_args[0] == ',') + verbose_args++; + } + while (verbose_args[0] != '\0'); + } + else if (option_string[len] == 'g' + && option_string[len + 1] == 'c' + && option_string[len + 2] == '\0') + { + // FIXME: we should add functions to boehm-gc that + // toggle GC_print_stats, GC_PRINT_ADDRESS_MAP and + // GC_print_back_height. + return 0; + } + else if (option_string[len] == '\0') + { + gcj::verbose_class_flag = true; + return 0; + } + else { - fprintf (stderr, "%s\n", msg); - ::exit(1); + // unrecognized option beginning with -verbose + return -1; } + return 0; +} -#ifdef WITH_JVMPI - if (_Jv_JVMPI_Notify_THREAD_START) +#ifdef INTERPRETER +// This function loads the agent functions for JVMTI from the library indicated +// by name. It returns a negative value on failure, the value of which +// indicates where ltdl failed, it also prints an error message. +static jint +load_jvmti_agent (const char *name) +{ +#ifdef USE_LTDL + if (lt_dlinit ()) + { + fprintf (stderr, + "libgcj: Error in ltdl init while loading agent library.\n"); + return -1; + } + + lt_dlhandle lib = lt_dlopenext (name); + if (!lib) + { + fprintf (stderr, + "libgcj: Error opening agent library.\n"); + return -2; + } + + if (lib) + { + jvmti_agentonload + = (jvmti_agent_onload_func *) lt_dlsym (lib, "Agent_OnLoad"); + + if (!jvmti_agentonload) + { + fprintf (stderr, + "libgcj: Error finding agent function in library %s.\n", + name); + lt_dlclose (lib); + lib = NULL; + return -4; + } + else + { + jvmti_agentonunload + = (jvmti_agent_onunload_func *) lt_dlsym (lib, "Agent_OnUnload"); + + return 0; + } + } + else { - JVMPI_Event event; + fprintf (stderr, "libgcj: Library %s not found in library path.\n", name); + return -3; + } + +#endif /* USE_LTDL */ + + // If LTDL cannot be used, return an error code indicating this. + return -99; +} +#endif // INTERPRETER - jstring thread_name = getName (); - jstring group_name = NULL, parent_name = NULL; - java::lang::ThreadGroup *group = getThreadGroup (); +static jint +parse_init_args (JvVMInitArgs* vm_args) +{ + // if _Jv_Compiler_Properties is non-NULL then it needs to be + // re-allocated dynamically. + if (_Jv_Compiler_Properties) + { + const char** props = _Jv_Compiler_Properties; + _Jv_Compiler_Properties = NULL; - if (group) + for (int i = 0; props[i]; i++) { - group_name = group->getName (); - group = group->getParent (); + _Jv_Compiler_Properties = (const char**) _Jv_Realloc + (_Jv_Compiler_Properties, + (_Jv_Properties_Count + 1) * sizeof (const char*)); + _Jv_Compiler_Properties[_Jv_Properties_Count++] = props[i]; + } + } + + if (vm_args == NULL) + return 0; - if (group) - parent_name = group->getName (); + for (int i = 0; i < vm_args->nOptions; ++i) + { + char* option_string = vm_args->options[i].optionString; + + if (! strcmp (option_string, "vfprintf") + || ! strcmp (option_string, "exit") + || ! strcmp (option_string, "abort")) + { + // FIXME: we are required to recognize these, but for + // now we don't handle them in any way. + continue; } + else if (! strncmp (option_string, + "-verbose", sizeof ("-verbose") - 1)) + { + jint result = parse_verbose_args (option_string, + vm_args->ignoreUnrecognized); + if (result < 0) + return result; + } + else if (! strncmp (option_string, "-D", 2)) + { + _Jv_Compiler_Properties = (const char**) _Jv_Realloc + (_Jv_Compiler_Properties, + (_Jv_Properties_Count + 1) * sizeof (char*)); - int thread_len = thread_name ? JvGetStringUTFLength (thread_name) : 0; - int group_len = group_name ? JvGetStringUTFLength (group_name) : 0; - int parent_len = parent_name ? JvGetStringUTFLength (parent_name) : 0; - - char thread_chars[thread_len + 1]; - char group_chars[group_len + 1]; - char parent_chars[parent_len + 1]; - - if (thread_name) - JvGetStringUTFRegion (thread_name, 0, - thread_name->length(), thread_chars); - if (group_name) - JvGetStringUTFRegion (group_name, 0, - group_name->length(), group_chars); - if (parent_name) - JvGetStringUTFRegion (parent_name, 0, - parent_name->length(), parent_chars); - - thread_chars[thread_len] = '\0'; - group_chars[group_len] = '\0'; - parent_chars[parent_len] = '\0'; - - event.event_type = JVMPI_EVENT_THREAD_START; - event.env_id = NULL; - event.u.thread_start.thread_name = thread_chars; - event.u.thread_start.group_name = group_chars; - event.u.thread_start.parent_name = parent_chars; - event.u.thread_start.thread_id = (jobjectID) this; - event.u.thread_start.thread_env_id = _Jv_GetCurrentJNIEnv (); - - _Jv_DisableGC (); - (*_Jv_JVMPI_Notify_THREAD_START) (&event); - _Jv_EnableGC (); - } -#endif + _Jv_Compiler_Properties[_Jv_Properties_Count++] = + strdup (option_string + 2); - main_func *real_main = (main_func *) meth->ncode; - (*real_main) (args); + continue; + } +#ifdef INTERPRETER + else if (! strncmp (option_string, "-agentlib", sizeof ("-agentlib") - 1)) + { + char *strPtr; + + if (strlen(option_string) > (sizeof ("-agentlib:") - 1)) + strPtr = &option_string[sizeof ("-agentlib:") - 1]; + else + { + fprintf (stderr, + "libgcj: Malformed agentlib argument %s: expected lib name\n", + option_string); + return -1; + } + + // These are optional arguments to pass to the agent library. + jvmti_agent_opts = strchr (strPtr, '='); + + if (! strncmp (strPtr, "jdwp", 4)) + { + // We want to run JDWP here so set the correct variables. + remoteDebug = true; + jdwpOptions = ++jvmti_agent_opts; + } + else + { + jint nameLength; + + if (jvmti_agent_opts == NULL) + nameLength = strlen (strPtr); + else + { + nameLength = jvmti_agent_opts - strPtr; + jvmti_agent_opts++; + } + + char lib_name[nameLength + 3 + 1]; + strcpy (lib_name, "lib"); + strncat (lib_name, strPtr, nameLength); + + jint result = load_jvmti_agent (lib_name); + + if (result < 0) + { + return -1; + } + + // Mark JVMTI active + JVMTI::enabled = true; + } + + continue; + } + else if (! strncmp (option_string, "-agentpath:", + sizeof ("-agentpath:") - 1)) + { + char *strPtr; + + if (strlen(option_string) > 10) + strPtr = &option_string[10]; + else + { + fprintf (stderr, + "libgcj: Malformed agentlib argument %s: expected lib path\n", + option_string); + return -1; + } + + // These are optional arguments to pass to the agent library. + jvmti_agent_opts = strchr (strPtr, '='); + + jint nameLength; + + if (jvmti_agent_opts == NULL) + nameLength = strlen (strPtr); + else + { + nameLength = jvmti_agent_opts - strPtr; + jvmti_agent_opts++; + } + + char lib_name[nameLength + 3 + 1]; + strcpy (lib_name, "lib"); + strncat (lib_name, strPtr, nameLength); + jint result = load_jvmti_agent (strPtr); + + if (result < 0) + { + return -1; + } + + // Mark JVMTI active + JVMTI::enabled = true; + continue; + } +#endif // INTERPRETER + else + { + int r = -1; + if (option_string[0] == '_') + r = parse_x_arg (option_string + 1); + else if (! strncmp (option_string, "-X", 2)) + r = parse_x_arg (option_string + 2); + + if (r == -1 && ! vm_args->ignoreUnrecognized) + { + fprintf (stderr, "libgcj: unknown option: %s\n", option_string); + return -1; + } + } + } + return 0; } -void -JvRunMain (jclass klass, int argc, const char **argv) +jint +_Jv_CreateJavaVM (JvVMInitArgs* vm_args) { - _Jv_argv = argv; - _Jv_argc = argc; + using namespace gcj; - _Jv_CreateJavaVM (NULL); -#ifdef HAVE_PROC_SELF_EXE - char exec_name[20]; - sprintf (exec_name, "/proc/%d/exe", getpid ()); - _Jv_ThisExecutable (exec_name); -#else - _Jv_ThisExecutable (argv[0]); + if (runtimeInitialized) + return -1; + + runtimeInitialized = true; + startTime = _Jv_platform_gettimeofday(); + + jint result = parse_init_args (vm_args); + if (result < 0) + return -1; + + PROCESS_GCJ_PROPERTIES; + + /* Threads must be initialized before the GC, so that it inherits the + signal mask. */ + _Jv_InitThreads (); + _Jv_InitGC (); + _Jv_InitializeSyncMutex (); + +#ifdef INTERPRETER + _Jv_InitInterpreter (); +#endif + +#ifdef HANDLE_SEGV + INIT_SEGV; #endif - // 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. - java::lang::Runtime *rtime = java::lang::Runtime::getRuntime (); +#ifdef HANDLE_FPE + INIT_FPE; +#endif - main_thread = _Jv_AttachCurrentThread (JvNewStringLatin1 ("main"), NULL); - arg_vec = JvConvertArgv (argc - 1, argv + 1); - runFirst (klass, arg_vec); - _Jv_ThreadWait (); + /* 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_InitPrimClass (&_Jv_shortClass, "short", 'S', 2); + _Jv_InitPrimClass (&_Jv_intClass, "int", 'I', 4); + _Jv_InitPrimClass (&_Jv_longClass, "long", 'J', 8); + _Jv_InitPrimClass (&_Jv_booleanClass, "boolean", 'Z', 1); + _Jv_InitPrimClass (&_Jv_charClass, "char", 'C', 2); + _Jv_InitPrimClass (&_Jv_floatClass, "float", 'F', 4); + _Jv_InitPrimClass (&_Jv_doubleClass, "double", 'D', 8); + _Jv_InitPrimClass (&_Jv_voidClass, "void", 'V', 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$); + + // Set up the system class loader and the bootstrap class loader. + gnu::gcj::runtime::ExtensionClassLoader::initialize(); + java::lang::VMClassLoader::initialize(JvNewStringLatin1(TOOLEXECLIBDIR)); + + _Jv_RegisterBootstrapPackages(); - int status = (int) java::lang::ThreadGroup::had_uncaught_exception; - - rtime->_exit (status); + no_memory = new java::lang::OutOfMemoryError; + +#ifdef USE_LTDL + LTDL_SET_PRELOADED_SYMBOLS (); +#endif + + _Jv_platform_initialize (); + + _Jv_JNI_Init (); + +#ifdef INTERPRETER + _Jv_JVMTI_Init (); +#endif + + _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) + { + } + + runtimeInitialized = true; + + return 0; } void -_Jv_RunMain (const char *name, int argc, const char **argv, bool is_jar) +_Jv_RunMain (JvVMInitArgs *vm_args, jclass klass, const char *name, int argc, + const char **argv, bool is_jar) { - jstring class_name; +#ifndef DISABLE_MAIN_ARGS + _Jv_SetArgs (argc, argv); +#endif + + java::lang::Runtime *runtime = NULL; + + try + { + if (_Jv_CreateJavaVM (vm_args) < 0) + { + fprintf (stderr, "libgcj: couldn't create virtual machine\n"); + exit (1); + } + + if (vm_args == NULL) + gcj::vmArgs = JvConvertArgv(0, NULL); + else + { + const char* vmArgs[vm_args->nOptions]; + const char** vmPtr = vmArgs; + struct _Jv_VMOption* optionPtr = vm_args->options; + for (int i = 0; i < vm_args->nOptions; ++i) + *vmPtr++ = (*optionPtr++).optionString; + gcj::vmArgs = JvConvertArgv(vm_args->nOptions, vmArgs); + } - _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 HAVE_PROC_SELF_EXE - char exec_name[20]; - sprintf (exec_name, "/proc/%d/exe", getpid ()); - _Jv_ThisExecutable (exec_name); +#ifdef DISABLE_MAIN_ARGS + arg_vec = JvConvertArgv (0, 0); +#else + arg_vec = JvConvertArgv (argc - 1, argv + 1); #endif - // 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. - java::lang::Runtime *rtime = java::lang::Runtime::getRuntime (); + using namespace gnu::java::lang; + if (klass) + main_thread = new MainThread (klass, arg_vec); + else + main_thread = new MainThread (JvNewStringUTF (name), + arg_vec, is_jar); + _Jv_AttachCurrentThread (main_thread); - main_thread = _Jv_AttachCurrentThread (JvNewStringLatin1 ("main"), NULL); +#ifdef INTERPRETER + // Start JVMTI if an agent function has been found. + if (jvmti_agentonload) + (*jvmti_agentonload) (_Jv_GetJavaVM (), jvmti_agent_opts, NULL); - if (is_jar) + // Start JDWP + if (remoteDebug) + { + using namespace gnu::classpath::jdwp; + VMVirtualMachine::initialize (); + Jdwp *jdwp = new Jdwp (); + jdwp->setDaemon (true); + jdwp->configure (JvNewStringLatin1 (jdwpOptions)); + jdwp->start (); + + // Wait for JDWP to initialize and start + jdwp->join (); + } + // Send VMInit + if (JVMTI_REQUESTED_EVENT (VMInit)) + _Jv_JVMTI_PostEvent (JVMTI_EVENT_VM_INIT, main_thread); +#endif // INTERPRETER + } + catch (java::lang::Throwable *t) { - // name specifies a jar file. We must now extract the - // Main-Class attribute from the jar's manifest file. - // This is done by gnu.gcj.runtime.FirstThread.getMain. - _Jv_Jar_Class_Path = strdup (name); - jstring jar_name = JvNewStringLatin1 (name); - // FirstThread.getMain extracts the main class name. - class_name = gnu::gcj::runtime::FirstThread::getMain (jar_name); - - // We need a new ClassLoader because the classpath must be the - // jar file only. The easiest way to do this is to lose our - // reference to the previous classloader. - java::lang::ClassLoader::system = NULL; + java::lang::System::err->println (JvNewStringLatin1 + ("Exception during runtime initialization")); + t->printStackTrace(); + if (runtime) + java::lang::Runtime::exitNoChecksAccessor (1); + // In case the runtime creation failed. + ::exit (1); } - else - class_name = JvNewStringLatin1 (name); - arg_vec = JvConvertArgv (argc - 1, argv + 1); + _Jv_ThreadRun (main_thread); - if (class_name) +#ifdef INTERPRETER + // Send VMDeath + if (JVMTI_REQUESTED_EVENT (VMDeath)) { - runFirst(java::lang::Class::forName (class_name), arg_vec); - _Jv_ThreadWait (); + java::lang::Thread *thread = java::lang::Thread::currentThread (); + JNIEnv *jni_env = _Jv_GetCurrentJNIEnv (); + _Jv_JVMTI_PostEvent (JVMTI_EVENT_VM_DEATH, thread, jni_env); } - int status = (int) java::lang::ThreadGroup::had_uncaught_exception; + // Run JVMTI AgentOnUnload if it exists and an agent is loaded. + if (jvmti_agentonunload) + (*jvmti_agentonunload) (_Jv_GetJavaVM ()); +#endif // INTERPRETER + + // If we got here then something went wrong, as MainThread is not + // supposed to terminate. + ::exit (1); +} - rtime->exit (status); +void +_Jv_RunMain (jclass klass, const char *name, int argc, const char **argv, + bool is_jar) +{ + _Jv_RunMain (NULL, klass, name, argc, argv, is_jar); +} + +void +JvRunMain (jclass klass, int argc, const char **argv) +{ + _Jv_RunMain (klass, NULL, argc, argv, false); +} + +void +JvRunMainName (const char *name, int argc, const char **argv) +{ + _Jv_RunMain (NULL, name, argc, argv, false); } // Parse a string and return a heap size. static size_t -parse_heap_size (const char *spec) +parse_memory_size (const char *spec) { char *end; unsigned long val = strtoul (spec, &end, 10); @@ -1099,7 +1846,7 @@ parse_heap_size (const char *spec) void _Jv_SetInitialHeapSize (const char *arg) { - size_t size = parse_heap_size (arg); + size_t size = parse_memory_size (arg); _Jv_GCSetInitialHeapSize (size); } @@ -1108,11 +1855,16 @@ _Jv_SetInitialHeapSize (const char *arg) void _Jv_SetMaximumHeapSize (const char *arg) { - size_t size = parse_heap_size (arg); + size_t size = parse_memory_size (arg); _Jv_GCSetMaximumHeapSize (size); } - +void +_Jv_SetStackSize (const char *arg) +{ + size_t size = parse_memory_size (arg); + gcj::stack_size = size; +} void * _Jv_Malloc (jsize size) @@ -1161,7 +1913,11 @@ jint _Jv_divI (jint dividend, jint divisor) { if (__builtin_expect (divisor == 0, false)) - _Jv_ThrowSignal (arithexception); + { + java::lang::ArithmeticException *arithexception + = new java::lang::ArithmeticException (JvNewStringLatin1 ("/ by zero")); + throw arithexception; + } if (dividend == (jint) 0x80000000L && divisor == -1) return dividend; @@ -1173,11 +1929,15 @@ jint _Jv_remI (jint dividend, jint divisor) { if (__builtin_expect (divisor == 0, false)) - _Jv_ThrowSignal (arithexception); + { + java::lang::ArithmeticException *arithexception + = new java::lang::ArithmeticException (JvNewStringLatin1 ("/ by zero")); + throw arithexception; + } if (dividend == (jint) 0x80000000L && divisor == -1) return 0; - + return dividend % divisor; } @@ -1185,8 +1945,12 @@ jlong _Jv_divJ (jlong dividend, jlong divisor) { if (__builtin_expect (divisor == 0, false)) - _Jv_ThrowSignal (arithexception); - + { + java::lang::ArithmeticException *arithexception + = new java::lang::ArithmeticException (JvNewStringLatin1 ("/ by zero")); + throw arithexception; + } + if (dividend == (jlong) 0x8000000000000000LL && divisor == -1) return dividend; @@ -1197,10 +1961,77 @@ jlong _Jv_remJ (jlong dividend, jlong divisor) { if (__builtin_expect (divisor == 0, false)) - _Jv_ThrowSignal (arithexception); - + { + 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) + && _Jv_IsAssignableFromSlow (self_klass, other_klass)) + || (((flags & Modifier::PRIVATE) == 0) + && _Jv_ClassNameSamePackage (self_klass->name, + other_klass->name))); +} + +// Prepend GCJ_VERSIONED_LIBDIR to a module search path stored in a C +// char array, if the path is not already prefixed by +// GCJ_VERSIONED_LIBDIR. Return a newly JvMalloc'd char buffer. The +// result should be freed using JvFree. +char* +_Jv_PrependVersionedLibdir (char* libpath) +{ + char* retval = 0; + + if (libpath && libpath[0] != '\0') + { + if (! strncmp (libpath, + GCJ_VERSIONED_LIBDIR, + sizeof (GCJ_VERSIONED_LIBDIR) - 1)) + { + // LD_LIBRARY_PATH is already prefixed with + // GCJ_VERSIONED_LIBDIR. + retval = (char*) _Jv_Malloc (strlen (libpath) + 1); + strcpy (retval, libpath); + } + else + { + // LD_LIBRARY_PATH is not prefixed with + // GCJ_VERSIONED_LIBDIR. + char path_sep[2]; + path_sep[0] = (char) _Jv_platform_path_separator; + path_sep[1] = '\0'; + jsize total = ((sizeof (GCJ_VERSIONED_LIBDIR) - 1) + + 1 /* path separator */ + strlen (libpath) + 1); + retval = (char*) _Jv_Malloc (total); + strcpy (retval, GCJ_VERSIONED_LIBDIR); + strcat (retval, path_sep); + strcat (retval, libpath); + } + } + else + { + // LD_LIBRARY_PATH was not specified or is empty. + retval = (char*) _Jv_Malloc (sizeof (GCJ_VERSIONED_LIBDIR)); + strcpy (retval, GCJ_VERSIONED_LIBDIR); + } + + return retval; +}