X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libjava%2Fjava%2Flang%2FnatClass.cc;h=1b1a32d1babd1ad3893608fea36ee8690b198d93;hb=d3dcef523dc4306338e31b9d4053d32e8711441e;hp=995e631ace56ae1119c81605a3f123d49a7f14da;hpb=b67b9f3c279eead23e060be2b74c4074fb04b42f;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libjava/java/lang/natClass.cc b/libjava/java/lang/natClass.cc index 995e631ace5..1b1a32d1bab 100644 --- a/libjava/java/lang/natClass.cc +++ b/libjava/java/lang/natClass.cc @@ -1,6 +1,7 @@ // natClass.cc - Implementation of java.lang.Class native methods. -/* Copyright (C) 1998, 1999, 2000 Free Software Foundation +/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation This file is part of libgcj. @@ -12,6 +13,7 @@ details. */ #include #include +#include #pragma implementation "Class.h" @@ -34,7 +36,10 @@ details. */ #include #include #include +#include #include +#include +#include #include #include #include @@ -42,80 +47,128 @@ details. */ #include #include #include +#include #include #include #include +#include +#include #include +#include +#include #include +#include -#define CloneableClass _CL_Q34java4lang9Cloneable -extern java::lang::Class CloneableClass; -#define ObjectClass _CL_Q34java4lang6Object -extern java::lang::Class ObjectClass; -#define ErrorClass _CL_Q34java4lang5Error -extern java::lang::Class ErrorClass; -#define ClassClass _CL_Q34java4lang5Class -extern java::lang::Class ClassClass; -#define MethodClass _CL_Q44java4lang7reflect6Method -extern java::lang::Class MethodClass; -#define FieldClass _CL_Q44java4lang7reflect5Field -extern java::lang::Class FieldClass; -#define ConstructorClass _CL_Q44java4lang7reflect11Constructor -extern java::lang::Class ConstructorClass; - -// Some constants we use to look up the class initializer. -static _Jv_Utf8Const *void_signature = _Jv_makeUtf8Const ("()V", 3); -static _Jv_Utf8Const *clinit_name = _Jv_makeUtf8Const ("", 8); -static _Jv_Utf8Const *init_name = _Jv_makeUtf8Const ("", 6); -static _Jv_Utf8Const *finit_name = _Jv_makeUtf8Const ("$finit$", 7); +using namespace gcj; - +bool gcj::verbose_class_flag; jclass -java::lang::Class::forName (jstring className) +java::lang::Class::forName (jstring className, jboolean initialize, + java::lang::ClassLoader *loader) { if (! className) - JvThrow (new java::lang::NullPointerException); - -#if 0 - // FIXME: should check syntax of CLASSNAME and throw - // IllegalArgumentException on failure. + throw new java::lang::NullPointerException; - // FIXME: should use class loader from calling method. - jclass klass = _Jv_FindClass (className, NULL); -#else jsize length = _Jv_GetStringUTFLength (className); char buffer[length]; - _Jv_GetStringUTFRegion (className, 0, length, buffer); + _Jv_GetStringUTFRegion (className, 0, className->length(), buffer); - // FIXME: should check syntax of CLASSNAME and throw - // IllegalArgumentException on failure. _Jv_Utf8Const *name = _Jv_makeUtf8Const (buffer, length); - // FIXME: should use class loader from calling method. + if (! _Jv_VerifyClassName (name)) + throw new java::lang::ClassNotFoundException (className); + jclass klass = (buffer[0] == '[' - ? _Jv_FindClassFromSignature (name->data, NULL) - : _Jv_FindClass (name, NULL)); -#endif - if (! klass) - JvThrow (new java::lang::ClassNotFoundException (className)); + ? _Jv_FindClassFromSignature (name->data, loader) + : _Jv_FindClass (name, loader)); + + if (klass == NULL) + throw new java::lang::ClassNotFoundException (className); + + if (initialize) + _Jv_InitClass (klass); return klass; } +jclass +java::lang::Class::forName (jstring className) +{ + java::lang::ClassLoader *loader = NULL; + gnu::gcj::runtime::StackTrace *t + = new gnu::gcj::runtime::StackTrace(4); + java::lang::Class *klass = NULL; + try + { + for (int i = 1; !klass; i++) + { + klass = t->classAt (i); + } + loader = klass->getClassLoaderInternal(); + } + catch (::java::lang::ArrayIndexOutOfBoundsException *e) + { + } + + return forName (className, true, loader); +} + +java::lang::ClassLoader * +java::lang::Class::getClassLoader (void) +{ + java::lang::SecurityManager *s = java::lang::System::getSecurityManager(); + if (s != NULL) + { + gnu::gcj::runtime::StackTrace *t + = new gnu::gcj::runtime::StackTrace(4); + Class *caller = NULL; + ClassLoader *caller_loader = NULL; + try + { + for (int i = 1; !caller; i++) + { + caller = t->classAt (i); + } + caller_loader = caller->getClassLoaderInternal(); + } + catch (::java::lang::ArrayIndexOutOfBoundsException *e) + { + } + + // If the caller has a non-null class loader, and that loader + // is not this class' loader or an ancestor thereof, then do a + // security check. + if (caller_loader != NULL && ! caller_loader->isAncestorOf(loader)) + s->checkPermission (new RuntimePermission (JvNewStringLatin1 ("getClassLoader"))); + } + + // The spec requires us to return `null' for primitive classes. In + // other cases we have the option of returning `null' for classes + // loaded with the bootstrap loader. All gcj-compiled classes which + // are linked into the application used to return `null' here, but + // that confuses some poorly-written applications. It is a useful + // and apparently harmless compatibility hack to simply never return + // `null' instead. + if (isPrimitive ()) + return NULL; + return loader ? loader : ClassLoader::systemClassLoader; +} + java::lang::reflect::Constructor * java::lang::Class::getConstructor (JArray *param_types) { + memberAccessCheck(java::lang::reflect::Member::PUBLIC); + jstring partial_sig = getSignature (param_types, true); jint hash = partial_sig->hashCode (); int i = isPrimitive () ? 0 : method_count; while (--i >= 0) { - // FIXME: access checks. if (_Jv_equalUtf8Consts (methods[i].name, init_name) && _Jv_equal (methods[i].signature, partial_sig, hash)) { @@ -130,13 +183,13 @@ java::lang::Class::getConstructor (JArray *param_types) return cons; } } - JvThrow (new java::lang::NoSuchMethodException); + throw new java::lang::NoSuchMethodException (_Jv_NewStringUtf8Const (init_name)); } JArray * java::lang::Class::_getConstructors (jboolean declared) { - // FIXME: this method needs access checks. + memberAccessCheck(java::lang::reflect::Member::PUBLIC); int numConstructors = 0; int max = isPrimitive () ? 0 : method_count; @@ -154,7 +207,9 @@ java::lang::Class::_getConstructors (jboolean declared) } JArray *result = (JArray *) - JvNewObjectArray (numConstructors, &ConstructorClass, NULL); + JvNewObjectArray (numConstructors, + &java::lang::reflect::Constructor::class$, + NULL); java::lang::reflect::Constructor** cptr = elements (result); for (i = 0; i < max; i++) { @@ -177,13 +232,14 @@ java::lang::Class::_getConstructors (jboolean declared) java::lang::reflect::Constructor * java::lang::Class::getDeclaredConstructor (JArray *param_types) { + memberAccessCheck(java::lang::reflect::Member::DECLARED); + jstring partial_sig = getSignature (param_types, true); jint hash = partial_sig->hashCode (); int i = isPrimitive () ? 0 : method_count; while (--i >= 0) { - // FIXME: access checks. if (_Jv_equalUtf8Consts (methods[i].name, init_name) && _Jv_equal (methods[i].signature, partial_sig, hash)) { @@ -195,7 +251,7 @@ java::lang::Class::getDeclaredConstructor (JArray *param_types) return cons; } } - JvThrow (new java::lang::NoSuchMethodException); + throw new java::lang::NoSuchMethodException (_Jv_NewStringUtf8Const (init_name)); } java::lang::reflect::Field * @@ -227,9 +283,7 @@ java::lang::Class::getField (jstring name, jint hash) java::lang::reflect::Field * java::lang::Class::getDeclaredField (jstring name) { - java::lang::SecurityManager *s = java::lang::System::getSecurityManager(); - if (s != NULL) - s->checkMemberAccess (this, java::lang::reflect::Member::DECLARED); + memberAccessCheck(java::lang::reflect::Member::DECLARED); int hash = name->hashCode(); for (int i = 0; i < field_count; i++) { @@ -242,22 +296,36 @@ java::lang::Class::getDeclaredField (jstring name) rfield->name = name; return rfield; } - JvThrow (new java::lang::NoSuchFieldException (name)); + throw new java::lang::NoSuchFieldException (name); } JArray * -java::lang::Class::getDeclaredFields (void) +java::lang::Class::getDeclaredFields (jboolean public_only) { - java::lang::SecurityManager *s = java::lang::System::getSecurityManager(); - if (s != NULL) - s->checkMemberAccess (this, java::lang::reflect::Member::DECLARED); + int size; + if (public_only) + { + size = 0; + for (int i = 0; i < field_count; ++i) + { + _Jv_Field *field = &fields[i]; + if ((field->flags & java::lang::reflect::Modifier::PUBLIC)) + ++size; + } + } + else + size = field_count; + JArray *result = (JArray *) - JvNewObjectArray (field_count, &FieldClass, NULL); + JvNewObjectArray (size, &java::lang::reflect::Field::class$, NULL); java::lang::reflect::Field** fptr = elements (result); for (int i = 0; i < field_count; i++) { _Jv_Field *field = &fields[i]; + if (public_only + && ! (field->flags & java::lang::reflect::Modifier::PUBLIC)) + continue; java::lang::reflect::Field* rfield = new java::lang::reflect::Field (); rfield->offset = (char*) field - (char*) fields; rfield->declaringClass = this; @@ -290,9 +358,13 @@ java::lang::Class::getSignature (JArray *param_types, { java::lang::StringBuffer *buf = new java::lang::StringBuffer (); buf->append((jchar) '('); - jclass *v = elements (param_types); - for (int i = 0; i < param_types->length; ++i) - v[i]->getSignature(buf); + // A NULL param_types means "no parameters". + if (param_types != NULL) + { + jclass *v = elements (param_types); + for (int i = 0; i < param_types->length; ++i) + v[i]->getSignature(buf); + } buf->append((jchar) ')'); if (is_constructor) buf->append((jchar) 'V'); @@ -300,8 +372,8 @@ java::lang::Class::getSignature (JArray *param_types, } java::lang::reflect::Method * -java::lang::Class::getDeclaredMethod (jstring name, - JArray *param_types) +java::lang::Class::_getDeclaredMethod (jstring name, + JArray *param_types) { jstring partial_sig = getSignature (param_types, false); jint p_len = partial_sig->length(); @@ -309,9 +381,10 @@ java::lang::Class::getDeclaredMethod (jstring name, int i = isPrimitive () ? 0 : method_count; while (--i >= 0) { - // FIXME: access checks. if (_Jv_equalUtf8Consts (methods[i].name, utf_name) - && _Jv_equaln (methods[i].signature, partial_sig, p_len)) + && _Jv_equaln (methods[i].signature, partial_sig, p_len) + && (methods[i].accflags + & java::lang::reflect::Modifier::INVISIBLE) == 0) { // Found it. using namespace java::lang::reflect; @@ -321,12 +394,14 @@ java::lang::Class::getDeclaredMethod (jstring name, return rmethod; } } - JvThrow (new java::lang::NoSuchMethodException); + return NULL; } JArray * java::lang::Class::getDeclaredMethods (void) { + memberAccessCheck(java::lang::reflect::Member::DECLARED); + int numMethods = 0; int max = isPrimitive () ? 0 : method_count; int i; @@ -336,13 +411,15 @@ java::lang::Class::getDeclaredMethods (void) if (method->name == NULL || _Jv_equalUtf8Consts (method->name, clinit_name) || _Jv_equalUtf8Consts (method->name, init_name) - || _Jv_equalUtf8Consts (method->name, finit_name)) + || _Jv_equalUtf8Consts (method->name, finit_name) + || (methods[i].accflags + & java::lang::reflect::Modifier::INVISIBLE) != 0) continue; numMethods++; } JArray *result = (JArray *) - JvNewObjectArray (numMethods, &MethodClass, NULL); + JvNewObjectArray (numMethods, &java::lang::reflect::Method::class$, NULL); java::lang::reflect::Method** mptr = elements (result); for (i = 0; i < max; i++) { @@ -350,7 +427,9 @@ java::lang::Class::getDeclaredMethods (void) if (method->name == NULL || _Jv_equalUtf8Consts (method->name, clinit_name) || _Jv_equalUtf8Consts (method->name, init_name) - || _Jv_equalUtf8Consts (method->name, finit_name)) + || _Jv_equalUtf8Consts (method->name, finit_name) + || (methods[i].accflags + & java::lang::reflect::Modifier::INVISIBLE) != 0) continue; java::lang::reflect::Method* rmethod = new java::lang::reflect::Method (); @@ -373,21 +452,25 @@ java::lang::Class::getName (void) JArray * java::lang::Class::getClasses (void) { + // FIXME: security checking. + // Until we have inner classes, it always makes sense to return an // empty array. JArray *result - = (JArray *) JvNewObjectArray (0, &ClassClass, NULL); + = (JArray *) JvNewObjectArray (0, &java::lang::Class::class$, + NULL); return result; } JArray * java::lang::Class::getDeclaredClasses (void) { - checkMemberAccess (java::lang::reflect::Member::DECLARED); + memberAccessCheck (java::lang::reflect::Member::DECLARED); // Until we have inner classes, it always makes sense to return an // empty array. JArray *result - = (JArray *) JvNewObjectArray (0, &ClassClass, NULL); + = (JArray *) JvNewObjectArray (0, &java::lang::Class::class$, + NULL); return result; } @@ -399,60 +482,6 @@ java::lang::Class::getDeclaringClass (void) return NULL; } -jint -java::lang::Class::_getFields (JArray *result, - jint offset) -{ - int count = 0; - for (int i = 0; i < field_count; i++) - { - _Jv_Field *field = &fields[i]; - if (! (field->getModifiers() & java::lang::reflect::Modifier::PUBLIC)) - continue; - ++count; - - if (result != NULL) - { - java::lang::reflect::Field *rfield - = new java::lang::reflect::Field (); - rfield->offset = (char *) field - (char *) fields; - rfield->declaringClass = this; - rfield->name = _Jv_NewStringUtf8Const (field->name); - (elements (result))[offset + i] = rfield; - } - } - jclass superclass = getSuperclass(); - if (superclass != NULL) - { - int s_count = superclass->_getFields (result, offset); - count += s_count; - offset += s_count; - } - for (int i = 0; i < interface_count; ++i) - { - int f_count = interfaces[i]->_getFields (result, offset); - count += f_count; - offset += f_count; - } - return count; -} - -JArray * -java::lang::Class::getFields (void) -{ - using namespace java::lang::reflect; - - int count = _getFields (NULL, 0); - - JArray *result - = ((JArray *) - JvNewObjectArray (count, &FieldClass, NULL)); - - _getFields (result, 0); - - return result; -} - JArray * java::lang::Class::getInterfaces (void) { @@ -464,7 +493,7 @@ java::lang::Class::getInterfaces (void) } java::lang::reflect::Method * -java::lang::Class::getMethod (jstring name, JArray *param_types) +java::lang::Class::_getMethod (jstring name, JArray *param_types) { jstring partial_sig = getSignature (param_types, false); jint p_len = partial_sig->length(); @@ -474,9 +503,10 @@ java::lang::Class::getMethod (jstring name, JArray *param_types) int i = klass->isPrimitive () ? 0 : klass->method_count; while (--i >= 0) { - // FIXME: access checks. if (_Jv_equalUtf8Consts (klass->methods[i].name, utf_name) - && _Jv_equaln (klass->methods[i].signature, partial_sig, p_len)) + && _Jv_equaln (klass->methods[i].signature, partial_sig, p_len) + && (klass->methods[i].accflags + & java::lang::reflect::Modifier::INVISIBLE) == 0) { // Found it. using namespace java::lang::reflect; @@ -493,7 +523,21 @@ java::lang::Class::getMethod (jstring name, JArray *param_types) } } } - JvThrow (new java::lang::NoSuchMethodException); + + // If we haven't found a match, and this class is an interface, then + // check all the superinterfaces. + if (isInterface()) + { + for (int i = 0; i < interface_count; ++i) + { + using namespace java::lang::reflect; + Method *rmethod = interfaces[i]->_getMethod (name, param_types); + if (rmethod != NULL) + return rmethod; + } + } + + return NULL; } // This is a very slow implementation, since it re-scans all the @@ -513,7 +557,9 @@ java::lang::Class::_getMethods (JArray *result, if (method->name == NULL || _Jv_equalUtf8Consts (method->name, clinit_name) || _Jv_equalUtf8Consts (method->name, init_name) - || _Jv_equalUtf8Consts (method->name, finit_name)) + || _Jv_equalUtf8Consts (method->name, finit_name) + || (method->accflags + & java::lang::reflect::Modifier::INVISIBLE) != 0) continue; // Only want public methods. if (! java::lang::reflect::Modifier::isPublic (method->accflags)) @@ -580,13 +626,15 @@ java::lang::Class::getMethods (void) { using namespace java::lang::reflect; - // FIXME: security checks. + memberAccessCheck(Member::PUBLIC); // This will overestimate the size we need. jint count = _getMethods (NULL, 0); JArray *result - = ((JArray *) JvNewObjectArray (count, &MethodClass, NULL)); + = ((JArray *) JvNewObjectArray (count, + &Method::class$, + NULL)); // When filling the array for real, we get the actual count. Then // we resize the array. @@ -595,7 +643,8 @@ java::lang::Class::getMethods (void) if (real_count != count) { JArray *r2 - = ((JArray *) JvNewObjectArray (real_count, &MethodClass, + = ((JArray *) JvNewObjectArray (real_count, + &Method::class$, NULL)); Method **destp = elements (r2); @@ -619,44 +668,33 @@ java::lang::Class::isAssignableFrom (jclass klass) return _Jv_IsAssignableFrom (this, klass); } -inline jboolean +jboolean java::lang::Class::isInstance (jobject obj) { - if (__builtin_expect (! obj || isPrimitive (), false)) + if (! obj) return false; _Jv_InitClass (this); return _Jv_IsAssignableFrom (this, JV_CLASS (obj)); } -inline jboolean -java::lang::Class::isInterface (void) -{ - return (accflags & java::lang::reflect::Modifier::INTERFACE) != 0; -} - jobject java::lang::Class::newInstance (void) { - // FIXME: do accessibility checks here. There currently doesn't - // seem to be any way to do these. - // FIXME: we special-case one check here just to pass a Plum Hall - // test. Once access checking is implemented, remove this. - if (this == &ClassClass) - JvThrow (new java::lang::IllegalAccessException); + memberAccessCheck(java::lang::reflect::Member::PUBLIC); if (isPrimitive () || isInterface () || isArray () || java::lang::reflect::Modifier::isAbstract(accflags)) - JvThrow (new java::lang::InstantiationException); + throw new java::lang::InstantiationException (getName ()); _Jv_InitClass (this); _Jv_Method *meth = _Jv_GetMethodLocal (this, init_name, void_signature); if (! meth) - JvThrow (new java::lang::NoSuchMethodException); + throw new java::lang::InstantiationException (getName()); - jobject r = JvAllocObject (this); + jobject r = _Jv_AllocObject (this); ((void (*) (jobject)) meth->ncode) (r); return r; } @@ -675,7 +713,7 @@ java::lang::Class::finalize (void) void java::lang::Class::initializeClass (void) { - // jshort-circuit to avoid needless locking. + // short-circuit to avoid needless locking. if (state == JV_STATE_DONE) return; @@ -689,7 +727,7 @@ java::lang::Class::initializeClass (void) { // this can throw exceptions, so exit the monitor as a precaution. _Jv_MonitorExit (this); - java::lang::ClassLoader::resolveClass0 (this); + java::lang::VMClassLoader::resolveClass (this); _Jv_MonitorEnter (this); } else @@ -698,9 +736,6 @@ java::lang::Class::initializeClass (void) _Jv_PrepareCompiledClass (this); } } - - if (state <= JV_STATE_LINKED) - _Jv_PrepareConstantTimeTables (this); // Step 2. java::lang::Thread *self = java::lang::Thread::currentThread(); @@ -710,9 +745,21 @@ java::lang::Class::initializeClass (void) wait (); // Steps 3 & 4. - if (state == JV_STATE_DONE || state == JV_STATE_IN_PROGRESS || thread == self) + if (state == JV_STATE_DONE) + { + _Jv_MonitorExit (this); + return; + } + if (state == JV_STATE_IN_PROGRESS) { _Jv_MonitorExit (this); + + /* Initialization in progress. The class is linked now, + so ensure internal tables are built. */ + _Jv_PrepareConstantTimeTables (this); + _Jv_MakeVTable(this); + _Jv_LinkSymbolTable(this); + return; } @@ -720,7 +767,7 @@ java::lang::Class::initializeClass (void) if (state == JV_STATE_ERROR) { _Jv_MonitorExit (this); - JvThrow (new java::lang::NoClassDefFoundError); + throw new java::lang::NoClassDefFoundError (getName()); } // Step 6. @@ -733,7 +780,7 @@ java::lang::Class::initializeClass (void) { try { - superclass->initializeClass (); + _Jv_InitClass (superclass); } catch (java::lang::Throwable *except) { @@ -746,6 +793,16 @@ java::lang::Class::initializeClass (void) } } + _Jv_PrepareConstantTimeTables (this); + + if (vtable == NULL) + _Jv_MakeVTable(this); + + if (otable || atable) + _Jv_LinkSymbolTable(this); + + _Jv_linkExceptionClassTable (this); + // Steps 8, 9, 10, 11. try { @@ -756,7 +813,7 @@ java::lang::Class::initializeClass (void) } catch (java::lang::Throwable *except) { - if (! ErrorClass.isInstance(except)) + if (! java::lang::Error::class$.isInstance(except)) { try { @@ -771,7 +828,7 @@ java::lang::Class::initializeClass (void) state = JV_STATE_ERROR; notifyAll (); _Jv_MonitorExit (this); - JvThrow (except); + throw except; } _Jv_MonitorEnter (this); @@ -848,14 +905,14 @@ static void _Jv_AddMethodToCache (jclass klass, _Jv_Method *method) { - _Jv_MonitorEnter (&ClassClass); + _Jv_MonitorEnter (&java::lang::Class::class$); int index = method->name->hash & MCACHE_SIZE; method_cache[index].method = method; method_cache[index].klass = klass; - _Jv_MonitorExit (&ClassClass); + _Jv_MonitorExit (&java::lang::Class::class$); } void * @@ -875,21 +932,20 @@ _Jv_LookupInterfaceMethod (jclass klass, _Jv_Utf8Const *name, continue; if (Modifier::isStatic(meth->accflags)) - JvThrow (new java::lang::IncompatibleClassChangeError - (_Jv_GetMethodString (klass, meth->name))); + throw new java::lang::IncompatibleClassChangeError + (_Jv_GetMethodString (klass, meth->name)); if (Modifier::isAbstract(meth->accflags)) - JvThrow (new java::lang::AbstractMethodError - (_Jv_GetMethodString (klass, meth->name))); + throw new java::lang::AbstractMethodError + (_Jv_GetMethodString (klass, meth->name)); if (! Modifier::isPublic(meth->accflags)) - JvThrow (new java::lang::IllegalAccessError - (_Jv_GetMethodString (klass, meth->name))); + throw new java::lang::IllegalAccessError + (_Jv_GetMethodString (klass, meth->name)); _Jv_AddMethodToCache (klass, meth); return meth->ncode; } - JvThrow (new java::lang::IncompatibleClassChangeError); - return NULL; // Placate compiler. + throw new java::lang::IncompatibleClassChangeError; } // Fast interface method lookup by index. @@ -901,22 +957,19 @@ _Jv_LookupInterfaceMethodIdx (jclass klass, jclass iface, int method_idx) return cldt->cls.itable[idx]; } -inline jboolean +jboolean _Jv_IsAssignableFrom (jclass target, jclass source) { - if (target == &ObjectClass - || source == target - || (source->ancestors != NULL - && source->ancestors[source->depth - target->depth] == target)) - return true; - + if (source == target) + return true; + // If target is array, so must source be. - if (target->isArray ()) + while (target->isArray ()) { if (! source->isArray()) return false; - return _Jv_IsAssignableFrom(target->getComponentType(), - source->getComponentType()); + target = target->getComponentType(); + source = source->getComponentType(); } if (target->isInterface()) @@ -924,25 +977,49 @@ _Jv_IsAssignableFrom (jclass target, jclass source) // Abstract classes have no IDT, and IDTs provide no way to check // two interfaces for assignability. if (__builtin_expect - (java::lang::reflect::Modifier::isAbstract (source->accflags) - || source->isInterface(), false)) + (source->idt == NULL || source->isInterface(), false)) return _Jv_InterfaceAssignableFrom (target, source); - + _Jv_IDispatchTable *cl_idt = source->idt; _Jv_IDispatchTable *if_idt = target->idt; if (__builtin_expect ((if_idt == NULL), false)) return false; // No class implementing TARGET has been loaded. jshort cl_iindex = cl_idt->cls.iindex; - if (cl_iindex <= if_idt->iface.ioffsets[0]) + if (cl_iindex < if_idt->iface.ioffsets[0]) { jshort offset = if_idt->iface.ioffsets[cl_iindex]; - if (offset < cl_idt->cls.itable_length + if (offset != -1 && offset < cl_idt->cls.itable_length && cl_idt->cls.itable[offset] == target) return true; } + return false; } - + + // Primitive TYPE classes are only assignable to themselves. + if (__builtin_expect (target->isPrimitive() || source->isPrimitive(), false)) + return false; + + if (target == &java::lang::Object::class$) + return true; + else if (source->ancestors == NULL || target->ancestors == NULL) + { + // We need this case when either SOURCE or TARGET has not has + // its constant-time tables prepared. + + // At this point we know that TARGET can't be Object, so it is + // safe to use that as the termination point. + while (source && source != &java::lang::Object::class$) + { + if (source == target) + return true; + source = source->getSuperclass(); + } + } + else if (source->depth >= target->depth + && source->ancestors[source->depth - target->depth] == target) + return true; + return false; } @@ -981,7 +1058,12 @@ _Jv_CheckCast (jclass c, jobject obj) { if (__builtin_expect (obj != NULL && ! _Jv_IsAssignableFrom(c, JV_CLASS (obj)), false)) - JvThrow (new java::lang::ClassCastException); + throw new java::lang::ClassCastException + ((new java::lang::StringBuffer + (obj->getClass()->getName()))->append + (JvNewStringUTF(" cannot be cast to "))->append + (c->getName())->toString()); + return obj; } @@ -992,20 +1074,29 @@ _Jv_CheckArrayStore (jobject arr, jobject obj) { JvAssert (arr != NULL); jclass elt_class = (JV_CLASS (arr))->getComponentType(); + if (elt_class == &java::lang::Object::class$) + return; jclass obj_class = JV_CLASS (obj); if (__builtin_expect (! _Jv_IsAssignableFrom (elt_class, obj_class), false)) - JvThrow (new java::lang::ArrayStoreException); + throw new java::lang::ArrayStoreException + ((new java::lang::StringBuffer + (JvNewStringUTF("Cannot store ")))->append + (obj_class->getName())->append + (JvNewStringUTF(" in array of type "))->append + (elt_class->getName())->toString()); } } #define INITIAL_IOFFSETS_LEN 4 #define INITIAL_IFACES_LEN 4 +static _Jv_IDispatchTable null_idt = { {SHRT_MAX, 0, NULL} }; + // Generate tables for constant-time assignment testing and interface // method lookup. This implements the technique described by Per Bothner // on the java-discuss mailing list on 1999-09-02: -// http://sourceware.cygnus.com/ml/java-discuss/1999-q3/msg00377.html +// http://gcc.gnu.org/ml/java/1999-q3/msg00377.html void _Jv_PrepareConstantTimeTables (jclass klass) { @@ -1023,8 +1114,10 @@ _Jv_PrepareConstantTimeTables (jclass klass) // interfaces or primitive types. jclass klass0 = klass; - while (klass0 != &ObjectClass) + jboolean has_interfaces = 0; + while (klass0 != &java::lang::Object::class$) { + has_interfaces += klass0->interface_count; klass0 = klass0->superclass; klass->depth++; } @@ -1046,6 +1139,14 @@ _Jv_PrepareConstantTimeTables (jclass klass) if (java::lang::reflect::Modifier::isAbstract (klass->accflags)) return; + + // Optimization: If class implements no interfaces, use a common + // predefined interface table. + if (!has_interfaces) + { + klass->idt = &null_idt; + return; + } klass->idt = (_Jv_IDispatchTable *) _Jv_Malloc (sizeof (_Jv_IDispatchTable)); @@ -1090,7 +1191,7 @@ _Jv_PrepareConstantTimeTables (jclass klass) } // Return index of item in list, or -1 if item is not present. -jshort +inline jshort _Jv_IndexOf (void *item, void **list, jshort list_len) { for (int i=0; i < list_len; i++) @@ -1113,6 +1214,10 @@ _Jv_GetInterfaces (jclass klass, _Jv_ifaces *ifaces) for (int i=0; i < klass->interface_count; i++) { jclass iface = klass->interfaces[i]; + + /* Make sure interface is linked. */ + _Jv_WaitForState(iface, JV_STATE_LINKED); + if (_Jv_IndexOf (iface, (void **) ifaces->list, ifaces->count) == -1) { if (ifaces->count + 1 >= ifaces->len) @@ -1156,8 +1261,7 @@ _Jv_GenerateITable (jclass klass, _Jv_ifaces *ifaces, jshort *itable_offsets) { jclass iface = ifaces->list[i]; itable_offsets[i] = itable_pos; - itable_pos = _Jv_AppendPartialITable (klass, iface, itable, - itable_pos); + itable_pos = _Jv_AppendPartialITable (klass, iface, itable, itable_pos); /* Create interface dispatch table for iface */ if (iface->idt == NULL) @@ -1190,7 +1294,7 @@ _Jv_GetMethodString (jclass klass, _Jv_Utf8Const *name) void _Jv_ThrowNoSuchMethodError () { - JvThrow (new java::lang::NoSuchMethodError ()); + throw new java::lang::NoSuchMethodError; } // Each superinterface of a class (i.e. each interface that the class @@ -1219,7 +1323,7 @@ _Jv_AppendPartialITable (jclass klass, jclass iface, void **itable, for (jclass cl = klass; cl; cl = cl->getSuperclass()) { meth = _Jv_GetMethodLocal (cl, iface->methods[j].name, - iface->methods[j].signature); + iface->methods[j].signature); if (meth) break; @@ -1233,14 +1337,14 @@ _Jv_AppendPartialITable (jclass klass, jclass iface, void **itable, else if (meth) { if (Modifier::isStatic(meth->accflags)) - JvThrow (new java::lang::IncompatibleClassChangeError - (_Jv_GetMethodString (klass, meth->name))); + throw new java::lang::IncompatibleClassChangeError + (_Jv_GetMethodString (klass, meth->name)); if (Modifier::isAbstract(meth->accflags)) - JvThrow (new java::lang::AbstractMethodError - (_Jv_GetMethodString (klass, meth->name))); + throw new java::lang::AbstractMethodError + (_Jv_GetMethodString (klass, meth->name)); if (! Modifier::isPublic(meth->accflags)) - JvThrow (new java::lang::IllegalAccessError - (_Jv_GetMethodString (klass, meth->name))); + throw new java::lang::IllegalAccessError + (_Jv_GetMethodString (klass, meth->name)); itable[pos] = meth->ncode; } @@ -1258,7 +1362,7 @@ _Jv_AppendPartialITable (jclass klass, jclass iface, void **itable, } static _Jv_Mutex_t iindex_mutex; -bool iindex_mutex_initialized = false; +static bool iindex_mutex_initialized = false; // We need to find the correct offset in the Class Interface Dispatch // Table for a given interface. Once we have that, invoking an interface @@ -1300,7 +1404,7 @@ _Jv_FindIIndex (jclass *ifaces, jshort *offsets, jshort num) { if (j >= num) goto found; - if (i > ifaces[j]->idt->iface.ioffsets[0]) + if (i >= ifaces[j]->idt->iface.ioffsets[0]) continue; int ioffset = ifaces[j]->idt->iface.ioffsets[i]; /* We can potentially share this position with another class. */ @@ -1375,7 +1479,6 @@ java::lang::Class::getPrivateMethod (jstring name, JArray *param_types) int i = klass->isPrimitive () ? 0 : klass->method_count; while (--i >= 0) { - // FIXME: access checks. if (_Jv_equalUtf8Consts (klass->methods[i].name, utf_name) && _Jv_equaln (klass->methods[i].signature, partial_sig, p_len)) { @@ -1390,6 +1493,496 @@ java::lang::Class::getPrivateMethod (jstring name, JArray *param_types) } } } - JvThrow (new java::lang::NoSuchMethodException); + throw new java::lang::NoSuchMethodException (name); +} + +// Private accessor method for Java code to retrieve the protection domain. +java::security::ProtectionDomain * +java::lang::Class::getProtectionDomain0 () +{ + return protectionDomain; +} + +JArray * +java::lang::Class::getSigners() +{ + return hack_signers; +} + +void +java::lang::Class::setSigners(JArray *s) +{ + hack_signers = s; +} + +// Functions for indirect dispatch (symbolic virtual binding) support. + +// There are two tables, atable and otable. atable is an array of +// addresses, and otable is an array of offsets, and these are used +// for static and virtual members respectively. + +// {a,o}table_syms is an array of _Jv_MethodSymbols. Each such symbol +// is a tuple of {classname, member name, signature}. +// _Jv_LinkSymbolTable() scans these two arrays and fills in the +// corresponding atable and otable with the addresses of static +// members and the offsets of virtual members. + +// The offset (in bytes) for each resolved method or field is placed +// at the corresponding position in the virtual method offset table +// (klass->otable). + +// The same otable and atable may be shared by many classes. + +void +_Jv_LinkSymbolTable(jclass klass) +{ + //// FIXME: Need to lock the tables //// + + int index = 0; + _Jv_MethodSymbol sym; + if (klass->otable == NULL + || klass->otable->state != 0) + goto atable; + + klass->otable->state = 1; + + for (index = 0; sym = klass->otable_syms[index], sym.name != NULL; index++) + { + // FIXME: Why are we passing NULL as the class loader? + jclass target_class = _Jv_FindClass (sym.class_name, NULL); + _Jv_Method *meth = NULL; + + const _Jv_Utf8Const *signature = sym.signature; + + { + static char *bounce = (char *)_Jv_ThrowNoSuchMethodError; + ptrdiff_t offset = (char *)(klass->vtable) - bounce; + klass->otable->offsets[index] = offset; + } + + if (target_class == NULL) + continue; + + if (target_class->isInterface()) + { + // FIXME: This does not yet fully conform to binary compatibility + // rules. It will break if a declaration is moved into a + // superinterface. + for (jclass cls = target_class; cls != 0; cls = cls->getSuperclass ()) + { + for (int i=0; i < cls->method_count; i++) + { + meth = &cls->methods[i]; + if (_Jv_equalUtf8Consts (sym.name, meth->name) + && _Jv_equalUtf8Consts (signature, meth->signature)) + { + klass->otable->offsets[index] = i + 1; + goto found; + } + } + + } + found: + continue; + } + + // We're looking for a field or a method, and we can tell + // which is needed by looking at the signature. + if (signature->length >= 2 + && signature->data[0] == '(') + { + // If the target class does not have a vtable_method_count yet, + // then we can't tell the offsets for its methods, so we must lay + // it out now. + if (target_class->vtable_method_count == -1) + { + JvSynchronize sync (target_class); + _Jv_LayoutVTableMethods (target_class); + } + + meth = _Jv_LookupDeclaredMethod(target_class, sym.name, + sym.signature); + + if (meth != NULL) + { + klass->otable->offsets[index] = + _Jv_VTable::idx_to_offset (meth->index); + } + + continue; + } + + // try fields + { + _Jv_Field *the_field = NULL; + + for (jclass cls = target_class; cls != 0; cls = cls->getSuperclass ()) + { + for (int i = 0; i < cls->field_count; i++) + { + _Jv_Field *field = &cls->fields[i]; + if (! _Jv_equalUtf8Consts (field->name, sym.name)) + continue; + + // FIXME: What access checks should we perform here? +// if (_Jv_CheckAccess (klass, cls, field->flags)) +// { + + if (!field->isResolved ()) + _Jv_ResolveField (field, cls->loader); + +// if (field_type != 0 && field->type != field_type) +// throw new java::lang::LinkageError +// (JvNewStringLatin1 +// ("field type mismatch with different loaders")); + + the_field = field; + goto end_of_field_search; + } + } + end_of_field_search: + if (the_field != NULL) + { + if (the_field->flags & 0x0008 /* Modifier::STATIC */) + { + throw new java::lang::IncompatibleClassChangeError; + } + else + { + klass->otable->offsets[index] = the_field->u.boffset; + } + } + else + { + throw new java::lang::NoSuchFieldError + (_Jv_NewStringUtf8Const (sym.name)); + } + } + } + + atable: + if (klass->atable == NULL + || klass->atable->state != 0) + return; + + klass->atable->state = 1; + + for (index = 0; sym = klass->atable_syms[index], sym.name != NULL; index++) + { + // FIXME: Why are we passing NULL as the class loader? + jclass target_class = _Jv_FindClass (sym.class_name, NULL); + _Jv_Method *meth = NULL; + const _Jv_Utf8Const *signature = sym.signature; + + // ??? Setting this pointer to null will at least get us a + // NullPointerException + klass->atable->addresses[index] = NULL; + + if (target_class == NULL) + continue; + + // We're looking for a static field or a static method, and we + // can tell which is needed by looking at the signature. + if (signature->length >= 2 + && signature->data[0] == '(') + { + // If the target class does not have a vtable_method_count yet, + // then we can't tell the offsets for its methods, so we must lay + // it out now. + if (target_class->vtable_method_count == -1) + { + JvSynchronize sync (target_class); + _Jv_LayoutVTableMethods (target_class); + } + + meth = _Jv_LookupDeclaredMethod(target_class, sym.name, + sym.signature); + + if (meth != NULL) + { + if (meth->ncode) // Maybe abstract? + klass->atable->addresses[index] = meth->ncode; +#ifdef INTERPRETER + else if (_Jv_IsInterpretedClass (target_class)) + _Jv_Defer_Resolution (target_class, meth, + &klass->atable->addresses[index]); +#endif + } + else + klass->atable->addresses[index] = (void *)_Jv_ThrowNoSuchMethodError; + + continue; + } + + // try fields + { + _Jv_Field *the_field = NULL; + + for (jclass cls = target_class; cls != 0; cls = cls->getSuperclass ()) + { + for (int i = 0; i < cls->field_count; i++) + { + _Jv_Field *field = &cls->fields[i]; + if (! _Jv_equalUtf8Consts (field->name, sym.name)) + continue; + + // FIXME: What access checks should we perform here? +// if (_Jv_CheckAccess (klass, cls, field->flags)) +// { + + if (!field->isResolved ()) + _Jv_ResolveField (field, cls->loader); + +// if (field_type != 0 && field->type != field_type) +// throw new java::lang::LinkageError +// (JvNewStringLatin1 +// ("field type mismatch with different loaders")); + + the_field = field; + goto end_of_static_field_search; + } + } + end_of_static_field_search: + if (the_field != NULL) + { + if (the_field->flags & 0x0008 /* Modifier::STATIC */) + { + klass->atable->addresses[index] = the_field->u.addr; + } + else + { + throw new java::lang::IncompatibleClassChangeError; + } + } + else + { + throw new java::lang::NoSuchFieldError + (_Jv_NewStringUtf8Const (sym.name)); + } + } + } +} + + +// For each catch_record in the list of caught classes, fill in the +// address field. +void +_Jv_linkExceptionClassTable (jclass self) +{ + struct _Jv_CatchClass *catch_record = self->catch_classes; + if (!catch_record || catch_record->classname) + return; + catch_record++; + while (catch_record->classname) + { + jclass target_class = _Jv_FindClass (catch_record->classname, + self->getClassLoaderInternal ()); + *catch_record->address = target_class; + catch_record++; + } + self->catch_classes->classname = (_Jv_Utf8Const *)-1; +} + +// This is put in empty vtable slots. +static void +_Jv_abstractMethodError (void) +{ + throw new java::lang::AbstractMethodError(); } +// Set itable method indexes for members of interface IFACE. +void +_Jv_LayoutInterfaceMethods (jclass iface) +{ + if (! iface->isInterface()) + return; + + // itable indexes start at 1. + // FIXME: Static initalizers currently get a NULL placeholder entry in the + // itable so they are also assigned an index here. + for (int i = 0; i < iface->method_count; i++) + iface->methods[i].index = i + 1; +} + +// Prepare virtual method declarations in KLASS, and any superclasses as +// required, by determining their vtable index, setting method->index, and +// finally setting the class's vtable_method_count. Must be called with the +// lock for KLASS held. +void +_Jv_LayoutVTableMethods (jclass klass) +{ + if (klass->vtable != NULL || klass->isInterface() + || klass->vtable_method_count != -1) + return; + + jclass superclass = klass->superclass; + + typedef unsigned int uaddr __attribute__ ((mode (pointer))); + + // If superclass looks like a constant pool entry, + // resolve it now. + if ((uaddr)superclass < (uaddr)klass->constants.size) + { + if (klass->state < JV_STATE_LINKED) + { + _Jv_Utf8Const *name = klass->constants.data[(int)superclass].utf8; + superclass = _Jv_FindClass (name, klass->loader); + if (! superclass) + { + jstring str = _Jv_NewStringUTF (name->data); + throw new java::lang::NoClassDefFoundError (str); + } + } + else + superclass = klass->constants.data[(int)superclass].clazz; + } + + if (superclass != NULL && superclass->vtable_method_count == -1) + { + JvSynchronize sync (superclass); + _Jv_LayoutVTableMethods (superclass); + } + + int index = (superclass == NULL ? 0 : superclass->vtable_method_count); + + for (int i = 0; i < klass->method_count; ++i) + { + _Jv_Method *meth = &klass->methods[i]; + _Jv_Method *super_meth = NULL; + + if (! _Jv_isVirtualMethod (meth)) + continue; + + // FIXME: Must check that we don't override: + // - Package-private method where superclass is in different package. + // - Final or less-accessible declaration in superclass (check binary + // spec, do we allocate new vtable entry or put throw node in vtable?) + // - Static or private method in superclass. + + if (superclass != NULL) + { + super_meth = _Jv_LookupDeclaredMethod (superclass, meth->name, + meth->signature); + } + + if (super_meth) + meth->index = super_meth->index; + else + meth->index = index++; + } + + klass->vtable_method_count = index; +} + +// Set entries in VTABLE for virtual methods declared in KLASS. If +// KLASS has an immediate abstract parent, recursively do its methods +// first. FLAGS is used to determine which slots we've actually set. +void +_Jv_SetVTableEntries (jclass klass, _Jv_VTable *vtable, jboolean *flags) +{ + using namespace java::lang::reflect; + + jclass superclass = klass->getSuperclass(); + + if (superclass != NULL && (superclass->getModifiers() & Modifier::ABSTRACT)) + _Jv_SetVTableEntries (superclass, vtable, flags); + + for (int i = klass->method_count - 1; i >= 0; i--) + { + _Jv_Method *meth = &klass->methods[i]; + if (meth->index == (_Jv_ushort) -1) + continue; + if ((meth->accflags & Modifier::ABSTRACT)) + { + vtable->set_method(meth->index, (void *) &_Jv_abstractMethodError); + flags[meth->index] = false; + } + else + { + vtable->set_method(meth->index, meth->ncode); + flags[meth->index] = true; + } + } +} + +// Allocate and lay out the virtual method table for KLASS. This will also +// cause vtables to be generated for any non-abstract superclasses, and +// virtual method layout to occur for any abstract superclasses. Must be +// called with monitor lock for KLASS held. +void +_Jv_MakeVTable (jclass klass) +{ + using namespace java::lang::reflect; + + if (klass->vtable != NULL || klass->isInterface() + || (klass->accflags & Modifier::ABSTRACT)) + return; + + // Class must be laid out before we can create a vtable. + if (klass->vtable_method_count == -1) + _Jv_LayoutVTableMethods (klass); + + // Allocate the new vtable. + _Jv_VTable *vtable = _Jv_VTable::new_vtable (klass->vtable_method_count); + klass->vtable = vtable; + + jboolean flags[klass->vtable_method_count]; + for (int i = 0; i < klass->vtable_method_count; ++i) + flags[i] = false; + + // Copy the vtable of the closest non-abstract superclass. + jclass superclass = klass->superclass; + if (superclass != NULL) + { + while ((superclass->accflags & Modifier::ABSTRACT) != 0) + superclass = superclass->superclass; + + if (superclass->vtable == NULL) + { + JvSynchronize sync (superclass); + _Jv_MakeVTable (superclass); + } + + for (int i = 0; i < superclass->vtable_method_count; ++i) + { + vtable->set_method (i, superclass->vtable->get_method (i)); + flags[i] = true; + } + } + + // Set the class pointer and GC descriptor. + vtable->clas = klass; + vtable->gc_descr = _Jv_BuildGCDescr (klass); + + // For each virtual declared in klass and any immediate abstract + // superclasses, set new vtable entry or override an old one. + _Jv_SetVTableEntries (klass, vtable, flags); + + // It is an error to have an abstract method in a concrete class. + if (! (klass->accflags & Modifier::ABSTRACT)) + { + for (int i = 0; i < klass->vtable_method_count; ++i) + if (! flags[i]) + { + using namespace java::lang; + while (klass != NULL) + { + for (int j = 0; j < klass->method_count; ++j) + { + if (klass->methods[i].index == i) + { + StringBuffer *buf = new StringBuffer (); + buf->append (_Jv_NewStringUtf8Const (klass->methods[i].name)); + buf->append ((jchar) ' '); + buf->append (_Jv_NewStringUtf8Const (klass->methods[i].signature)); + throw new AbstractMethodError (buf->toString ()); + } + } + klass = klass->getSuperclass (); + } + // Couldn't find the name, which is weird. + // But we still must throw the error. + throw new AbstractMethodError (); + } + } +}