blob: d165c80b242694e6c371164a34e3825b8e01feff [file] [log] [blame]
// resolve.cc - Code for linking and resolving classes and pool entries.
/* Copyright (C) 1999, 2000, 2001 Free Software Foundation
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
/* Author: Kresten Krab Thorup <krab@gnu.org> */
#include <config.h>
#include <java-interp.h>
#include <jvm.h>
#include <gcj/cni.h>
#include <string.h>
#include <java-cpool.h>
#include <java/lang/Class.h>
#include <java/lang/String.h>
#include <java/lang/Thread.h>
#include <java/lang/InternalError.h>
#include <java/lang/VirtualMachineError.h>
#include <java/lang/NoSuchFieldError.h>
#include <java/lang/NoSuchMethodError.h>
#include <java/lang/ClassFormatError.h>
#include <java/lang/IllegalAccessError.h>
#include <java/lang/AbstractMethodError.h>
#include <java/lang/ClassNotFoundException.h>
#include <java/lang/IncompatibleClassChangeError.h>
#include <java/lang/reflect/Modifier.h>
void
_Jv_ResolveField (_Jv_Field *field, java::lang::ClassLoader *loader)
{
if (! field->isResolved ())
{
_Jv_Utf8Const *sig = (_Jv_Utf8Const*)field->type;
field->type = _Jv_FindClassFromSignature (sig->data, loader);
field->flags &= ~_Jv_FIELD_UNRESOLVED_FLAG;
}
}
#ifdef INTERPRETER
static void throw_internal_error (char *msg)
__attribute__ ((__noreturn__));
static void throw_class_format_error (jstring msg)
__attribute__ ((__noreturn__));
static void throw_class_format_error (char *msg)
__attribute__ ((__noreturn__));
// Exceptional return values for _Jv_DetermineVTableIndex
#define METHOD_NOT_THERE (-2)
#define METHOD_INACCESSIBLE (-1)
static int get_alignment_from_class (jclass);
static _Jv_ResolvedMethod*
_Jv_BuildResolvedMethod (_Jv_Method*,
jclass,
jboolean,
jint);
// We need to know the name of a constructor.
static _Jv_Utf8Const *init_name = _Jv_makeUtf8Const ("<init>", 6);
static void throw_incompatible_class_change_error (jstring msg)
{
throw new java::lang::IncompatibleClassChangeError (msg);
}
_Jv_word
_Jv_ResolvePoolEntry (jclass klass, int index)
{
using namespace java::lang::reflect;
_Jv_Constants *pool = &klass->constants;
if ((pool->tags[index] & JV_CONSTANT_ResolvedFlag) != 0)
return pool->data[index];
switch (pool->tags[index]) {
case JV_CONSTANT_Class:
{
_Jv_Utf8Const *name = pool->data[index].utf8;
jclass found;
if (name->data[0] == '[')
found = _Jv_FindClassFromSignature (&name->data[0],
klass->loader);
else
found = _Jv_FindClass (name, klass->loader);
if (! found)
{
jstring str = _Jv_NewStringUTF (name->data);
throw new java::lang::ClassNotFoundException (str);
}
if ((found->accflags & Modifier::PUBLIC) == Modifier::PUBLIC
|| (_Jv_ClassNameSamePackage (found->name,
klass->name)))
{
pool->data[index].clazz = found;
pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
}
else
{
throw new java::lang::IllegalAccessError (found->getName());
}
}
break;
case JV_CONSTANT_String:
{
jstring str;
str = _Jv_NewStringUtf8Const (pool->data[index].utf8);
pool->data[index].o = str;
pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
}
break;
case JV_CONSTANT_Fieldref:
{
_Jv_ushort class_index, name_and_type_index;
_Jv_loadIndexes (&pool->data[index],
class_index,
name_and_type_index);
jclass owner = (_Jv_ResolvePoolEntry (klass, class_index)).clazz;
if (owner != klass)
_Jv_InitClass (owner);
_Jv_ushort name_index, type_index;
_Jv_loadIndexes (&pool->data[name_and_type_index],
name_index,
type_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->data,
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;
// now, check field access.
if ( (cls == klass)
|| ((field->flags & Modifier::PUBLIC) != 0)
|| (((field->flags & Modifier::PROTECTED) != 0)
&& cls->isAssignableFrom (klass))
|| (((field->flags & Modifier::PRIVATE) == 0)
&& _Jv_ClassNameSamePackage (cls->name,
klass->name)))
{
/* resove the field using the class' own loader
if necessary */
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;
}
else
{
throw new java::lang::IllegalAccessError;
}
}
}
end_of_field_search:
if (the_field == 0)
{
jstring msg = JvNewStringLatin1 ("field ");
msg = msg->concat (owner->getName ());
msg = msg->concat (JvNewStringLatin1("."));
msg = msg->concat (_Jv_NewStringUTF (field_name->data));
msg = msg->concat (JvNewStringLatin1(" was not found."));
throw_incompatible_class_change_error (msg);
}
pool->data[index].field = the_field;
pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
}
break;
case JV_CONSTANT_Methodref:
case JV_CONSTANT_InterfaceMethodref:
{
_Jv_ushort class_index, name_and_type_index;
_Jv_loadIndexes (&pool->data[index],
class_index,
name_and_type_index);
jclass owner = (_Jv_ResolvePoolEntry (klass, class_index)).clazz;
if (owner != klass)
_Jv_InitClass (owner);
_Jv_ushort name_index, type_index;
_Jv_loadIndexes (&pool->data[name_and_type_index],
name_index,
type_index);
_Jv_Utf8Const *method_name = pool->data[name_index].utf8;
_Jv_Utf8Const *method_signature = pool->data[type_index].utf8;
int vtable_index = -1;
_Jv_Method *the_method = 0;
jclass found_class = 0;
// First search the class itself.
the_method = _Jv_SearchMethodInClass (owner, klass,
method_name, method_signature);
if (the_method != 0)
{
found_class = owner;
goto end_of_method_search;
}
// If we are resolving an interface method, search the interface's
// superinterfaces (A superinterface is not an interface's superclass -
// a superinterface is implemented by the interface).
if (pool->tags[index] == JV_CONSTANT_InterfaceMethodref)
{
_Jv_ifaces ifaces;
ifaces.count = 0;
ifaces.len = 4;
ifaces.list = (jclass *) _Jv_Malloc (ifaces.len * sizeof (jclass *));
_Jv_GetInterfaces (owner, &ifaces);
for (int i=0; i < ifaces.count; i++)
{
jclass cls = ifaces.list[i];
the_method = _Jv_SearchMethodInClass (cls, klass, method_name,
method_signature);
if (the_method != 0)
{
found_class = cls;
break;
}
}
_Jv_Free (ifaces.list);
if (the_method != 0)
goto end_of_method_search;
}
// Finally, search superclasses.
for (jclass cls = owner->getSuperclass (); cls != 0;
cls = cls->getSuperclass ())
{
the_method = _Jv_SearchMethodInClass (cls, klass,
method_name, method_signature);
if (the_method != 0)
{
found_class = cls;
break;
}
}
end_of_method_search:
// FIXME: if (cls->loader != klass->loader), then we
// must actually check that the types of arguments
// correspond. That is, for each argument type, and
// the return type, doing _Jv_FindClassFromSignature
// with either loader should produce the same result,
// i.e., exactly the same jclass object. JVMS 5.4.3.3
if (pool->tags[index] == JV_CONSTANT_InterfaceMethodref)
vtable_index = -1;
else
vtable_index = _Jv_DetermineVTableIndex
(found_class, method_name, method_signature);
if (vtable_index == METHOD_NOT_THERE)
throw_incompatible_class_change_error
(JvNewStringLatin1 ("method not found"));
if (the_method == 0)
{
jstring msg = JvNewStringLatin1 ("method ");
msg = msg->concat (owner->getName ());
msg = msg->concat (JvNewStringLatin1("."));
msg = msg->concat (_Jv_NewStringUTF (method_name->data));
msg = msg->concat (JvNewStringLatin1(" was not found."));
throw new java::lang::NoSuchMethodError (msg);
}
pool->data[index].rmethod =
_Jv_BuildResolvedMethod(the_method,
found_class,
(the_method->accflags & Modifier::STATIC) != 0,
vtable_index);
pool->tags[index] |= JV_CONSTANT_ResolvedFlag;
}
break;
}
return pool->data[index];
}
// Find a method declared in the cls that is referenced from klass and
// perform access checks.
_Jv_Method *
_Jv_SearchMethodInClass (jclass cls, jclass klass,
_Jv_Utf8Const *method_name,
_Jv_Utf8Const *method_signature)
{
using namespace java::lang::reflect;
for (int i = 0; i < cls->method_count; i++)
{
_Jv_Method *method = &cls->methods[i];
if ( (!_Jv_equalUtf8Consts (method->name,
method_name))
|| (!_Jv_equalUtf8Consts (method->signature,
method_signature)))
continue;
if (cls == klass
|| ((method->accflags & Modifier::PUBLIC) != 0)
|| (((method->accflags & Modifier::PROTECTED) != 0)
&& cls->isAssignableFrom (klass))
|| (((method->accflags & Modifier::PRIVATE) == 0)
&& _Jv_ClassNameSamePackage (cls->name,
klass->name)))
{
return method;
}
else
{
throw new java::lang::IllegalAccessError;
}
}
return 0;
}
/** FIXME: this is a terribly inefficient algorithm! It would improve
things if compiled classes to know vtable offset, and _Jv_Method had
a field for this.
Returns METHOD_NOT_THERE if this class does not declare the given method.
Returns METHOD_INACCESSIBLE if the given method does not appear in the
vtable, i.e., it is static, private, final or a constructor.
Otherwise, returns the vtable index. */
int
_Jv_DetermineVTableIndex (jclass klass,
_Jv_Utf8Const *name,
_Jv_Utf8Const *signature)
{
using namespace java::lang::reflect;
jclass super_class = klass->getSuperclass ();
if (super_class != NULL)
{
int prev = _Jv_DetermineVTableIndex (super_class,
name,
signature);
if (prev != METHOD_NOT_THERE)
return prev;
}
/* at this point, we know that the super-class does not declare
* the method. Otherwise, the above call would have found it, and
* determined the result of this function (-1 or some positive
* number).
*/
_Jv_Method *meth = _Jv_GetMethodLocal (klass, name, signature);
/* now, if we do not declare this method, return zero */
if (meth == NULL)
return METHOD_NOT_THERE;
/* so now, we know not only that the super class does not declare the
* method, but we do! So, this is a first declaration of the method. */
/* now, the checks for things that are declared in this class, but do
* not go into the vtable. There are three cases.
* 1) the method is static, private or final
* 2) the class itself is final, or
* 3) it is the method <init>
*/
if ((meth->accflags & (Modifier::STATIC
| Modifier::PRIVATE
| Modifier::FINAL)) != 0
|| (klass->accflags & Modifier::FINAL) != 0
|| _Jv_equalUtf8Consts (name, init_name))
return METHOD_INACCESSIBLE;
/* reaching this point, we know for sure, that the method in question
* will be in the vtable. The question is where. */
/* the base offset, is where we will start assigning vtable
* indexes for this class. It is 0 for base classes
* and for non-base classes it is the
* number of entries in the super class' vtable. */
int base_offset;
if (super_class == 0)
base_offset = 0;
else
base_offset = super_class->vtable_method_count;
/* we will consider methods 0..this_method_index-1. And for each one,
* determine if it is new (i.e., if it appears in the super class),
* and if it should go in the vtable. If so, increment base_offset */
int this_method_index = meth - (&klass->methods[0]);
for (int i = 0; i < this_method_index; i++)
{
_Jv_Method *m = &klass->methods[i];
/* fist some checks for things that surely do not go in the
* vtable */
if ((m->accflags & (Modifier::STATIC | Modifier::PRIVATE)) != 0)
continue;
if (_Jv_equalUtf8Consts (m->name, init_name))
continue;
/* Then, we need to know if this method appears in the
superclass. (This is where this function gets expensive) */
_Jv_Method *sm = _Jv_LookupDeclaredMethod (super_class,
m->name,
m->signature);
/* if it was somehow declared in the superclass, skip this */
if (sm != NULL)
continue;
/* but if it is final, and not declared in the super class,
* then we also skip it */
if ((m->accflags & Modifier::FINAL) != 0)
continue;
/* finally, we can assign the index of this method */
/* m->vtable_index = base_offset */
base_offset += 1;
}
return base_offset;
}
/* this is installed in place of abstract methods */
static void
_Jv_abstractMethodError ()
{
throw new java::lang::AbstractMethodError;
}
void
_Jv_PrepareClass(jclass klass)
{
using namespace java::lang::reflect;
/*
* The job of this function is to: 1) assign storage to fields, and 2)
* build the vtable. static fields are assigned real memory, instance
* fields are assigned offsets.
*
* NOTE: we have a contract with the garbage collector here. Static
* reference fields must not be resolved, until after they have storage
* assigned which is the check used by the collector to see if it
* should indirect the static field reference and mark the object
* pointed to.
*
* Most fields are resolved lazily (i.e. have their class-type
* assigned) when they are accessed the first time by calling as part
* of _Jv_ResolveField, which is allways called after _Jv_PrepareClass.
* Static fields with initializers are resolved as part of this
* function, as are fields with primitive types.
*/
if (! _Jv_IsInterpretedClass (klass))
return;
if (klass->state >= JV_STATE_PREPARED)
return;
// make sure super-class is linked. This involves taking a lock on
// the super class, so we use the Java method resolveClass, which will
// unlock it properly, should an exception happen.
java::lang::ClassLoader::resolveClass0 (klass->superclass);
_Jv_InterpClass *clz = (_Jv_InterpClass*)klass;
/************ PART ONE: OBJECT LAYOUT ***************/
int instance_size;
int static_size;
// java.lang.Object is never interpreted!
instance_size = clz->superclass->size ();
static_size = 0;
for (int i = 0; i < clz->field_count; i++)
{
int field_size;
int field_align;
_Jv_Field *field = &clz->fields[i];
if (! field->isRef ())
{
// it's safe to resolve the field here, since it's
// a primitive class, which does not cause loading to happen.
_Jv_ResolveField (field, clz->loader);
field_size = field->type->size ();
field_align = get_alignment_from_class (field->type);
}
else
{
field_size = sizeof (jobject);
field_align = __alignof__ (jobject);
}
#ifndef COMPACT_FIELDS
field->bsize = field_size;
#endif
if (field->flags & Modifier::STATIC)
{
/* this computes an offset into a region we'll allocate
shortly, and then add this offset to the start address */
static_size = ROUND (static_size, field_align);
field->u.boffset = static_size;
static_size += field_size;
}
else
{
instance_size = ROUND (instance_size, field_align);
field->u.boffset = instance_size;
instance_size += field_size;
}
}
// set the instance size for the class
clz->size_in_bytes = instance_size;
// allocate static memory
if (static_size != 0)
{
char *static_data = (char*)_Jv_AllocBytesChecked (static_size);
memset (static_data, 0, static_size);
for (int i = 0; i < clz->field_count; i++)
{
_Jv_Field *field = &clz->fields[i];
if ((field->flags & Modifier::STATIC) != 0)
{
field->u.addr = static_data + field->u.boffset;
if (clz->field_initializers[i] != 0)
{
_Jv_ResolveField (field, clz->loader);
_Jv_InitField (0, clz, i);
}
}
}
// now we don't need the field_initializers anymore, so let the
// collector get rid of it!
clz->field_initializers = 0;
}
/************ PART TWO: VTABLE LAYOUT ***************/
/* preparation: build the vtable stubs (even interfaces can)
have code -- for static constructors. */
for (int i = 0; i < clz->method_count; i++)
{
_Jv_MethodBase *imeth = clz->interpreted_methods[i];
if ((clz->methods[i].accflags & Modifier::NATIVE) != 0)
{
// You might think we could use a virtual `ncode' method in
// the _Jv_MethodBase and unify the native and non-native
// cases. Well, we can't, because we don't allocate these
// objects using `new', and thus they don't get a vtable.
_Jv_JNIMethod *jnim = reinterpret_cast<_Jv_JNIMethod *> (imeth);
clz->methods[i].ncode = jnim->ncode ();
}
else if (imeth != 0) // it could be abstract
{
_Jv_InterpMethod *im = reinterpret_cast<_Jv_InterpMethod *> (imeth);
clz->methods[i].ncode = im->ncode ();
}
}
if (clz->accflags & Modifier::INTERFACE)
{
clz->state = JV_STATE_PREPARED;
clz->notifyAll ();
return;
}
/* Now onto the actual job: vtable layout. First, count how many new
methods we have */
int new_method_count = 0;
jclass super_class = clz->getSuperclass ();
if (super_class == 0)
throw_internal_error ("cannot handle interpreted base classes");
for (int i = 0; i < clz->method_count; i++)
{
_Jv_Method *this_meth = &clz->methods[i];
if ((this_meth->accflags & (Modifier::STATIC | Modifier::PRIVATE)) != 0
|| _Jv_equalUtf8Consts (this_meth->name, init_name))
{
/* skip this, it doesn't go in the vtable */
continue;
}
_Jv_Method *orig_meth = _Jv_LookupDeclaredMethod (super_class,
this_meth->name,
this_meth->signature);
if (orig_meth == 0)
{
// new methods that are final, also don't go in the vtable
if ((this_meth->accflags & Modifier::FINAL) != 0)
continue;
new_method_count += 1;
continue;
}
if ((orig_meth->accflags & (Modifier::STATIC
| Modifier::PRIVATE
| Modifier::FINAL)) != 0
|| ((orig_meth->accflags & Modifier::ABSTRACT) == 0
&& (this_meth->accflags & Modifier::ABSTRACT) != 0
&& (klass->accflags & Modifier::ABSTRACT) == 0))
{
clz->state = JV_STATE_ERROR;
clz->notifyAll ();
throw new java::lang::IncompatibleClassChangeError (clz->getName ());
}
/* FIXME: At this point, if (loader != super_class->loader), we
* need to "impose class loader constraints" for the types
* involved in the signature of this method */
}
/* determine size */
int vtable_count = (super_class->vtable_method_count) + new_method_count;
clz->vtable_method_count = vtable_count;
/* allocate vtable structure */
_Jv_VTable *vtable = (_Jv_VTable*)
_Jv_AllocBytesChecked (sizeof (_Jv_VTable)
+ (sizeof (void*) * (vtable_count)));
vtable->clas = clz;
vtable->gc_descr = _Jv_BuildGCDescr(clz);
{
jclass effective_superclass = super_class;
/* If super_class is abstract or an interface it has no vtable.
We need to find a real one... */
while (effective_superclass && effective_superclass->vtable == NULL)
effective_superclass = effective_superclass->superclass;
/* copy super class' vtable entries. */
if (effective_superclass && effective_superclass->vtable)
memcpy ((void*)&vtable->method[0],
(void*)&effective_superclass->vtable->method[0],
sizeof (void*) * effective_superclass->vtable_method_count);
}
/* now, install our own vtable entries, reprise... */
for (int i = 0; i < clz->method_count; i++)
{
_Jv_Method *this_meth = &clz->methods[i];
int index = _Jv_DetermineVTableIndex (clz,
this_meth->name,
this_meth->signature);
if (index == METHOD_NOT_THERE)
throw_internal_error ("method now found in own class");
if (index != METHOD_INACCESSIBLE)
{
if (index > clz->vtable_method_count)
throw_internal_error ("vtable problem...");
if (clz->interpreted_methods[i] == 0)
vtable->method[index] = (void*)&_Jv_abstractMethodError;
else
vtable->method[index] = this_meth->ncode;
}
}
/* finally, assign the vtable! */
clz->vtable = vtable;
/* wooha! we're done. */
clz->state = JV_STATE_PREPARED;
clz->notifyAll ();
}
/** Do static initialization for fields with a constant initializer */
void
_Jv_InitField (jobject obj, jclass klass, int index)
{
using namespace java::lang::reflect;
if (obj != 0 && klass == 0)
klass = obj->getClass ();
if (!_Jv_IsInterpretedClass (klass))
return;
_Jv_InterpClass *clz = (_Jv_InterpClass*)klass;
_Jv_Field * field = (&clz->fields[0]) + index;
if (index > clz->field_count)
throw_internal_error ("field out of range");
int init = clz->field_initializers[index];
if (init == 0)
return;
_Jv_Constants *pool = &clz->constants;
int tag = pool->tags[init];
if (! field->isResolved ())
throw_internal_error ("initializing unresolved field");
if (obj==0 && ((field->flags & Modifier::STATIC) == 0))
throw_internal_error ("initializing non-static field with no object");
void *addr = 0;
if ((field->flags & Modifier::STATIC) != 0)
addr = (void*) field->u.addr;
else
addr = (void*) (((char*)obj) + field->u.boffset);
switch (tag)
{
case JV_CONSTANT_String:
{
_Jv_MonitorEnter (clz);
jstring str;
str = _Jv_NewStringUtf8Const (pool->data[init].utf8);
pool->data[init].string = str;
pool->tags[init] = JV_CONSTANT_ResolvedString;
_Jv_MonitorExit (clz);
}
/* fall through */
case JV_CONSTANT_ResolvedString:
if (! (field->type == &StringClass
|| field->type == &java::lang::Class::class$))
throw_class_format_error ("string initialiser to non-string field");
*(jstring*)addr = pool->data[init].string;
break;
case JV_CONSTANT_Integer:
{
int value = pool->data[init].i;
if (field->type == JvPrimClass (boolean))
*(jboolean*)addr = (jboolean)value;
else if (field->type == JvPrimClass (byte))
*(jbyte*)addr = (jbyte)value;
else if (field->type == JvPrimClass (char))
*(jchar*)addr = (jchar)value;
else if (field->type == JvPrimClass (short))
*(jshort*)addr = (jshort)value;
else if (field->type == JvPrimClass (int))
*(jint*)addr = (jint)value;
else
throw_class_format_error ("erroneous field initializer");
}
break;
case JV_CONSTANT_Long:
if (field->type != JvPrimClass (long))
throw_class_format_error ("erroneous field initializer");
*(jlong*)addr = _Jv_loadLong (&pool->data[init]);
break;
case JV_CONSTANT_Float:
if (field->type != JvPrimClass (float))
throw_class_format_error ("erroneous field initializer");
*(jfloat*)addr = pool->data[init].f;
break;
case JV_CONSTANT_Double:
if (field->type != JvPrimClass (double))
throw_class_format_error ("erroneous field initializer");
*(jdouble*)addr = _Jv_loadDouble (&pool->data[init]);
break;
default:
throw_class_format_error ("erroneous field initializer");
}
}
static int
get_alignment_from_class (jclass klass)
{
if (klass == JvPrimClass (byte))
return __alignof__ (jbyte);
else if (klass == JvPrimClass (short))
return __alignof__ (jshort);
else if (klass == JvPrimClass (int))
return __alignof__ (jint);
else if (klass == JvPrimClass (long))
return __alignof__ (jlong);
else if (klass == JvPrimClass (boolean))
return __alignof__ (jboolean);
else if (klass == JvPrimClass (char))
return __alignof__ (jchar);
else if (klass == JvPrimClass (float))
return __alignof__ (jfloat);
else if (klass == JvPrimClass (double))
return __alignof__ (jdouble);
else
return __alignof__ (jobject);
}
inline static unsigned char*
skip_one_type (unsigned char* ptr)
{
int ch = *ptr++;
while (ch == '[')
{
ch = *ptr++;
}
if (ch == 'L')
{
do { ch = *ptr++; } while (ch != ';');
}
return ptr;
}
static ffi_type*
get_ffi_type_from_signature (unsigned char* ptr)
{
switch (*ptr)
{
case 'L':
case '[':
return &ffi_type_pointer;
break;
case 'Z':
// On some platforms a bool is a byte, on others an int.
if (sizeof (jboolean) == sizeof (jbyte))
return &ffi_type_sint8;
else
{
JvAssert (sizeof (jbyte) == sizeof (jint));
return &ffi_type_sint32;
}
break;
case 'B':
return &ffi_type_sint8;
break;
case 'C':
return &ffi_type_uint16;
break;
case 'S':
return &ffi_type_sint16;
break;
case 'I':
return &ffi_type_sint32;
break;
case 'J':
return &ffi_type_sint64;
break;
case 'F':
return &ffi_type_float;
break;
case 'D':
return &ffi_type_double;
break;
case 'V':
return &ffi_type_void;
break;
}
throw_internal_error ("unknown type in signature");
}
/* this function yields the number of actual arguments, that is, if the
* function is non-static, then one is added to the number of elements
* found in the signature */
static int
count_arguments (_Jv_Utf8Const *signature,
jboolean staticp)
{
unsigned char *ptr = (unsigned char*) signature->data;
int arg_count = staticp ? 0 : 1;
/* first, count number of arguments */
// skip '('
ptr++;
// count args
while (*ptr != ')')
{
ptr = skip_one_type (ptr);
arg_count += 1;
}
return arg_count;
}
/* This beast will build a cif, given the signature. Memory for
* the cif itself and for the argument types must be allocated by the
* caller.
*/
static int
init_cif (_Jv_Utf8Const* signature,
int arg_count,
jboolean staticp,
ffi_cif *cif,
ffi_type **arg_types,
ffi_type **rtype_p)
{
unsigned char *ptr = (unsigned char*) signature->data;
int arg_index = 0; // arg number
int item_count = 0; // stack-item count
// setup receiver
if (!staticp)
{
arg_types[arg_index++] = &ffi_type_pointer;
item_count += 1;
}
// skip '('
ptr++;
// assign arg types
while (*ptr != ')')
{
arg_types[arg_index++] = get_ffi_type_from_signature (ptr);
if (*ptr == 'J' || *ptr == 'D')
item_count += 2;
else
item_count += 1;
ptr = skip_one_type (ptr);
}
// skip ')'
ptr++;
ffi_type *rtype = get_ffi_type_from_signature (ptr);
ptr = skip_one_type (ptr);
if (ptr != (unsigned char*)signature->data + signature->length)
throw_internal_error ("did not find end of signature");
if (ffi_prep_cif (cif, FFI_DEFAULT_ABI,
arg_count, rtype, arg_types) != FFI_OK)
throw_internal_error ("ffi_prep_cif failed");
if (rtype_p != NULL)
*rtype_p = rtype;
return item_count;
}
#if FFI_NATIVE_RAW_API
# define FFI_PREP_RAW_CLOSURE ffi_prep_raw_closure
# define FFI_RAW_SIZE ffi_raw_size
#else
# define FFI_PREP_RAW_CLOSURE ffi_prep_java_raw_closure
# define FFI_RAW_SIZE ffi_java_raw_size
#endif
/* we put this one here, and not in interpret.cc because it
* calls the utility routines count_arguments
* which are static to this module. The following struct defines the
* layout we use for the stubs, it's only used in the ncode method. */
typedef struct {
ffi_raw_closure closure;
ffi_cif cif;
ffi_type *arg_types[0];
} ncode_closure;
typedef void (*ffi_closure_fun) (ffi_cif*,void*,ffi_raw*,void*);
void *
_Jv_InterpMethod::ncode ()
{
using namespace java::lang::reflect;
if (self->ncode != 0)
return self->ncode;
jboolean staticp = (self->accflags & Modifier::STATIC) != 0;
int arg_count = count_arguments (self->signature, staticp);
ncode_closure *closure =
(ncode_closure*)_Jv_AllocBytesChecked (sizeof (ncode_closure)
+ arg_count * sizeof (ffi_type*));
init_cif (self->signature,
arg_count,
staticp,
&closure->cif,
&closure->arg_types[0],
NULL);
ffi_closure_fun fun;
args_raw_size = FFI_RAW_SIZE (&closure->cif);
JvAssert ((self->accflags & Modifier::NATIVE) == 0);
if ((self->accflags & Modifier::SYNCHRONIZED) != 0)
{
if (staticp)
fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_class;
else
fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_object;
}
else
{
fun = (ffi_closure_fun)&_Jv_InterpMethod::run_normal;
}
FFI_PREP_RAW_CLOSURE (&closure->closure,
&closure->cif,
fun,
(void*)this);
self->ncode = (void*)closure;
return self->ncode;
}
void *
_Jv_JNIMethod::ncode ()
{
using namespace java::lang::reflect;
if (self->ncode != 0)
return self->ncode;
jboolean staticp = (self->accflags & Modifier::STATIC) != 0;
int arg_count = count_arguments (self->signature, staticp);
ncode_closure *closure =
(ncode_closure*)_Jv_AllocBytesChecked (sizeof (ncode_closure)
+ arg_count * sizeof (ffi_type*));
ffi_type *rtype;
init_cif (self->signature,
arg_count,
staticp,
&closure->cif,
&closure->arg_types[0],
&rtype);
ffi_closure_fun fun;
args_raw_size = FFI_RAW_SIZE (&closure->cif);
// Initialize the argument types and CIF that represent the actual
// underlying JNI function.
int extra_args = 1;
if ((self->accflags & Modifier::STATIC))
++extra_args;
jni_arg_types = (ffi_type **) _Jv_Malloc ((extra_args + arg_count)
* sizeof (ffi_type *));
int offset = 0;
jni_arg_types[offset++] = &ffi_type_pointer;
if ((self->accflags & Modifier::STATIC))
jni_arg_types[offset++] = &ffi_type_pointer;
memcpy (&jni_arg_types[offset], &closure->arg_types[0],
arg_count * sizeof (ffi_type *));
if (ffi_prep_cif (&jni_cif, FFI_DEFAULT_ABI,
extra_args + arg_count, rtype,
jni_arg_types) != FFI_OK)
throw_internal_error ("ffi_prep_cif failed for JNI function");
JvAssert ((self->accflags & Modifier::NATIVE) != 0);
// FIXME: for now we assume that all native methods for
// interpreted code use JNI.
fun = (ffi_closure_fun) &_Jv_JNIMethod::call;
FFI_PREP_RAW_CLOSURE (&closure->closure,
&closure->cif,
fun,
(void*) this);
self->ncode = (void *) closure;
return self->ncode;
}
/* A _Jv_ResolvedMethod is what is put in the constant pool for a
* MethodRef or InterfacemethodRef. */
static _Jv_ResolvedMethod*
_Jv_BuildResolvedMethod (_Jv_Method* method,
jclass klass,
jboolean staticp,
jint vtable_index)
{
int arg_count = count_arguments (method->signature, staticp);
_Jv_ResolvedMethod* result = (_Jv_ResolvedMethod*)
_Jv_AllocBytesChecked (sizeof (_Jv_ResolvedMethod)
+ arg_count*sizeof (ffi_type*));
result->stack_item_count
= init_cif (method->signature,
arg_count,
staticp,
&result->cif,
&result->arg_types[0],
NULL);
result->vtable_index = vtable_index;
result->method = method;
result->klass = klass;
return result;
}
static void
throw_class_format_error (jstring msg)
{
throw (msg
? new java::lang::ClassFormatError (msg)
: new java::lang::ClassFormatError);
}
static void
throw_class_format_error (char *msg)
{
throw_class_format_error (JvNewStringLatin1 (msg));
}
static void
throw_internal_error (char *msg)
{
throw new java::lang::InternalError (JvNewStringLatin1 (msg));
}
#endif /* INTERPRETER */