8sa1-gcc/libjava/java/lang/reflect/natMethod.cc
Tom Tromey 0f918fea8b [multiple changes]
2000-01-04  Tom Tromey  <tromey@cygnus.com>

	* java/lang/reflect/natConstructor.cc (newInstance): Pass
	declaring class as return_type argument to
	_Jv_CallNonvirtualMethodA.
	* java/lang/reflect/natMethod.cc (_Jv_CallNonvirtualMethodA): In
	constructor case, create object and use it as `this' argument.
	* java/lang/Class.h (_getConstructors): Declare.
	(_getFields): Declare.
	* java/lang/Class.java (getConstructors): Wrote.
	(_getConstructors): New native method.
	(getDeclaredConstructors): Wrote.
	(_getFields): Declare new native method.
	* java/lang/natClass.cc (_Jv_LookupInterfaceMethod): Removed
	incorrect comment.
	(getMethod): Work correctly when class is primitive.
	(getDeclaredMethods): Likewise.  Compute offset using `method',
	not `mptr'.
	(getDeclaredMethod): Likewise.
	(getConstructor): Wrote.
	(ConstructorClass): New define.
	(getDeclaredConstructor): Wrote.
	(_getConstructors): New method.
	(_getFields): New method.
	(getFields): Wrote.

	* Makefile.in: Rebuilt.
	* Makefile.am (AM_CXXFLAGS): Added -D_GNU_SOURCE.

	* prims.cc: Remove `#pragma implementation'.
	* gcj/array.h: Remove `#pragma interface'.

	* prims.cc (_Jv_equaln): New function.
	* java/lang/Class.java (getSignature): Declare.
	* resolve.cc (_Jv_LookupDeclaredMethod): Moved to natClass.cc.
	* java/lang/natClass.cc (_Jv_LookupDeclaredMethod): Moved from
	resolve.cc.
	(getSignature): New method.
	(getDeclaredMethod): Wrote.
	(getMethod): Wrote.
	Include StringBuffer.h.
	* java/lang/Class.h (Class): Added _Jv_FromReflectedConstructor
	as a friend.  Unconditionally declare _Jv_LookupDeclaredMethod as
	a friend.
	(getSignature): Declare.
	* include/jvm.h (_Jv_GetTypesFromSignature): Declare.
	(_Jv_equaln): Declare.
	(_Jv_CallNonvirtualMethodA): Declare.
	* Makefile.in: Rebuilt.
	* Makefile.am (nat_source_files): Added natConstructor.cc.
	(java/lang/reflect/Constructor.h): New target.
	* java/lang/reflect/natConstructor.cc: New file.
	* java/lang/reflect/Constructor.java (newInstance): Now native.
	(declaringClass): Renamed from decl_class.
	(offset): Renamed from index.
	(getType): New native method.
	(getModifiers): Now native.
	(getParameterTypes): Call getType if required.
	(hashCode): Include hash code from declaring class.
	(modifiers): Removed.
	(toString): Call getType if required.
	* gcj/method.h (_Jv_FromReflectedConstructor): New function.
	* java/lang/reflect/natMethod.cc (hack_call): New method.
	Removed `#if 0' around FFI code.
	Include <gnu/gcj/RawData.h>.
	(invoke): Use _Jv_CallNonvirtualMethodA.  Throw
	IllegalArgumentException when argument object and class disagree.
	(_Jv_GetTypesFromSignature): New function.
	(getType): Use it.
	(ObjectClass): New define.
	(_Jv_CallNonvirtualMethodA): New function.
	* java/lang/reflect/Method.java (hack_trampoline): New method.
	(hack_call): New native method.

1999-12-21  Per Bothner  <per@bothner.com>

	* java/lang/natClass.cc (getDeclaredMethods): Correctly compute
	offset in new Method.

From-SVN: r31199
2000-01-04 08:46:52 +00:00

480 lines
12 KiB
C++

// natMethod.cc - Native code for Method class.
/* Copyright (C) 1998, 1999, 2000 Cygnus Solutions
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. */
#include <config.h>
#include <gcj/cni.h>
#include <jvm.h>
#include <java/lang/reflect/Method.h>
#include <java/lang/reflect/Constructor.h>
#include <java/lang/reflect/InvocationTargetException.h>
#include <java/lang/reflect/Modifier.h>
#include <java/lang/Void.h>
#include <java/lang/Byte.h>
#include <java/lang/Boolean.h>
#include <java/lang/Character.h>
#include <java/lang/Short.h>
#include <java/lang/Integer.h>
#include <java/lang/Long.h>
#include <java/lang/Float.h>
#include <java/lang/Double.h>
#include <java/lang/IllegalArgumentException.h>
#include <java/lang/NullPointerException.h>
#include <java/lang/Class.h>
#include <gcj/method.h>
#include <gnu/gcj/RawData.h>
#define ObjectClass _CL_Q34java4lang6Object
extern java::lang::Class ObjectClass;
#define ClassClass _CL_Q34java4lang5Class
extern java::lang::Class ClassClass;
#include <stdlib.h>
#include <ffi.h>
#define VoidClass _CL_Q34java4lang4Void
extern java::lang::Class VoidClass;
#define ByteClass _CL_Q34java4lang4Byte
extern java::lang::Class ByteClass;
#define ShortClass _CL_Q34java4lang5Short
extern java::lang::Class ShortClass;
#define CharacterClass _CL_Q34java4lang9Character
extern java::lang::Class CharacterClass;
#define IntegerClass _CL_Q34java4lang7Integer
extern java::lang::Class IntegerClass;
#define LongClass _CL_Q34java4lang4Long
extern java::lang::Class LongClass;
#define FloatClass _CL_Q34java4lang5Float
extern java::lang::Class FloatClass;
#define DoubleClass _CL_Q34java4lang6Double
extern java::lang::Class DoubleClass;
struct cpair
{
jclass prim;
jclass wrap;
};
// This is used to determine when a primitive widening conversion is
// allowed.
static cpair primitives[] =
{
#define VOID 0
{ JvPrimClass (void), &VoidClass },
{ JvPrimClass (byte), &ByteClass },
#define SHORT 2
{ JvPrimClass (short), &ShortClass },
#define CHAR 3
{ JvPrimClass (char), &CharacterClass },
{ JvPrimClass (int), &IntegerClass },
{ JvPrimClass (long), &LongClass },
{ JvPrimClass (float), &FloatClass },
{ JvPrimClass (double), &DoubleClass },
{ NULL, NULL }
};
static jboolean
can_widen (jclass from, jclass to)
{
int fromx = -1, tox = -1;
for (int i = 0; primitives[i].prim; ++i)
{
if (primitives[i].wrap == from)
fromx = i;
if (primitives[i].prim == to)
tox = i;
}
// Can't handle a miss.
if (fromx == -1 || tox == -1)
return false;
// Can't handle Void arguments.
if (fromx == VOID || tox == VOID)
return false;
// Special-case short/char conversions.
if ((fromx == SHORT && tox == CHAR) || (fromx == CHAR && tox == SHORT))
return false;
return fromx <= tox;
}
static ffi_type *
get_ffi_type (jclass klass)
{
// A special case.
if (klass == NULL)
return &ffi_type_pointer;
ffi_type *r;
if (klass == JvPrimClass (byte))
r = &ffi_type_sint8;
else if (klass == JvPrimClass (short))
r = &ffi_type_sint16;
else if (klass == JvPrimClass (int))
r = &ffi_type_sint32;
else if (klass == JvPrimClass (long))
r = &ffi_type_sint64;
else if (klass == JvPrimClass (float))
r = &ffi_type_float;
else if (klass == JvPrimClass (double))
r = &ffi_type_double;
else if (klass == JvPrimClass (boolean))
{
// FIXME.
r = &ffi_type_sint8;
}
else if (klass == JvPrimClass (char))
r = &ffi_type_uint16;
else
{
JvAssert (! klass->isPrimitive());
r = &ffi_type_pointer;
}
return r;
}
// Actually perform an FFI call.
void
java::lang::reflect::Method::hack_call (gnu::gcj::RawData *rcif,
gnu::gcj::RawData *rmethod,
gnu::gcj::RawData *rret_value,
gnu::gcj::RawData *rvalues)
{
ffi_cif *cif = (ffi_cif *) rcif;
void (*method) (...) = (void (*) (...)) rmethod;
void *ret_value = (void *) rret_value;
void **values = (void **) rvalues;
ffi_call (cif, method, ret_value, values);
}
jobject
java::lang::reflect::Method::invoke (jobject obj, jobjectArray args)
{
if (parameter_types == NULL)
getType ();
jmethodID meth = _Jv_FromReflectedMethod (this);
if (! java::lang::reflect::Modifier::isStatic(meth->accflags))
{
jclass k = obj ? obj->getClass() : NULL;
if (! obj)
JvThrow (new java::lang::NullPointerException);
if (! declaringClass->isAssignableFrom(k))
JvThrow (new java::lang::IllegalArgumentException);
// FIXME: access checks.
// Find the possibly overloaded method based on the runtime type
// of the object.
meth = _Jv_LookupDeclaredMethod (k, meth->name, meth->signature);
}
return _Jv_CallNonvirtualMethodA (obj, return_type, meth, false,
parameter_types, args);
}
jint
java::lang::reflect::Method::getModifiers ()
{
return _Jv_FromReflectedMethod (this)->accflags;
}
jstring
java::lang::reflect::Method::getName ()
{
if (name == NULL)
name = _Jv_NewStringUtf8Const (_Jv_FromReflectedMethod (this)->name);
return name;
}
/* Internal method to set return_type and parameter_types fields. */
void
java::lang::reflect::Method::getType ()
{
_Jv_GetTypesFromSignature (_Jv_FromReflectedMethod (this),
declaringClass,
&parameter_types,
&return_type);
}
void
_Jv_GetTypesFromSignature (jmethodID method,
jclass declaringClass,
JArray<jclass> **arg_types_out,
jclass *return_type_out)
{
_Jv_Utf8Const* sig = method->signature;
java::lang::ClassLoader *loader = declaringClass->getClassLoader();
char *ptr = sig->data;
int numArgs = 0;
/* First just count the number of parameters. */
for (; ; ptr++)
{
switch (*ptr)
{
case 0:
case ')':
case 'V':
break;
case '[':
case '(':
continue;
case 'B':
case 'C':
case 'D':
case 'F':
case 'S':
case 'I':
case 'J':
case 'Z':
numArgs++;
continue;
case 'L':
numArgs++;
do
ptr++;
while (*ptr != ';' && ptr[1] != '\0');
continue;
}
break;
}
JArray<jclass> *args = (JArray<jclass> *)
JvNewObjectArray (numArgs, &ClassClass, NULL);
jclass* argPtr = elements (args);
for (ptr = sig->data; *ptr != '\0'; ptr++)
{
int num_arrays = 0;
jclass type;
for (; *ptr == '['; ptr++)
num_arrays++;
switch (*ptr)
{
default:
return;
case ')':
argPtr = return_type_out;
continue;
case '(':
continue;
case 'V':
case 'B':
case 'C':
case 'D':
case 'F':
case 'S':
case 'I':
case 'J':
case 'Z':
type = _Jv_FindClassFromSignature(ptr, loader);
break;
case 'L':
type = _Jv_FindClassFromSignature(ptr, loader);
do
ptr++;
while (*ptr != ';' && ptr[1] != '\0');
break;
}
// FIXME: 2'nd argument should be "current loader"
while (--num_arrays >= 0)
type = _Jv_FindArrayClass (type, 0);
// ARGPTR can be NULL if we are processing the return value of a
// call from Constructor.
if (argPtr)
*argPtr++ = type;
}
*arg_types_out = args;
}
// This is a very rough analog of the JNI CallNonvirtual<type>MethodA
// functions. It handles both Methods and Constructors, and it can
// handle any return type. In the Constructor case, the `obj'
// argument is unused and should be NULL; also, the `return_type' is
// the class that the constructor will construct.
jobject
_Jv_CallNonvirtualMethodA (jobject obj,
jclass return_type,
jmethodID meth,
jboolean is_constructor,
JArray<jclass> *parameter_types,
jobjectArray args)
{
JvAssert (! is_constructor || ! obj);
JvAssert (! is_constructor || ! return_type);
// FIXME: access checks.
if (parameter_types->length != args->length)
JvThrow (new java::lang::IllegalArgumentException);
// See whether call needs an object as the first argument. A
// constructor does need a `this' argument, but it is one we create.
jboolean needs_this = false;
if (is_constructor
|| ! java::lang::reflect::Modifier::isStatic(meth->accflags))
needs_this = true;
int param_count = parameter_types->length;
if (needs_this)
++param_count;
ffi_type *rtype = get_ffi_type (return_type);
ffi_type **argtypes = (ffi_type **) alloca (param_count
* sizeof (ffi_type *));
jclass *paramelts = elements (parameter_types);
jobject *argelts = elements (args);
// FIXME: at some point the compiler is going to add extra arguments
// to some functions. In particular we are going to do this for
// handling access checks in reflection. We must add these hidden
// arguments here.
// Special case for the `this' argument of a constructor. Note that
// the JDK 1.2 docs specify that the new object must be allocated
// before argument conversions are done.
if (is_constructor)
{
// FIXME: must special-case String, arrays, maybe others here.
obj = JvAllocObject (return_type);
}
int i = 0;
int size = 0;
if (needs_this)
{
// The `NULL' type is `Object'.
argtypes[i++] = get_ffi_type (NULL);
size += sizeof (jobject);
}
for (; i < param_count; ++i)
{
jclass k = argelts[i] ? argelts[i]->getClass() : NULL;
argtypes[i] = get_ffi_type (k);
if (paramelts[i]->isPrimitive())
{
if (! argelts[i]
|| ! k->isPrimitive ()
|| ! can_widen (k, paramelts[i]))
JvThrow (new java::lang::IllegalArgumentException);
size += paramelts[i]->size();
}
else
{
if (argelts[i] && ! paramelts[i]->isAssignableFrom (k))
JvThrow (new java::lang::IllegalArgumentException);
size += sizeof (jobject);
}
}
ffi_cif cif;
if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, param_count,
rtype, argtypes) != FFI_OK)
{
// FIXME: throw some kind of VirtualMachineError here.
}
char *values = (char *) alloca (size);
char *p = values;
#define COPY(Where, What, Type) \
do { \
Type val = (What); \
memcpy ((Where), &val, sizeof (Type)); \
Where += sizeof (Type); \
} while (0)
i = 0;
if (needs_this)
{
COPY (p, obj, jobject);
++i;
}
for (; i < param_count; ++i)
{
java::lang::Number *num = (java::lang::Number *) paramelts[i];
if (paramelts[i] == JvPrimClass (byte))
COPY (p, num->byteValue(), jbyte);
else if (paramelts[i] == JvPrimClass (short))
COPY (p, num->shortValue(), jshort);
else if (paramelts[i] == JvPrimClass (int))
COPY (p, num->intValue(), jint);
else if (paramelts[i] == JvPrimClass (long))
COPY (p, num->longValue(), jlong);
else if (paramelts[i] == JvPrimClass (float))
COPY (p, num->floatValue(), jfloat);
else if (paramelts[i] == JvPrimClass (double))
COPY (p, num->doubleValue(), jdouble);
else if (paramelts[i] == JvPrimClass (boolean))
COPY (p, ((java::lang::Boolean *) argelts[i])->booleanValue(),
jboolean);
else if (paramelts[i] == JvPrimClass (char))
COPY (p, ((java::lang::Character *) argelts[i])->charValue(), jchar);
else
{
JvAssert (! paramelts[i]->isPrimitive());
COPY (p, argelts[i], jobject);
}
}
// FIXME: initialize class here.
// Largest possible value. Hopefully it is aligned!
jdouble ret_value;
java::lang::Throwable *ex;
using namespace java::lang;
using namespace java::lang::reflect;
ex = Method::hack_trampoline ((gnu::gcj::RawData *) &cif,
(gnu::gcj::RawData *) meth->ncode,
(gnu::gcj::RawData *) &ret_value,
(gnu::gcj::RawData *) values);
if (ex)
JvThrow (new InvocationTargetException (ex));
jobject r;
#define VAL(Wrapper, Type) (new Wrapper (* (Type *) &ret_value))
if (return_type == JvPrimClass (byte))
r = VAL (java::lang::Byte, jbyte);
else if (return_type == JvPrimClass (short))
r = VAL (java::lang::Short, jshort);
else if (return_type == JvPrimClass (int))
r = VAL (java::lang::Integer, jint);
else if (return_type == JvPrimClass (long))
r = VAL (java::lang::Long, jlong);
else if (return_type == JvPrimClass (float))
r = VAL (java::lang::Float, jfloat);
else if (return_type == JvPrimClass (double))
r = VAL (java::lang::Double, jdouble);
else if (return_type == JvPrimClass (boolean))
r = VAL (java::lang::Boolean, jboolean);
else if (return_type == JvPrimClass (char))
r = VAL (java::lang::Character, jchar);
else if (return_type == JvPrimClass (void))
r = NULL;
else
{
JvAssert (return_type == NULL || ! return_type->isPrimitive());
r = * (Object **) &ret_value;
}
return r;
}