From dd8ca693536657bf0f66ae1a51b3e2e84588d85a Mon Sep 17 00:00:00 2001 From: Joshua Granick Date: Tue, 11 Aug 2015 15:39:41 -0700 Subject: [PATCH] Port JNI class from Lime legacy --- LICENSE.md | 1 + lime/system/JNI.hx | 371 +++ lime/utils/JNI.hx | 33 - project/Build.xml | 2 +- project/include/{utils => system}/JNI.h | 4 +- project/include/utils/Object.h | 25 +- project/src/ExternalInterface.cpp | 2 +- project/src/backend/sdl/SDLJNI.cpp | 21 - project/src/backend/sdl/SDLSystem.cpp | 12 + project/src/system/JNI.cpp | 2099 +++++++++++++++++ .../src/org/haxe/lime/GameActivity.java | 15 + 11 files changed, 2523 insertions(+), 62 deletions(-) create mode 100644 lime/system/JNI.hx delete mode 100644 lime/utils/JNI.hx rename project/include/{utils => system}/JNI.h (63%) delete mode 100644 project/src/backend/sdl/SDLJNI.cpp create mode 100644 project/src/system/JNI.cpp diff --git a/LICENSE.md b/LICENSE.md index 422b2f51e..5ff75c9f6 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -85,6 +85,7 @@ https://github.com/haxenme/nme project/include/utils/QuickVec.h project/src/graphics/format/ project/src/graphics/opengl/ + project/src/system/JNI.cpp project/src/text/Font.cpp project/src/utils/ByteArray.cpp project/src/utils/LZMA.cpp diff --git a/lime/system/JNI.hx b/lime/system/JNI.hx new file mode 100644 index 000000000..3e135964f --- /dev/null +++ b/lime/system/JNI.hx @@ -0,0 +1,371 @@ +package lime.system; + + +class JNI { + + + private static var alreadyCreated = new Map (); + private static var initialized = false; + + + public static function callMember (method:Dynamic, jobject:Dynamic, a:Array):Dynamic { + + switch (a.length) { + + case 0: return method (jobject); + case 1: return method (jobject, a[0]); + case 2: return method (jobject, a[0], a[1]); + case 3: return method (jobject, a[0], a[1], a[2]); + case 4: return method (jobject, a[0], a[1], a[2], a[3]); + case 5: return method (jobject, a[0], a[1], a[2], a[3], a[4]); + case 6: return method (jobject, a[0], a[1], a[2], a[3], a[4], a[5]); + case 7: return method (jobject, a[0], a[1], a[2], a[3], a[4], a[5], a[6]); + default: return null; + + } + + } + + + public static function callStatic (method:Dynamic, a:Array):Dynamic { + + switch (a.length) { + + case 0: return method (); + case 1: return method (a[0]); + case 2: return method (a[0], a[1]); + case 3: return method (a[0], a[1], a[2]); + case 4: return method (a[0], a[1], a[2], a[3]); + case 5: return method (a[0], a[1], a[2], a[3], a[4]); + case 6: return method (a[0], a[1], a[2], a[3], a[4], a[5]); + case 7: return method (a[0], a[1], a[2], a[3], a[4], a[5], a[6]); + default: return null; + + } + + } + + + public static function createMemberField (className:String, memberName:String, signature:String):JNIMemberField { + + init (); + + #if android + return new JNIMemberField (lime_jni_create_field (className, memberName, signature, false)); + #else + return null; + #end + + } + + + public static function createMemberMethod (className:String, memberName:String, signature:String, useArray:Bool = false, quietFail:Bool = false):Dynamic { + + init (); + + #if android + className = className.split (".").join ("/"); + var handle = lime_jni_create_method (className, memberName, signature, false, quietFail); + + if (handle == null) { + + if (quietFail) { + + return null; + + } + + throw "Could not find member function \"" + memberName + "\""; + + } + + var method = new JNIMethod (handle); + return method.getMemberMethod (useArray); + #else + return null; + #end + + } + + + public static function createStaticField (className:String, memberName:String, signature:String):JNIStaticField { + + init (); + + #if android + return new JNIStaticField (lime_jni_create_field (className, memberName, signature, true)); + #else + return null; + #end + + } + + + public static function createStaticMethod (className:String, memberName:String, signature:String, useArray:Bool = false, quietFail:Bool = false):Dynamic { + + init (); + + #if android + className = className.split (".").join ("/"); + var handle = lime_jni_create_method (className, memberName, signature, true, quietFail); + + if (handle == null) { + + if (quietFail) { + + return null; + + } + + throw "Could not find static function \"" + memberName + "\""; + + } + + var method = new JNIMethod (handle); + return method.getStaticMethod (useArray); + #else + return null; + #end + + } + + + public static function getEnv ():Dynamic { + + init (); + + return lime_jni_get_env (); + + } + + + private static function init ():Void { + + if (!initialized) { + + initialized = true; + + #if android + var method = System.load ("lime", "lime_jni_init_callback", 1); + method (onCallback); + #end + + } + + } + + + private static function onCallback (object:Dynamic, method:Dynamic, args:Dynamic):Dynamic { + + var field = Reflect.field (object, method); + + if (field != null) { + + return Reflect.callMethod (object, field, args); + + } + + trace ("onCallback - unknown field " + method); + return null; + + } + + + + + // Native Methods + + + + + #if (cpp || neko || nodejs) + private static var lime_jni_create_field = System.load ("lime", "lime_jni_create_field", 4); + private static var lime_jni_create_method = System.load ("lime", "lime_jni_create_method", 5); + private static var lime_jni_get_env = System.load ("lime", "lime_jni_get_env", 0); + private static var lime_jni_call_member = System.load ("lime", "lime_jni_call_member", 3); + private static var lime_jni_call_static = System.load ("lime", "lime_jni_call_static", 2); + #end + + +} + + +class JNIMemberField { + + + private var field:Dynamic; + + + public function new (field:Dynamic) { + + this.field = field; + + } + + + public function get (jobject:Dynamic):Dynamic { + + #if android + return lime_jni_get_member (field, jobject); + #else + return null; + #end + + } + + + public function set (jobject:Dynamic, value:Dynamic):Dynamic { + + #if android + lime_jni_set_member (field, jobject, value); + #end + return value; + + } + + + + + // Native Methods + + + + + #if (cpp || neko || nodejs) + private static var lime_jni_get_member = System.load ("lime", "lime_jni_get_member", 2); + private static var lime_jni_set_member = System.load ("lime", "lime_jni_set_member", 3); + #end + + +} + + +class JNIStaticField { + + + private var field:Dynamic; + + + public function new (field:Dynamic) { + + this.field = field; + + } + + + public function get ():Dynamic { + + #if android + return lime_jni_get_static (field); + #else + return null; + #end + + } + + + public function set (value:Dynamic):Dynamic { + + #if android + lime_jni_set_static (field, value); + #end + return value; + + } + + + + + // Native Methods + + + + + #if (cpp || neko || nodejs) + private static var lime_jni_get_static = System.load ("lime", "lime_jni_get_static", 1); + private static var lime_jni_set_static = System.load ("lime", "lime_jni_set_static", 2); + #end + + +} + + +class JNIMethod { + + + private var method:Dynamic; + + + public function new (method:Dynamic) { + + this.method = method; + + } + + public function callMember (args:Array):Dynamic { + + #if android + var jobject = args.shift (); + return lime_jni_call_member (method, jobject, args); + #else + return null; + #end + + } + + + public function callStatic (args:Array):Dynamic { + + #if android + return lime_jni_call_static (method, args); + #else + return null; + #end + + } + + + public function getMemberMethod (useArray:Bool):Dynamic { + + if (useArray) { + + return callMember; + + } else { + + return Reflect.makeVarArgs (callMember); + + } + + } + + + public function getStaticMethod (useArray:Bool):Dynamic { + + if (useArray) { + + return callStatic; + + } else { + + return Reflect.makeVarArgs (callStatic); + + } + + } + + + + + // Native Methods + + + + + #if (cpp || neko || nodejs) + private static var lime_jni_call_member = System.load ("lime", "lime_jni_call_member", 3); + private static var lime_jni_call_static = System.load ("lime", "lime_jni_call_static", 2); + #end + + +} \ No newline at end of file diff --git a/lime/utils/JNI.hx b/lime/utils/JNI.hx deleted file mode 100644 index ec2aa92ba..000000000 --- a/lime/utils/JNI.hx +++ /dev/null @@ -1,33 +0,0 @@ -package lime.utils; - - -import lime.system.System; - - -class JNI { - - - public static function getEnv ():Dynamic { - - #if android - return lime_jni_getenv (); - #else - return null; - #end - - } - - - - - // Native Methods - - - - - #if (cpp || neko || nodejs) - private static var lime_jni_getenv = System.load ("lime", "lime_jni_getenv", 0); - #end - - -} \ No newline at end of file diff --git a/project/Build.xml b/project/Build.xml index de7e4bbb9..dd95cab1a 100644 --- a/project/Build.xml +++ b/project/Build.xml @@ -180,7 +180,6 @@ - @@ -203,6 +202,7 @@ + diff --git a/project/include/utils/JNI.h b/project/include/system/JNI.h similarity index 63% rename from project/include/utils/JNI.h rename to project/include/system/JNI.h index 60a232108..39ceb0de9 100644 --- a/project/include/utils/JNI.h +++ b/project/include/system/JNI.h @@ -1,5 +1,5 @@ -#ifndef LIME_UTILS_JNI_H -#define LIME_UTILS_JNI_H +#ifndef LIME_SYSTEM_JNI_H +#define LIME_SYSTEM_JNI_H namespace lime { diff --git a/project/include/utils/Object.h b/project/include/utils/Object.h index 709ce21c1..350a30b8a 100644 --- a/project/include/utils/Object.h +++ b/project/include/utils/Object.h @@ -8,10 +8,6 @@ namespace lime { class Object { - public: - - int ref_count; - protected: virtual ~Object () {} @@ -28,6 +24,25 @@ namespace lime { } + Object *IncRef () { + + ref_count++; + return this; + + } + + void DecRef () { + + ref_count--; + + if (ref_count <= 0) { + + delete this; + + } + + } + void drop () { ref_count--; @@ -37,6 +52,8 @@ namespace lime { } } + + int ref_count; }; diff --git a/project/src/ExternalInterface.cpp b/project/src/ExternalInterface.cpp index 9b102f6ca..e7ae85221 100644 --- a/project/src/ExternalInterface.cpp +++ b/project/src/ExternalInterface.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,6 @@ #include #include #include -#include #include #include diff --git a/project/src/backend/sdl/SDLJNI.cpp b/project/src/backend/sdl/SDLJNI.cpp deleted file mode 100644 index aa18bd649..000000000 --- a/project/src/backend/sdl/SDLJNI.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifdef ANDROID - - -#include -#include - - -namespace lime { - - - void *JNI::GetEnv () { - - return SDL_AndroidGetJNIEnv (); - - } - - -} - - -#endif \ No newline at end of file diff --git a/project/src/backend/sdl/SDLSystem.cpp b/project/src/backend/sdl/SDLSystem.cpp index 842791c6a..d19c2690b 100644 --- a/project/src/backend/sdl/SDLSystem.cpp +++ b/project/src/backend/sdl/SDLSystem.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #ifdef HX_MACOS @@ -46,6 +47,17 @@ namespace lime { static bool init = false; + void *JNI::GetEnv () { + + #ifdef ANDROID + return SDL_AndroidGetJNIEnv (); + #else + return 0; + #endif + + } + + const char* System::GetDirectory (SystemDirectory type, const char* company, const char* title) { switch (type) { diff --git a/project/src/system/JNI.cpp b/project/src/system/JNI.cpp new file mode 100644 index 000000000..662fc452b --- /dev/null +++ b/project/src/system/JNI.cpp @@ -0,0 +1,2099 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ELOG(args...) __android_log_print (ANDROID_LOG_ERROR, "Lime", args) + +#ifdef __GNUC__ +#define JAVA_EXPORT __attribute__ ((visibility("default"))) JNIEXPORT +#else +#define JAVA_EXPORT JNIEXPORT +#endif + + +namespace lime { + + + vkind gObjectKind; + + + inline void release_object (value inValue) { + + if (val_is_kind (inValue, gObjectKind)) { + + Object* obj = (Object*)val_to_kind (inValue, gObjectKind); + + if (obj) { + + obj->DecRef (); + + } + + } + + } + + + inline value ObjectToAbstract (Object *inObject) { + + inObject->IncRef (); + value result = alloc_abstract (gObjectKind, inObject); + val_gc (result, release_object); + return result; + + } + + + template + bool AbstractToObject (value inValue, OBJ *&outObj) { + + outObj = 0; + + if (!val_is_kind (inValue, gObjectKind)) { + + return false; + + } + + Object* obj = (Object*)val_to_kind (inValue, gObjectKind); + outObj = dynamic_cast (obj); + return outObj; + + } + + + void CheckException (JNIEnv *env, bool inThrow = true) { + + jthrowable exc = env->ExceptionOccurred (); + + if (exc) { + + env->ExceptionDescribe (); + env->ExceptionClear (); + + if (inThrow) { + + val_throw (alloc_string ("JNI Exception")); + + } + + } + + } + + + std::map jClassCache; + + + jclass FindClass (const char *className, bool inQuiet = false) { + + std::string cppClassName (className); + jclass ret; + + if (jClassCache[cppClassName] != NULL) { + + ret = jClassCache[cppClassName]; + + } else { + + JNIEnv *env = (JNIEnv*)JNI::GetEnv (); + jclass tmp = env->FindClass (className); + + if (!tmp) { + + if (inQuiet) { + + jthrowable exc = env->ExceptionOccurred (); + + if (exc) { + + env->ExceptionClear (); + + } + + } else { + + CheckException (env); + + } + + return 0; + + } + + ret = (jclass)env->NewGlobalRef (tmp); + jClassCache[cppClassName] = ret; + env->DeleteLocalRef (tmp); + + } + + return ret; + + } + + + struct AutoHaxe { + + + int base; + const char *message; + + + AutoHaxe (const char *inMessage) { + + base = 0; + message = inMessage; + gc_set_top_of_stack (&base, true); + //__android_log_print (ANDROID_LOG_VERBOSE, "Lime", "Enter %s %p", message, pthread_self ()); + + } + + + ~AutoHaxe () { + + //__android_log_print (ANDROID_LOG_VERBOSE, "Lime", "Leave %s %p", message, pthread_self ()); + gc_set_top_of_stack (0, true); + + } + + + }; + + + static bool sInit = false; + jclass GameActivity; + jclass ObjectClass; + jmethodID postUICallback; + jmethodID isArrayClass; + jclass HaxeObject; + jclass ValueObject; + jmethodID HaxeObject_create; + jfieldID __haxeHandle; + + + enum JNIElement { + + jniUnknown, + jniObjectString, + jniObjectHaxe, + jniValueObject, + jniObject, + jniPODStart, + jniBoolean = jniPODStart, + jniByte, + jniChar, + jniShort, + jniInt, + jniLong, + jniFloat, + jniDouble, + jniVoid, + jniELEMENTS + + }; + + + std::string ClassNameOf (JNIEnv *inEnv, jclass inObject) { + + if (inObject == 0) { + + return "NULL"; + + } else { + + jclass classClass = FindClass ("java/lang/Class"); + jmethodID mid_getName = inEnv->GetMethodID (classClass, "getName", "()Ljava/lang/String;"); + jstring name = (jstring)inEnv->CallObjectMethod (inObject, mid_getName); + jthrowable exc = inEnv->ExceptionOccurred (); + + if (exc) { + + inEnv->ExceptionClear (); + + } + + jboolean is_copy; + const char *utf8 = inEnv->GetStringUTFChars (name, &is_copy); + std::string result = utf8; + inEnv->ReleaseStringUTFChars (name, utf8); + inEnv->DeleteLocalRef (name); + return result; + + } + + } + + + std::string ClassOf (JNIEnv *inEnv, jobject inObject) { + + if (inObject == 0) { + + return "NULL"; + + } else { + + jclass cls = inEnv->GetObjectClass (inObject); + return ClassNameOf (inEnv, cls); + + } + + } + + + struct JNIType { + + + typedef std::map ClassMap; + + JNIType () : element (jniUnknown), arrayDepth (0) { } + JNIType (JNIElement inElem, int inDepth) : element (inElem), arrayDepth (inDepth) { } + JNIType elemType () { return (arrayDepth > 0) ? JNIType (element, arrayDepth - 1) : JNIType (); } + + + bool isUnknownType () const { return element == jniUnknown && arrayDepth == 0; } + bool isUnknown () const { return element == jniUnknown; } + bool isObject () const { return element < jniPODStart || arrayDepth > 0; } + + bool operator < (const JNIType &inRHS) const { + + if (arrayDepth!=inRHS.arrayDepth) { + + return arrayDepth 1 || (arrayDepth == 1 && element < jniPODStart)) { + + return ObjectClass; + + } + + ClassMap::iterator i = mClasses.find (*this); + + if (i != mClasses.end ()) { + + return i->second; + + } + + std::string name (arrayDepth ? "[" : ""); + + switch (element) { + + case jniObjectString: name += "java/lang/String"; break; + case jniObjectHaxe: name += "org/haxe/lime/HaxeObject"; break; + case jniValueObject: name += "org/haxe/lime/Value"; break; + + case jniUnknown: + case jniObject: name += "java/lang/Object"; break; + + case jniBoolean: name += "Z"; break; + case jniVoid: name += "V"; break; + case jniByte: name += "B"; break; + case jniChar: name += "C"; break; + case jniShort: name += "S"; break; + case jniInt: name += "I"; break; + case jniLong: name += "J"; break; + case jniFloat: name += "F"; break; + case jniDouble: name += "D"; break; + + default: + mClasses[*this] = 0; + return 0; + + } + + jclass result = inEnv->FindClass (name.c_str ()); + + if (result) { + + mClasses[*this] = (jclass)inEnv->NewGlobalRef (result); + inEnv->DeleteLocalRef (result); + + } + + return mClasses[*this]; + + } + + + static void init (JNIEnv *inEnv) { + + for (int i = 0; i < jniELEMENTS; i++) { + + elementGetValue[i] = 0; + + } + + elementClass[jniBoolean] = FindClass ("java/lang/Boolean"); + elementGetValue[jniBoolean] = inEnv->GetMethodID (elementClass[jniBoolean], "booleanValue", "()Z"); + CheckException (inEnv, false); + + elementClass[jniByte] = FindClass ("java/lang/Byte"); + elementGetValue[jniByte] = inEnv->GetMethodID (elementClass[jniByte], "doubleValue", "()D"); + CheckException (inEnv, false); + + elementClass[jniChar] = FindClass ("java/lang/Character"); + elementGetValue[jniChar] = inEnv->GetMethodID (elementClass[jniChar], "charValue", "()C"); + CheckException (inEnv, false); + + elementClass[jniShort] = FindClass ("java/lang/Short"); + elementGetValue[jniShort] = inEnv->GetMethodID (elementClass[jniShort], "doubleValue", "()D"); + CheckException (inEnv, false); + + elementClass[jniInt] = FindClass ("java/lang/Integer"); + elementGetValue[jniInt] = inEnv->GetMethodID (elementClass[jniInt], "doubleValue", "()D"); + CheckException (inEnv, false); + + elementClass[jniLong] = FindClass ("java/lang/Long"); + elementGetValue[jniLong] = inEnv->GetMethodID (elementClass[jniLong], "doubleValue", "()D"); + CheckException (inEnv, false); + + elementClass[jniFloat] = FindClass ("java/lang/Float"); + elementGetValue[jniFloat] = inEnv->GetMethodID (elementClass[jniFloat], "doubleValue", "()D"); + CheckException (inEnv, false); + + elementClass[jniDouble] = FindClass ("java/lang/Double"); + elementGetValue[jniDouble] = inEnv->GetMethodID (elementClass[jniDouble], "doubleValue", "()D"); + CheckException (inEnv, false); + elementClass[jniVoid] = 0; + + for (int i = 0; i < jniELEMENTS; i++) { + + JNIType type ((JNIElement)i, 1); + + if (i == jniVoid) { + + elementArrayClass[i] = 0; + + } else { + + elementArrayClass[i] = type.getClass (inEnv); + + } + + if (i < jniPODStart) { + + elementClass[i] = JNIType ((JNIElement)i, 0).getClass (inEnv); + + } else { + + //ELOG("POD type %d = %p", i, elementClass[i]); + + } + + CheckException (inEnv, false); + + } + + elementGetValue[jniValueObject] = inEnv->GetMethodID (elementClass[jniValueObject], "getDouble", "()D"); + + } + + + JNIElement element; + static jclass elementClass[jniELEMENTS]; + static jclass elementArrayClass[jniELEMENTS]; + static jmethodID elementGetValue[jniELEMENTS]; + int arrayDepth; + static ClassMap mClasses; + + + }; + + + JNIType::ClassMap JNIType::mClasses; + //JNIType JNIType::elementClass[jniELEMENTS]; + jclass JNIType::elementClass[jniELEMENTS]; + jmethodID JNIType::elementGetValue[jniELEMENTS]; + jclass JNIType::elementArrayClass[jniELEMENTS]; + + AutoGCRoot *gCallback = 0; + + + void JNIInit (JNIEnv *env) { + + if (sInit) { + + return; + + } + + GameActivity = FindClass ("org/haxe/lime/GameActivity"); + postUICallback = env->GetStaticMethodID (GameActivity, "postUICallback", "(J)V"); + + ObjectClass = FindClass ("java/lang/Object"); + ValueObject = FindClass ("org/haxe/lime/Value"); + + HaxeObject = JNIType (jniObjectHaxe, 0).getClass (env); + HaxeObject_create = env->GetStaticMethodID (HaxeObject, "create", "(J)Lorg/haxe/lime/HaxeObject;"); + __haxeHandle = env->GetFieldID (HaxeObject, "__haxeHandle", "J"); + + jclass classClass = FindClass ("java/lang/Class"); + isArrayClass = env->GetMethodID (classClass, "isArray", "()Z"); + + JNIType::init (env); + + sInit = true; + + } + + + value lime_jni_init_callback (value inCallback) { + + if (!gCallback) { + + gCallback = new AutoGCRoot (inCallback); + + } + + return alloc_null (); + + } + + DEFINE_PRIM (lime_jni_init_callback, 1); + + + struct JavaHaxeReference { + + JavaHaxeReference (value inValue) : root (inValue) { + + refCount = 1; + + } + + int refCount; + AutoGCRoot root; + + }; + + + typedef std::map JavaHaxeReferenceMap; + JavaHaxeReferenceMap gJavaObjects; + bool gJavaObjectsMutexInit = false; + pthread_mutex_t gJavaObjectsMutex; + + + jobject CreateJavaHaxeObjectRef (JNIEnv *env, value inValue) { + + JNIInit (env); + + if (!gJavaObjectsMutexInit) { + + gJavaObjectsMutexInit = false; + pthread_mutex_init (&gJavaObjectsMutex, 0); + + } + + pthread_mutex_lock (&gJavaObjectsMutex); + JavaHaxeReferenceMap::iterator it = gJavaObjects.find (inValue); + + if (it != gJavaObjects.end ()) { + + it->second->refCount++; + + } else { + + gJavaObjects[inValue] = new JavaHaxeReference (inValue); + + } + + pthread_mutex_unlock (&gJavaObjectsMutex); + + jobject result = env->CallStaticObjectMethod (HaxeObject, HaxeObject_create, (jlong)inValue); + jthrowable exc = env->ExceptionOccurred (); + CheckException (env); + + return result; + + } + + + void RemoveJavaHaxeObjectRef (value inValue) { + + pthread_mutex_lock (&gJavaObjectsMutex); + + JavaHaxeReferenceMap::iterator it = gJavaObjects.find (inValue); + + if (it != gJavaObjects.end ()) { + + it->second->refCount--; + + if (!it->second->refCount) { + + delete it->second; + gJavaObjects.erase (it); + + } + + } else { + + ELOG ("Bad jni reference count"); + + } + + pthread_mutex_unlock (&gJavaObjectsMutex); + + } + + + struct JNIObject : public lime::Object { + + JNIObject (jobject inObject) { + + mObject = inObject; + + if (mObject) { + + mObject = ((JNIEnv*)JNI::GetEnv ())->NewGlobalRef (mObject); + + } + + } + + ~JNIObject () { + + if (mObject) { + + ((JNIEnv*)JNI::GetEnv ())->DeleteGlobalRef (mObject); + + } + + } + + operator jobject () { return mObject; } + jobject GetJObject () { return mObject; } + jobject mObject; + + }; + + + bool AbstractToJObject (value inValue, jobject &outObject) { + + JNIObject *jniobj = 0; + + if (AbstractToObject (inValue, jniobj)) { + + outObject = jniobj->GetJObject (); + return true; + + } + + static int id__jobject = -1; + + if (id__jobject < 0) { + + id__jobject = val_id ("__jobject"); + + } + + value jobj = val_field (inValue, id__jobject); + + if (val_is_null (jobj)) { + + return false; + + } + + return AbstractToJObject (jobj, outObject); + + } + + + value JStringToHaxe (JNIEnv *inEnv, jobject inObject) { + + jboolean is_copy; + const char *str = inEnv->GetStringUTFChars ((jstring)inObject, &is_copy); + value result = alloc_string (str); + inEnv->ReleaseStringUTFChars ((jstring)inObject, str); + return result; + + } + + + value JObjectToHaxeObject (JNIEnv *env, jobject inObject) { + + if (inObject) { + + jlong val = env->GetLongField (inObject, __haxeHandle); + return (value)val; + + } + + return alloc_null (); + + } + + + #define ARRAY_SET(PRIM,JTYPE,CREATE) \ + case jni##PRIM: \ + { \ + if (len > 0) {\ + \ + jboolean copy; \ + JTYPE *data = inEnv->Get##PRIM##ArrayElements ((JTYPE##Array)inObject, ©); \ + for (int i = 0; i < len; i++) { \ + \ + val_array_set_i (result, i, CREATE (data[i])); \ + \ + } \ + \ + inEnv->Release##PRIM##ArrayElements ((JTYPE##Array)inObject, data, JNI_ABORT); \ + \ + } \ + \ + }\ + break; + + + void DebugObject (JNIEnv *inEnv, const char *inMessage, jobject inObject) { + + if (inObject == 0) { + + ELOG ("%s : null", inMessage); + + } else { + + jclass cls = inEnv->GetObjectClass (inObject); + jmethodID mid = inEnv->GetMethodID (cls, "toString", "()V"); + jthrowable exc = inEnv->ExceptionOccurred (); + + if (exc) { + + inEnv->ExceptionClear (); + + } + + CheckException (inEnv, false); + + if (mid) { + + jstring str = (jstring)inEnv->CallObjectMethod (cls, mid); + + jboolean is_copy; + const char *utf8 = inEnv->GetStringUTFChars (str, &is_copy); + ELOG ("%s : '%s'", inMessage, utf8); + inEnv->ReleaseStringUTFChars (str, utf8); + inEnv->DeleteLocalRef (str); + + } else { + + ELOG ("%s : no toString in class '%s'", inMessage, ClassOf (inEnv, inObject).c_str ()); + + } + + } + + } + + + value JObjectToHaxe (JNIEnv *inEnv, JNIType inType, jobject inObject) { + + if (inObject == 0) { + + return alloc_null (); + + } + + if (inType.isUnknownType ()) { + + jclass cls = inEnv->GetObjectClass (inObject); + + if (cls) { + + for (int i = 0; i < jniELEMENTS; i++) { + + if (JNIType::elementClass[i] == 0) continue; + + if (inEnv->IsSameObject (cls, JNIType::elementClass[i])) { + + inType = JNIType ((JNIElement)i, 0); + break; + + } + + } + + if (inType.isUnknownType ()) { + + for (int i = 0; i < jniELEMENTS; i++) { + + if (JNIType::elementArrayClass[i] == 0) continue; + + if (inEnv->IsSameObject (cls, JNIType::elementArrayClass[i])) { + + inType = JNIType ((JNIElement)i, 1); + break; + + } + + } + + } + + if (inType.isUnknownType ()) { + + if (inEnv->CallBooleanMethod (cls, isArrayClass)) { + + inType = JNIType (jniUnknown, 1); + + } + + } + + } + + if (inType.isUnknownType ()) { + + inType = JNIType (jniObject, 0); + + } + + } + + if (inType.arrayDepth > 1 || (inType.arrayDepth == 1 && inType.element < jniPODStart)) { + + int len = inEnv->GetArrayLength ((jarray)inObject); + value result = alloc_array (len); + JNIType child = inType.elemType (); + + for (int i = 0; i < len; i++) { + + val_array_set_i (result, i, JObjectToHaxe (inEnv, child, inEnv->GetObjectArrayElement ((jobjectArray)inObject, i))); + + } + + return result; + + } else if (inType.arrayDepth == 1) { + + int len = inEnv->GetArrayLength ((jarray)inObject); + value result = alloc_array (len); + + switch (inType.element) { + + ARRAY_SET (Boolean, jboolean, alloc_bool) + //ARRAY_SET (Byte, jbyte, alloc_int) + ARRAY_SET (Char, jchar, alloc_int) + ARRAY_SET (Short, jshort, alloc_int) + ARRAY_SET (Int, jint, alloc_int) + ARRAY_SET (Long, jlong, alloc_int) + ARRAY_SET (Float, jfloat, alloc_float) + ARRAY_SET (Double, jdouble, alloc_float) + + case jniByte: + { + if (len > 0) { + + jboolean copy; + jbyte *data = inEnv->GetByteArrayElements ((jbyteArray)inObject, ©); + + for (int i = 0; i < len; i++) { + + val_array_set_i (result, i, alloc_int (data[i])); + + } + + inEnv->ReleaseByteArrayElements ((jbyteArray)inObject, data, JNI_ABORT); + + } + } + break; + + } + + return result; + + } else { + + switch (inType.element) { + + case jniObject: + { + JNIObject *obj = new JNIObject (inObject); + return ObjectToAbstract (obj); + } + + case jniObjectHaxe: + + return JObjectToHaxeObject (inEnv, inObject); + + case jniObjectString: + + return JStringToHaxe (inEnv, inObject); + + case jniVoid: + + return alloc_null (); + + case jniBoolean: + + return alloc_bool (inEnv->CallBooleanMethod (inObject, JNIType::elementGetValue[jniBoolean])); + + case jniChar: + + return alloc_int (inEnv->CallCharMethod (inObject, JNIType::elementGetValue[jniChar])); + + case jniShort: + case jniByte: + case jniInt: + case jniLong: + case jniFloat: + case jniDouble: + case jniValueObject: + + return alloc_float (inEnv->CallDoubleMethod (inObject, JNIType::elementGetValue[inType.element])); + + default: + { + jclass cls = inEnv->GetObjectClass (inObject); + + if (cls) { + + jmethodID mid = inEnv->GetMethodID (cls, "toString", "()V"); + + if (mid) { + + jstring str = (jstring)inEnv->CallObjectMethod (cls, mid); + value result = JStringToHaxe (inEnv, str); + inEnv->DeleteLocalRef (str); + return result; + + } + + } + + } + + } + + } + + return alloc_null (); + + } + + + const char *JNIParseType (const char *inStr, JNIType &outType, int inDepth = 0) { + + switch (*inStr++) { + + case 'B': outType = JNIType (jniByte, inDepth); return inStr; + case 'C': outType = JNIType (jniChar, inDepth); return inStr; + case 'D': outType = JNIType (jniDouble, inDepth); return inStr; + case 'F': outType = JNIType (jniFloat, inDepth); return inStr; + case 'I': outType = JNIType (jniInt, inDepth); return inStr; + case 'J': outType = JNIType (jniLong, inDepth); return inStr; + case 'S': outType = JNIType (jniShort, inDepth); return inStr; + case 'V': outType = JNIType (jniVoid, inDepth); return inStr; + case 'Z': outType = JNIType (jniBoolean, inDepth); return inStr; + case '[': + { + return JNIParseType (inStr, outType, inDepth + 1); + } + case 'L': + { + const char *src = inStr; + + while (*inStr != '\0' && *inStr != ';' && *inStr != ')') { + + inStr++; + + } + + if (*inStr != ';') { + + break; + + } + + if (!strncmp (src, "java/lang/String;", 17) || !strncmp (src, "java/lang/CharSequence;", 23)) { + + outType = JNIType (jniObjectString, inDepth); + + } else if (!strncmp(src,"org/haxe/lime/HaxeObject;", 24)) { + + outType = JNIType (jniObjectHaxe, inDepth); + + } else { + + outType = JNIType (jniObject, inDepth); + + } + + return inStr + 1; + + } + + } + + outType = JNIType (); + return inStr; + + } + + + #define ARRAY_COPY(PRIM,JTYPE) \ + case jni##PRIM: \ + { \ + JTYPE##Array arr = inEnv->New##PRIM##Array (len); \ + \ + if (len > 0) { \ + \ + jboolean copy; \ + JTYPE *data = inEnv->Get##PRIM##ArrayElements (arr, ©); \ + \ + for (int i = 0; i < len; i++) { \ + \ + data[i] = (JTYPE)val_number (val_array_i (inValue, i)); \ + \ + } \ + \ + inEnv->Release##PRIM##ArrayElements (arr, data, 0); \ + \ + } \ + \ + out.l = arr; \ + return true; \ + \ + } + + + bool HaxeToJNI (JNIEnv *inEnv, value inValue, JNIType inType, jvalue &out) { + + if (inType.isUnknown ()) { + + return false; + + } else if (inType.arrayDepth > 1 || (inType.arrayDepth == 1 && inType.element < jniPODStart)) { + + int len = val_array_size (inValue); + JNIType etype = inType.elemType (); + jclass cls = etype.getClass (inEnv); + + if (!cls) { + + cls = JNIType (jniObject, inType.arrayDepth - 1).getClass (inEnv); + + } + + jobjectArray arr = (jobjectArray)inEnv->NewObjectArray (len, cls, 0); + + for (int i = 0; i < len; i++) { + + jvalue elem_i; + HaxeToJNI (inEnv, val_array_i (inValue, i), etype, elem_i); + inEnv->SetObjectArrayElement (arr, i, elem_i.l); + + } + + out.l = arr; + return true; + + } else if (inType.arrayDepth == 1) { + + int len = val_array_size (inValue); + + switch (inType.element) { + + ARRAY_COPY (Boolean, jboolean) + //ARRAY_COPY (Byte, jbyte) + ARRAY_COPY (Char, jchar) + ARRAY_COPY (Short, jshort) + ARRAY_COPY (Int, jint) + ARRAY_COPY (Long, jlong) + ARRAY_COPY (Float, jfloat) + ARRAY_COPY (Double, jdouble) + + case jniByte: + { + jbyteArray arr = inEnv->NewByteArray (len); + + if (len > 0) { + + jboolean copy; + jbyte *data = inEnv->GetByteArrayElements (arr, ©); + + for (int i = 0; i < len; i++) { + + data[i] = (int)val_number (val_array_i (inValue, i)); + + } + + inEnv->ReleaseByteArrayElements (arr, data, 0); + + } + + out.l = arr; + return true; + } + + case jniVoid: + + out.l = 0; + return true; + + } + + return false; + + } else { + + switch (inType.element) { + + case jniObjectString: + { + out.l = inEnv->NewStringUTF (val_string (inValue)); + return true; + } + case jniObjectHaxe: + + out.l = CreateJavaHaxeObjectRef(inEnv,inValue); + return true; + + case jniObject: + { + jobject obj = 0; + + if (!AbstractToJObject (inValue, obj)) { + + if (val_is_string (inValue)) { + + out.l = inEnv->NewStringUTF (val_string (inValue)); + return true; + + } + + ELOG ("HaxeToJNI : jniObject not an object %p", inValue); + return false; + + } + + out.l = obj; + return true; + } + case jniBoolean: out.z = (bool)val_number (inValue); return true; + case jniByte: out.b = (int)val_number (inValue); return true; + case jniChar: out.c = (int)val_number (inValue); return true; + case jniShort: out.s = (int)val_number (inValue); return true; + case jniInt: out.i = (int)val_number (inValue); return true; + case jniLong: out.j = (long)val_number (inValue); return true; + case jniFloat: out.f = (float)val_number (inValue); return true; + case jniDouble: out.d = val_number (inValue); return true; + case jniVoid: out.l = 0; return true; + + } + + } + + return false; + + } + + + struct JNIField : public lime::Object { + + + JNIField (value inClass, value inField, value inSignature, bool inStatic) { + + JNIEnv *env = (JNIEnv*)JNI::GetEnv (); + JNIInit (env); + + mClass = 0; + mField = 0; + mFieldType = JNIType (jniVoid, 0); + + const char *field = val_string (inField); + + mClass = FindClass (val_string (inClass)); + const char *signature = val_string (inSignature); + + if (mClass) { + + if (inStatic) { + + mField = env->GetStaticFieldID (mClass, field, signature); + + } else { + + mField = env->GetFieldID (mClass, field, signature); + + } + } + + if (Ok ()) { + + bool ok = ParseSignature (signature); + + if (!ok) { + + ELOG ("Bad JNI signature: %s", signature); + mField = 0; + + } + + } + + } + + + ~JNIField () { + + + + } + + + bool ParseSignature (const char *inSig) { + + JNIParseType (inSig, mFieldType); + return (!mFieldType.isUnknown ()); + + } + + + bool Ok () const { + + return mField > 0; + + } + + + value GetStatic () { + + JNIEnv *env = (JNIEnv*)JNI::GetEnv (); + value result = 0; + + if (mFieldType.isObject ()) { + + result = JObjectToHaxe (env, mFieldType, env->GetStaticObjectField (mClass, mField)); + + } else { + + switch (mFieldType.element) { + + case jniBoolean: + + result = alloc_bool (env->GetStaticBooleanField (mClass, mField)); + break; + + case jniByte: + + result = alloc_int (env->GetStaticByteField (mClass, mField)); + break; + + case jniChar: + + result = alloc_int (env->GetStaticCharField (mClass, mField)); + break; + + case jniShort: + + result = alloc_int (env->GetStaticShortField (mClass, mField)); + break; + + case jniInt: + + result = alloc_int (env->GetStaticIntField (mClass, mField)); + break; + + case jniLong: + + result = alloc_int (env->GetStaticLongField (mClass, mField)); + break; + + case jniFloat: + + result = alloc_float (env->GetStaticFloatField (mClass, mField)); + break; + + case jniDouble: + + result = alloc_float (env->GetStaticDoubleField (mClass, mField)); + break; + + } + + } + + CheckException (env); + return result; + + } + + + void SetStatic (value inValue) { + + JNIEnv *env = (JNIEnv*)JNI::GetEnv (); + jvalue setValue; + + if (!HaxeToJNI (env, inValue, mFieldType, setValue)) { + + ELOG ("SetStatic - bad value"); + return; + + } + + if (mFieldType.isObject ()) { + + env->SetStaticObjectField (mClass, mField, setValue.l); + + } else { + + switch (mFieldType.element) { + + case jniBoolean: + + env->SetStaticBooleanField (mClass, mField, setValue.z); + break; + + case jniByte: + + env->SetStaticByteField (mClass, mField, setValue.b); + break; + + case jniChar: + + env->SetStaticCharField (mClass, mField, setValue.c); + break; + + case jniShort: + + env->SetStaticShortField (mClass, mField, setValue.s); + break; + + case jniInt: + + env->SetStaticIntField (mClass, mField, setValue.i); + break; + + case jniLong: + + env->SetStaticLongField (mClass, mField, setValue.j); + break; + + case jniFloat: + + env->SetStaticFloatField (mClass, mField, setValue.f); + break; + + case jniDouble: + + env->SetStaticDoubleField (mClass, mField, setValue.d); + break; + + } + + } + + CheckException (env); + + } + + + value GetMember (jobject inObject) { + + JNIEnv *env = (JNIEnv*)JNI::GetEnv (); + value result = 0; + + if (mFieldType.isObject ()) { + + result = JObjectToHaxe (env, mFieldType, env->GetObjectField (inObject, mField)); + + } else { + + switch (mFieldType.element) { + + case jniBoolean: + + result = alloc_bool(env->GetBooleanField (inObject, mField)); + break; + + case jniByte: + + result = alloc_int(env->GetByteField (inObject, mField)); + break; + + case jniChar: + + result = alloc_int(env->GetCharField (inObject, mField)); + break; + + case jniShort: + + result = alloc_int(env->GetShortField (inObject, mField)); + break; + + case jniInt: + + result = alloc_int(env->GetIntField (inObject, mField)); + break; + + case jniLong: + + result = alloc_int(env->GetLongField (inObject, mField)); + break; + + case jniFloat: + + result = alloc_float(env->GetFloatField (inObject, mField)); + break; + + case jniDouble: + + result = alloc_float(env->GetDoubleField (inObject, mField)); + break; + + } + + } + + CheckException (env); + return result; + + } + + + void SetMember (jobject inObject, value inValue) { + + JNIEnv *env = (JNIEnv*)JNI::GetEnv (); + jvalue setValue; + + if (!HaxeToJNI (env, inValue, mFieldType, setValue)) { + + ELOG ("SetMember - bad value"); + return; + + } + + if (mFieldType.isObject ()) { + + env->SetObjectField (inObject, mField, setValue.l); + + } else { + + switch (mFieldType.element) { + + case jniBoolean: + + env->SetBooleanField (inObject, mField, setValue.z); + break; + + case jniByte: + + env->SetByteField (inObject, mField, setValue.b); + break; + + case jniChar: + + env->SetCharField (inObject, mField, setValue.c); + break; + + case jniShort: + + env->SetShortField (inObject, mField, setValue.s); + break; + + case jniInt: + + env->SetIntField (inObject, mField, setValue.i); + break; + + case jniLong: + + env->SetLongField (inObject, mField, setValue.j); + break; + + case jniFloat: + + env->SetFloatField (inObject, mField, setValue.f); + break; + + case jniDouble: + + env->SetDoubleField (inObject, mField, setValue.d); + break; + + } + + } + + CheckException (env); + + } + + + jclass mClass; + jfieldID mField; + JNIType mFieldType; + + + }; + + + value lime_jni_create_field (value inClass, value inField, value inSig, value inStatic) { + + JNIField *field = new JNIField (inClass, inField, inSig, val_bool (inStatic)); + + if (field->Ok ()) { + + return ObjectToAbstract (field); + + } + + ELOG ("lime_jni_create_field - failed"); + delete field; + return alloc_null (); + + } + + DEFINE_PRIM (lime_jni_create_field, 4); + + + value lime_jni_get_static (value inField) { + + JNIField *field; + + if (!AbstractToObject (inField, field)) { + + return alloc_null (); + + } + + value result = field->GetStatic (); + return result; + + } + + DEFINE_PRIM (lime_jni_get_static, 1); + + + void lime_jni_set_static (value inField, value inValue) { + + JNIField *field; + + if (!AbstractToObject (inField, field)) { + + return; + + } + + field->SetStatic (inValue); + + } + + DEFINE_PRIM (lime_jni_set_static, 2); + + + value lime_jni_get_member (value inField, value inObject) { + + JNIField *field; + jobject object; + + if (!AbstractToObject (inField, field)) { + + ELOG ("lime_jni_get_member - not a field"); + return alloc_null (); + + } + + if (!AbstractToJObject (inObject, object)) { + + ELOG ("lime_jni_get_member - invalid this"); + return alloc_null (); + + } + + return field->GetMember (object); + + } + + DEFINE_PRIM (lime_jni_get_member, 2); + + + void lime_jni_set_member (value inField, value inObject, value inValue) { + + JNIField *field; + jobject object; + + if (!AbstractToObject (inField, field)) { + + ELOG ("lime_jni_set_member - not a field"); + return; + + } + + if (!AbstractToJObject (inObject, object)) { + + ELOG ("lime_jni_set_member - invalid this"); + return; + + } + + field->SetMember (object, inValue); + + } + + DEFINE_PRIM (lime_jni_set_member, 3); + + + struct JNIMethod : public lime::Object { + + + enum { MAX = 20 }; + + + JNIMethod (value inClass, value inMethod, value inSignature, bool inStatic, bool inQuiet) { + + JNIEnv *env = (JNIEnv*)JNI::GetEnv (); + JNIInit (env); + + mClass = 0; + mMethod = 0; + mReturn = JNIType (jniVoid, 0); + mArgCount = 0; + + const char *method = val_string (inMethod); + mIsConstructor = !strncmp (method, "", 6); + + mClass = FindClass (val_string (inClass), inQuiet); + + if (mClass) { + + const char *signature = val_string (inSignature); + + if (inStatic && !mIsConstructor) { + + mMethod = env->GetStaticMethodID (mClass, method, signature); + + } else { + + mMethod = env->GetMethodID (mClass, method, signature); + + } + + if (inQuiet) { + + jthrowable exc = env->ExceptionOccurred (); + + if (exc) { + + env->ExceptionClear (); + + } + + } else { + + CheckException(env); + + } + + if (Ok ()) { + + bool ok = ParseSignature (signature); + + if (!ok) { + + ELOG ("Bad signature %s.", signature); + mMethod = 0; + + } + + } + + } + + } + + + ~JNIMethod () { + + + + } + + + bool HaxeToJNIArgs (JNIEnv *inEnv, value inArray, jvalue *outValues) { + + if (val_array_size (inArray) != mArgCount) { + + ELOG ("Invalid array count: %d != %d", val_array_size (inArray), mArgCount); + return false; + + } + + for (int i = 0; i < mArgCount; i++) { + + value arg_i = val_array_i (inArray, i); + + if (!HaxeToJNI (inEnv, arg_i, mArgType[i], outValues[i])) { + + ELOG ("HaxeToJNI could not convert param %d (%p) to %dx%d", i, arg_i, mArgType[i].element, mArgType[i].arrayDepth); + return false; + + } + + } + + return true; + + } + + + void CleanStringArgs () { + + + + } + + + bool ParseSignature (const char *inSig) { + + if (*inSig++ != '(') { + + return false; + + } + + mArgCount = 0; + + while (*inSig != ')') { + + if (mArgCount == MAX) { + + return false; + + } + + JNIType type; + inSig = JNIParseType (inSig, type); + + if (type.isUnknown ()) { + + return false; + + } + + mArgType[mArgCount++] = type; + + } + + inSig++; + JNIParseType (inSig, mReturn); + return !mReturn.isUnknown (); + + } + + + bool Ok () const { + + return mMethod > 0; + + } + + + value CallStatic (value inArgs) { + + JNIEnv *env = (JNIEnv*)JNI::GetEnv (); + jvalue jargs[MAX]; + + if (!HaxeToJNIArgs (env, inArgs, jargs)) { + + CleanStringArgs (); + ELOG ("CallStatic - bad argument list"); + return alloc_null (); + + } + + value result = 0; + + if (mIsConstructor) { + + jobject obj = env->NewObjectA (mClass, mMethod, jargs); + result = JObjectToHaxe (env, JNIType (jniObject, 0), obj); + + } else if (mReturn.isObject ()) { + + result = JObjectToHaxe (env, mReturn, env->CallStaticObjectMethodA (mClass, mMethod, jargs)); + + } else { + + switch (mReturn.element) { + + case jniVoid: + + result = alloc_null (); + env->CallStaticVoidMethodA (mClass, mMethod, jargs); + break; + + case jniBoolean: + + result = alloc_bool (env->CallStaticBooleanMethodA (mClass, mMethod, jargs)); + break; + + case jniByte: + + result = alloc_int (env->CallStaticByteMethodA (mClass, mMethod, jargs)); + break; + + case jniChar: + + result = alloc_int (env->CallStaticCharMethodA (mClass, mMethod, jargs)); + break; + + case jniShort: + + result = alloc_int (env->CallStaticShortMethodA (mClass, mMethod, jargs)); + break; + + case jniInt: + + result = alloc_int (env->CallStaticIntMethodA (mClass, mMethod, jargs)); + break; + + case jniLong: + + result = alloc_int (env->CallStaticLongMethodA (mClass, mMethod, jargs)); + break; + + case jniFloat: + + result = alloc_float (env->CallStaticFloatMethodA (mClass, mMethod, jargs)); + break; + + case jniDouble: + + result = alloc_float (env->CallStaticDoubleMethodA (mClass, mMethod, jargs)); + break; + + } + + } + + CleanStringArgs (); + CheckException (env); + return result; + + } + + + value CallMember (jobject inObject, value inArgs) { + + JNIEnv *env = (JNIEnv*)JNI::GetEnv (); + jvalue jargs[MAX]; + + if (!HaxeToJNIArgs (env, inArgs, jargs)) { + + CleanStringArgs (); + ELOG ("CallMember - bad argument list"); + return alloc_null (); + + } + + value result = 0; + + if (mReturn.isObject ()) { + + result = JObjectToHaxe (env, mReturn, env->CallObjectMethodA (inObject, mMethod, jargs)); + + } else { + + switch (mReturn.element) { + + case jniVoid: + + result = alloc_null (); + env->CallVoidMethodA (inObject, mMethod, jargs); + break; + + case jniBoolean: + + result = alloc_bool (env->CallBooleanMethodA (inObject, mMethod, jargs)); + break; + + case jniByte: + + result = alloc_int (env->CallByteMethodA (inObject, mMethod, jargs)); + break; + + case jniChar: + + result = alloc_int (env->CallCharMethodA (inObject, mMethod, jargs)); + break; + + case jniShort: + + result = alloc_int (env->CallShortMethodA (inObject, mMethod, jargs)); + break; + + case jniInt: + + result = alloc_int (env->CallIntMethodA (inObject, mMethod, jargs)); + break; + + case jniLong: + + result = alloc_int (env->CallLongMethodA (inObject, mMethod, jargs)); + break; + + case jniFloat: + + result = alloc_float (env->CallFloatMethodA (inObject, mMethod, jargs)); + break; + + case jniDouble: + + result = alloc_float (env->CallDoubleMethodA (inObject, mMethod, jargs)); + break; + + } + + } + + CleanStringArgs (); + return result; + + } + + + jclass mClass; + jmethodID mMethod; + JNIType mReturn; + JNIType mArgType[MAX]; + int mArgCount; + bool mIsConstructor; + + + }; + + + value lime_jni_create_method (value inClass, value inMethod, value inSig, value inStatic, value inQuiet) { + + bool quiet = val_bool (inQuiet); + JNIMethod *method = new JNIMethod (inClass, inMethod, inSig, val_bool (inStatic), quiet); + + if (method->Ok ()) { + + return ObjectToAbstract (method); + + } + + if (!quiet) { + + ELOG ("lime_jni_create_method - failed"); + + } + + delete method; + return alloc_null (); + + } + + DEFINE_PRIM (lime_jni_create_method, 5); + + + value lime_jni_call_static (value inMethod, value inArgs) { + + JNIMethod *method; + + if (!AbstractToObject (inMethod, method)) { + + return alloc_null (); + + } + + value result = method->CallStatic (inArgs); + return result; + + } + + DEFINE_PRIM (lime_jni_call_static, 2); + + + value lime_jni_call_member (value inMethod, value inObject, value inArgs) { + + JNIMethod *method; + jobject object; + + if (!AbstractToObject (inMethod, method)) { + + ELOG ("lime_jni_call_member - not a method"); + return alloc_null (); + + } + + if (!AbstractToJObject (inObject, object)) { + + ELOG ("lime_jni_call_member - invalid this"); + return alloc_null (); + + } + + return method->CallMember (object, inArgs); + + } + + DEFINE_PRIM (lime_jni_call_member, 3); + + + value lime_jni_get_env () { + + JNIEnv *env = (JNIEnv*)JNI::GetEnv (); + return alloc_int ((intptr_t)env); + + } + + DEFINE_PRIM (lime_jni_get_env, 0); + + + value lime_jni_get_jobject (value inValue) { + + jobject obj = 0; + + if (AbstractToJObject (inValue, obj)) { + + return alloc_int ((int)obj); + + } + + return alloc_null (); + + } + + DEFINE_PRIM (lime_jni_get_jobject, 1); + + + value lime_post_ui_callback (value inCallback) { + + JNIEnv *env = (JNIEnv*)JNI::GetEnv (); + JNIInit (env); + + AutoGCRoot *root = new AutoGCRoot (inCallback); + ELOG ("NME set onCallback %p",root); + env->CallStaticVoidMethod (GameActivity, postUICallback, (jlong)root); + jthrowable exc = env->ExceptionOccurred (); + + if (exc) { + + env->ExceptionDescribe (); + env->ExceptionClear (); + delete root; + val_throw (alloc_string ("JNI Exception")); + + } + + return alloc_null (); + + } + + DEFINE_PRIM (lime_post_ui_callback, 1); + + +} + + +extern "C" { + + + JAVA_EXPORT void JNICALL Java_org_haxe_lime_Lime_onCallback (JNIEnv * env, jobject obj, jlong handle) { + + lime::AutoHaxe haxe ("onCallback"); + ELOG ("NME onCallback %p", (void *)handle); + AutoGCRoot *root = (AutoGCRoot *)handle; + val_call0 (root->get ()); + delete root; + + } + + + JAVA_EXPORT jobject JNICALL Java_org_haxe_lime_Lime_releaseReference (JNIEnv * env, jobject obj, jlong handle) { + + lime::AutoHaxe haxe ("releaseReference"); + value val = (value)handle; + lime::RemoveJavaHaxeObjectRef (val); + return 0; + + } + + + value CallHaxe (JNIEnv * env, jobject obj, jlong handle, jstring function, jobject inArgs) { + + //ELOG ("CallHaxe %p", gCallback); + + if (lime::gCallback) { + + value objValue = (value)handle; + value funcName = lime::JStringToHaxe (env, function); + value args = lime::JObjectToHaxe (env, lime::JNIType (lime::jniUnknown, 1), inArgs); + //ELOG ("Using %d args", val_array_size (args)); + return val_call3 (lime::gCallback->get (), objValue, funcName, args); + + } else { + + ELOG ("NME CallHaxe - init not called."); + return alloc_null (); + + } + + } + + + JAVA_EXPORT jobject JNICALL Java_org_haxe_lime_Lime_callObjectFunction (JNIEnv * env, jobject obj, jlong handle, jstring function, jobject args) { + + lime::AutoHaxe haxe ("callObject"); + + value result = CallHaxe (env, obj, handle, function, args); + + jobject val = 0; + + // TODO - other cases + + if (val_is_string (result)) { + + const char *string = val_string (result); + val = env->NewStringUTF (string); + + } else if (!val_is_null (result)) { + + ELOG ("only string return is supported"); + + } + + //jobject val = JAnonToHaxe(result); + + return val; + + } + + + JAVA_EXPORT jdouble JNICALL Java_org_haxe_lime_Lime_callNumericFunction (JNIEnv * env, jobject obj, jlong handle, jstring function, jobject args) { + + lime::AutoHaxe haxe ("callNumeric"); + + value result = CallHaxe (env, obj, handle, function, args); + double val = val_number (result); + return val; + + } + + +} \ No newline at end of file diff --git a/templates/android/template/src/org/haxe/lime/GameActivity.java b/templates/android/template/src/org/haxe/lime/GameActivity.java index 3df81389c..03dc42d55 100644 --- a/templates/android/template/src/org/haxe/lime/GameActivity.java +++ b/templates/android/template/src/org/haxe/lime/GameActivity.java @@ -24,6 +24,21 @@ public class GameActivity extends SDLActivity { public Handler handler; + public static void postUICallback (final long handle) { + + Extension.callbackHandler.post (new Runnable () { + + @Override public void run () { + + Lime.onCallback (handle); + + } + + }); + + } + + @Override protected void onActivityResult (int requestCode, int resultCode, Intent data) { for (Extension extension : extensions) {