packages feed

java-bridge-0.9: src/ffijni.c

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include "ffijni.h"
#include "jni.h"

#if defined(FFIJNI_LINUX) || defined(FFIJNI_MACOSX)
#include <dlfcn.h>
#else
#if defined(FFIJNI_WINDOWS)
#include <windows.h>
#endif
#endif

#define FFIJNI_VERSION JNI_VERSION_1_6

#ifdef FFIJNI_DEBUG /* def FFIJNI_DEBUG */

static jboolean
_ffijni_debug = JNI_FALSE;

jboolean
getDebugStatus()
{
    return _ffijni_debug;
}

void
setDebugStatus(jboolean status)
{
    _ffijni_debug = status;
}
#define DEBUG(STR) \
    if (_ffijni_debug) { \
        printf("ffijni: %s\n", STR); \
    }
#define DEBUG1(STR, ARG1) \
    if (_ffijni_debug) { \
        printf("ffijni: " STR "\n", ARG1); \
        fflush(stdout); \
    }
#define DEBUG2(STR, ARG1, ARG2) \
    if (_ffijni_debug) { \
        printf("ffijni: " STR "\n", ARG1, ARG2); \
        fflush(stdout); \
    }
#define DEBUG3(STR, ARG1, ARG2, ARG3) \
    if (_ffijni_debug) { \
        printf("ffijni: " STR "\n", ARG1, ARG2, ARG3); \
        fflush(stdout); \
    }
#define DEBUG4(STR, ARG1, ARG2, ARG3, ARG4) \
    if (_ffijni_debug) { \
        printf("ffijni: " STR "\n", ARG1, ARG2, ARG3, ARG4); \
        fflush(stdout); \
    }
#else /* def FFIJNI_DEBUG */
jboolean
getDebugStatus() { return JNI_FALSE; }

void
setDebugStatus(jboolean _state)
{
    fprintf(stderr,
            "ffijni: setDebugStatus(%s) was called, but this is not a debug build.\n"
            "ffijni: Use the DEBUG flag to enable a debug build.\n",
            _state ? "true" : "false");
}
#define DEBUG(STR)
#define DEBUG1(STR, ARG1)
#define DEBUG2(STR, ARG1, ARG2)
#define DEBUG3(STR, ARG1, ARG2, ARG3)
#define DEBUG4(STR, ARG1, ARG2, ARG3, ARG4)
#endif /* def FFIJNI_DEBUG */

#define XSTR(s) STR(s)
#define STR(s) #s

#define GLOBAL_REF(VMI, LOCAL, GLOBAL) \
    jobject GLOBAL; \
    if (LOCAL == NULL) { \
        GLOBAL = NULL; \
    } else { \
        GLOBAL = (*VMI->env)->NewGlobalRef(VMI->env, LOCAL); \
        (*VMI->env)->DeleteLocalRef(VMI->env, LOCAL); \
    } \
    DEBUG2("-> new global ref from local: %p -> %p", LOCAL, GLOBAL)

#ifdef FFIJNI_OSX_FRAMEWORK /* def FFIJNI_OSX_FRAMEWORK */

#define GET_CREATED_JAVA_VMS(a, b, c) JNI_GetCreatedJavaVMs(a, b, c)
#define CREATE_JAVA_VM(a, b, c) JNI_CreateJavaVM(a, b, c)

void
setLibjvmPath(char* path) {}

char*
getLibjvmPath() { return "Linked with JavaVM Framework at compile time."; }

char*
getCompiledLibjvmPath() { return "Linked with JavaVM Framework at compile time."; }

#else /* def FFIJNI_OSX_FRAMEWORK */

#ifndef FFIJNI_LIBJVM
#error "Please specify -DFFIJNI_LIBJVM=<path-to-libjvm>."
#endif

#define GET_CREATED_JAVA_VMS(a, b, c) _GetCreatedJavaVMs(a, b, c)
#define CREATE_JAVA_VM(a, b, c) _CreateJavaVM(a, b, c)

static
jint (*_GetCreatedJavaVMs) (JavaVM** vmBuf, jsize bufLen, jsize* nVMs) = NULL;

static
jint (*_CreateJavaVM) (JavaVM** pvm, void** penv, void* vmargs) = NULL;

static
char* _libjvm_path = NULL;

void
setLibjvmPath(char* path)
{
    _libjvm_path = path;
}

char*
getLibjvmPath()
{
    return _libjvm_path;
}

char*
getCompiledLibjvmPath()
{
    return XSTR(FFIJNI_LIBJVM);
}

jint
_load_JNI()
{
    if (!_GetCreatedJavaVMs) {
        if (!_libjvm_path) {
            /* The location of libjvm can be overriden by the
             * environment variable FFIJNI_LIBJVM. */
            _libjvm_path = getenv("FFIJNI_LIBJVM");
        }
        if (!_libjvm_path) {
            /* If no such environment variable exists, use the built in
             * location (needs to be given at compile time) */
            _libjvm_path = XSTR(FFIJNI_LIBJVM);
        }
        DEBUG1("Loading libjvm from %s.", _libjvm_path)
        #if defined(FFIJNI_LINUX) || defined(FFIJNI_MACOSX)
        /* On Linux and MacOSX use dlopen(...) to manually
         * load the dynamic library. */
        void* libVM = dlopen(_libjvm_path, RTLD_LAZY);
        if (!libVM) {
            return -41;
        }
        _GetCreatedJavaVMs = (jint (*)(JavaVM**,jsize,jsize*)) dlsym(libVM, "JNI_GetCreatedJavaVMs");
        _CreateJavaVM = (jint (*)(JavaVM**,void**,void*)) dlsym(libVM, "JNI_CreateJavaVM");
        #else
        #if defined(FFIJNI_WINDOWS)
        /* On Windows use LoadLibrary(...) to manually
         * load the dynamic library. */
        HINSTANCE hVM = LoadLibrary(_libjvm_path);
        if (!hVM) {
            return -41;
        }
        _GetCreatedJavaVMs = (jint (*)(JavaVM**,jsize,jsize*)) GetProcAddress(hVM, "JNI_GetCreatedJavaVMs");
        _CreateJavaVM = (jint (*)(JavaVM**,void**,void*)) GetProcAddress(hVM, "JNI_CreateJavaVM");
        #else
            #error "Unsupported platform."
        #endif
        #endif
    }
    return 0;
}
#endif /* def FFIJNI_OSX_FRAMEWORK */


void
runCocoaMain()
{
    #if defined(FFIJNI_MACOSX) && defined(FFIJNI_OSX_GUI)
    DEBUG("Retrieving NSApp...")
    void* clazz = objc_getClass("NSApplication");
    void* app = objc_msgSend(clazz, sel_registerName("sharedApplication"));

    DEBUG1("-> %p", app)

    DEBUG("Starting cocoa main runloop")
    objc_msgSend(app, sel_registerName("run"));
    #endif
}


jobject JNICALL
_callCallback(JNIEnv* env, jobject self, jlong func, jobject method, jobjectArray args)
{
    WrappedFun funPtr = (WrappedFun) (intptr_t) func;
    DEBUG3("Callback called (func: %d -> %p, method: %p)\m", func, funPtr, method)
    vm_t vmi;
    vmi.env = env;
    (*env)->GetJavaVM(env, &vmi.vm);
    return funPtr(&vmi, self, method, args);
}


void JNICALL
_releaseCallback(JNIEnv* env, jobject self, jlong func)
{
    WrappedFun funPtr = (WrappedFun) (intptr_t) func;
    DEBUG2("Callback released (func: %d -> %p)", func, funPtr)
    freeFunPtr(funPtr);
}


void
_loadHFunctionClass(JNIEnv* env)
{
    /* boilerplate:
     * - find java.lang.ClassLoader
     * - get #getSystemClassLoader
     * - get #defineClass
     * - retrieve the systemClassLoader
     */
    jclass clazz = (*env)->FindClass(env, "java/lang/ClassLoader");
    DEBUG1("Loaded java.lang.ClassLoader: %p", clazz)

    jmethodID getSystemClassLoader =
        (*env)->GetStaticMethodID(env, clazz, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
    DEBUG1("Got #getSystemClassLoader: %p", getSystemClassLoader)

    jmethodID defineClass =
        (*env)->GetMethodID(env, clazz, "defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
    DEBUG1("Got #defineClass: %p", defineClass)

    jobject systemClassLoader = (*env)->CallStaticObjectMethod(env, clazz, getSystemClassLoader);
    DEBUG1("Retrieved systemClassLoader: %p", systemClassLoader)

    /* define the class:
     * - create the string className ("HFunction")
     * - create the jbyteArray bytes from hFunctionClass (see hfunction.h)
     * - call #defineClass on systemClassLoader
     */
    jstring className = (*env)->NewStringUTF(env, "HFunction");
    DEBUG1("Allocated className: %p", className)

    jbyteArray classBytes = (*env)->NewByteArray(env, FFIJNI_HFUNCTION_LENGTH);
    (*env)->SetByteArrayRegion(env, classBytes, 0, FFIJNI_HFUNCTION_LENGTH, hFunctionClass);
    DEBUG1("Allocated classBytes: %p", classBytes)

    jclass hFunction =
        (*env)->CallObjectMethod(env, systemClassLoader, defineClass,
                                 className, classBytes, 0, FFIJNI_HFUNCTION_LENGTH);
    DEBUG1("Defined class HFunction: %p", hFunction)
    
    /* clean up */
    (*env)->DeleteLocalRef(env, systemClassLoader);
    (*env)->DeleteLocalRef(env, className);
    (*env)->DeleteLocalRef(env, classBytes);
    (*env)->DeleteLocalRef(env, hFunction);
}

jboolean
registerCallbacks(vm_t* vmi, jclass hFunction) {
    JNIEnv* env = vmi->env;

    JNINativeMethod method;
    jint result;

    /* register native call function */
    DEBUG1("Registering call(...): %p", _callCallback)

    method.name = "call";
    method.signature = "(JLjava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;";
    method.fnPtr = _callCallback;

    result = (*env)->RegisterNatives(env, hFunction, &method, 1);
    if (result < 0) {
        (*env)->ExceptionDescribe(env);
        return JNI_FALSE;
    }
    
    /* register native release function */
    DEBUG1("Registering release(...): %p", _releaseCallback)

    method.name = "release";
    method.signature = "(J)V";
    method.fnPtr = _releaseCallback;

    result = (*env)->RegisterNatives(env, hFunction, &method, 1);
    if (result < 0) {
        (*env)->ExceptionDescribe(env);
        return JNI_FALSE;
    }

    return JNI_TRUE;
}


jint
_get_vm(JavaVM** jvm, unsigned int argc, char** argv)
{
    #ifndef FFIJNI_OSX_FRAMEWORK
    /* First things first: load the dynamic library
     * (this is not necessary if the library was linked with the
     * JavaVM framework on OSX, see OSX_FRAMEWORK flag / cabal file). */
    if (_load_JNI() < 0) {
        DEBUG("Could not load libjvm")
        return -41;
    }
    #endif

    jsize numCreatedVMs = 0;
    GET_CREATED_JAVA_VMS(jvm, 1, &numCreatedVMs);
    if (numCreatedVMs > 0) {
        DEBUG("JVM already created")
        return 1;
    }

    JNIEnv* env;
    JavaVMInitArgs vm_args;
    vm_args.version = FFIJNI_VERSION;
    vm_args.ignoreUnrecognized = JNI_FALSE;

    /* Create a new Virtual Machine: */
    JavaVMOption* options = calloc(argc, sizeof(JavaVMOption));
    if (argc == 0) {
        vm_args.nOptions = 0;
    } else {
        for (int i = 0; i < argc; i++) {
            options[i].optionString = argv[i];
        }
        vm_args.nOptions = argc;
        vm_args.options = options;
    }

    jint status;
    if ((status = CREATE_JAVA_VM(jvm, (void**) &env, &vm_args)) < 0) {
        /* Creation of Virtual Machine failed. */
        free(options);
        return status;
    }
    /* Creation of Virtual Machine was successfull. */
    free(options);

    _loadHFunctionClass(env);

    return 0;
}

vm_t*
createVM ()
{
    return createVM2(0, NULL);
}

vm_t*
createVM2 (unsigned int argc, char** argv)
{
    JavaVM* jvm;
    jint status = _get_vm(&jvm, argc, argv);

    if (status < 0) {
        return NULL;
    }
    JNIEnv* env;

    vm_t* vmi = malloc(sizeof(vm_t));
    vmi->attached = false;

    (*jvm)->GetEnv(jvm, (void**) &env, FFIJNI_VERSION);
    if (env == NULL) {
        DEBUG("Attach current thread")
        if ((*jvm)->AttachCurrentThread(jvm, (void**) &env, NULL) < 0) {
            free(vmi);
            return NULL;
        } else {
            vmi->attached = true;
        }
    }
    vmi->env = env;
    vmi->vm = jvm;

    return vmi;
}

void
destroyVM (vm_t* vmi)
{
    if (vmi->attached) {
        DEBUG("Detach current thread")
        (*vmi->vm)->DetachCurrentThread(vmi->vm);
    } else {
        DEBUG("Destroy Java VM")
        (*vmi->vm)->DestroyJavaVM(vmi->vm);
    }
    free(vmi);
}

void
persistVM (vm_t* vmi)
{
    DEBUG("persistVM()")

    vmi->attached = true;
}

jclass
findClass (vm_t* vmi, const char* className)
{
    DEBUG1("findClass(%s)", className)

    jclass clazzLocal = (*vmi->env)->FindClass(vmi->env, className);
    GLOBAL_REF(vmi, clazzLocal, clazz)
    return clazz;
}

jobject
newObject (vm_t* vmi, jclass clazz, jmethodID constructor, jvalue* args)
{
    DEBUG2("newObject(%p, %p)", clazz, constructor)
    jobject objLocal = (*vmi->env)->NewObjectA(vmi->env, clazz, constructor, args);
    GLOBAL_REF(vmi, objLocal, obj)
    return obj;
}

jmethodID
getConstructorID (vm_t* vmi, jclass clazz, const char* sig)
{
    DEBUG1("getConstructorID(%s)", sig)
    jmethodID methodID = (*vmi->env)->GetMethodID(vmi->env, clazz, "<init>", sig);
    DEBUG1("-> %p", methodID)
    return methodID;
}

jmethodID
getStaticMethodID(vm_t* vmi, jclass clazz, const char* methodName, const char* sig)
{
    DEBUG2("getStaticMethodID(%s, %s)", methodName, sig)
    jmethodID methodID = (*vmi->env)->GetStaticMethodID(vmi->env, clazz, methodName, sig);
    DEBUG1("-> %p", methodID)
    return methodID;
}

jmethodID
getMethodID(vm_t* vmi, jclass clazz, const char* methodName, const char* sig)
{
    DEBUG2("getMethodID(%s, %s)", methodName, sig)
    jmethodID methodID = (*vmi->env)->GetMethodID(vmi->env, clazz, methodName, sig);
    DEBUG1("-> %p", methodID)
    return methodID;
}

jfieldID
getStaticFieldID(vm_t* vmi, jclass clazz, const char* fieldName, const char* sig)
{
    DEBUG2("getStaticFieldID(%s, %s)", fieldName, sig)

    return (*vmi->env)->GetStaticFieldID(vmi->env, clazz, fieldName, sig);
}

jfieldID
getFieldID(vm_t* vmi, jclass clazz, const char* fieldName, const char* sig)
{
    DEBUG2("getFieldID(%s, %s)", fieldName, sig)

    return (*vmi->env)->GetFieldID(vmi->env, clazz, fieldName, sig);
}

void
callStaticVoidMethod (vm_t* vmi, jclass clazz, jmethodID method, jvalue* args)
{
    DEBUG2("callStaticVoidMethod(%p, %p)", clazz, method)

    (*vmi->env)->CallStaticVoidMethodA(vmi->env, clazz, method, args);
}

jint
callStaticIntMethod (vm_t* vmi, jclass clazz, jmethodID method, jvalue* args)
{
    DEBUG2("callStaticIntMethod(%p, %p)", clazz, method)

    return (*vmi->env)->CallStaticIntMethodA(vmi->env, clazz, method, args);
}

jlong
callStaticLongMethod (vm_t* vmi, jclass clazz, jmethodID method, jvalue* args)
{
    DEBUG2("callStaticLongMethod(%p, %p)", clazz, method)

    return (*vmi->env)->CallStaticLongMethodA(vmi->env, clazz, method, args);
}

jshort
callStaticShortMethod (vm_t* vmi, jclass clazz, jmethodID method, jvalue* args)
{
    DEBUG2("callStaticShortMethod(%p, %p)", clazz, method)

    return (*vmi->env)->CallStaticShortMethodA(vmi->env, clazz, method, args);
}

jbyte
callStaticByteMethod (vm_t* vmi, jclass clazz, jmethodID method, jvalue* args)
{
    DEBUG2("callStaticVoidMethod(%p, %p)", clazz, method)

    return (*vmi->env)->CallStaticByteMethodA(vmi->env, clazz, method, args);
}

jfloat
callStaticFloatMethod (vm_t* vmi, jclass clazz, jmethodID method, jvalue* args)
{
    DEBUG2("callStaticFloatMethod(%p, %p)", clazz, method)

    return (*vmi->env)->CallStaticFloatMethodA(vmi->env, clazz, method, args);
}

jdouble
callStaticDoubleMethod (vm_t* vmi, jclass clazz, jmethodID method, jvalue* args)
{
    DEBUG2("callStaticVoidMethod(%p, %p)", clazz, method)

    return (*vmi->env)->CallStaticDoubleMethodA(vmi->env, clazz, method, args);
}

jboolean
callStaticBooleanMethod (vm_t* vmi, jclass clazz, jmethodID method, jvalue* args)
{
    DEBUG2("callStaticBooleanMethod(%p, %p)", clazz, method)

    return (*vmi->env)->CallStaticBooleanMethodA(vmi->env, clazz, method, args);
}

jchar
callStaticCharMethod (vm_t* vmi, jclass clazz, jmethodID method, jvalue* args)
{
    DEBUG2("callStaticCharMethod(%p, %p)", clazz, method)

    return (*vmi->env)->CallStaticCharMethodA(vmi->env, clazz, method, args);
}

jobject
callStaticObjectMethod (vm_t* vmi, jclass clazz, jmethodID method, jvalue* args)
{
    DEBUG2("callStaticObjectMethod(%p, %p)", clazz, method)

    jobject objLocal = (*vmi->env)->CallStaticObjectMethodA(vmi->env, clazz, method, args);
    GLOBAL_REF(vmi, objLocal, obj)
    return obj;
}

const char*
callStaticStringMethod (vm_t* vmi, jclass clazz, jmethodID method, jvalue* args)
{
    DEBUG2("callStaticStringMethod(%p, %p)", clazz, method)

    jobject string = (*vmi->env)->CallStaticObjectMethodA(vmi->env, clazz, method, args);
    if (string) {
        const char* cstring = jStringToCString(vmi, string);
        (*vmi->env)->DeleteLocalRef(vmi->env, string);
        return cstring;
    } else {
        return NULL;
    }
}

void
callVoidMethod (vm_t* vmi, jobject object, jmethodID method, jvalue* args)
{
    DEBUG2("callVoidMethod(%p, %p)", object, method)

    (*vmi->env)->CallVoidMethodA(vmi->env, object, method, args);
}

jlong
callLongMethod (vm_t* vmi, jobject object, jmethodID method, jvalue* args)
{
    DEBUG2("callLongMethod(%p, %p)", object, method)

    return (*vmi->env)->CallLongMethodA(vmi->env, object, method, args);
}

jint
callIntMethod (vm_t* vmi, jobject object, jmethodID method, jvalue* args)
{
    DEBUG2("callIntMethod(%p, %p)", object, method)
        
    return (*vmi->env)->CallIntMethodA(vmi->env, object, method, args);
}

jshort
callShortMethod (vm_t* vmi, jobject object, jmethodID method, jvalue* args)
{
    DEBUG2("callShortMethod(%p, %p)", object, method)

    return (*vmi->env)->CallShortMethodA(vmi->env, object, method, args);
}

jbyte
callByteMethod (vm_t* vmi, jobject object, jmethodID method, jvalue* args)
{
    DEBUG2("callByteMethod(%p, %p)", object, method)

    return (*vmi->env)->CallByteMethodA(vmi->env, object, method, args);
}

jfloat
callFloatMethod (vm_t* vmi, jobject object, jmethodID method, jvalue* args)
{
    DEBUG2("callFloatMethod(%p, %p)", object, method)

    return (*vmi->env)->CallFloatMethodA(vmi->env, object, method, args);
}

jdouble
callDoubleMethod (vm_t* vmi, jobject object, jmethodID method, jvalue* args)
{
    DEBUG2("callDoubleMethod(%p, %p)", object, method)

    return (*vmi->env)->CallDoubleMethodA(vmi->env, object, method, args);
}

jboolean
callBooleanMethod (vm_t* vmi, jobject object, jmethodID method, jvalue* args)
{
    DEBUG2("callBooleanMethod(%p, %p)", object, method)

    return (*vmi->env)->CallBooleanMethodA(vmi->env, object, method, args);
}

jchar
callCharMethod (vm_t* vmi, jobject object, jmethodID method, jvalue* args)
{
    DEBUG2("callCharMethod(%p, %p)", object, method)

    return (*vmi->env)->CallCharMethodA(vmi->env, object, method, args);
}

jobject
callObjectMethod (vm_t* vmi, jobject object, jmethodID method, jvalue* args)
{
    DEBUG2("callObjectMethod(%p, %p)", object, method)

    jobject objLocal = (*vmi->env)->CallObjectMethodA(vmi->env, object, method, args);
    GLOBAL_REF(vmi, objLocal, obj)
    return obj;
}

const char*
callStringMethod (vm_t* vmi, jobject object, jmethodID method, jvalue* args)
{
    DEBUG2("callStringMethod(%p, %p)", object, method)

    jobject string = (*vmi->env)->CallObjectMethodA(vmi->env, object, method, args);
    if (string) {
        const char* cstring = jStringToCString(vmi, string);
        (*vmi->env)->DeleteLocalRef(vmi->env, string);
        return cstring;
    } else {
        return NULL;
    }
}

jlong
getStaticLongField (vm_t* vmi, jclass clazz, jfieldID field)
{
    return (*vmi->env)->GetStaticLongField(vmi->env, clazz, field);
}

jint
getStaticIntField (vm_t* vmi, jclass clazz, jfieldID field)
{
    return (*vmi->env)->GetStaticIntField(vmi->env, clazz, field);
}

jshort
getStaticShortField (vm_t* vmi, jclass clazz, jfieldID field)
{
    return (*vmi->env)->GetStaticShortField(vmi->env, clazz, field);
}

jbyte
getStaticByteField (vm_t* vmi, jclass clazz, jfieldID field)
{
    return (*vmi->env)->GetStaticByteField(vmi->env, clazz, field);
}

jfloat
getStaticFloatField (vm_t* vmi, jclass clazz, jfieldID field)
{
    return (*vmi->env)->GetStaticFloatField(vmi->env, clazz, field);
}

jdouble
getStaticDoubleField (vm_t* vmi, jclass clazz, jfieldID field)
{
    return (*vmi->env)->GetStaticDoubleField(vmi->env, clazz, field);
}

jboolean
getStaticBooleanField (vm_t* vmi, jclass clazz, jfieldID field)
{
    return (*vmi->env)->GetStaticBooleanField(vmi->env, clazz, field);
}

jchar
getStaticCharField (vm_t* vmi, jclass clazz, jfieldID field)
{
    return (*vmi->env)->GetStaticCharField(vmi->env, clazz, field);
}

jobject
getStaticObjectField (vm_t* vmi, jclass clazz, jfieldID field)
{
    jobject objLocal = (*vmi->env)->GetStaticObjectField(vmi->env, clazz, field);
    GLOBAL_REF(vmi, objLocal, obj)
    return obj;
}

const char*
getStaticStringField (vm_t* vmi, jclass clazz, jfieldID field)
{
    jobject string = (*vmi->env)->GetStaticObjectField(vmi->env, clazz, field);
    return jStringToCString(vmi, string);
}

jlong
getLongField (vm_t* vmi, jobject object, jfieldID field)
{
    return (*vmi->env)->GetLongField(vmi->env, object, field);
}

jint
getIntField (vm_t* vmi, jobject object, jfieldID field)
{
    return (*vmi->env)->GetIntField(vmi->env, object, field);
}

jshort
getShortField (vm_t* vmi, jobject object, jfieldID field)
{
    return (*vmi->env)->GetShortField(vmi->env, object, field);
}

jbyte
getByteField (vm_t* vmi, jobject object, jfieldID field)
{
    return (*vmi->env)->GetByteField(vmi->env, object, field);
}

jfloat
getFloatField (vm_t* vmi, jobject object, jfieldID field)
{
    return (*vmi->env)->GetFloatField(vmi->env, object, field);
}

jdouble
getDoubleField (vm_t* vmi, jobject object, jfieldID field)
{
    return (*vmi->env)->GetDoubleField(vmi->env, object, field);
}

jboolean
getBooleanField (vm_t* vmi, jobject object, jfieldID field)
{
    return (*vmi->env)->GetBooleanField(vmi->env, object, field);
}

jchar
getCharField (vm_t* vmi, jobject object, jfieldID field)
{
    return (*vmi->env)->GetCharField(vmi->env, object, field);
}

jobject
getObjectField (vm_t* vmi, jobject object, jfieldID field)
{
    jobject objLocal = (*vmi->env)->GetObjectField(vmi->env, object, field);
    
    GLOBAL_REF(vmi, objLocal, obj)
    return obj;
}

const char*
getStringField (vm_t* vmi, jobject object, jfieldID field)
{
    jobject string = (*vmi->env)->GetObjectField(vmi->env, object, field);
    const char* cstring = jStringToCString(vmi, string);
    (*vmi->env)->DeleteLocalRef(vmi->env, string);
    return cstring;
}

void
setStaticLongField (vm_t* vmi, jclass clazz, jfieldID field, jlong value)
{
    (*vmi->env)->SetStaticLongField(vmi->env, clazz, field, value);
}

void
setStaticIntField (vm_t* vmi, jclass clazz, jfieldID field, jint value)
{
    (*vmi->env)->SetStaticIntField(vmi->env, clazz, field, value);
}

void
setStaticShortField (vm_t* vmi, jclass clazz, jfieldID field, jshort value)
{
    (*vmi->env)->SetStaticShortField(vmi->env, clazz, field, value);
}

void
setStaticByteField (vm_t* vmi, jclass clazz, jfieldID field, jbyte value)
{
    (*vmi->env)->SetStaticByteField(vmi->env, clazz, field, value);
}

void
setStaticFloatField (vm_t* vmi, jclass clazz, jfieldID field, jfloat value)
{
    (*vmi->env)->SetStaticFloatField(vmi->env, clazz, field, value);
}

void
setStaticDoubleField (vm_t* vmi, jclass clazz, jfieldID field, jdouble value)
{
    (*vmi->env)->SetStaticDoubleField(vmi->env, clazz, field, value);
}

void
setStaticBooleanField (vm_t* vmi, jclass clazz, jfieldID field, jboolean value)
{
    (*vmi->env)->SetStaticBooleanField(vmi->env, clazz, field, value);
}

void
setStaticCharField (vm_t* vmi, jclass clazz, jfieldID field, jchar value)
{
    (*vmi->env)->SetStaticCharField(vmi->env, clazz, field, value);
}

void
setStaticObjectField (vm_t* vmi, jclass clazz, jfieldID field, jobject value)
{
    (*vmi->env)->SetStaticObjectField(vmi->env, clazz, field, value);
}

void
setStaticStringField (vm_t* vmi, jclass clazz, jfieldID field, const char* value)
{
    jstring string = (*vmi->env)->NewStringUTF(vmi->env, value);
    (*vmi->env)->SetStaticObjectField(vmi->env, clazz, field, string);
}

void
setLongField (vm_t* vmi, jobject object, jfieldID field, jlong value)
{
    (*vmi->env)->SetLongField(vmi->env, object, field, value);
}

void
setIntField (vm_t* vmi, jobject object, jfieldID field, jint value)
{
    (*vmi->env)->SetIntField(vmi->env, object, field, value);
}

void
setShortField (vm_t* vmi, jobject object, jfieldID field, jshort value)
{
    (*vmi->env)->SetShortField(vmi->env, object, field, value);
}

void
setByteField (vm_t* vmi, jobject object, jfieldID field, jbyte value)
{
    (*vmi->env)->SetByteField(vmi->env, object, field, value);
}

void
setFloatField (vm_t* vmi, jobject object, jfieldID field, jfloat value)
{
    (*vmi->env)->SetFloatField(vmi->env, object, field, value);
}

void
setDoubleField (vm_t* vmi, jobject object, jfieldID field, jdouble value)
{
    (*vmi->env)->SetDoubleField(vmi->env, object, field, value);
}

void
setBooleanField (vm_t* vmi, jobject object, jfieldID field, jboolean value)
{
    (*vmi->env)->SetBooleanField(vmi->env, object, field, value);
}

void
setCharField (vm_t* vmi, jobject object, jfieldID field, jchar value)
{
    (*vmi->env)->SetCharField(vmi->env, object, field, value);
}

void
setObjectField (vm_t* vmi, jobject object, jfieldID field, jobject value)
{
    (*vmi->env)->SetObjectField(vmi->env, object, field, value);
}

void
setStringField (vm_t* vmi, jobject object, jfieldID field, const char* value)
{
    jstring string = (*vmi->env)->NewStringUTF(vmi->env, value);
    (*vmi->env)->SetObjectField(vmi->env, object, field, string);
}

jvalue*
newJValues (int size)
{
    jvalue* arrayPointer = calloc (size, sizeof(jvalue));
    return arrayPointer;
}

void
setJValueBoolean (jvalue* array, int ix, jboolean val)
{
    (array + ix)->z = val;
}

void
setJValueInt (jvalue* array, int ix, jint val)
{
    (array + ix)->i = val;
}

void
setJValueLong (jvalue* array, int ix, jlong val)
{
    (array + ix)->j = val;
}

void
setJValueShort (jvalue* array, int ix, jshort val)
{
    (array + ix)->s = val;
}

void
setJValueByte (jvalue* array, int ix, jbyte val)
{
    (array + ix)->b = val;
}

void
setJValueFloat (jvalue* array, int ix, jfloat val)
{
    (array + ix)->f = val;
}

void
setJValueDouble (jvalue* array, int ix, jdouble val)
{
    (array + ix)->d = val;
}

void
setJValueChar (jvalue* array, int ix, jchar val)
{
    (array + ix)->c = val;
}

void
setJValueString (vm_t* vmi, jvalue* array, int ix, const char* string)
{
    jstring jstr = (*vmi->env)->NewStringUTF(vmi->env, string);
    (array + ix)->l = jstr;
}

void
setJValueObject (jvalue* array, int ix, jobject object)
{
    (array + ix)->l = object;
}

jarray
newBooleanArray (vm_t* vmi, jsize size)
{
    DEBUG1("newBooleanArray(%d)", size)

    return (*vmi->env)->NewBooleanArray(vmi->env, size);
}

jarray
newLongArray (vm_t* vmi, jsize size)
{
    DEBUG1("newLongArray(%d)", size)

    return (*vmi->env)->NewLongArray(vmi->env, size);
}

jarray
newIntArray (vm_t* vmi, jsize size)
{
    DEBUG1("newIntArray(%d)", size)

    return (*vmi->env)->NewIntArray(vmi->env, size);
}

jarray
newShortArray (vm_t* vmi, jsize size)
{
    DEBUG1("newShortArray(%d)", size)

    return (*vmi->env)->NewShortArray(vmi->env, size);
}

jarray
newByteArray (vm_t* vmi, jsize size)
{
    DEBUG1("newByteArray(%d)", size)

    return (*vmi->env)->NewByteArray(vmi->env, size);
}

jarray
newFloatArray (vm_t* vmi, jsize size)
{
    DEBUG1("newFloatArray(%d)", size)

    return (*vmi->env)->NewFloatArray(vmi->env, size);
}

jarray
newDoubleArray (vm_t* vmi, jsize size)
{
    DEBUG1("newDoubleArray(%d)", size)

    return (*vmi->env)->NewDoubleArray(vmi->env, size);
}

jarray
newCharArray (vm_t* vmi, jsize size)
{
    DEBUG1("newCharArray(%d)", size)

    return (*vmi->env)->NewCharArray(vmi->env, size);
}

jarray
newObjectArray (vm_t* vmi, jsize size, jclass elementType)
{
    DEBUG2("newObjectArray(%d, %p)", size, elementType)

    return (*vmi->env)->NewObjectArray(vmi->env, size, elementType, NULL);
}

jobject
newJString (vm_t* vmi, const char* string)
{
    DEBUG1("newJString(\"%s\")", string)

    return (*vmi->env)->NewStringUTF(vmi->env, string);
}

jint
getArrayLength (vm_t* vmi, jobject array)
{
    jint length = (*vmi->env)->GetArrayLength(vmi->env, array);
    DEBUG2("getArrayLength(%p) -> %i", array, length)
    return length;
}

jclass
getObjectClass (vm_t* vmi, jobject object)
{
    DEBUG1("getObjectClass(%p)", object)

    jobject objLocal = (*vmi->env)->GetObjectClass(vmi->env, object);
    GLOBAL_REF(vmi, objLocal, obj)
    return obj;
}

jboolean
isInstanceOf (vm_t* vmi, jobject object, jclass clazz)
{
    DEBUG2("isInstanceOf(%p, %p)", object, clazz)

    return (*vmi->env)->IsInstanceOf(vmi->env, object, clazz);
}

const jchar*
charsFromJString (vm_t* vmi, jobject string)
{
    DEBUG1("charsFromString(%p)", string)
    const jchar* chars = (*vmi->env)->GetStringChars(vmi->env, string, NULL);
    DEBUG1("-> %p", chars)
    return chars;
}

const char*
bytesFromJString (vm_t* vmi, jobject string)
{
    DEBUG1("bytesFromString(%p)", string)
    const char* bytes = (*vmi->env)->GetStringUTFChars(vmi->env, string, NULL);
    DEBUG1("-> %p", bytes)
    return bytes;
}

void
releaseJChars (vm_t* vmi, jobject string, const jchar* chars)
{
    DEBUG2("releaseJChars(%p, %p)", string, chars)
    (*vmi->env)->ReleaseStringChars(vmi->env, string, chars);
}

void
releaseJBytes (vm_t* vmi, jobject string, const char* bytes)
{
    DEBUG2("releaseJBytes(%p, %p)", string, bytes)
    (*vmi->env)->ReleaseStringUTFChars(vmi->env, string, bytes);
}

const char*
jStringToCString (vm_t* vmi, jobject string)
{
    if (string == NULL) {
        return NULL;
    }
    jsize length = (*vmi->env)->GetStringUTFLength(vmi->env, string);
    const char* bytes = (*vmi->env)->GetStringUTFChars(vmi->env, string, NULL);
    char* cstring = malloc(length + 1);
    memcpy(cstring, bytes, length);
    cstring[length] = '\0';
    (*vmi->env)->ReleaseStringUTFChars(vmi->env, string, bytes);
    return cstring;
}

jboolean
exceptionCheck (vm_t* vmi)
{
    return (*vmi->env)->ExceptionCheck(vmi->env);
}

void
exceptionClear (vm_t* vmi)
{
    (*vmi->env)->ExceptionClear(vmi->env);
}

void
exceptionDescribe (vm_t* vmi)
{
    (*vmi->env)->ExceptionDescribe(vmi->env);
}

jthrowable
exceptionOccurred (vm_t* vmi)
{
    jthrowable exc = (*vmi->env)->ExceptionOccurred(vmi->env);
    DEBUG1("exceptionOccurred() -> %p", exc)
    return exc;
}

jthrowable
exceptionOccurredClear (vm_t* vmi)
{
    jthrowable excLocal = (*vmi->env)->ExceptionOccurred(vmi->env);
    DEBUG1("exceptionOccurredClear() -> %p", excLocal)
    if (excLocal) {
        (*vmi->env)->ExceptionClear(vmi->env);
        GLOBAL_REF(vmi, excLocal, exc)
        return exc;
    } else {
        return NULL;
    }
}

void
releaseJObjectRef (vm_t* vmi, jobject obj)
{
    (*vmi->env)->DeleteGlobalRef(vmi->env, obj);
}

void
releaseJClassRef (vm_t* vmi, jclass obj)
{
    (*vmi->env)->DeleteGlobalRef(vmi->env, obj);
}

void
releaseJThrowableRef (vm_t* vmi, jthrowable obj)
{
    (*vmi->env)->DeleteGlobalRef(vmi->env, obj);
}

void
release (jobject obj)
{
    JavaVM* jvm;
    jsize numCreatedVMs = 0;

    GET_CREATED_JAVA_VMS(&jvm, 1, &numCreatedVMs);
    if (numCreatedVMs == 1) {
        JNIEnv* env;
        if ((*jvm)->GetEnv(jvm, (void**) &env, FFIJNI_VERSION) == JNI_OK) {
            DEBUG1("release(%p)\n", (void*) obj)
            (*env)->DeleteGlobalRef(env, obj);
        }
    } else if (numCreatedVMs > 1) {
        fprintf(stderr, "THE IMPOSSIBLE HAPPENED - This should not have happend.\n"
                        "Somehow more than one JVM was created.\n"
                        "Please report this as a bug.\n");
        fflush(stderr);
    } else {
        DEBUG1("release(%p) - already shut down", (void*) obj)
        /* This is not an error, since the virtual machine
         * has already been teared down and no objects are
         * alive anymore - therefor nothing has to be freed
         * anymore (nor can it be).
         */
    }
}