Files
lime/project/src/platform/android/JNI.cpp
2013-12-04 20:32:40 +06:00

1322 lines
35 KiB
C++

#include <ExternalInterface.h>
#include <Utils.h>
#include <jni.h>
#include <pthread.h>
#include <ByteArray.h>
#include <map>
#include <android/log.h>
#include "AndroidCommon.h"
using namespace lime;
enum JNIElement
{
jniUnknown,
jniObjectString,
jniObjectHaxe,
jniObject,
jniPODStart,
jniBoolean = jniPODStart,
jniByte,
jniChar,
jniShort,
jniInt,
jniLong,
jniFloat,
jniDouble,
jniVoid,
jniELEMENTS,
};
static bool sInit = false;
jclass GameActivity;
jclass ObjectClass;
jmethodID postUICallback;
jclass HaxeObject;
jmethodID HaxeObject_create;
jfieldID __haxeHandle;
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"));
}
}
struct JNIType
{
typedef std::map<JNIType,jclass> 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<inRHS.arrayDepth;
return element < inRHS.element;
}
jclass getClass(JNIEnv *inEnv)
{
if (!isObject())
{
return 0;
}
if (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 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 = FindClass(name.c_str());
mClasses[*this] = result;
return result;
}
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);
}
}
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");
HaxeObject = JNIType(jniObjectHaxe,0).getClass(env);
HaxeObject_create = env->GetStaticMethodID(HaxeObject, "create", "(J)Lorg/haxe/lime/HaxeObject;");
__haxeHandle = env->GetFieldID(HaxeObject, "__haxeHandle", "J");
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<value,JavaHaxeReference *> 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 = (GetEnv()->NewGlobalRef(mObject));
}
~JNIObject()
{
if (mObject)
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,&copy); \
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;
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())
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,&copy);
for(int i=0;i<len;i++) {
val_array_set_i(result,i,alloc_int(data[i]));
}
inEnv->ReleaseByteArrayElements((jbyteArray)inObject,data,JNI_ABORT);
}
}
}
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:
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)
return JStringToHaxe(inEnv, inEnv->CallObjectMethod(cls,mid) );
}
}
}
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,&copy); \
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,&copy);
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 = GetEnv();
JNIInit(env);
mClass = 0;
mField = 0;
mFieldType = JNIType(jniVoid,0);
const char *field = val_string(inField);
mClass = (jclass)env->NewGlobalRef(env->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()
{
GetEnv()->DeleteGlobalRef(mClass);
}
bool ParseSignature(const char *inSig)
{
JNIParseType(inSig,mFieldType);
return (!mFieldType.isUnknown());
}
bool Ok() const { return mField>0; }
value GetStatic()
{
JNIEnv *env = 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 = 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 = 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 = 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)
{
JNIEnv *env = GetEnv();
JNIInit(env);
mClass = 0;
mMethod = 0;
mReturn = JNIType(jniVoid,0);
mArgCount = 0;
const char *method = val_string(inMethod);
mIsConstructor = !strncmp(method,"<init>",6);
mClass = (jclass)env->NewGlobalRef(env->FindClass(val_string(inClass)));
const char *signature = val_string(inSignature);
if (mClass)
{
if (inStatic && !mIsConstructor)
mMethod = env->GetStaticMethodID(mClass, method, signature);
else
mMethod = env->GetMethodID(mClass, method, signature);
}
if (Ok())
{
bool ok = ParseSignature(signature);
if (!ok)
{
ELOG("Bad signature %s.", signature);
mMethod = 0;
}
}
}
~JNIMethod()
{
GetEnv()->DeleteGlobalRef(mClass);
}
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 = 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 = 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)
{
JNIMethod *method = new JNIMethod(inClass,inMethod,inSig,val_bool(inStatic) );
if (method->Ok())
return ObjectToAbstract(method);
ELOG("lime_jni_create_method - failed");
delete method;
return alloc_null();
}
DEFINE_PRIM(lime_jni_create_method,4);
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 = 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 = GetEnv();
JNIInit(env);
AutoGCRoot *root = new AutoGCRoot(inCallback);
ELOG("lime 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"
{
#ifdef __GNUC__
#define JAVA_EXPORT __attribute__ ((visibility("default"))) JNIEXPORT
#else
#define JAVA_EXPORT JNIEXPORT
#endif
JAVA_EXPORT void JNICALL Java_org_haxe_lime_Lime_onCallback(JNIEnv * env, jobject obj, jlong handle)
{
int top = 0;
gc_set_top_of_stack(&top,true);
ELOG("lime onCallback %p",(void *)handle);
AutoGCRoot *root = (AutoGCRoot *)handle;
val_call0( root->get() );
delete root;
gc_set_top_of_stack(0,true);
}
JAVA_EXPORT jobject JNICALL Java_org_haxe_lime_Lime_releaseReference(JNIEnv * env, jobject obj, jlong handle)
{
value val = (value)handle;
RemoveJavaHaxeObjectRef(val);
return 0;
}
value CallHaxe(JNIEnv * env, jobject obj, jlong handle, jstring function, jobject inArgs)
{
ELOG("CallHaxe %p", gCallback);
if (gCallback)
{
value objValue = (value)handle;
value funcName = JStringToHaxe(env,function);
value args = JObjectToHaxe(env,JNIType(jniUnknown,1),inArgs);
ELOG("Using %d args", val_array_size(args) );
return val_call3(gCallback->get(),objValue,funcName,args);
}
else
{
ELOG("lime 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)
{
int top = 0;
gc_set_top_of_stack(&top,true);
value result = CallHaxe(env,obj,handle,function,args);
// TODO:
//jobject val = JAnonToHaxe(result);
jobject val = 0;
gc_set_top_of_stack(0,true);
return val;
}
JAVA_EXPORT jdouble JNICALL Java_org_haxe_lime_Lime_callNumericFunction(JNIEnv * env, jobject obj, jlong handle, jstring function, jobject args)
{
int top = 0;
gc_set_top_of_stack(&top,true);
value result = CallHaxe(env,obj,handle,function,args);
double val = val_number(result);
gc_set_top_of_stack(0,true);
return val;
}
}