gcc/libjava/jni.cc
Tom Tromey 55cc31c086 jni.cc (_Jv_JNI_GetPrimitiveArrayRegion): Fixed bounds checking.
* jni.cc (_Jv_JNI_GetPrimitiveArrayRegion): Fixed bounds
	checking.
	(_Jv_JNI_SetPrimitiveArrayRegion): Likewise.

From-SVN: r41634
2001-04-27 16:09:54 +00:00

2360 lines
61 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// jni.cc - JNI implementation, including the jump table.
/* Copyright (C) 1998, 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. */
#include <config.h>
#include <stddef.h>
#include <string.h>
// Define this before including jni.h.
#define __GCJ_JNI_IMPL__
#include <gcj/cni.h>
#include <jvm.h>
#include <java-assert.h>
#include <jni.h>
#ifdef ENABLE_JVMPI
#include <jvmpi.h>
#endif
#include <java/lang/Class.h>
#include <java/lang/ClassLoader.h>
#include <java/lang/Throwable.h>
#include <java/lang/ArrayIndexOutOfBoundsException.h>
#include <java/lang/StringIndexOutOfBoundsException.h>
#include <java/lang/AbstractMethodError.h>
#include <java/lang/InstantiationException.h>
#include <java/lang/NoSuchFieldError.h>
#include <java/lang/NoSuchMethodError.h>
#include <java/lang/reflect/Constructor.h>
#include <java/lang/reflect/Method.h>
#include <java/lang/reflect/Modifier.h>
#include <java/lang/OutOfMemoryError.h>
#include <java/util/Hashtable.h>
#include <java/lang/Integer.h>
#include <java/lang/ThreadGroup.h>
#include <gnu/gcj/jni/NativeThread.h>
#include <gcj/method.h>
#include <gcj/field.h>
#include <java-interp.h>
// FIXME: remove these defines.
#define ClassClass java::lang::Class::class$
#define ObjectClass java::lang::Object::class$
#define ThrowableClass java::lang::Throwable::class$
#define MethodClass java::lang::reflect::Method::class$
#define ThreadGroupClass java::lang::ThreadGroup::class$
#define NativeThreadClass gnu::gcj::jni::NativeThread::class$
// This enum is used to select different template instantiations in
// the invocation code.
enum invocation_type
{
normal,
nonvirtual,
static_type,
constructor
};
// Forward declarations.
extern struct JNINativeInterface _Jv_JNIFunctions;
extern struct JNIInvokeInterface _Jv_JNI_InvokeFunctions;
// Number of slots in the default frame. The VM must allow at least
// 16.
#define FRAME_SIZE 32
// Mark value indicating this is an overflow frame.
#define MARK_NONE 0
// Mark value indicating this is a user frame.
#define MARK_USER 1
// Mark value indicating this is a system frame.
#define MARK_SYSTEM 2
// This structure is used to keep track of local references.
struct _Jv_JNI_LocalFrame
{
// This is true if this frame object represents a pushed frame (eg
// from PushLocalFrame).
int marker : 2;
// Number of elements in frame.
int size : 30;
// Next frame in chain.
_Jv_JNI_LocalFrame *next;
// The elements. These are allocated using the C "struct hack".
jobject vec[0];
};
// This holds a reference count for all local and global references.
static java::util::Hashtable *ref_table;
// The only VM.
static JavaVM *the_vm;
#ifdef ENABLE_JVMPI
// The only JVMPI interface description.
static JVMPI_Interface _Jv_JVMPI_Interface;
static jint
jvmpiEnableEvent (jint event_type, void *)
{
switch (event_type)
{
case JVMPI_EVENT_OBJECT_ALLOC:
_Jv_JVMPI_Notify_OBJECT_ALLOC = _Jv_JVMPI_Interface.NotifyEvent;
break;
case JVMPI_EVENT_THREAD_START:
_Jv_JVMPI_Notify_THREAD_START = _Jv_JVMPI_Interface.NotifyEvent;
break;
case JVMPI_EVENT_THREAD_END:
_Jv_JVMPI_Notify_THREAD_END = _Jv_JVMPI_Interface.NotifyEvent;
break;
default:
return JVMPI_NOT_AVAILABLE;
}
return JVMPI_SUCCESS;
}
static jint
jvmpiDisableEvent (jint event_type, void *)
{
switch (event_type)
{
case JVMPI_EVENT_OBJECT_ALLOC:
_Jv_JVMPI_Notify_OBJECT_ALLOC = NULL;
break;
default:
return JVMPI_NOT_AVAILABLE;
}
return JVMPI_SUCCESS;
}
#endif
void
_Jv_JNI_Init (void)
{
ref_table = new java::util::Hashtable;
#ifdef ENABLE_JVMPI
_Jv_JVMPI_Interface.version = 1;
_Jv_JVMPI_Interface.EnableEvent = &jvmpiEnableEvent;
_Jv_JVMPI_Interface.DisableEvent = &jvmpiDisableEvent;
_Jv_JVMPI_Interface.EnableGC = &_Jv_EnableGC;
_Jv_JVMPI_Interface.DisableGC = &_Jv_DisableGC;
_Jv_JVMPI_Interface.RunGC = &_Jv_RunGC;
#endif
}
// Tell the GC that a certain pointer is live.
static void
mark_for_gc (jobject obj)
{
JvSynchronize sync (ref_table);
using namespace java::lang;
Integer *refcount = (Integer *) ref_table->get (obj);
jint val = (refcount == NULL) ? 0 : refcount->intValue ();
// FIXME: what about out of memory error?
ref_table->put (obj, new Integer (val + 1));
}
// Unmark a pointer.
static void
unmark_for_gc (jobject obj)
{
JvSynchronize sync (ref_table);
using namespace java::lang;
Integer *refcount = (Integer *) ref_table->get (obj);
JvAssert (refcount);
jint val = refcount->intValue () - 1;
if (val == 0)
ref_table->remove (obj);
else
// FIXME: what about out of memory error?
ref_table->put (obj, new Integer (val));
}
static jobject
_Jv_JNI_NewGlobalRef (JNIEnv *, jobject obj)
{
mark_for_gc (obj);
return obj;
}
static void
_Jv_JNI_DeleteGlobalRef (JNIEnv *, jobject obj)
{
unmark_for_gc (obj);
}
static void
_Jv_JNI_DeleteLocalRef (JNIEnv *env, jobject obj)
{
_Jv_JNI_LocalFrame *frame;
for (frame = env->locals; frame != NULL; frame = frame->next)
{
for (int i = 0; i < FRAME_SIZE; ++i)
{
if (frame->vec[i] == obj)
{
frame->vec[i] = NULL;
unmark_for_gc (obj);
return;
}
}
// Don't go past a marked frame.
JvAssert (frame->marker == MARK_NONE);
}
JvAssert (0);
}
static jint
_Jv_JNI_EnsureLocalCapacity (JNIEnv *env, jint size)
{
// It is easier to just always allocate a new frame of the requested
// size. This isn't the most efficient thing, but for now we don't
// care. Note that _Jv_JNI_PushLocalFrame relies on this right now.
_Jv_JNI_LocalFrame *frame;
try
{
frame = (_Jv_JNI_LocalFrame *) _Jv_Malloc (sizeof (_Jv_JNI_LocalFrame)
+ size * sizeof (jobject));
}
catch (jthrowable t)
{
env->ex = t;
return JNI_ERR;
}
frame->marker = MARK_NONE;
frame->size = size;
memset (&frame->vec[0], 0, size * sizeof (jobject));
frame->next = env->locals;
env->locals = frame;
return 0;
}
static jint
_Jv_JNI_PushLocalFrame (JNIEnv *env, jint size)
{
jint r = _Jv_JNI_EnsureLocalCapacity (env, size);
if (r < 0)
return r;
// The new frame is on top.
env->locals->marker = MARK_USER;
return 0;
}
static jobject
_Jv_JNI_NewLocalRef (JNIEnv *env, jobject obj)
{
// Try to find an open slot somewhere in the topmost frame.
_Jv_JNI_LocalFrame *frame = env->locals;
bool done = false, set = false;
while (frame != NULL && ! done)
{
for (int i = 0; i < frame->size; ++i)
if (frame->vec[i] == NULL)
{
set = true;
done = true;
frame->vec[i] = obj;
break;
}
}
if (! set)
{
// No slots, so we allocate a new frame. According to the spec
// we could just die here. FIXME: return value.
_Jv_JNI_EnsureLocalCapacity (env, 16);
// We know the first element of the new frame will be ok.
env->locals->vec[0] = obj;
}
mark_for_gc (obj);
return obj;
}
static jobject
_Jv_JNI_PopLocalFrame (JNIEnv *env, jobject result, int stop)
{
_Jv_JNI_LocalFrame *rf = env->locals;
bool done = false;
while (rf != NULL && ! done)
{
for (int i = 0; i < rf->size; ++i)
if (rf->vec[i] != NULL)
unmark_for_gc (rf->vec[i]);
// If the frame we just freed is the marker frame, we are done.
done = (rf->marker == stop);
_Jv_JNI_LocalFrame *n = rf->next;
// When N==NULL, we've reached the stack-allocated frame, and we
// must not free it. However, we must be sure to clear all its
// elements, since we might conceivably reuse it.
if (n == NULL)
{
memset (&rf->vec[0], 0, rf->size * sizeof (jobject));
break;
}
_Jv_Free (rf);
rf = n;
}
// Update the local frame information.
env->locals = rf;
return result == NULL ? NULL : _Jv_JNI_NewLocalRef (env, result);
}
static jobject
_Jv_JNI_PopLocalFrame (JNIEnv *env, jobject result)
{
return _Jv_JNI_PopLocalFrame (env, result, MARK_USER);
}
// Pop a `system' frame from the stack. This is `extern "C"' as it is
// used by the compiler.
extern "C" void
_Jv_JNI_PopSystemFrame (JNIEnv *env)
{
_Jv_JNI_PopLocalFrame (env, NULL, MARK_SYSTEM);
if (env->ex)
{
jthrowable t = env->ex;
env->ex = NULL;
throw t;
}
}
// This function is used from other template functions. It wraps the
// return value appropriately; we specialize it so that object returns
// are turned into local references.
template<typename T>
static T
wrap_value (JNIEnv *, T value)
{
return value;
}
// This specialization is used for jobject, jclass, jstring, jarray,
// etc.
template<typename T>
static T *
wrap_value (JNIEnv *env, T *value)
{
return (value == NULL
? value
: (T *) _Jv_JNI_NewLocalRef (env, (jobject) value));
}
static jint
_Jv_JNI_GetVersion (JNIEnv *)
{
return JNI_VERSION_1_2;
}
static jclass
_Jv_JNI_DefineClass (JNIEnv *env, jobject loader,
const jbyte *buf, jsize bufLen)
{
try
{
jbyteArray bytes = JvNewByteArray (bufLen);
jbyte *elts = elements (bytes);
memcpy (elts, buf, bufLen * sizeof (jbyte));
java::lang::ClassLoader *l
= reinterpret_cast<java::lang::ClassLoader *> (loader);
jclass result = l->defineClass (bytes, 0, bufLen);
return (jclass) wrap_value (env, result);
}
catch (jthrowable t)
{
env->ex = t;
return NULL;
}
}
static jclass
_Jv_JNI_FindClass (JNIEnv *env, const char *name)
{
// FIXME: assume that NAME isn't too long.
int len = strlen (name);
char s[len + 1];
for (int i = 0; i <= len; ++i)
s[i] = (name[i] == '/') ? '.' : name[i];
jclass r = NULL;
try
{
// This might throw an out of memory exception.
jstring n = JvNewStringUTF (s);
java::lang::ClassLoader *loader = NULL;
if (env->klass != NULL)
loader = env->klass->getClassLoader ();
if (loader == NULL)
{
// FIXME: should use getBaseClassLoader, but we don't have that
// yet.
loader = java::lang::ClassLoader::getSystemClassLoader ();
}
r = loader->loadClass (n);
}
catch (jthrowable t)
{
env->ex = t;
}
return (jclass) wrap_value (env, r);
}
static jclass
_Jv_JNI_GetSuperclass (JNIEnv *env, jclass clazz)
{
return (jclass) wrap_value (env, clazz->getSuperclass ());
}
static jboolean
_Jv_JNI_IsAssignableFrom(JNIEnv *, jclass clazz1, jclass clazz2)
{
return clazz1->isAssignableFrom (clazz2);
}
static jint
_Jv_JNI_Throw (JNIEnv *env, jthrowable obj)
{
// We check in case the user did some funky cast.
JvAssert (obj != NULL && (&ThrowableClass)->isInstance (obj));
env->ex = obj;
return 0;
}
static jint
_Jv_JNI_ThrowNew (JNIEnv *env, jclass clazz, const char *message)
{
using namespace java::lang::reflect;
JvAssert ((&ThrowableClass)->isAssignableFrom (clazz));
int r = JNI_OK;
try
{
JArray<jclass> *argtypes
= (JArray<jclass> *) JvNewObjectArray (1, &ClassClass, NULL);
jclass *elts = elements (argtypes);
elts[0] = &StringClass;
Constructor *cons = clazz->getConstructor (argtypes);
jobjectArray values = JvNewObjectArray (1, &StringClass, NULL);
jobject *velts = elements (values);
velts[0] = JvNewStringUTF (message);
jobject obj = cons->newInstance (values);
env->ex = reinterpret_cast<jthrowable> (obj);
}
catch (jthrowable t)
{
env->ex = t;
r = JNI_ERR;
}
return r;
}
static jthrowable
_Jv_JNI_ExceptionOccurred (JNIEnv *env)
{
return (jthrowable) wrap_value (env, env->ex);
}
static void
_Jv_JNI_ExceptionDescribe (JNIEnv *env)
{
if (env->ex != NULL)
env->ex->printStackTrace();
}
static void
_Jv_JNI_ExceptionClear (JNIEnv *env)
{
env->ex = NULL;
}
static jboolean
_Jv_JNI_ExceptionCheck (JNIEnv *env)
{
return env->ex != NULL;
}
static void
_Jv_JNI_FatalError (JNIEnv *, const char *message)
{
JvFail (message);
}
static jboolean
_Jv_JNI_IsSameObject (JNIEnv *, jobject obj1, jobject obj2)
{
return obj1 == obj2;
}
static jobject
_Jv_JNI_AllocObject (JNIEnv *env, jclass clazz)
{
jobject obj = NULL;
using namespace java::lang::reflect;
try
{
JvAssert (clazz && ! clazz->isArray ());
if (clazz->isInterface() || Modifier::isAbstract(clazz->getModifiers()))
env->ex = new java::lang::InstantiationException ();
else
{
// FIXME: will this work for String?
obj = JvAllocObject (clazz);
}
}
catch (jthrowable t)
{
env->ex = t;
}
return wrap_value (env, obj);
}
static jclass
_Jv_JNI_GetObjectClass (JNIEnv *env, jobject obj)
{
JvAssert (obj);
return (jclass) wrap_value (env, obj->getClass());
}
static jboolean
_Jv_JNI_IsInstanceOf (JNIEnv *, jobject obj, jclass clazz)
{
return clazz->isInstance(obj);
}
//
// This section concerns method invocation.
//
template<jboolean is_static>
static jmethodID
_Jv_JNI_GetAnyMethodID (JNIEnv *env, jclass clazz,
const char *name, const char *sig)
{
try
{
_Jv_InitClass (clazz);
_Jv_Utf8Const *name_u = _Jv_makeUtf8Const ((char *) name, -1);
// FIXME: assume that SIG isn't too long.
int len = strlen (sig);
char s[len + 1];
for (int i = 0; i <= len; ++i)
s[i] = (sig[i] == '/') ? '.' : sig[i];
_Jv_Utf8Const *sig_u = _Jv_makeUtf8Const ((char *) s, -1);
JvAssert (! clazz->isPrimitive());
using namespace java::lang::reflect;
while (clazz != NULL)
{
jint count = JvNumMethods (clazz);
jmethodID meth = JvGetFirstMethod (clazz);
for (jint i = 0; i < count; ++i)
{
if (((is_static && Modifier::isStatic (meth->accflags))
|| (! is_static && ! Modifier::isStatic (meth->accflags)))
&& _Jv_equalUtf8Consts (meth->name, name_u)
&& _Jv_equalUtf8Consts (meth->signature, sig_u))
return meth;
meth = meth->getNextMethod();
}
clazz = clazz->getSuperclass ();
}
env->ex = new java::lang::NoSuchMethodError ();
}
catch (jthrowable t)
{
env->ex = t;
}
return NULL;
}
// This is a helper function which turns a va_list into an array of
// `jvalue's. It needs signature information in order to do its work.
// The array of values must already be allocated.
static void
array_from_valist (jvalue *values, JArray<jclass> *arg_types, va_list vargs)
{
jclass *arg_elts = elements (arg_types);
for (int i = 0; i < arg_types->length; ++i)
{
if (arg_elts[i] == JvPrimClass (byte))
values[i].b = va_arg (vargs, jbyte);
else if (arg_elts[i] == JvPrimClass (short))
values[i].s = va_arg (vargs, jshort);
else if (arg_elts[i] == JvPrimClass (int))
values[i].i = va_arg (vargs, jint);
else if (arg_elts[i] == JvPrimClass (long))
values[i].j = va_arg (vargs, jlong);
else if (arg_elts[i] == JvPrimClass (float))
values[i].f = va_arg (vargs, jfloat);
else if (arg_elts[i] == JvPrimClass (double))
values[i].d = va_arg (vargs, jdouble);
else if (arg_elts[i] == JvPrimClass (boolean))
values[i].z = va_arg (vargs, jboolean);
else if (arg_elts[i] == JvPrimClass (char))
values[i].c = va_arg (vargs, jchar);
else
{
// An object.
values[i].l = va_arg (vargs, jobject);
}
}
}
// This can call any sort of method: virtual, "nonvirtual", static, or
// constructor.
template<typename T, invocation_type style>
static T
_Jv_JNI_CallAnyMethodV (JNIEnv *env, jobject obj, jclass klass,
jmethodID id, va_list vargs)
{
if (style == normal)
id = _Jv_LookupDeclaredMethod (obj->getClass (), id->name, id->signature);
jclass decl_class = klass ? klass : obj->getClass ();
JvAssert (decl_class != NULL);
jclass return_type;
JArray<jclass> *arg_types;
try
{
_Jv_GetTypesFromSignature (id, decl_class,
&arg_types, &return_type);
jvalue args[arg_types->length];
array_from_valist (args, arg_types, vargs);
// For constructors we need to pass the Class we are instantiating.
if (style == constructor)
return_type = klass;
jvalue result;
jthrowable ex = _Jv_CallAnyMethodA (obj, return_type, id,
style == constructor,
arg_types, args, &result);
if (ex != NULL)
env->ex = ex;
// We cheat a little here. FIXME.
return wrap_value (env, * (T *) &result);
}
catch (jthrowable t)
{
env->ex = t;
}
return wrap_value (env, (T) 0);
}
template<typename T, invocation_type style>
static T
_Jv_JNI_CallAnyMethod (JNIEnv *env, jobject obj, jclass klass,
jmethodID method, ...)
{
va_list args;
T result;
va_start (args, method);
result = _Jv_JNI_CallAnyMethodV<T, style> (env, obj, klass, method, args);
va_end (args);
return result;
}
template<typename T, invocation_type style>
static T
_Jv_JNI_CallAnyMethodA (JNIEnv *env, jobject obj, jclass klass,
jmethodID id, jvalue *args)
{
if (style == normal)
id = _Jv_LookupDeclaredMethod (obj->getClass (), id->name, id->signature);
jclass decl_class = klass ? klass : obj->getClass ();
JvAssert (decl_class != NULL);
jclass return_type;
JArray<jclass> *arg_types;
try
{
_Jv_GetTypesFromSignature (id, decl_class,
&arg_types, &return_type);
// For constructors we need to pass the Class we are instantiating.
if (style == constructor)
return_type = klass;
jvalue result;
jthrowable ex = _Jv_CallAnyMethodA (obj, return_type, id,
style == constructor,
arg_types, args, &result);
if (ex != NULL)
env->ex = ex;
// We cheat a little here. FIXME.
return wrap_value (env, * (T *) &result);
}
catch (jthrowable t)
{
env->ex = t;
}
return wrap_value (env, (T) 0);
}
template<invocation_type style>
static void
_Jv_JNI_CallAnyVoidMethodV (JNIEnv *env, jobject obj, jclass klass,
jmethodID id, va_list vargs)
{
if (style == normal)
id = _Jv_LookupDeclaredMethod (obj->getClass (), id->name, id->signature);
jclass decl_class = klass ? klass : obj->getClass ();
JvAssert (decl_class != NULL);
jclass return_type;
JArray<jclass> *arg_types;
try
{
_Jv_GetTypesFromSignature (id, decl_class,
&arg_types, &return_type);
jvalue args[arg_types->length];
array_from_valist (args, arg_types, vargs);
// For constructors we need to pass the Class we are instantiating.
if (style == constructor)
return_type = klass;
jthrowable ex = _Jv_CallAnyMethodA (obj, return_type, id,
style == constructor,
arg_types, args, NULL);
if (ex != NULL)
env->ex = ex;
}
catch (jthrowable t)
{
env->ex = t;
}
}
template<invocation_type style>
static void
_Jv_JNI_CallAnyVoidMethod (JNIEnv *env, jobject obj, jclass klass,
jmethodID method, ...)
{
va_list args;
va_start (args, method);
_Jv_JNI_CallAnyVoidMethodV<style> (env, obj, klass, method, args);
va_end (args);
}
template<invocation_type style>
static void
_Jv_JNI_CallAnyVoidMethodA (JNIEnv *env, jobject obj, jclass klass,
jmethodID id, jvalue *args)
{
if (style == normal)
id = _Jv_LookupDeclaredMethod (obj->getClass (), id->name, id->signature);
jclass decl_class = klass ? klass : obj->getClass ();
JvAssert (decl_class != NULL);
jclass return_type;
JArray<jclass> *arg_types;
try
{
_Jv_GetTypesFromSignature (id, decl_class,
&arg_types, &return_type);
jthrowable ex = _Jv_CallAnyMethodA (obj, return_type, id,
style == constructor,
arg_types, args, NULL);
if (ex != NULL)
env->ex = ex;
}
catch (jthrowable t)
{
env->ex = t;
}
}
// Functions with this signature are used to implement functions in
// the CallMethod family.
template<typename T>
static T
_Jv_JNI_CallMethodV (JNIEnv *env, jobject obj, jmethodID id, va_list args)
{
return _Jv_JNI_CallAnyMethodV<T, normal> (env, obj, NULL, id, args);
}
// Functions with this signature are used to implement functions in
// the CallMethod family.
template<typename T>
static T
_Jv_JNI_CallMethod (JNIEnv *env, jobject obj, jmethodID id, ...)
{
va_list args;
T result;
va_start (args, id);
result = _Jv_JNI_CallAnyMethodV<T, normal> (env, obj, NULL, id, args);
va_end (args);
return result;
}
// Functions with this signature are used to implement functions in
// the CallMethod family.
template<typename T>
static T
_Jv_JNI_CallMethodA (JNIEnv *env, jobject obj, jmethodID id, jvalue *args)
{
return _Jv_JNI_CallAnyMethodA<T, normal> (env, obj, NULL, id, args);
}
static void
_Jv_JNI_CallVoidMethodV (JNIEnv *env, jobject obj, jmethodID id, va_list args)
{
_Jv_JNI_CallAnyVoidMethodV<normal> (env, obj, NULL, id, args);
}
static void
_Jv_JNI_CallVoidMethod (JNIEnv *env, jobject obj, jmethodID id, ...)
{
va_list args;
va_start (args, id);
_Jv_JNI_CallAnyVoidMethodV<normal> (env, obj, NULL, id, args);
va_end (args);
}
static void
_Jv_JNI_CallVoidMethodA (JNIEnv *env, jobject obj, jmethodID id, jvalue *args)
{
_Jv_JNI_CallAnyVoidMethodA<normal> (env, obj, NULL, id, args);
}
// Functions with this signature are used to implement functions in
// the CallStaticMethod family.
template<typename T>
static T
_Jv_JNI_CallStaticMethodV (JNIEnv *env, jclass klass,
jmethodID id, va_list args)
{
JvAssert (((id->accflags) & java::lang::reflect::Modifier::STATIC));
JvAssert ((&ClassClass)->isInstance (klass));
return _Jv_JNI_CallAnyMethodV<T, static_type> (env, NULL, klass, id, args);
}
// Functions with this signature are used to implement functions in
// the CallStaticMethod family.
template<typename T>
static T
_Jv_JNI_CallStaticMethod (JNIEnv *env, jclass klass, jmethodID id, ...)
{
va_list args;
T result;
JvAssert (((id->accflags) & java::lang::reflect::Modifier::STATIC));
JvAssert ((&ClassClass)->isInstance (klass));
va_start (args, id);
result = _Jv_JNI_CallAnyMethodV<T, static_type> (env, NULL, klass,
id, args);
va_end (args);
return result;
}
// Functions with this signature are used to implement functions in
// the CallStaticMethod family.
template<typename T>
static T
_Jv_JNI_CallStaticMethodA (JNIEnv *env, jclass klass, jmethodID id,
jvalue *args)
{
JvAssert (((id->accflags) & java::lang::reflect::Modifier::STATIC));
JvAssert ((&ClassClass)->isInstance (klass));
return _Jv_JNI_CallAnyMethodA<T, static_type> (env, NULL, klass, id, args);
}
static void
_Jv_JNI_CallStaticVoidMethodV (JNIEnv *env, jclass klass, jmethodID id,
va_list args)
{
_Jv_JNI_CallAnyVoidMethodV<static_type> (env, NULL, klass, id, args);
}
static void
_Jv_JNI_CallStaticVoidMethod (JNIEnv *env, jclass klass, jmethodID id, ...)
{
va_list args;
va_start (args, id);
_Jv_JNI_CallAnyVoidMethodV<static_type> (env, NULL, klass, id, args);
va_end (args);
}
static void
_Jv_JNI_CallStaticVoidMethodA (JNIEnv *env, jclass klass, jmethodID id,
jvalue *args)
{
_Jv_JNI_CallAnyVoidMethodA<static_type> (env, NULL, klass, id, args);
}
static jobject
_Jv_JNI_NewObjectV (JNIEnv *env, jclass klass,
jmethodID id, va_list args)
{
JvAssert (klass && ! klass->isArray ());
JvAssert (! strcmp (id->name->data, "<init>")
&& id->signature->length > 2
&& id->signature->data[0] == '('
&& ! strcmp (&id->signature->data[id->signature->length - 2],
")V"));
return _Jv_JNI_CallAnyMethodV<jobject, constructor> (env, NULL, klass,
id, args);
}
static jobject
_Jv_JNI_NewObject (JNIEnv *env, jclass klass, jmethodID id, ...)
{
JvAssert (klass && ! klass->isArray ());
JvAssert (! strcmp (id->name->data, "<init>")
&& id->signature->length > 2
&& id->signature->data[0] == '('
&& ! strcmp (&id->signature->data[id->signature->length - 2],
")V"));
va_list args;
jobject result;
va_start (args, id);
result = _Jv_JNI_CallAnyMethodV<jobject, constructor> (env, NULL, klass,
id, args);
va_end (args);
return result;
}
static jobject
_Jv_JNI_NewObjectA (JNIEnv *env, jclass klass, jmethodID id,
jvalue *args)
{
JvAssert (klass && ! klass->isArray ());
JvAssert (! strcmp (id->name->data, "<init>")
&& id->signature->length > 2
&& id->signature->data[0] == '('
&& ! strcmp (&id->signature->data[id->signature->length - 2],
")V"));
return _Jv_JNI_CallAnyMethodA<jobject, constructor> (env, NULL, klass,
id, args);
}
template<typename T>
static T
_Jv_JNI_GetField (JNIEnv *env, jobject obj, jfieldID field)
{
JvAssert (obj);
T *ptr = (T *) ((char *) obj + field->getOffset ());
return wrap_value (env, *ptr);
}
template<typename T>
static void
_Jv_JNI_SetField (JNIEnv *, jobject obj, jfieldID field, T value)
{
JvAssert (obj);
T *ptr = (T *) ((char *) obj + field->getOffset ());
*ptr = value;
}
template<jboolean is_static>
static jfieldID
_Jv_JNI_GetAnyFieldID (JNIEnv *env, jclass clazz,
const char *name, const char *sig)
{
try
{
_Jv_InitClass (clazz);
_Jv_Utf8Const *a_name = _Jv_makeUtf8Const ((char *) name, -1);
// FIXME: assume that SIG isn't too long.
int len = strlen (sig);
char s[len + 1];
for (int i = 0; i <= len; ++i)
s[i] = (sig[i] == '/') ? '.' : sig[i];
jclass field_class = _Jv_FindClassFromSignature ((char *) s, NULL);
// FIXME: what if field_class == NULL?
java::lang::ClassLoader *loader = clazz->getClassLoader ();
while (clazz != NULL)
{
// We acquire the class lock so that fields aren't resolved
// while we are running.
JvSynchronize sync (clazz);
jint count = (is_static
? JvNumStaticFields (clazz)
: JvNumInstanceFields (clazz));
jfieldID field = (is_static
? JvGetFirstStaticField (clazz)
: JvGetFirstInstanceField (clazz));
for (jint i = 0; i < count; ++i)
{
_Jv_Utf8Const *f_name = field->getNameUtf8Const(clazz);
// The field might be resolved or it might not be. It
// is much simpler to always resolve it.
_Jv_ResolveField (field, loader);
if (_Jv_equalUtf8Consts (f_name, a_name)
&& field->getClass() == field_class)
return field;
field = field->getNextField ();
}
clazz = clazz->getSuperclass ();
}
env->ex = new java::lang::NoSuchFieldError ();
}
catch (jthrowable t)
{
env->ex = t;
}
return NULL;
}
template<typename T>
static T
_Jv_JNI_GetStaticField (JNIEnv *env, jclass, jfieldID field)
{
T *ptr = (T *) field->u.addr;
return wrap_value (env, *ptr);
}
template<typename T>
static void
_Jv_JNI_SetStaticField (JNIEnv *, jclass, jfieldID field, T value)
{
T *ptr = (T *) field->u.addr;
*ptr = value;
}
static jstring
_Jv_JNI_NewString (JNIEnv *env, const jchar *unichars, jsize len)
{
try
{
jstring r = _Jv_NewString (unichars, len);
return (jstring) wrap_value (env, r);
}
catch (jthrowable t)
{
env->ex = t;
return NULL;
}
}
static jsize
_Jv_JNI_GetStringLength (JNIEnv *, jstring string)
{
return string->length();
}
static const jchar *
_Jv_JNI_GetStringChars (JNIEnv *, jstring string, jboolean *isCopy)
{
jchar *result = _Jv_GetStringChars (string);
mark_for_gc (string);
if (isCopy)
*isCopy = false;
return (const jchar *) result;
}
static void
_Jv_JNI_ReleaseStringChars (JNIEnv *, jstring string, const jchar *)
{
unmark_for_gc (string);
}
static jstring
_Jv_JNI_NewStringUTF (JNIEnv *env, const char *bytes)
{
try
{
jstring result = JvNewStringUTF (bytes);
return (jstring) wrap_value (env, result);
}
catch (jthrowable t)
{
env->ex = t;
return NULL;
}
}
static jsize
_Jv_JNI_GetStringUTFLength (JNIEnv *, jstring string)
{
return JvGetStringUTFLength (string);
}
static const char *
_Jv_JNI_GetStringUTFChars (JNIEnv *env, jstring string, jboolean *isCopy)
{
jsize len = JvGetStringUTFLength (string);
try
{
char *r = (char *) _Jv_Malloc (len + 1);
JvGetStringUTFRegion (string, 0, len, r);
r[len] = '\0';
if (isCopy)
*isCopy = true;
return (const char *) r;
}
catch (jthrowable t)
{
env->ex = t;
return NULL;
}
}
static void
_Jv_JNI_ReleaseStringUTFChars (JNIEnv *, jstring, const char *utf)
{
_Jv_Free ((void *) utf);
}
static void
_Jv_JNI_GetStringRegion (JNIEnv *env, jstring string, jsize start, jsize len,
jchar *buf)
{
jchar *result = _Jv_GetStringChars (string);
if (start < 0 || start > string->length ()
|| len < 0 || start + len > string->length ())
{
try
{
env->ex = new java::lang::StringIndexOutOfBoundsException ();
}
catch (jthrowable t)
{
env->ex = t;
}
}
else
memcpy (buf, &result[start], len * sizeof (jchar));
}
static void
_Jv_JNI_GetStringUTFRegion (JNIEnv *env, jstring str, jsize start,
jsize len, char *buf)
{
if (start < 0 || start > str->length ()
|| len < 0 || start + len > str->length ())
{
try
{
env->ex = new java::lang::StringIndexOutOfBoundsException ();
}
catch (jthrowable t)
{
env->ex = t;
}
}
else
_Jv_GetStringUTFRegion (str, start, len, buf);
}
static const jchar *
_Jv_JNI_GetStringCritical (JNIEnv *, jstring str, jboolean *isCopy)
{
jchar *result = _Jv_GetStringChars (str);
if (isCopy)
*isCopy = false;
return result;
}
static void
_Jv_JNI_ReleaseStringCritical (JNIEnv *, jstring, const jchar *)
{
// Nothing.
}
static jsize
_Jv_JNI_GetArrayLength (JNIEnv *, jarray array)
{
return array->length;
}
static jarray
_Jv_JNI_NewObjectArray (JNIEnv *env, jsize length, jclass elementClass,
jobject init)
{
try
{
jarray result = JvNewObjectArray (length, elementClass, init);
return (jarray) wrap_value (env, result);
}
catch (jthrowable t)
{
env->ex = t;
return NULL;
}
}
static jobject
_Jv_JNI_GetObjectArrayElement (JNIEnv *env, jobjectArray array, jsize index)
{
jobject *elts = elements (array);
return wrap_value (env, elts[index]);
}
static void
_Jv_JNI_SetObjectArrayElement (JNIEnv *env, jobjectArray array, jsize index,
jobject value)
{
try
{
_Jv_CheckArrayStore (array, value);
jobject *elts = elements (array);
elts[index] = value;
}
catch (jthrowable t)
{
env->ex = t;
}
}
template<typename T, jclass K>
static JArray<T> *
_Jv_JNI_NewPrimitiveArray (JNIEnv *env, jsize length)
{
try
{
return (JArray<T> *) wrap_value (env, _Jv_NewPrimArray (K, length));
}
catch (jthrowable t)
{
env->ex = t;
return NULL;
}
}
template<typename T>
static T *
_Jv_JNI_GetPrimitiveArrayElements (JNIEnv *, JArray<T> *array,
jboolean *isCopy)
{
T *elts = elements (array);
if (isCopy)
{
// We elect never to copy.
*isCopy = false;
}
mark_for_gc (array);
return elts;
}
template<typename T>
static void
_Jv_JNI_ReleasePrimitiveArrayElements (JNIEnv *, JArray<T> *array,
T *, jint /* mode */)
{
// Note that we ignore MODE. We can do this because we never copy
// the array elements. My reading of the JNI documentation is that
// this is an option for the implementor.
unmark_for_gc (array);
}
template<typename T>
static void
_Jv_JNI_GetPrimitiveArrayRegion (JNIEnv *env, JArray<T> *array,
jsize start, jsize len,
T *buf)
{
// The cast to unsigned lets us save a comparison.
if (start < 0 || len < 0
|| (unsigned long) (start + len) >= (unsigned long) array->length)
{
try
{
// FIXME: index.
env->ex = new java::lang::ArrayIndexOutOfBoundsException ();
}
catch (jthrowable t)
{
// Could have thown out of memory error.
env->ex = t;
}
}
else
{
T *elts = elements (array) + start;
memcpy (buf, elts, len * sizeof (T));
}
}
template<typename T>
static void
_Jv_JNI_SetPrimitiveArrayRegion (JNIEnv *env, JArray<T> *array,
jsize start, jsize len, T *buf)
{
// The cast to unsigned lets us save a comparison.
if (start < 0 || len < 0
|| (unsigned long) (start + len) >= (unsigned long) array->length)
{
try
{
// FIXME: index.
env->ex = new java::lang::ArrayIndexOutOfBoundsException ();
}
catch (jthrowable t)
{
env->ex = t;
}
}
else
{
T *elts = elements (array) + start;
memcpy (elts, buf, len * sizeof (T));
}
}
static void *
_Jv_JNI_GetPrimitiveArrayCritical (JNIEnv *, jarray array,
jboolean *isCopy)
{
// FIXME: does this work?
jclass klass = array->getClass()->getComponentType();
JvAssert (klass->isPrimitive ());
char *r = _Jv_GetArrayElementFromElementType (array, klass);
if (isCopy)
*isCopy = false;
return r;
}
static void
_Jv_JNI_ReleasePrimitiveArrayCritical (JNIEnv *, jarray, void *, jint)
{
// Nothing.
}
static jint
_Jv_JNI_MonitorEnter (JNIEnv *env, jobject obj)
{
try
{
_Jv_MonitorEnter (obj);
return 0;
}
catch (jthrowable t)
{
env->ex = t;
}
return JNI_ERR;
}
static jint
_Jv_JNI_MonitorExit (JNIEnv *env, jobject obj)
{
try
{
_Jv_MonitorExit (obj);
return 0;
}
catch (jthrowable t)
{
env->ex = t;
}
return JNI_ERR;
}
// JDK 1.2
jobject
_Jv_JNI_ToReflectedField (JNIEnv *env, jclass cls, jfieldID fieldID,
jboolean)
{
try
{
java::lang::reflect::Field *field = new java::lang::reflect::Field();
field->declaringClass = cls;
field->offset = (char*) fieldID - (char *) cls->fields;
field->name = _Jv_NewStringUtf8Const (fieldID->getNameUtf8Const (cls));
return wrap_value (env, field);
}
catch (jthrowable t)
{
env->ex = t;
}
return NULL;
}
// JDK 1.2
static jfieldID
_Jv_JNI_FromReflectedField (JNIEnv *, jobject f)
{
using namespace java::lang::reflect;
Field *field = reinterpret_cast<Field *> (f);
return _Jv_FromReflectedField (field);
}
jobject
_Jv_JNI_ToReflectedMethod (JNIEnv *env, jclass klass, jmethodID id,
jboolean)
{
using namespace java::lang::reflect;
// FIXME.
static _Jv_Utf8Const *init_name = _Jv_makeUtf8Const ("<init>", 6);
jobject result = NULL;
try
{
if (_Jv_equalUtf8Consts (id->name, init_name))
{
// A constructor.
Constructor *cons = new Constructor ();
cons->offset = (char *) id - (char *) &klass->methods;
cons->declaringClass = klass;
result = cons;
}
else
{
Method *meth = new Method ();
meth->offset = (char *) id - (char *) &klass->methods;
meth->declaringClass = klass;
result = meth;
}
}
catch (jthrowable t)
{
env->ex = t;
}
return wrap_value (env, result);
}
static jmethodID
_Jv_JNI_FromReflectedMethod (JNIEnv *, jobject method)
{
using namespace java::lang::reflect;
if ((&MethodClass)->isInstance (method))
return _Jv_FromReflectedMethod (reinterpret_cast<Method *> (method));
return
_Jv_FromReflectedConstructor (reinterpret_cast<Constructor *> (method));
}
static jint
_Jv_JNI_RegisterNatives (JNIEnv *env, jclass k,
const JNINativeMethod *methods,
jint nMethods)
{
#ifdef INTERPRETER
// For now, this only matters for interpreted methods. FIXME.
if (! _Jv_IsInterpretedClass (k))
{
// FIXME: throw exception.
return JNI_ERR;
}
_Jv_InterpClass *klass = reinterpret_cast<_Jv_InterpClass *> (k);
// Look at each descriptor given us, and find the corresponding
// method in the class.
for (int j = 0; j < nMethods; ++j)
{
bool found = false;
_Jv_MethodBase **imeths = _Jv_GetFirstMethod (klass);
for (int i = 0; i < JvNumMethods (klass); ++i)
{
_Jv_MethodBase *meth = imeths[i];
_Jv_Method *self = meth->get_method ();
if (! strcmp (self->name->data, methods[j].name)
&& ! strcmp (self->signature->data, methods[j].signature))
{
if (! (self->accflags
& java::lang::reflect::Modifier::NATIVE))
break;
// Found a match that is native.
_Jv_JNIMethod *jmeth = reinterpret_cast<_Jv_JNIMethod *> (meth);
jmeth->set_function (methods[i].fnPtr);
found = true;
break;
}
}
if (! found)
{
jstring m = JvNewStringUTF (methods[j].name);
try
{
env->ex =new java::lang::NoSuchMethodError (m);
}
catch (jthrowable t)
{
env->ex = t;
}
return JNI_ERR;
}
}
return JNI_OK;
#else /* INTERPRETER */
return JNI_ERR;
#endif /* INTERPRETER */
}
static jint
_Jv_JNI_UnregisterNatives (JNIEnv *, jclass)
{
return JNI_ERR;
}
// Add a character to the buffer, encoding properly.
static void
add_char (char *buf, jchar c, int *here)
{
if (c == '_')
{
buf[(*here)++] = '_';
buf[(*here)++] = '1';
}
else if (c == ';')
{
buf[(*here)++] = '_';
buf[(*here)++] = '2';
}
else if (c == '[')
{
buf[(*here)++] = '_';
buf[(*here)++] = '3';
}
// Also check for `.' here because we might be passed an internal
// qualified class name like `foo.bar'.
else if (c == '/' || c == '.')
buf[(*here)++] = '_';
else if ((c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z'))
buf[(*here)++] = (char) c;
else
{
// "Unicode" character.
buf[(*here)++] = '_';
buf[(*here)++] = '0';
for (int i = 0; i < 4; ++i)
{
int val = c & 0x0f;
buf[(*here) + 3 - i] = (val > 10) ? ('a' + val - 10) : ('0' + val);
c >>= 4;
}
*here += 4;
}
}
// Compute a mangled name for a native function. This computes the
// long name, and also returns an index which indicates where a NUL
// can be placed to create the short name. This function assumes that
// the buffer is large enough for its results.
static void
mangled_name (jclass klass, _Jv_Utf8Const *func_name,
_Jv_Utf8Const *signature, char *buf, int *long_start)
{
strcpy (buf, "Java_");
int here = 5;
// Add fully qualified class name.
jchar *chars = _Jv_GetStringChars (klass->getName ());
jint len = klass->getName ()->length ();
for (int i = 0; i < len; ++i)
add_char (buf, chars[i], &here);
// Don't use add_char because we need a literal `_'.
buf[here++] = '_';
const unsigned char *fn = (const unsigned char *) func_name->data;
const unsigned char *limit = fn + func_name->length;
for (int i = 0; ; ++i)
{
int ch = UTF8_GET (fn, limit);
if (ch < 0)
break;
add_char (buf, ch, &here);
}
// This is where the long signature begins.
*long_start = here;
buf[here++] = '_';
buf[here++] = '_';
const unsigned char *sig = (const unsigned char *) signature->data;
limit = sig + signature->length;
JvAssert (sig[0] == '(');
++sig;
while (1)
{
int ch = UTF8_GET (sig, limit);
if (ch == ')' || ch < 0)
break;
add_char (buf, ch, &here);
}
buf[here] = '\0';
}
// Return the current thread's JNIEnv; if one does not exist, create
// it. Also create a new system frame for use. This is `extern "C"'
// because the compiler calls it.
extern "C" JNIEnv *
_Jv_GetJNIEnvNewFrame (jclass klass)
{
JNIEnv *env = _Jv_GetCurrentJNIEnv ();
if (env == NULL)
{
env = (JNIEnv *) _Jv_MallocUnchecked (sizeof (JNIEnv));
env->p = &_Jv_JNIFunctions;
env->ex = NULL;
env->klass = klass;
env->locals = NULL;
_Jv_SetCurrentJNIEnv (env);
}
_Jv_JNI_LocalFrame *frame
= (_Jv_JNI_LocalFrame *) _Jv_MallocUnchecked (sizeof (_Jv_JNI_LocalFrame)
+ (FRAME_SIZE
* sizeof (jobject)));
frame->marker = MARK_SYSTEM;
frame->size = FRAME_SIZE;
frame->next = env->locals;
env->locals = frame;
for (int i = 0; i < frame->size; ++i)
frame->vec[i] = NULL;
return env;
}
// Return the function which implements a particular JNI method. If
// we can't find the function, we throw the appropriate exception.
// This is `extern "C"' because the compiler uses it.
extern "C" void *
_Jv_LookupJNIMethod (jclass klass, _Jv_Utf8Const *name,
_Jv_Utf8Const *signature)
{
char buf[10 + 6 * (name->length + signature->length)];
int long_start;
void *function;
mangled_name (klass, name, signature, buf, &long_start);
char c = buf[long_start];
buf[long_start] = '\0';
function = _Jv_FindSymbolInExecutable (buf);
if (function == NULL)
{
buf[long_start] = c;
function = _Jv_FindSymbolInExecutable (buf);
if (function == NULL)
{
jstring str = JvNewStringUTF (name->data);
throw new java::lang::AbstractMethodError (str);
}
}
return function;
}
#ifdef INTERPRETER
// This function is the stub which is used to turn an ordinary (CNI)
// method call into a JNI call.
void
_Jv_JNIMethod::call (ffi_cif *, void *ret, ffi_raw *args, void *__this)
{
_Jv_JNIMethod* _this = (_Jv_JNIMethod *) __this;
JNIEnv *env = _Jv_GetJNIEnvNewFrame (_this->defining_class);
// FIXME: we should mark every reference parameter as a local. For
// now we assume a conservative GC, and we assume that the
// references are on the stack somewhere.
// We cache the value that we find, of course, but if we don't find
// a value we don't cache that fact -- we might subsequently load a
// library which finds the function in question.
if (_this->function == NULL)
_this->function = _Jv_LookupJNIMethod (_this->defining_class,
_this->self->name,
_this->self->signature);
JvAssert (_this->args_raw_size % sizeof (ffi_raw) == 0);
ffi_raw real_args[2 + _this->args_raw_size / sizeof (ffi_raw)];
int offset = 0;
// First argument is always the environment pointer.
real_args[offset++].ptr = env;
// For a static method, we pass in the Class. For non-static
// methods, the `this' argument is already handled.
if ((_this->self->accflags & java::lang::reflect::Modifier::STATIC))
real_args[offset++].ptr = _this->defining_class;
// Copy over passed-in arguments.
memcpy (&real_args[offset], args, _this->args_raw_size);
// The actual call to the JNI function.
ffi_raw_call (&_this->jni_cif, (void (*)()) _this->function,
ret, real_args);
_Jv_JNI_PopSystemFrame (env);
}
#endif /* INTERPRETER */
//
// Invocation API.
//
// An internal helper function.
static jint
_Jv_JNI_AttachCurrentThread (JavaVM *, jstring name, void **penv, void *args)
{
JavaVMAttachArgs *attach = reinterpret_cast<JavaVMAttachArgs *> (args);
java::lang::ThreadGroup *group = NULL;
if (attach)
{
// FIXME: do we really want to support 1.1?
if (attach->version != JNI_VERSION_1_2
&& attach->version != JNI_VERSION_1_1)
return JNI_EVERSION;
JvAssert ((&ThreadGroupClass)->isInstance (attach->group));
group = reinterpret_cast<java::lang::ThreadGroup *> (attach->group);
}
// Attaching an already-attached thread is a no-op.
if (_Jv_GetCurrentJNIEnv () != NULL)
return 0;
JNIEnv *env = (JNIEnv *) _Jv_MallocUnchecked (sizeof (JNIEnv));
if (env == NULL)
return JNI_ERR;
env->p = &_Jv_JNIFunctions;
env->ex = NULL;
env->klass = NULL;
env->locals
= (_Jv_JNI_LocalFrame *) _Jv_MallocUnchecked (sizeof (_Jv_JNI_LocalFrame)
+ (FRAME_SIZE
* sizeof (jobject)));
if (env->locals == NULL)
{
_Jv_Free (env);
return JNI_ERR;
}
*penv = reinterpret_cast<void *> (env);
// This thread might already be a Java thread -- this function might
// have been called simply to set the new JNIEnv.
if (_Jv_ThreadCurrent () == NULL)
{
try
{
(void) new gnu::gcj::jni::NativeThread (group, name);
}
catch (jthrowable t)
{
return JNI_ERR;
}
}
_Jv_SetCurrentJNIEnv (env);
return 0;
}
// This is the one actually used by JNI.
static jint
_Jv_JNI_AttachCurrentThread (JavaVM *vm, void **penv, void *args)
{
return _Jv_JNI_AttachCurrentThread (vm, NULL, penv, args);
}
static jint
_Jv_JNI_DestroyJavaVM (JavaVM *vm)
{
JvAssert (the_vm && vm == the_vm);
JNIEnv *env;
if (_Jv_ThreadCurrent () != NULL)
{
jstring main_name;
// This sucks.
try
{
main_name = JvNewStringLatin1 ("main");
}
catch (jthrowable t)
{
return JNI_ERR;
}
jint r = _Jv_JNI_AttachCurrentThread (vm,
main_name,
reinterpret_cast<void **> (&env),
NULL);
if (r < 0)
return r;
}
else
env = _Jv_GetCurrentJNIEnv ();
_Jv_ThreadWait ();
// Docs say that this always returns an error code.
return JNI_ERR;
}
static jint
_Jv_JNI_DetachCurrentThread (JavaVM *)
{
java::lang::Thread *t = _Jv_ThreadCurrent ();
if (t == NULL)
return JNI_EDETACHED;
// FIXME: we only allow threads attached via AttachCurrentThread to
// be detached. I have no idea how we could implement detaching
// other threads, given the requirement that we must release all the
// monitors. That just seems evil.
JvAssert ((&NativeThreadClass)->isInstance (t));
// FIXME: release the monitors. We'll take this to mean all
// monitors acquired via the JNI interface. This means we have to
// keep track of them.
gnu::gcj::jni::NativeThread *nt
= reinterpret_cast<gnu::gcj::jni::NativeThread *> (t);
nt->finish ();
return 0;
}
static jint
_Jv_JNI_GetEnv (JavaVM *, void **penv, jint version)
{
if (_Jv_ThreadCurrent () == NULL)
{
*penv = NULL;
return JNI_EDETACHED;
}
#ifdef ENABLE_JVMPI
// Handle JVMPI requests.
if (version == JVMPI_VERSION_1)
{
*penv = (void *) &_Jv_JVMPI_Interface;
return 0;
}
#endif
// FIXME: do we really want to support 1.1?
if (version != JNI_VERSION_1_2 && version != JNI_VERSION_1_1)
{
*penv = NULL;
return JNI_EVERSION;
}
*penv = (void *) _Jv_GetCurrentJNIEnv ();
return 0;
}
jint
JNI_GetDefaultJavaVMInitArgs (void *args)
{
jint version = * (jint *) args;
// Here we only support 1.2.
if (version != JNI_VERSION_1_2)
return JNI_EVERSION;
JavaVMInitArgs *ia = reinterpret_cast<JavaVMInitArgs *> (args);
ia->version = JNI_VERSION_1_2;
ia->nOptions = 0;
ia->options = NULL;
ia->ignoreUnrecognized = true;
return 0;
}
jint
JNI_CreateJavaVM (JavaVM **vm, void **penv, void *args)
{
JvAssert (! the_vm);
// FIXME: synchronize
JavaVM *nvm = (JavaVM *) _Jv_MallocUnchecked (sizeof (JavaVM));
if (nvm == NULL)
return JNI_ERR;
nvm->functions = &_Jv_JNI_InvokeFunctions;
// Parse the arguments.
if (args != NULL)
{
jint version = * (jint *) args;
// We only support 1.2.
if (version != JNI_VERSION_1_2)
return JNI_EVERSION;
JavaVMInitArgs *ia = reinterpret_cast<JavaVMInitArgs *> (args);
for (int i = 0; i < ia->nOptions; ++i)
{
if (! strcmp (ia->options[i].optionString, "vfprintf")
|| ! strcmp (ia->options[i].optionString, "exit")
|| ! strcmp (ia->options[i].optionString, "abort"))
{
// We are required to recognize these, but for now we
// don't handle them in any way. FIXME.
continue;
}
else if (! strncmp (ia->options[i].optionString,
"-verbose", sizeof ("-verbose") - 1))
{
// We don't do anything with this option either. We
// might want to make sure the argument is valid, but we
// don't really care all that much for now.
continue;
}
else if (! strncmp (ia->options[i].optionString, "-D", 2))
{
// FIXME.
continue;
}
else if (ia->ignoreUnrecognized)
{
if (ia->options[i].optionString[0] == '_'
|| ! strncmp (ia->options[i].optionString, "-X", 2))
continue;
}
return JNI_ERR;
}
}
jint r =_Jv_JNI_AttachCurrentThread (nvm, penv, NULL);
if (r < 0)
return r;
the_vm = nvm;
*vm = the_vm;
return 0;
}
jint
JNI_GetCreatedJavaVMs (JavaVM **vm_buffer, jsize buf_len, jsize *n_vms)
{
if (buf_len <= 0)
return JNI_ERR;
// We only support a single VM.
if (the_vm != NULL)
{
vm_buffer[0] = the_vm;
*n_vms = 1;
}
else
*n_vms = 0;
return 0;
}
JavaVM *
_Jv_GetJavaVM ()
{
// FIXME: synchronize
if (! the_vm)
{
JavaVM *nvm = (JavaVM *) _Jv_MallocUnchecked (sizeof (JavaVM));
if (nvm != NULL)
nvm->functions = &_Jv_JNI_InvokeFunctions;
the_vm = nvm;
}
// If this is a Java thread, we want to make sure it has an
// associated JNIEnv.
if (_Jv_ThreadCurrent () != NULL)
{
void *ignore;
_Jv_JNI_AttachCurrentThread (the_vm, &ignore, NULL);
}
return the_vm;
}
static jint
_Jv_JNI_GetJavaVM (JNIEnv *, JavaVM **vm)
{
*vm = _Jv_GetJavaVM ();
return *vm == NULL ? JNI_ERR : JNI_OK;
}
#define NOT_IMPL NULL
#define RESERVED NULL
struct JNINativeInterface _Jv_JNIFunctions =
{
RESERVED,
RESERVED,
RESERVED,
RESERVED,
_Jv_JNI_GetVersion, // GetVersion
_Jv_JNI_DefineClass, // DefineClass
_Jv_JNI_FindClass, // FindClass
_Jv_JNI_FromReflectedMethod, // FromReflectedMethod
_Jv_JNI_FromReflectedField, // FromReflectedField
_Jv_JNI_ToReflectedMethod, // ToReflectedMethod
_Jv_JNI_GetSuperclass, // GetSuperclass
_Jv_JNI_IsAssignableFrom, // IsAssignableFrom
_Jv_JNI_ToReflectedField, // ToReflectedField
_Jv_JNI_Throw, // Throw
_Jv_JNI_ThrowNew, // ThrowNew
_Jv_JNI_ExceptionOccurred, // ExceptionOccurred
_Jv_JNI_ExceptionDescribe, // ExceptionDescribe
_Jv_JNI_ExceptionClear, // ExceptionClear
_Jv_JNI_FatalError, // FatalError
_Jv_JNI_PushLocalFrame, // PushLocalFrame
_Jv_JNI_PopLocalFrame, // PopLocalFrame
_Jv_JNI_NewGlobalRef, // NewGlobalRef
_Jv_JNI_DeleteGlobalRef, // DeleteGlobalRef
_Jv_JNI_DeleteLocalRef, // DeleteLocalRef
_Jv_JNI_IsSameObject, // IsSameObject
_Jv_JNI_NewLocalRef, // NewLocalRef
_Jv_JNI_EnsureLocalCapacity, // EnsureLocalCapacity
_Jv_JNI_AllocObject, // AllocObject
_Jv_JNI_NewObject, // NewObject
_Jv_JNI_NewObjectV, // NewObjectV
_Jv_JNI_NewObjectA, // NewObjectA
_Jv_JNI_GetObjectClass, // GetObjectClass
_Jv_JNI_IsInstanceOf, // IsInstanceOf
_Jv_JNI_GetAnyMethodID<false>, // GetMethodID
_Jv_JNI_CallMethod<jobject>, // CallObjectMethod
_Jv_JNI_CallMethodV<jobject>, // CallObjectMethodV
_Jv_JNI_CallMethodA<jobject>, // CallObjectMethodA
_Jv_JNI_CallMethod<jboolean>, // CallBooleanMethod
_Jv_JNI_CallMethodV<jboolean>, // CallBooleanMethodV
_Jv_JNI_CallMethodA<jboolean>, // CallBooleanMethodA
_Jv_JNI_CallMethod<jbyte>, // CallByteMethod
_Jv_JNI_CallMethodV<jbyte>, // CallByteMethodV
_Jv_JNI_CallMethodA<jbyte>, // CallByteMethodA
_Jv_JNI_CallMethod<jchar>, // CallCharMethod
_Jv_JNI_CallMethodV<jchar>, // CallCharMethodV
_Jv_JNI_CallMethodA<jchar>, // CallCharMethodA
_Jv_JNI_CallMethod<jshort>, // CallShortMethod
_Jv_JNI_CallMethodV<jshort>, // CallShortMethodV
_Jv_JNI_CallMethodA<jshort>, // CallShortMethodA
_Jv_JNI_CallMethod<jint>, // CallIntMethod
_Jv_JNI_CallMethodV<jint>, // CallIntMethodV
_Jv_JNI_CallMethodA<jint>, // CallIntMethodA
_Jv_JNI_CallMethod<jlong>, // CallLongMethod
_Jv_JNI_CallMethodV<jlong>, // CallLongMethodV
_Jv_JNI_CallMethodA<jlong>, // CallLongMethodA
_Jv_JNI_CallMethod<jfloat>, // CallFloatMethod
_Jv_JNI_CallMethodV<jfloat>, // CallFloatMethodV
_Jv_JNI_CallMethodA<jfloat>, // CallFloatMethodA
_Jv_JNI_CallMethod<jdouble>, // CallDoubleMethod
_Jv_JNI_CallMethodV<jdouble>, // CallDoubleMethodV
_Jv_JNI_CallMethodA<jdouble>, // CallDoubleMethodA
_Jv_JNI_CallVoidMethod, // CallVoidMethod
_Jv_JNI_CallVoidMethodV, // CallVoidMethodV
_Jv_JNI_CallVoidMethodA, // CallVoidMethodA
// Nonvirtual method invocation functions follow.
_Jv_JNI_CallAnyMethod<jobject, nonvirtual>, // CallNonvirtualObjectMethod
_Jv_JNI_CallAnyMethodV<jobject, nonvirtual>, // CallNonvirtualObjectMethodV
_Jv_JNI_CallAnyMethodA<jobject, nonvirtual>, // CallNonvirtualObjectMethodA
_Jv_JNI_CallAnyMethod<jboolean, nonvirtual>, // CallNonvirtualBooleanMethod
_Jv_JNI_CallAnyMethodV<jboolean, nonvirtual>, // CallNonvirtualBooleanMethodV
_Jv_JNI_CallAnyMethodA<jboolean, nonvirtual>, // CallNonvirtualBooleanMethodA
_Jv_JNI_CallAnyMethod<jbyte, nonvirtual>, // CallNonvirtualByteMethod
_Jv_JNI_CallAnyMethodV<jbyte, nonvirtual>, // CallNonvirtualByteMethodV
_Jv_JNI_CallAnyMethodA<jbyte, nonvirtual>, // CallNonvirtualByteMethodA
_Jv_JNI_CallAnyMethod<jchar, nonvirtual>, // CallNonvirtualCharMethod
_Jv_JNI_CallAnyMethodV<jchar, nonvirtual>, // CallNonvirtualCharMethodV
_Jv_JNI_CallAnyMethodA<jchar, nonvirtual>, // CallNonvirtualCharMethodA
_Jv_JNI_CallAnyMethod<jshort, nonvirtual>, // CallNonvirtualShortMethod
_Jv_JNI_CallAnyMethodV<jshort, nonvirtual>, // CallNonvirtualShortMethodV
_Jv_JNI_CallAnyMethodA<jshort, nonvirtual>, // CallNonvirtualShortMethodA
_Jv_JNI_CallAnyMethod<jint, nonvirtual>, // CallNonvirtualIntMethod
_Jv_JNI_CallAnyMethodV<jint, nonvirtual>, // CallNonvirtualIntMethodV
_Jv_JNI_CallAnyMethodA<jint, nonvirtual>, // CallNonvirtualIntMethodA
_Jv_JNI_CallAnyMethod<jlong, nonvirtual>, // CallNonvirtualLongMethod
_Jv_JNI_CallAnyMethodV<jlong, nonvirtual>, // CallNonvirtualLongMethodV
_Jv_JNI_CallAnyMethodA<jlong, nonvirtual>, // CallNonvirtualLongMethodA
_Jv_JNI_CallAnyMethod<jfloat, nonvirtual>, // CallNonvirtualFloatMethod
_Jv_JNI_CallAnyMethodV<jfloat, nonvirtual>, // CallNonvirtualFloatMethodV
_Jv_JNI_CallAnyMethodA<jfloat, nonvirtual>, // CallNonvirtualFloatMethodA
_Jv_JNI_CallAnyMethod<jdouble, nonvirtual>, // CallNonvirtualDoubleMethod
_Jv_JNI_CallAnyMethodV<jdouble, nonvirtual>, // CallNonvirtualDoubleMethodV
_Jv_JNI_CallAnyMethodA<jdouble, nonvirtual>, // CallNonvirtualDoubleMethodA
_Jv_JNI_CallAnyVoidMethod<nonvirtual>, // CallNonvirtualVoidMethod
_Jv_JNI_CallAnyVoidMethodV<nonvirtual>, // CallNonvirtualVoidMethodV
_Jv_JNI_CallAnyVoidMethodA<nonvirtual>, // CallNonvirtualVoidMethodA
_Jv_JNI_GetAnyFieldID<false>, // GetFieldID
_Jv_JNI_GetField<jobject>, // GetObjectField
_Jv_JNI_GetField<jboolean>, // GetBooleanField
_Jv_JNI_GetField<jbyte>, // GetByteField
_Jv_JNI_GetField<jchar>, // GetCharField
_Jv_JNI_GetField<jshort>, // GetShortField
_Jv_JNI_GetField<jint>, // GetIntField
_Jv_JNI_GetField<jlong>, // GetLongField
_Jv_JNI_GetField<jfloat>, // GetFloatField
_Jv_JNI_GetField<jdouble>, // GetDoubleField
_Jv_JNI_SetField, // SetObjectField
_Jv_JNI_SetField, // SetBooleanField
_Jv_JNI_SetField, // SetByteField
_Jv_JNI_SetField, // SetCharField
_Jv_JNI_SetField, // SetShortField
_Jv_JNI_SetField, // SetIntField
_Jv_JNI_SetField, // SetLongField
_Jv_JNI_SetField, // SetFloatField
_Jv_JNI_SetField, // SetDoubleField
_Jv_JNI_GetAnyMethodID<true>, // GetStaticMethodID
_Jv_JNI_CallStaticMethod<jobject>, // CallStaticObjectMethod
_Jv_JNI_CallStaticMethodV<jobject>, // CallStaticObjectMethodV
_Jv_JNI_CallStaticMethodA<jobject>, // CallStaticObjectMethodA
_Jv_JNI_CallStaticMethod<jboolean>, // CallStaticBooleanMethod
_Jv_JNI_CallStaticMethodV<jboolean>, // CallStaticBooleanMethodV
_Jv_JNI_CallStaticMethodA<jboolean>, // CallStaticBooleanMethodA
_Jv_JNI_CallStaticMethod<jbyte>, // CallStaticByteMethod
_Jv_JNI_CallStaticMethodV<jbyte>, // CallStaticByteMethodV
_Jv_JNI_CallStaticMethodA<jbyte>, // CallStaticByteMethodA
_Jv_JNI_CallStaticMethod<jchar>, // CallStaticCharMethod
_Jv_JNI_CallStaticMethodV<jchar>, // CallStaticCharMethodV
_Jv_JNI_CallStaticMethodA<jchar>, // CallStaticCharMethodA
_Jv_JNI_CallStaticMethod<jshort>, // CallStaticShortMethod
_Jv_JNI_CallStaticMethodV<jshort>, // CallStaticShortMethodV
_Jv_JNI_CallStaticMethodA<jshort>, // CallStaticShortMethodA
_Jv_JNI_CallStaticMethod<jint>, // CallStaticIntMethod
_Jv_JNI_CallStaticMethodV<jint>, // CallStaticIntMethodV
_Jv_JNI_CallStaticMethodA<jint>, // CallStaticIntMethodA
_Jv_JNI_CallStaticMethod<jlong>, // CallStaticLongMethod
_Jv_JNI_CallStaticMethodV<jlong>, // CallStaticLongMethodV
_Jv_JNI_CallStaticMethodA<jlong>, // CallStaticLongMethodA
_Jv_JNI_CallStaticMethod<jfloat>, // CallStaticFloatMethod
_Jv_JNI_CallStaticMethodV<jfloat>, // CallStaticFloatMethodV
_Jv_JNI_CallStaticMethodA<jfloat>, // CallStaticFloatMethodA
_Jv_JNI_CallStaticMethod<jdouble>, // CallStaticDoubleMethod
_Jv_JNI_CallStaticMethodV<jdouble>, // CallStaticDoubleMethodV
_Jv_JNI_CallStaticMethodA<jdouble>, // CallStaticDoubleMethodA
_Jv_JNI_CallStaticVoidMethod, // CallStaticVoidMethod
_Jv_JNI_CallStaticVoidMethodV, // CallStaticVoidMethodV
_Jv_JNI_CallStaticVoidMethodA, // CallStaticVoidMethodA
_Jv_JNI_GetAnyFieldID<true>, // GetStaticFieldID
_Jv_JNI_GetStaticField<jobject>, // GetStaticObjectField
_Jv_JNI_GetStaticField<jboolean>, // GetStaticBooleanField
_Jv_JNI_GetStaticField<jbyte>, // GetStaticByteField
_Jv_JNI_GetStaticField<jchar>, // GetStaticCharField
_Jv_JNI_GetStaticField<jshort>, // GetStaticShortField
_Jv_JNI_GetStaticField<jint>, // GetStaticIntField
_Jv_JNI_GetStaticField<jlong>, // GetStaticLongField
_Jv_JNI_GetStaticField<jfloat>, // GetStaticFloatField
_Jv_JNI_GetStaticField<jdouble>, // GetStaticDoubleField
_Jv_JNI_SetStaticField, // SetStaticObjectField
_Jv_JNI_SetStaticField, // SetStaticBooleanField
_Jv_JNI_SetStaticField, // SetStaticByteField
_Jv_JNI_SetStaticField, // SetStaticCharField
_Jv_JNI_SetStaticField, // SetStaticShortField
_Jv_JNI_SetStaticField, // SetStaticIntField
_Jv_JNI_SetStaticField, // SetStaticLongField
_Jv_JNI_SetStaticField, // SetStaticFloatField
_Jv_JNI_SetStaticField, // SetStaticDoubleField
_Jv_JNI_NewString, // NewString
_Jv_JNI_GetStringLength, // GetStringLength
_Jv_JNI_GetStringChars, // GetStringChars
_Jv_JNI_ReleaseStringChars, // ReleaseStringChars
_Jv_JNI_NewStringUTF, // NewStringUTF
_Jv_JNI_GetStringUTFLength, // GetStringUTFLength
_Jv_JNI_GetStringUTFChars, // GetStringUTFLength
_Jv_JNI_ReleaseStringUTFChars, // ReleaseStringUTFChars
_Jv_JNI_GetArrayLength, // GetArrayLength
_Jv_JNI_NewObjectArray, // NewObjectArray
_Jv_JNI_GetObjectArrayElement, // GetObjectArrayElement
_Jv_JNI_SetObjectArrayElement, // SetObjectArrayElement
_Jv_JNI_NewPrimitiveArray<jboolean, JvPrimClass (boolean)>,
// NewBooleanArray
_Jv_JNI_NewPrimitiveArray<jbyte, JvPrimClass (byte)>, // NewByteArray
_Jv_JNI_NewPrimitiveArray<jchar, JvPrimClass (char)>, // NewCharArray
_Jv_JNI_NewPrimitiveArray<jshort, JvPrimClass (short)>, // NewShortArray
_Jv_JNI_NewPrimitiveArray<jint, JvPrimClass (int)>, // NewIntArray
_Jv_JNI_NewPrimitiveArray<jlong, JvPrimClass (long)>, // NewLongArray
_Jv_JNI_NewPrimitiveArray<jfloat, JvPrimClass (float)>, // NewFloatArray
_Jv_JNI_NewPrimitiveArray<jdouble, JvPrimClass (double)>, // NewDoubleArray
_Jv_JNI_GetPrimitiveArrayElements, // GetBooleanArrayElements
_Jv_JNI_GetPrimitiveArrayElements, // GetByteArrayElements
_Jv_JNI_GetPrimitiveArrayElements, // GetCharArrayElements
_Jv_JNI_GetPrimitiveArrayElements, // GetShortArrayElements
_Jv_JNI_GetPrimitiveArrayElements, // GetIntArrayElements
_Jv_JNI_GetPrimitiveArrayElements, // GetLongArrayElements
_Jv_JNI_GetPrimitiveArrayElements, // GetFloatArrayElements
_Jv_JNI_GetPrimitiveArrayElements, // GetDoubleArrayElements
_Jv_JNI_ReleasePrimitiveArrayElements, // ReleaseBooleanArrayElements
_Jv_JNI_ReleasePrimitiveArrayElements, // ReleaseByteArrayElements
_Jv_JNI_ReleasePrimitiveArrayElements, // ReleaseCharArrayElements
_Jv_JNI_ReleasePrimitiveArrayElements, // ReleaseShortArrayElements
_Jv_JNI_ReleasePrimitiveArrayElements, // ReleaseIntArrayElements
_Jv_JNI_ReleasePrimitiveArrayElements, // ReleaseLongArrayElements
_Jv_JNI_ReleasePrimitiveArrayElements, // ReleaseFloatArrayElements
_Jv_JNI_ReleasePrimitiveArrayElements, // ReleaseDoubleArrayElements
_Jv_JNI_GetPrimitiveArrayRegion, // GetBooleanArrayRegion
_Jv_JNI_GetPrimitiveArrayRegion, // GetByteArrayRegion
_Jv_JNI_GetPrimitiveArrayRegion, // GetCharArrayRegion
_Jv_JNI_GetPrimitiveArrayRegion, // GetShortArrayRegion
_Jv_JNI_GetPrimitiveArrayRegion, // GetIntArrayRegion
_Jv_JNI_GetPrimitiveArrayRegion, // GetLongArrayRegion
_Jv_JNI_GetPrimitiveArrayRegion, // GetFloatArrayRegion
_Jv_JNI_GetPrimitiveArrayRegion, // GetDoubleArrayRegion
_Jv_JNI_SetPrimitiveArrayRegion, // SetBooleanArrayRegion
_Jv_JNI_SetPrimitiveArrayRegion, // SetByteArrayRegion
_Jv_JNI_SetPrimitiveArrayRegion, // SetCharArrayRegion
_Jv_JNI_SetPrimitiveArrayRegion, // SetShortArrayRegion
_Jv_JNI_SetPrimitiveArrayRegion, // SetIntArrayRegion
_Jv_JNI_SetPrimitiveArrayRegion, // SetLongArrayRegion
_Jv_JNI_SetPrimitiveArrayRegion, // SetFloatArrayRegion
_Jv_JNI_SetPrimitiveArrayRegion, // SetDoubleArrayRegion
_Jv_JNI_RegisterNatives, // RegisterNatives
_Jv_JNI_UnregisterNatives, // UnregisterNatives
_Jv_JNI_MonitorEnter, // MonitorEnter
_Jv_JNI_MonitorExit, // MonitorExit
_Jv_JNI_GetJavaVM, // GetJavaVM
_Jv_JNI_GetStringRegion, // GetStringRegion
_Jv_JNI_GetStringUTFRegion, // GetStringUTFRegion
_Jv_JNI_GetPrimitiveArrayCritical, // GetPrimitiveArrayCritical
_Jv_JNI_ReleasePrimitiveArrayCritical, // ReleasePrimitiveArrayCritical
_Jv_JNI_GetStringCritical, // GetStringCritical
_Jv_JNI_ReleaseStringCritical, // ReleaseStringCritical
NOT_IMPL /* newweakglobalref */,
NOT_IMPL /* deleteweakglobalref */,
_Jv_JNI_ExceptionCheck
};
struct JNIInvokeInterface _Jv_JNI_InvokeFunctions =
{
RESERVED,
RESERVED,
RESERVED,
_Jv_JNI_DestroyJavaVM,
_Jv_JNI_AttachCurrentThread,
_Jv_JNI_DetachCurrentThread,
_Jv_JNI_GetEnv
};