This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
RFC: make linking BC-compiled classes more lazy - part one
- From: Robert Schuster <theBohemian at gmx dot net>
- To: java-patches at gcc dot gnu dot org
- Date: Wed, 23 Nov 2005 14:36:22 +0100
- Subject: RFC: make linking BC-compiled classes more lazy - part one
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi,
this patch fixes half of the problems I mentioned in bug 24616[0]. For further
details about the approach taken see the bug report. Another patch which fixes
the remaining bits is already prepared.
This patch makes a lot more sense with my previous fixes for the verifier[1]. :)
2005-11-23 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.
(_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.
* jvm.h:
(_Jv_FindClassNoException): New method declaration.
(_Jv_FindClassFromSignatureNoException): New method declaration.
* prims.cc:
(_Jv_FindClassFromSignatureNoException): New method.
cya
Robert
[0] - http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24616
[1] - http://gcc.gnu.org/ml/java-patches/2005-q4/msg00213.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFDhHBVG9cfwmwwEtoRAnDTAJwNITGv/VvpU4VrFy4JrRBn+75AHwCfW1Oy
76UB97O/WitB1jnvTUoQGG4=
=129U
-----END PGP SIGNATURE-----
Index: link.cc
===================================================================
--- link.cc (Revision 106248)
+++ link.cc (Arbeitskopie)
@@ -34,6 +34,7 @@
#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 @@
// 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,9 +113,27 @@
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
// (and terminate the search) if it has the correct
@@ -123,7 +142,10 @@
// 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 @@
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,19 @@
{
// 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());
+ // 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->name, found_class);
+ (field_type ?
+ field_type->name :
+ field_type_name ),
+ field_type, found_class);
if (the_field == 0)
{
@@ -194,6 +222,12 @@
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
@@ -707,12 +741,26 @@
return buf->toString();
}
-void
+void
_Jv_ThrowNoSuchMethodError ()
{
throw new java::lang::NoSuchMethodError;
}
+// A function that 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);
+}
+
+
// This is put in empty vtable slots.
void
_Jv_ThrowAbstractMethodError ()
@@ -990,21 +1038,55 @@
(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 to a function
+ // that simply throws a NoClassDefFoundError and store the address in the
+ // atable. The effect is that the user can run code of classes where the
+ // missing class is part of the execution environment but gets never
+ // referenced.
if (target_class == NULL)
- throw new java::lang::NoClassDefFoundError
- (_Jv_NewStringUTF (sym.class_name->chars()));
-
+ {
+ 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
@@ -1048,7 +1130,8 @@
continue;
}
- // Try fields.
+ // Tries fields only if the target class exists.
+ if ( target_class != NULL )
{
wait_for_state(target_class, JV_STATE_PREPARED);
jclass found_class;
@@ -1453,9 +1536,14 @@
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$
- && (mod & java::lang::reflect::Modifier::STATIC) != 0)
+
+ // 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;
if (*strp)
Index: java/lang/natClassLoader.cc
===================================================================
--- java/lang/natClassLoader.cc (Revision 106248)
+++ java/lang/natClassLoader.cc (Arbeitskopie)
@@ -265,7 +265,31 @@
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)
{
// See if the class was already loaded by this loader. This handles
Index: java/lang/Class.h
===================================================================
--- java/lang/Class.h (Revision 106248)
+++ java/lang/Class.h (Arbeitskopie)
@@ -214,6 +214,8 @@
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);
@@ -443,6 +445,8 @@
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);
Index: include/jvm.h
===================================================================
--- include/jvm.h (Revision 106248)
+++ include/jvm.h (Arbeitskopie)
@@ -240,7 +240,7 @@
{
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);
@@ -452,11 +452,16 @@
extern "C" void _Jv_RegisterResource (void *vptr);
extern void _Jv_UnregisterClass (_Jv_Utf8Const*, java::lang::ClassLoader*);
+extern jclass _Jv_FindClassNoException (_Jv_Utf8Const *name,
+ java::lang::ClassLoader *loader);
extern jclass _Jv_FindClass (_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,
Index: prims.cc
===================================================================
--- prims.cc (Revision 106248)
+++ prims.cc (Arbeitskopie)
@@ -49,8 +49,10 @@
#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>
@@ -766,8 +886,28 @@
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)
{