#include #include #include "mlp_java_priv.h" JNIEnv *get_env(mlp_context *ctx, mlp_java_runtime *jrt){ JNIEnv *env ; jint rc = (*(jrt->jvm))->AttachCurrentThread(jrt->jvm, ((void **)&env), NULL) ; if (rc < 0){ mlp_context_set_error(ctx, MLP_RUNTIME_ERROR, "Can't attach current thread to Java VM") ; return NULL ; } return env ; } /* If an exception if pending, extract the class name and message and setup * the context error. */ char process_exception(mlp_context *ctx, mlp_java_runtime *jrt, JNIEnv *env, mlp_error err, const char *fmt, ...){ if ((*env)->ExceptionCheck(env)){ jthrowable e = (*env)->ExceptionOccurred(env) ; if (mlp_debug_on(1)){ (*env)->ExceptionDescribe(env) ; } (*env)->ExceptionClear(env) ; va_list va ; va_start(va, fmt) ; char imsg[MLP_MAX_ERROR_MSG_LENGTH] ; vsnprintf(imsg, MLP_MAX_ERROR_MSG_LENGTH - 1, fmt, va) ; va_end(va) ; jclass eclass = (*env)->GetObjectClass(env, e) ; jstring cname = (*env)->CallObjectMethod(env, eclass, jrt->class_getName) ; if ((*(env))->ExceptionCheck(env)){ mlp_context_set_error(ctx, err, "%s:\n (Can't call getName() method of jclass object)", imsg) ; (*env)->DeleteLocalRef(env, eclass) ; (*env)->DeleteLocalRef(env, e) ; return 1 ; } jstring msg = (*env)->CallObjectMethod(env, e, jrt->throwable_getMessage) ; if ((*(env))->ExceptionCheck(env)){ mlp_context_set_error(ctx, err, "%s:\n (Can't call getMessage() method on jthrowable object)", imsg) ; (*env)->DeleteLocalRef(env, eclass) ; (*env)->DeleteLocalRef(env, e) ; return 1 ; } const char *cnamestr = (const char *)((*env)->GetStringUTFChars(env, cname, NULL)) ; const char *msgstr = NULL ; if (msg != NULL){ msgstr = (const char *)((*env)->GetStringUTFChars(env, msg, NULL)) ; } mlp_context_set_error(ctx, err, "%s:\n Exception (%s): %s", imsg, cnamestr, msgstr) ; if (msg != NULL){ (*env)->ReleaseStringUTFChars(env, msg, msgstr) ; } (*env)->ReleaseStringUTFChars(env, cname, cnamestr) ; (*env)->DeleteLocalRef(env, cname) ; (*env)->DeleteLocalRef(env, msg) ; (*env)->DeleteLocalRef(env, e) ; return 1 ; } return 0 ; } /* Use this to check for exceptions without recuperating anything from the exception. */ static char check_exception(JNIEnv *env){ if ((*(env))->ExceptionCheck(env)){ (*(env))->ExceptionClear(env) ; return 1 ; } return 0 ; } static jclass find_class(mlp_context *ctx, JNIEnv *env, const char *class){ char *mclass = strdup(class) ; int i = 0, l = strlen(mclass) ; for (; i < l ; i++){ if (mclass[i] == '.'){ mclass[i] = '/' ; } } jclass cl = (*env)->FindClass(env, mclass) ; free(mclass) ; if (check_exception(env)){ mlp_context_set_error(ctx, MLP_RUNTIME_ERROR, "Can't find base Java class '%s'", class) ; return NULL ; } return cl ; } /* Used to load classes mlp works with */ jclass load_class(mlp_context *ctx, JNIEnv *env, const char *class){ jclass cl = find_class(ctx, env, class) ; RETURN_NULL_IF_NULL(cl) ; jclass c = (*(env))->NewGlobalRef(env, cl) ; (*env)->DeleteLocalRef(env, cl) ; return c ; } jmethodID get_method(mlp_context *ctx, JNIEnv *env, const char *cname, jclass class, const char *name, const char *proto){ jmethodID m = (*(env))->GetMethodID(env, class, name, proto) ; if (check_exception(env)){ mlp_context_set_error(ctx, MLP_RUNTIME_ERROR, "Can't get method '%s%s' for Java class %s", name, proto, cname) ; return NULL ; } return m ; } jmethodID get_static_method(mlp_context *ctx, JNIEnv *env, const char *cname, jclass class, const char *name, const char *proto){ jmethodID m = (*(env))->GetStaticMethodID(env, class, name, proto) ; if (check_exception(env)){ mlp_context_set_error(ctx, MLP_RUNTIME_ERROR, "Can't get method '%s%s' for Java class %s", name, proto, cname) ; return NULL ; } return m ; } jstring jstring_new(mlp_context *ctx, JNIEnv *env, const char *s){ jstring js = (*(env))->NewStringUTF(env, s) ; if (check_exception(env)){ mlp_context_set_error(ctx, MLP_RUNTIME_ERROR, "Can't create 'java.lang.String'") ; return NULL ; } return js ; } void jstring_delete(JNIEnv *env, jstring js){ (*(env))->DeleteLocalRef(env, js) ; } /* Make a Java MLPValue from a C mlp_value* */ jobject java_MLPValue_new(mlp_context *ctx, mlp_runtime *rt, JNIEnv *env, mlp_value *v){ mlp_java_runtime *jrt = (mlp_java_runtime *)rt->runtime ; mlp_type type = mlp_value_get_type(v) ; jobject vo = NULL ; mlp_debug(1, "type=%d", type) ; switch(type){ case MLP_BOOL: vo = (*env)->NewObject(env, jrt->value, jrt->value_new_bool, (jboolean)mlp_value_get_bool(v)) ; break ; case MLP_LONG: vo = (*env)->NewObject(env, jrt->value, jrt->value_new_long, (jlong)mlp_value_get_long(v)) ; break ; case MLP_DOUBLE: vo = (*env)->NewObject(env, jrt->value, jrt->value_new_double, (jdouble)mlp_value_get_double(v)) ; break ; case MLP_CHAR: vo = (*env)->NewObject(env, jrt->value, jrt->value_new_char, (jchar)mlp_value_get_char(v)) ; break ; case MLP_STRING: { jstring jvs = NULL ; const char *vs = mlp_value_get_string(v) ; if (vs != NULL){ jvs = jstring_new(ctx, env, vs) ; RETURN_NULL_IF_NULL(jvs) ; } vo = (*env)->NewObject(env, jrt->value, jrt->value_new_string, jvs) ; if (jvs != NULL){ jstring_delete(env, jvs) ; } break ; } case MLP_OBJECT: { mlp_object *o = mlp_value_get_object(v) ; mlp_debug(1, "o=%s", mlp_object_to_string(o)) ; jobject jo = java_MLPJavaObject_new(ctx, rt, env, o) ; RETURN_NULL_IF_NULL(jo) ; vo = (*env)->NewObject(env, jrt->value, jrt->value_new_object, jo) ; break ; } } if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't create 'mlp.MLPValue'")){ return NULL ; } return vo ; } void java_MLPValue_delete(JNIEnv *env, jobject arg){ (*env)->DeleteLocalRef(env, arg) ; } /* Allocates an array and fills it with the mlp_values */ jobjectArray java_MLPValue_array_new(mlp_context *ctx, mlp_runtime *rt, JNIEnv *env, mlp_value **args, int nb_args){ mlp_java_runtime *jrt = (mlp_java_runtime *)rt->runtime ; jobjectArray vargs = (*(env))->NewObjectArray(env, nb_args, jrt->value, NULL) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't create Java array of 'mlp.MLPValue'")){ return NULL ; } int i ; for (i = 0 ; i < nb_args ; i++){ jobject v = java_MLPValue_new(ctx, rt, env, args[i]) ; RETURN_NULL_IF_NULL(v) ; (*env)->SetObjectArrayElement(env, vargs, i, v) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't set Java array element of type 'mlp.MLPValue'")){ return NULL ; } } return vargs ; } void java_MLPValue_array_delete(JNIEnv *env, jobjectArray vargs, int nb_args){ int i ; for (i = 0 ; i < nb_args ; i++){ jobject v = (*env)->GetObjectArrayElement(env, vargs, i) ; java_MLPValue_delete(env, v) ; } (*env)->DeleteLocalRef(env, vargs) ; } /* Make a C mlp_value* from a Java MLPValue */ mlp_value *mlp_value_new(mlp_context *ctx, mlp_runtime *rt, JNIEnv *env, jobject v){ mlp_java_runtime *jrt = (mlp_java_runtime *)rt->runtime ; jint type = (*env)->CallIntMethod(env, v, jrt->value_getTypeNo) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getTypeNo() method of class 'mlp.MLPValue'")){ return NULL ; } mlp_value *val = NULL ; switch(type){ case MLP_BOOL: { jboolean b = (*env)->CallBooleanMethod(env, v, jrt->value_getBool) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getBool() method of class 'mlp.MLPValue'")){ return NULL ; } val = mlp_value_new_bool((char)b) ; break ; } case MLP_LONG: { jlong l = (*env)->CallLongMethod(env, v, jrt->value_getLong) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getLong() method of class 'mlp.MLPValue'")){ return NULL ; } val = mlp_value_new_long((long)l) ; break ; } case MLP_DOUBLE: { jdouble d = (*env)->CallDoubleMethod(env, v, jrt->value_getDouble) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getDouble() method of class 'mlp.MLPValue'")){ return NULL ; } val = mlp_value_new_double((double)d) ; break ; } case MLP_CHAR: { jchar c = (*env)->CallCharMethod(env, v, jrt->value_getChar) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getChar() method of class 'mlp.MLPValue'")){ return NULL ; } val = mlp_value_new_char((char)c) ; break ; } case MLP_STRING: { jstring s = (*env)->CallObjectMethod(env, v, jrt->value_getString) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getString() method of class 'mlp.MLPValue'")){ return NULL ; } const char *str = NULL ; if (s != NULL){ str = (const char *)((*env)->GetStringUTFChars(env, s, NULL)) ; } val = mlp_value_new_string(str) ; if (s != NULL){ (*env)->ReleaseStringUTFChars(env, s, str) ; } (*env)->DeleteLocalRef(env, s) ; break ; } case MLP_OBJECT: { jobject mo = (*env)->CallObjectMethod(env, v, jrt->value_getObject) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getObject() method of class 'mlp.MLPValue'")){ return NULL ; } mlp_debug(1, "mo: %p", mo) ; if (mo != NULL){ if ((*env)->IsInstanceOf(env, mo, jrt->java_object)){ jobject o = (*env)->CallObjectMethod(env, mo, jrt->java_object_getObject) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getObject() method of class 'mlp.java.MLPJavaObject'")){ (*env)->DeleteLocalRef(env, mo) ; return NULL ; } jstring cname = (*env)->CallObjectMethod(env, mo, jrt->java_object_getClassName) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getClassName() method of class 'mlp.java.MLPJavaObject'")){ (*env)->DeleteLocalRef(env, o) ; (*env)->DeleteLocalRef(env, mo) ; return NULL ; } const char *str = (const char *)((*env)->GetStringUTFChars(env, cname, NULL)) ; jobject go = (*env)->NewGlobalRef(env, o) ; mlp_object *obj = mlp_object_alloc(rt, str, (void *)go) ; (*env)->ReleaseStringUTFChars(env, cname, str) ; (*env)->DeleteLocalRef(env, cname) ; (*env)->DeleteLocalRef(env, o) ; (*env)->DeleteLocalRef(env, mo) ; val = mlp_value_new_object(obj) ; } else { /* TODO: Create mlp_object from mo, which is a MLPObject */ mlp_context_set_error(ctx, MLP_NOT_IMPLEMENTED, "Return of foreign object from Java") ; return NULL ; } } else { val = mlp_value_new_object(NULL) ; } break ; } case MLP_EXCEPTION: { jobject me = (*env)->CallObjectMethod(env, v, jrt->value_getException) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getException() method of class 'mlp.MLPValue'")){ return NULL ; } if ((*env)->IsInstanceOf(env, me, jrt->java_exception)){ jobject jv = (*env)->CallObjectMethod(env, me, jrt->java_exception_getValue) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getValue() method of class 'mlp.java.MLPJavaException'")){ (*env)->DeleteLocalRef(env, me) ; return NULL ; } jstring msg = (*env)->CallObjectMethod(env, me, jrt->java_exception_getMessage) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't call getMessage() method of class 'mlp.java.MLPJavaException'")){ (*env)->DeleteLocalRef(env, jv) ; (*env)->DeleteLocalRef(env, me) ; return NULL ; } const char *str = (const char *)((*env)->GetStringUTFChars(env, msg, NULL)) ; mlp_value *v = mlp_value_new(ctx, rt, env, jv) ; if (v == NULL){ (*env)->ReleaseStringUTFChars(env, msg, str) ; (*env)->DeleteLocalRef(env, msg) ; (*env)->DeleteLocalRef(env, jv) ; (*env)->DeleteLocalRef(env, me) ; return NULL ; } mlp_exception *e = mlp_exception_alloc(rt, str, v) ; (*env)->ReleaseStringUTFChars(env, msg, str) ; (*env)->DeleteLocalRef(env, msg) ; (*env)->DeleteLocalRef(env, jv) ; (*env)->DeleteLocalRef(env, me) ; val = mlp_value_new_exception(e) ; } else { /* TODO: Create mlp_exception from me, which is a MLPException */ mlp_context_set_error(ctx, MLP_NOT_IMPLEMENTED, "Throw of foreign exception from Java") ; return NULL ; } (*env)->DeleteLocalRef(env, me) ; break ; } } mlp_debug(2, "mlp_value: %s", mlp_value_to_string(val)) ; return val ; } jobject java_MLPJavaObject_new(mlp_context *ctx, mlp_runtime *rt, JNIEnv *env, mlp_object *obj){ mlp_java_runtime *jrt = (mlp_java_runtime *)rt->runtime ; if (rt != mlp_object_get_runtime(obj)){ mlp_context_set_error(ctx, MLP_RUNTIME_ERROR, "Runtime mismatch (can't pass a %s object as a Java object)!", mlp_language_get_name(mlp_runtime_get_language(rt))) ; return NULL ; } const char *class = mlp_object_get_class(obj) ; void *handle = mlp_object_get_handle(obj) ; jclass c = find_class(ctx, env, class) ; RETURN_NULL_IF_NULL(c) ; jobject o = (*env)->NewObject(env, jrt->java_object, jrt->java_object_new, (jobject)handle, c) ; if (process_exception(ctx, jrt, env, MLP_RUNTIME_ERROR, "Can't create object of class 'mlp.java.MLPJavaObject'")){ return NULL ; } return o ; }