// link.cc - Code for linking and resolving classes and pool entries.
-/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation
+/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation
This file is part of libgcj.
#include <config.h>
#include <platform.h>
+#include <stdio.h>
+
#include <java-interp.h>
#include <jvm.h>
#include <gcj/cni.h>
#include <string.h>
+#include <limits.h>
#include <java-cpool.h>
#include <execution.h>
#include <java/lang/Class.h>
{
if (! field->isResolved ())
{
- _Jv_Utf8Const *sig = (_Jv_Utf8Const*)field->type;
- field->type = _Jv_FindClassFromSignature (sig->chars(), loader);
+ _Jv_Utf8Const *sig = (_Jv_Utf8Const *) field->type;
+ jclass type = _Jv_FindClassFromSignature (sig->chars(), loader);
+ if (type == NULL)
+ throw new java::lang::NoClassDefFoundError(field->name->toString());
+ field->type = type;
field->flags &= ~_Jv_FIELD_UNRESOLVED_FLAG;
}
}
+// A helper for find_field that knows how to recursively search
+// superclasses and interfaces.
+_Jv_Field *
+_Jv_Linker::find_field_helper (jclass search, _Jv_Utf8Const *name,
+ _Jv_Utf8Const *type_name,
+ jclass *declarer)
+{
+ while (search)
+ {
+ // From 5.4.3.2. First search class itself.
+ for (int i = 0; i < search->field_count; ++i)
+ {
+ _Jv_Field *field = &search->fields[i];
+ if (! _Jv_equalUtf8Consts (field->name, name))
+ continue;
+
+ if (! field->isResolved ())
+ resolve_field (field, search->loader);
+
+ // 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
+ // descriptor -- but then later reject it if the class
+ // loader check results in different classes. We can't just
+ // 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))
+ {
+ *declarer = search;
+ return field;
+ }
+ }
+
+ // Next search direct interfaces.
+ for (int i = 0; i < search->interface_count; ++i)
+ {
+ _Jv_Field *result = find_field_helper (search->interfaces[i], name,
+ type_name, declarer);
+ if (result)
+ return result;
+ }
+
+ // Now search superclass.
+ search = search->superclass;
+ }
+
+ return NULL;
+}
+
+bool
+_Jv_Linker::has_field_p (jclass search, _Jv_Utf8Const *field_name)
+{
+ for (int i = 0; i < search->field_count; ++i)
+ {
+ _Jv_Field *field = &search->fields[i];
+ if (_Jv_equalUtf8Consts (field->name, field_name))
+ return true;
+ }
+ return false;
+}
+
+// Find a field.
+// KLASS is the class that is requesting the field.
+// OWNER is the class in which the field should be found.
+// FIELD_TYPE_NAME is the type descriptor for the field.
+// Fill FOUND_CLASS with the address of the class in which the field
+// is actually declared.
+// This function does the class loader type checks, and
+// also access checks. Returns the field, or throws an
+// exception on error.
+_Jv_Field *
+_Jv_Linker::find_field (jclass klass, jclass owner,
+ jclass *found_class,
+ _Jv_Utf8Const *field_name,
+ _Jv_Utf8Const *field_type_name)
+{
+ // 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);
+
+ if (the_field == 0)
+ {
+ java::lang::StringBuffer *sb = new java::lang::StringBuffer();
+ sb->append(JvNewStringLatin1("field "));
+ sb->append(owner->getName());
+ sb->append(JvNewStringLatin1("."));
+ sb->append(_Jv_NewStringUTF(field_name->chars()));
+ sb->append(JvNewStringLatin1(" was not found."));
+ throw new java::lang::NoSuchFieldError (sb->toString());
+ }
+
+ if (_Jv_CheckAccess (klass, *found_class, the_field->flags))
+ {
+ // Note that the field returned by find_field_helper is always
+ // resolved. There's no point checking class loaders here,
+ // since we already did the work to look up all the types.
+ // FIXME: being lazy here would be nice.
+ if (the_field->type != field_type)
+ throw new java::lang::LinkageError
+ (JvNewStringLatin1
+ ("field type mismatch with different loaders"));
+ }
+ else
+ {
+ java::lang::StringBuffer *sb
+ = new java::lang::StringBuffer ();
+ sb->append(klass->getName());
+ sb->append(JvNewStringLatin1(": "));
+ sb->append((*found_class)->getName());
+ sb->append(JvNewStringLatin1("."));
+ sb->append(_Jv_NewStringUtf8Const (field_name));
+ throw new java::lang::IllegalAccessError(sb->toString());
+ }
+
+ return the_field;
+}
+
_Jv_word
_Jv_Linker::resolve_pool_entry (jclass klass, int index)
{
_Jv_Utf8Const *field_name = pool->data[name_index].utf8;
_Jv_Utf8Const *field_type_name = pool->data[type_index].utf8;
- // FIXME: The implementation of this function
- // (_Jv_FindClassFromSignature) will generate an instance of
- // _Jv_Utf8Const for each call if the field type is a class name
- // (Lxx.yy.Z;). This may be too expensive to do for each and
- // every fieldref being resolved. For now, we fix the problem by
- // only doing it when we have a loader different from the class
- // declaring the field.
-
- jclass field_type = 0;
-
- if (owner->loader != klass->loader)
- field_type = _Jv_FindClassFromSignature (field_type_name->chars(),
- klass->loader);
-
- _Jv_Field* the_field = 0;
-
- for (jclass cls = owner; 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, field_name))
- continue;
-
- if (_Jv_CheckAccess (klass, cls, field->flags))
- {
- // Resolve the field using the class' own loader if
- // necessary.
-
- if (!field->isResolved ())
- resolve_field (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;
- }
- else
- {
- java::lang::StringBuffer *sb
- = new java::lang::StringBuffer ();
- sb->append(klass->getName());
- sb->append(JvNewStringLatin1(": "));
- sb->append(cls->getName());
- sb->append(JvNewStringLatin1("."));
- sb->append(_Jv_NewStringUtf8Const (field_name));
- throw new java::lang::IllegalAccessError(sb->toString());
- }
- }
- }
-
- end_of_field_search:
- if (the_field == 0)
- {
- java::lang::StringBuffer *sb = new java::lang::StringBuffer();
- sb->append(JvNewStringLatin1("field "));
- sb->append(owner->getName());
- sb->append(JvNewStringLatin1("."));
- sb->append(_Jv_NewStringUTF(field_name->chars()));
- sb->append(JvNewStringLatin1(" was not found."));
- throw
- new java::lang::IncompatibleClassChangeError (sb->toString());
- }
-
+ jclass found_class = 0;
+ _Jv_Field *the_field = find_field (klass, owner,
+ &found_class,
+ field_name,
+ field_type_name);
+ if (owner != found_class)
+ _Jv_InitClass (found_class);
pool->data[index].field = the_field;
pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
}
ifaces.list = (jclass *) _Jv_Malloc (ifaces.len
* sizeof (jclass *));
- get_interfaces (owner, &ifaces);
+ get_interfaces (owner, &ifaces);
for (int i = 0; i < ifaces.count; i++)
{
// Format method name for use in error messages.
jstring
-_Jv_GetMethodString (jclass klass, _Jv_Utf8Const *name)
+_Jv_GetMethodString (jclass klass, _Jv_Method *meth,
+ jclass derived)
{
- jstring r = klass->name->toString();
- r = r->concat (JvNewStringUTF ("."));
- r = r->concat (name->toString());
- return r;
+ using namespace java::lang;
+ StringBuffer *buf = new StringBuffer (klass->name->toString());
+ buf->append (jchar ('.'));
+ buf->append (meth->name->toString());
+ buf->append ((jchar) ' ');
+ buf->append (meth->signature->toString());
+ if (derived)
+ {
+ buf->append(JvNewStringLatin1(" in "));
+ buf->append(derived->name->toString());
+ }
+ return buf->toString();
}
void
throw new java::lang::NoSuchMethodError;
}
+// This is put in empty vtable slots.
+static void
+_Jv_abstractMethodError (void)
+{
+ throw new java::lang::AbstractMethodError();
+}
+
// Each superinterface of a class (i.e. each interface that the class
// directly or indirectly implements) has a corresponding "Partial
// Interface Dispatch Table" whose size is (number of methods + 1) words.
{
if ((meth->accflags & Modifier::STATIC) != 0)
throw new java::lang::IncompatibleClassChangeError
- (_Jv_GetMethodString (klass, meth->name));
- if ((meth->accflags & Modifier::ABSTRACT) != 0)
- throw new java::lang::AbstractMethodError
- (_Jv_GetMethodString (klass, meth->name));
+ (_Jv_GetMethodString (klass, meth));
if ((meth->accflags & Modifier::PUBLIC) == 0)
throw new java::lang::IllegalAccessError
- (_Jv_GetMethodString (klass, meth->name));
+ (_Jv_GetMethodString (klass, meth));
- itable[pos] = meth->ncode;
+ if ((meth->accflags & Modifier::ABSTRACT) != 0)
+ itable[pos] = (void *) &_Jv_abstractMethodError;
+ else
+ itable[pos] = meth->ncode;
}
else
{
continue;
}
- // try fields
+ // Try fields.
{
- _Jv_Field *the_field = NULL;
-
wait_for_state(target_class, JV_STATE_PREPARED);
- 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 ())
- resolve_field (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;
- if (debug_link)
- fprintf (stderr, " offsets[%d] = %d (class %s@%p : %s)\n",
- (int)index,
- (int)field->u.boffset,
- (const char*)cls->name->chars(),
- cls,
- (const char*)field->name->chars());
- goto end_of_field_search;
- }
- }
- end_of_field_search:
- if (the_field != NULL)
- {
- if ((the_field->flags & java::lang::reflect::Modifier::STATIC))
- throw new java::lang::IncompatibleClassChangeError;
- else
- klass->otable->offsets[index] = the_field->u.boffset;
- }
+ jclass found_class;
+ _Jv_Field *the_field = find_field (klass, target_class, &found_class,
+ sym.name, sym.signature);
+ if ((the_field->flags & java::lang::reflect::Modifier::STATIC))
+ throw new java::lang::IncompatibleClassChangeError;
else
- {
- throw new java::lang::NoSuchFieldError
- (_Jv_NewStringUtf8Const (sym.name));
- }
+ klass->otable->offsets[index] = the_field->u.boffset;
}
}
continue;
}
- // try fields
+ // Try fields.
{
- _Jv_Field *the_field = NULL;
-
wait_for_state(target_class, JV_STATE_PREPARED);
- 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 ())
- resolve_field (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 & java::lang::reflect::Modifier::STATIC))
- klass->atable->addresses[index] = the_field->u.addr;
- else
- throw new java::lang::IncompatibleClassChangeError;
- }
+ jclass found_class;
+ _Jv_Field *the_field = find_field (klass, target_class, &found_class,
+ sym.name, sym.signature);
+ if ((the_field->flags & java::lang::reflect::Modifier::STATIC))
+ klass->atable->addresses[index] = the_field->u.addr;
else
- {
- throw new java::lang::NoSuchFieldError
- (_Jv_NewStringUtf8Const (sym.name));
- }
+ throw new java::lang::IncompatibleClassChangeError;
}
}
if (found)
{
klass->itable->addresses[index * 2] = cls;
- klass->itable->addresses[index * 2 + 1] = (void *)(intptr_t) i;
+ klass->itable->addresses[index * 2 + 1] = (void *)(unsigned long) i;
if (debug_link)
{
fprintf (stderr, " interfaces[%d] = %p (interface %s@%p : %s(%s))\n",
(const char*)signature->chars());
fprintf (stderr, " [%d] = offset %d\n",
index + 1,
- (int)(intptr_t)klass->itable->addresses[index * 2 + 1]);
+ (int)(unsigned long)klass->itable->addresses[index * 2 + 1]);
}
}
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_Linker::layout_interface_methods (jclass iface)
using namespace java::lang;
StringBuffer *sb = new StringBuffer();
sb->append(JvNewStringLatin1("method "));
- sb->append(_Jv_GetMethodString(klass, meth->name));
+ sb->append(_Jv_GetMethodString(klass, meth));
sb->append(JvNewStringLatin1(" overrides final method "));
- sb->append(_Jv_GetMethodString(declarer, super_meth->name));
+ sb->append(_Jv_GetMethodString(declarer, super_meth));
throw new VerifyError(sb->toString());
}
}
if (meth->index == (_Jv_ushort) -1)
continue;
if ((meth->accflags & Modifier::ABSTRACT))
+ // FIXME: it might be nice to have a libffi trampoline here,
+ // so we could pass in the method name and other information.
vtable->set_method(meth->index, (void *) &_Jv_abstractMethodError);
else
vtable->set_method(meth->index, meth->ncode);
// override an old one.
set_vtable_entries (klass, vtable);
- // 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 (vtable->get_method(i) == (void *) &_Jv_abstractMethodError)
- {
- using namespace java::lang;
- while (klass != NULL)
- {
- for (int j = 0; j < klass->method_count; ++j)
- {
- if (klass->methods[j].index == i)
- {
- StringBuffer *buf = new StringBuffer ();
- buf->append (_Jv_NewStringUtf8Const (klass->methods[j].name));
- buf->append ((jchar) ' ');
- buf->append (_Jv_NewStringUtf8Const (klass->methods[j].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 ();
- }
- }
+ // Note that we don't check for abstract methods here. We used to,
+ // but there is a JVMS clarification that indicates that a check
+ // here would be too eager. And, a simple test case confirms this.
}
// Lay out the class, allocating space for static fields and computing