diff options
-rw-r--r-- | libjava/ChangeLog | 36 | ||||
-rw-r--r-- | libjava/gcj/javaprims.h | 3 | ||||
-rw-r--r-- | libjava/include/jvm.h | 23 | ||||
-rw-r--r-- | libjava/java/lang/Class.h | 16 | ||||
-rw-r--r-- | libjava/java/lang/natClass.cc | 5 | ||||
-rw-r--r-- | libjava/java/lang/natClassLoader.cc | 24 | ||||
-rw-r--r-- | libjava/link.cc | 191 | ||||
-rw-r--r-- | libjava/prims.cc | 148 | ||||
-rw-r--r-- | libjava/verify.cc | 95 |
9 files changed, 493 insertions, 48 deletions
diff --git a/libjava/ChangeLog b/libjava/ChangeLog index ec67fd7cb53..ff1d6dd9a65 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,39 @@ +2006-02-01 Robert Schuster <robertschuster@fsfe.org> + + * link.cc: + (_Jv_Linker::find_field_helper): Added checks. + (_Jv_Linker::find_field): Use exception swallowing class resolution + and added early return. + (_Jv_ThrowNoClassDefFoundErrorTrampoline): New function. + (_Jv_Linker::link_symbol_table): Use exception swallowing class + resolution, added ffi_closure installation routine, use + _Jv_ThrowNoClassDefFoundError for missing static method. + (_Jv_Linker::ensure_class_linked): Added string check which does + not trigger class resolution. + * java/lang/natClassLoader.cc: + (_Jv_FindClassNoException): New method. + * java/lang/Class.h: + (_Jv_FindClassNoException): New method declaration. + * include/jvm.h: + (_Jv_FindClassNoException): New method declaration. + (_Jv_FindClassFromSignatureNoException): New method declaration. + * prims.cc: + (_Jv_FindClassFromSignatureNoException): New method. + * gcj/javaprims.h: + (_Jv_equalsUtf8Classname): New method declaration. + (_Jv_isPrimitiveOrDerived): Dito. + * prims.cc: + (_Jv_equalsUtf8Classnames): New method. + (_Jv_isPrimitiveOrDerived): New method. + * verify.cc: + (ref_intersection::equals): Use new classname comparison method. + (type::compatible): Use new classname comparison method. Added + check whether LHS' type is java.lang.Object . + (type::resolve): Added new optional debug message and simplified + if-expression. + (type::to_array): Added codepath that generates an array type + without resolving the element type. + 2006-01-31 Mark Wielaard <mark@klomp.org> * NEWS: Add 4.1 updates. diff --git a/libjava/gcj/javaprims.h b/libjava/gcj/javaprims.h index 99fc7a3b345..c4f815f4d6b 100644 --- a/libjava/gcj/javaprims.h +++ b/libjava/gcj/javaprims.h @@ -566,6 +566,9 @@ class _Jv_Utf8Const friend jboolean _Jv_equalUtf8Consts (const _Jv_Utf8Const*, const _Jv_Utf8Const *); friend jboolean _Jv_equal (_Jv_Utf8Const*, jstring, jint); friend jboolean _Jv_equaln (_Jv_Utf8Const*, jstring, jint); + friend jboolean _Jv_equalUtf8Classnames (const _Jv_Utf8Const*, + const _Jv_Utf8Const*); + friend jboolean _Jv_isPrimitiveOrDerived (const _Jv_Utf8Const*); friend _Jv_Utf8Const *_Jv_makeUtf8Const (char*, int); friend _Jv_Utf8Const *_Jv_makeUtf8Const (jstring); friend jstring _Jv_NewStringUtf8Const (_Jv_Utf8Const*); diff --git a/libjava/include/jvm.h b/libjava/include/jvm.h index ca0aea8000e..7110971f882 100644 --- a/libjava/include/jvm.h +++ b/libjava/include/jvm.h @@ -239,7 +239,7 @@ class _Jv_Linker { private: static _Jv_Field *find_field_helper(jclass, _Jv_Utf8Const *, _Jv_Utf8Const *, - jclass *); + jclass, jclass *); static _Jv_Field *find_field(jclass, jclass, jclass *, _Jv_Utf8Const *, _Jv_Utf8Const *); static void prepare_constant_time_tables(jclass); @@ -271,7 +271,7 @@ public: static void print_class_loaded (jclass); static void resolve_class_ref (jclass, jclass *); static void wait_for_state(jclass, int); - static _Jv_word resolve_pool_entry (jclass, int); + static _Jv_word resolve_pool_entry (jclass, int, bool =false); static void resolve_field (_Jv_Field *, java::lang::ClassLoader *); static void verify_type_assertions (jclass); }; @@ -463,9 +463,18 @@ extern "C" jobject _Jv_UnwrapJNIweakReference (jobject); extern jclass _Jv_FindClass (_Jv_Utf8Const *name, java::lang::ClassLoader *loader); + +extern jclass _Jv_FindClassNoException (_Jv_Utf8Const *name, + java::lang::ClassLoader *loader); + extern jclass _Jv_FindClassFromSignature (char *, java::lang::ClassLoader *loader, char ** = NULL); + +extern jclass _Jv_FindClassFromSignatureNoException (char *, + java::lang::ClassLoader *loader, + char ** = NULL); + extern void _Jv_GetTypesFromSignature (jmethodID method, jclass declaringClass, JArray<jclass> **arg_types_out, @@ -643,4 +652,14 @@ _Jv_IsBinaryCompatibilityABI (jclass c) return c->otable_syms || c->atable_syms || c->itable_syms; } +// Returns whether the given class does not really exists (ie. we have no +// bytecode) but still allows us to do some very conservative actions. +// E.g. throwing a NoClassDefFoundError with the name of the missing +// class. +extern inline jboolean +_Jv_IsPhantomClass (jclass c) +{ + return c->state == JV_STATE_PHANTOM; +} + #endif /* __JAVA_JVM_H__ */ diff --git a/libjava/java/lang/Class.h b/libjava/java/lang/Class.h index fe31fa2a39c..2ddc8e16752 100644 --- a/libjava/java/lang/Class.h +++ b/libjava/java/lang/Class.h @@ -69,7 +69,13 @@ enum JV_STATE_ERROR = 12, - JV_STATE_DONE = 14 // Must be last. + JV_STATE_PHANTOM = 13, // Bytecode is missing. In many cases we can + // work around that. If not, throw a + // NoClassDefFoundError. + + JV_STATE_DONE = 14, // Must be last. + + }; struct _Jv_Field; @@ -240,6 +246,8 @@ void _Jv_RegisterClassHookDefault (jclass klass); void _Jv_RegisterInitiatingLoader (jclass,java::lang::ClassLoader*); void _Jv_UnregisterInitiatingLoader (jclass,java::lang::ClassLoader*); void _Jv_UnregisterClass (jclass); +jclass _Jv_FindClassNoException (_Jv_Utf8Const *name, + java::lang::ClassLoader *loader); jclass _Jv_FindClass (_Jv_Utf8Const *name, java::lang::ClassLoader *loader); jclass _Jv_FindClassInCache (_Jv_Utf8Const *name); @@ -263,6 +271,8 @@ jclass _Jv_GetArrayClass (jclass klass, java::lang::ClassLoader *loader); jboolean _Jv_IsInterpretedClass (jclass); jboolean _Jv_IsBinaryCompatibilityABI (jclass); +jboolean _Jv_IsPhantomClass (jclass); + void _Jv_CopyClassesToSystemLoader (gnu::gcj::runtime::SystemClassLoader *); #ifdef INTERPRETER @@ -469,6 +479,8 @@ private: friend void ::_Jv_RegisterInitiatingLoader (jclass,java::lang::ClassLoader*); friend void ::_Jv_UnregisterInitiatingLoader (jclass,java::lang::ClassLoader*); friend void ::_Jv_UnregisterClass (jclass); + friend jclass (::_Jv_FindClassNoException) (_Jv_Utf8Const *name, + java::lang::ClassLoader *loader); friend jclass (::_Jv_FindClass) (_Jv_Utf8Const *name, java::lang::ClassLoader *loader); friend jclass (::_Jv_FindClassInCache) (_Jv_Utf8Const *name); @@ -499,6 +511,8 @@ private: friend jboolean (::_Jv_IsInterpretedClass) (jclass); friend jboolean (::_Jv_IsBinaryCompatibilityABI) (jclass); + friend jboolean (::_Jv_IsPhantomClass) (jclass); + #ifdef INTERPRETER friend void ::_Jv_InitField (jobject, jclass, int); diff --git a/libjava/java/lang/natClass.cc b/libjava/java/lang/natClass.cc index 951bab974cc..04a5bc46310 100644 --- a/libjava/java/lang/natClass.cc +++ b/libjava/java/lang/natClass.cc @@ -668,8 +668,9 @@ java::lang::Class::finalize (void) void java::lang::Class::initializeClass (void) { - // Short-circuit to avoid needless locking. - if (state == JV_STATE_DONE) + // Short-circuit to avoid needless locking (expression includes + // JV_STATE_PHANTOM and JV_STATE_DONE). + if (state >= JV_STATE_PHANTOM) return; // Step 1. We introduce a new scope so we can synchronize more diff --git a/libjava/java/lang/natClassLoader.cc b/libjava/java/lang/natClassLoader.cc index 797005b54c7..5f4d957f61c 100644 --- a/libjava/java/lang/natClassLoader.cc +++ b/libjava/java/lang/natClassLoader.cc @@ -266,6 +266,30 @@ _Jv_CopyClassesToSystemLoader (gnu::gcj::runtime::SystemClassLoader *loader) system_class_list = SYSTEM_LOADER_INITIALIZED; } +// An internal variant of _Jv_FindClass which simply swallows a +// NoClassDefFoundError or a ClassNotFoundException. This gives the +// caller a chance to evaluate the situation and behave accordingly. +jclass +_Jv_FindClassNoException (_Jv_Utf8Const *name, java::lang::ClassLoader *loader) +{ + jclass klass; + + try + { + klass = _Jv_FindClass(name, loader); + } + catch ( java::lang::NoClassDefFoundError *ncdfe ) + { + return NULL; + } + catch ( java::lang::ClassNotFoundException *cnfe ) + { + return NULL; + } + + return klass; +} + jclass _Jv_FindClass (_Jv_Utf8Const *name, java::lang::ClassLoader *loader) { diff --git a/libjava/link.cc b/libjava/link.cc index 7070d729b4a..1118d9b8bab 100644 --- a/libjava/link.cc +++ b/libjava/link.cc @@ -34,6 +34,7 @@ details. */ #include <java/lang/NoSuchMethodError.h> #include <java/lang/ClassFormatError.h> #include <java/lang/IllegalAccessError.h> +#include <java/lang/InternalError.h> #include <java/lang/AbstractMethodError.h> #include <java/lang/NoClassDefFoundError.h> #include <java/lang/IncompatibleClassChangeError.h> @@ -100,7 +101,7 @@ _Jv_Linker::resolve_field (_Jv_Field *field, java::lang::ClassLoader *loader) // superclasses and interfaces. _Jv_Field * _Jv_Linker::find_field_helper (jclass search, _Jv_Utf8Const *name, - _Jv_Utf8Const *type_name, + _Jv_Utf8Const *type_name, jclass type, jclass *declarer) { while (search) @@ -112,8 +113,26 @@ _Jv_Linker::find_field_helper (jclass search, _Jv_Utf8Const *name, if (! _Jv_equalUtf8Consts (field->name, name)) continue; - if (! field->isResolved ()) - resolve_field (field, search->loader); + // Checks for the odd situation where we were able to retrieve the + // field's class from signature but the resolution of the field itself + // failed which means a different class was resolved. + if (type != NULL) + { + try + { + resolve_field (field, search->loader); + } + catch (java::lang::Throwable *exc) + { + java::lang::LinkageError *le = new java::lang::LinkageError + (JvNewStringLatin1 + ("field type mismatch with different loaders")); + + le->initCause(exc); + + throw le; + } + } // Note that we compare type names and not types. This is // bizarre, but we do it because we want to find a field @@ -123,7 +142,10 @@ _Jv_Linker::find_field_helper (jclass search, _Jv_Utf8Const *name, // pass in the descriptor and check that way, because when // the field is already resolved there is no easy way to // find its descriptor again. - if (_Jv_equalUtf8Consts (type_name, field->type->name)) + if ( (field->isResolved () ? + _Jv_equalUtf8Classnames (type_name, field->type->name) : + _Jv_equalUtf8Classnames ( + type_name, (_Jv_Utf8Const *) field->type)) ) { *declarer = search; return field; @@ -134,7 +156,7 @@ _Jv_Linker::find_field_helper (jclass search, _Jv_Utf8Const *name, for (int i = 0; i < search->interface_count; ++i) { _Jv_Field *result = find_field_helper (search->interfaces[i], name, - type_name, declarer); + type_name, type, declarer); if (result) return result; } @@ -175,13 +197,21 @@ _Jv_Linker::find_field (jclass klass, jclass owner, { // FIXME: this allocates a _Jv_Utf8Const each time. We should make // it cheaper. - jclass field_type = _Jv_FindClassFromSignature (field_type_name->chars(), - klass->loader); - if (field_type == NULL) - throw new java::lang::NoClassDefFoundError(field_name->toString()); - - _Jv_Field *the_field = find_field_helper (owner, field_name, - field_type->name, found_class); + // Note: This call will resolve the primitive type names ("Z", "B", ...) to + // their Java counterparts ("boolean", "byte", ...) if accessed via + // field_type->name later. Using these variants of the type name is in turn + // important for the find_field_helper function. However if the class + // resolution failed then we can only use the already given type name. + jclass field_type + = _Jv_FindClassFromSignatureNoException (field_type_name->chars(), + klass->loader); + + _Jv_Field *the_field + = find_field_helper (owner, field_name, + (field_type + ? field_type->name : + field_type_name ), + field_type, found_class); if (the_field == 0) { @@ -194,6 +224,12 @@ _Jv_Linker::find_field (jclass klass, jclass owner, throw new java::lang::NoSuchFieldError (sb->toString()); } + // Accept it when the field's class could not be resolved. + if (field_type == NULL) + // Silently ignore that we were not able to retrieve the type to make it + // possible to run code which does not access this field. + return the_field; + if (_Jv_CheckAccess (klass, *found_class, the_field->flags)) { // Note that the field returned by find_field_helper is always @@ -221,7 +257,7 @@ _Jv_Linker::find_field (jclass klass, jclass owner, } _Jv_word -_Jv_Linker::resolve_pool_entry (jclass klass, int index) +_Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy) { using namespace java::lang::reflect; @@ -238,13 +274,26 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index) jclass found; if (name->first() == '[') - found = _Jv_FindClassFromSignature (name->chars(), - klass->loader); - else - found = _Jv_FindClass (name, klass->loader); - + found = _Jv_FindClassFromSignatureNoException (name->chars(), + klass->loader); + else + found = _Jv_FindClassNoException (name, klass->loader); + + // If the class could not be loaded a phantom class is created. Any + // function that deals with such a class but cannot do something useful + // with it should just throw a NoClassDefFoundError with the class' + // name. if (! found) - throw new java::lang::NoClassDefFoundError (name->toString()); + if (lazy) + { + found = _Jv_NewClass(name, NULL, NULL); + found->state = JV_STATE_PHANTOM; + pool->tags[index] |= JV_CONSTANT_ResolvedFlag; + pool->data[index].clazz = found; + break; + } + else + throw new java::lang::NoClassDefFoundError (name->toString()); // Check accessibility, but first strip array types as // _Jv_ClassNameSamePackage can't handle arrays. @@ -286,7 +335,12 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index) _Jv_loadIndexes (&pool->data[index], class_index, name_and_type_index); - jclass owner = (resolve_pool_entry (klass, class_index)).clazz; + jclass owner = (resolve_pool_entry (klass, class_index, true)).clazz; + + // If a phantom class was resolved our field reference is + // unusable because of the missing class. + if (owner->state == JV_STATE_PHANTOM) + throw new java::lang::NoClassDefFoundError(owner->getName()); if (owner != klass) _Jv_InitClass (owner); @@ -707,12 +761,31 @@ _Jv_GetMethodString (jclass klass, _Jv_Method *meth, return buf->toString(); } -void +void _Jv_ThrowNoSuchMethodError () { throw new java::lang::NoSuchMethodError; } +// A function whose invocation is prepared using libffi. It gets called +// whenever a static method of a missing class is invoked. The data argument +// holds a reference to a String denoting the missing class. +// The prepared function call is stored in a class' atable. +void +_Jv_ThrowNoClassDefFoundErrorTrampoline(ffi_cif *, + void *, + void **, + void *data) +{ + throw new java::lang::NoClassDefFoundError((jstring) data); +} + +void +_Jv_ThrowNoClassDefFoundError() +{ + throw new java::lang::NoClassDefFoundError(); +} + // Throw a NoSuchFieldError. Called by compiler-generated code when // an otable entry is zero. OTABLE_INDEX is the index in the caller's // otable that refers to the missing field. This index may be used to @@ -723,7 +796,6 @@ _Jv_ThrowNoSuchFieldError (int /* otable_index */) throw new java::lang::NoSuchFieldError; } - // This is put in empty vtable slots. void _Jv_ThrowAbstractMethodError () @@ -1030,21 +1102,65 @@ _Jv_Linker::link_symbol_table (jclass klass) (sym = klass->atable_syms[index]).class_name != NULL; ++index) { - jclass target_class = _Jv_FindClass (sym.class_name, klass->loader); + jclass target_class = + _Jv_FindClassNoException (sym.class_name, klass->loader); + _Jv_Method *meth = NULL; _Jv_Utf8Const *signature = sym.signature; // ??? Setting this pointer to null will at least get us a // NullPointerException klass->atable->addresses[index] = NULL; - + + // If the target class is missing we prepare a function call + // that throws a NoClassDefFoundError and store the address of + // that newly prepare method in the atable. The user can run + // code in classes where the missing class is part of the + // execution environment as long as it is never referenced. if (target_class == NULL) - throw new java::lang::NoClassDefFoundError - (_Jv_NewStringUTF (sym.class_name->chars())); - + { + // TODO: The following structs/objects are heap allocated are + // unreachable by the garbage collector: + // - cif, arg_types + // - the Java string inside the if-statement + + ffi_closure *closure = + (ffi_closure *) _Jv_Malloc( sizeof( ffi_closure )); + ffi_cif *cif = (ffi_cif *) _Jv_Malloc( sizeof( ffi_cif )); + + // Pretends that we want to call a void (*) (void) function via + // ffi_call. + ffi_type **arg_types = (ffi_type **) _Jv_Malloc( sizeof( ffi_type * )); + arg_types[0] = &ffi_type_void; + + // Initializes the cif and the closure. If that worked the closure is + // stored as a function pointer in the atable. + if ( ffi_prep_cif(cif, FFI_DEFAULT_ABI, 1, + &ffi_type_void, arg_types) == FFI_OK + && (ffi_prep_closure + (closure, cif, + _Jv_ThrowNoClassDefFoundErrorTrampoline, + (void *) _Jv_NewStringUtf8Const(sym.class_name)) + == FFI_OK)) + { + klass->atable->addresses[index] = (void *) closure; + } + else + { + // If you land here it is possible that your architecture does + // not support the Closure API yet. Let's port it! + java::lang::StringBuffer *buffer = new java::lang::StringBuffer(); + buffer->append + (JvNewStringLatin1("Error setting up FFI closure" + " for static method of missing class: ")); + buffer->append (_Jv_NewStringUtf8Const(sym.class_name)); + + throw new java::lang::InternalError(buffer->toString()); + } + } // 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->first() == '(' && signature->len() >= 2) + else if (signature->first() == '(' && signature->len() >= 2) { // 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 @@ -1082,13 +1198,16 @@ _Jv_Linker::link_symbol_table (jclass klass) } } else + // TODO: Use _Jv_ThrowNoClassDefFoundErrorTrampoline to be able + // to print the class name. klass->atable->addresses[index] - = (void *)_Jv_ThrowNoSuchMethodError; + = (void *) _Jv_ThrowNoClassDefFoundError; continue; } - // Try fields. + // Try fields only if the target class exists. + if ( target_class != NULL ) { wait_for_state(target_class, JV_STATE_PREPARED); jclass found_class; @@ -1453,7 +1572,8 @@ _Jv_Linker::ensure_class_linked (jclass klass) for (int index = 1; index < pool->size; ++index) { if (pool->tags[index] == JV_CONSTANT_Class) - resolve_pool_entry (klass, index); + // Lazily resolve the entries. + resolve_pool_entry (klass, index, true); } } @@ -1493,8 +1613,13 @@ _Jv_Linker::ensure_class_linked (jclass klass) int mod = f->getModifiers (); // If we have a static String field with a non-null initial // value, we know it points to a Utf8Const. - resolve_field(f, klass->loader); - if (f->getClass () == &java::lang::String::class$ + + // Finds out whether we have to initialize a String without the + // need to resolve the field. + if ((f->isResolved() + ? (f->type == &java::lang::String::class$) + : _Jv_equalUtf8Classnames((_Jv_Utf8Const *) f->type, + java::lang::String::class$.name)) && (mod & java::lang::reflect::Modifier::STATIC) != 0) { jstring *strp = (jstring *) f->u.addr; diff --git a/libjava/prims.cc b/libjava/prims.cc index a968a9b3013..0352669315d 100644 --- a/libjava/prims.cc +++ b/libjava/prims.cc @@ -49,8 +49,10 @@ details. */ #include <java/lang/ArrayIndexOutOfBoundsException.h> #include <java/lang/ArithmeticException.h> #include <java/lang/ClassFormatError.h> +#include <java/lang/ClassNotFoundException.h> #include <java/lang/InternalError.h> #include <java/lang/NegativeArraySizeException.h> +#include <java/lang/NoClassDefFoundError.h> #include <java/lang/NullPointerException.h> #include <java/lang/OutOfMemoryError.h> #include <java/lang/System.h> @@ -168,7 +170,6 @@ SIGNAL_HANDLER (catch_fpe) } #endif - jboolean _Jv_equalUtf8Consts (const Utf8Const* a, const Utf8Const *b) @@ -236,6 +237,120 @@ _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) @@ -434,6 +549,9 @@ _Jv_AllocObjectNoInitNoFinalizer (jclass klass) 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); @@ -512,6 +630,11 @@ _Jv_AllocPtrFreeObject (jclass klass) 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; @@ -766,7 +889,28 @@ _Jv_FindClassFromSignature (char *sig, java::lang::ClassLoader *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<jstring> * JvConvertArgv (int argc, const char **argv) diff --git a/libjava/verify.cc b/libjava/verify.cc index 8b9cfcc759f..4df6ead95c5 100644 --- a/libjava/verify.cc +++ b/libjava/verify.cc @@ -14,6 +14,8 @@ details. */ #include <config.h> +#include <string.h> + #include <jvm.h> #include <gcj/cni.h> #include <java-insns.h> @@ -324,7 +326,7 @@ private: bool equals (ref_intersection *other, _Jv_BytecodeVerifier *verifier) { if (! is_resolved && ! other->is_resolved - && _Jv_equalUtf8Consts (data.name, other->data.name)) + && _Jv_equalUtf8Classnames (data.name, other->data.name)) return true; if (! is_resolved) resolve (verifier); @@ -364,11 +366,18 @@ private: if (is_resolved) return; + // This is useful if you want to see which classes have to be resolved + // while doing the class verification. + debug_print("resolving class: %s\n", data.name->chars()); + using namespace java::lang; java::lang::ClassLoader *loader = verifier->current_class->getClassLoaderInternal(); - // We might see either kind of name. Sigh. - if (data.name->first() == 'L' && data.name->limit()[-1] == ';') + + // Due to special handling in to_array() array classes will always + // be of the "L ... ;" kind. The separator char ('.' or '/' may vary + // however. + if (data.name->limit()[-1] == ';') { data.klass = _Jv_FindClassFromSignature (data.name->chars(), loader); if (data.klass == NULL) @@ -397,12 +406,21 @@ private: // Avoid resolving if possible. if (! self->is_resolved && ! other_iter->is_resolved - && _Jv_equalUtf8Consts (self->data.name, - other_iter->data.name)) + && _Jv_equalUtf8Classnames (self->data.name, + other_iter->data.name)) continue; if (! self->is_resolved) self->resolve(verifier); + + // If the LHS of the expression is of type + // java.lang.Object, assignment will succeed, no matter + // what the type of the RHS is. Using this short-cut we + // don't need to resolve the class of the RHS at + // verification time. + if (self->data.klass == &java::lang::Object::class$) + continue; + if (! other_iter->is_resolved) other_iter->resolve(verifier); @@ -852,9 +870,70 @@ private: if (key != reference_type) verifier->verify_fail ("internal error in type::to_array()"); - jclass k = klass->getclass (verifier); - return type (_Jv_GetArrayClass (k, k->getClassLoaderInternal()), - verifier); + // In case the class is already resolved we can simply ask the runtime + // to give us the array version. + // If it is not resolved we prepend "[" to the classname to make the + // array usage verification more lazy. In other words: makes new Foo[300] + // pass the verifier if Foo.class is missing. + if (klass->is_resolved) + { + jclass k = klass->getclass (verifier); + + return type (_Jv_GetArrayClass (k, k->getClassLoaderInternal()), + verifier); + } + else + { + int len = klass->data.name->len(); + + // If the classname is given in the Lp1/p2/cn; format we only need + // to add a leading '['. The same procedure has to be done for + // primitive arrays (ie. provided "[I", the result should be "[[I". + // If the classname is given as p1.p2.cn we have to embed it into + // "[L" and ';'. + if (klass->data.name->limit()[-1] == ';' || + _Jv_isPrimitiveOrDerived(klass->data.name)) + { + // Reserves space for leading '[' and trailing '\0' . + char arrayName[len + 2]; + + arrayName[0] = '['; + strcpy(&arrayName[1], klass->data.name->chars()); + +#ifdef VERIFY_DEBUG + // This is only needed when we want to print the string to the + // screen while debugging. + arrayName[len + 1] = '\0'; + + debug_print("len: %d - old: '%s' - new: '%s'\n", len, klass->data.name->chars(), arrayName); +#endif + + return type (verifier->make_utf8_const( arrayName, len + 1 ), + verifier); + } + else + { + // Reserves space for leading "[L" and trailing ';' and '\0' . + char arrayName[len + 4]; + + arrayName[0] = '['; + arrayName[1] = 'L'; + strcpy(&arrayName[2], klass->data.name->chars()); + arrayName[len + 2] = ';'; + +#ifdef VERIFY_DEBUG + // This is only needed when we want to print the string to the + // screen while debugging. + arrayName[len + 3] = '\0'; + + debug_print("len: %d - old: '%s' - new: '%s'\n", len, klass->data.name->chars(), arrayName); +#endif + + return type (verifier->make_utf8_const( arrayName, len + 3 ), + verifier); + } + } + } bool isreference () const |