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; switch(mReturn) { case jniVoid: result = alloc_null(); env->CallVoidMethodA(inObject, mMethod, jargs); break; case jniObject: result = JObjectToHaxe(env->CallObjectMethodA(inObject,mMethod, jargs)); break; case jniObjectString: result = JStringToHaxe(env,env->CallObjectMethodA(inObject, mMethod, jargs)); break; case jniObjectArray: result = JArrayToHaxe(env->CallObjectMethodA(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(mClass, 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; }
bool JavaClass::_call_method(JavaObject* p_instance,const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error,Variant& ret) { Map<StringName,List<MethodInfo> >::Element *M=methods.find(p_method); if (!M) return false; JNIEnv *env = ThreadAndroid::get_env(); MethodInfo *method=NULL; for (List<MethodInfo>::Element *E=M->get().front(); E; E=E->next()) { if (!p_instance && !E->get()._static) { r_error.error=Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL; continue; } int pc = E->get().param_types.size(); if (pc>p_argcount) { r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument=pc; continue; } if (pc<p_argcount) { r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; r_error.argument=pc; continue; } uint32_t *ptypes=E->get().param_types.ptr(); bool valid=true; for(int i=0; i<pc; i++) { Variant::Type arg_expected=Variant::NIL; switch(ptypes[i]) { case ARG_TYPE_VOID: { //bug? } break; case ARG_TYPE_BOOLEAN: { if (p_args[i]->get_type()!=Variant::BOOL) arg_expected=Variant::BOOL; } break; case ARG_NUMBER_CLASS_BIT|ARG_TYPE_BYTE: case ARG_NUMBER_CLASS_BIT|ARG_TYPE_CHAR: case ARG_NUMBER_CLASS_BIT|ARG_TYPE_SHORT: case ARG_NUMBER_CLASS_BIT|ARG_TYPE_INT: case ARG_NUMBER_CLASS_BIT|ARG_TYPE_LONG: case ARG_TYPE_BYTE: case ARG_TYPE_CHAR: case ARG_TYPE_SHORT: case ARG_TYPE_INT: case ARG_TYPE_LONG: { if (!p_args[i]->is_num()) arg_expected=Variant::INT; } break; case ARG_NUMBER_CLASS_BIT|ARG_TYPE_FLOAT: case ARG_NUMBER_CLASS_BIT|ARG_TYPE_DOUBLE: case ARG_TYPE_FLOAT: case ARG_TYPE_DOUBLE: { if (!p_args[i]->is_num()) arg_expected=Variant::REAL; } break; case ARG_TYPE_STRING: { if (p_args[i]->get_type()!=Variant::STRING) arg_expected=Variant::STRING; } break; case ARG_TYPE_CLASS: { if (p_args[i]->get_type()!=Variant::OBJECT) arg_expected=Variant::OBJECT; else { Ref<Reference> ref = *p_args[i]; if (!ref.is_null()) { if (ref->cast_to<JavaObject>() ) { Ref<JavaObject> jo=ref; //could be faster jclass c = env->FindClass(E->get().param_sigs[i].operator String().utf8().get_data()); if (!c || !env->IsInstanceOf(jo->instance,c)) { arg_expected=Variant::OBJECT; } else { //ok } } else { arg_expected=Variant::OBJECT; } } } } break; default: { if (p_args[i]->get_type()!=Variant::ARRAY) arg_expected=Variant::ARRAY; } break; } if (arg_expected!=Variant::NIL) { r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument=i; r_error.expected=arg_expected; valid=false; break; } } if (!valid) continue; method=&E->get(); break; } if (!method) return true; //no version convinces r_error.error=Variant::CallError::CALL_OK; jvalue *argv=NULL; if (method->param_types.size()) { argv=(jvalue*)alloca( sizeof(jvalue)*method->param_types.size() ); } List<jobject> to_free; for(int i=0; i<method->param_types.size(); i++) { switch(method->param_types[i]) { case ARG_TYPE_VOID: { //can't happen argv[i].l=NULL; //I hope this works } break; case ARG_TYPE_BOOLEAN: { argv[i].z=*p_args[i]; } break; case ARG_TYPE_BYTE: { argv[i].b=*p_args[i]; } break; case ARG_TYPE_CHAR: { argv[i].c=*p_args[i]; } break; case ARG_TYPE_SHORT: { argv[i].s=*p_args[i]; } break; case ARG_TYPE_INT: { argv[i].i=*p_args[i]; } break; case ARG_TYPE_LONG: { argv[i].j=*p_args[i]; } break; case ARG_TYPE_FLOAT: { argv[i].f=*p_args[i]; } break; case ARG_TYPE_DOUBLE: { argv[i].d=*p_args[i]; } break; case ARG_NUMBER_CLASS_BIT|ARG_TYPE_BOOLEAN: { jclass bclass = env->FindClass("java/lang/Boolean"); jmethodID ctor = env->GetMethodID(bclass, "<init>", "(Z)V"); jvalue val; val.z = (bool)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT|ARG_TYPE_BYTE: { jclass bclass = env->FindClass("java/lang/Byte"); jmethodID ctor = env->GetMethodID(bclass, "<init>", "(B)V"); jvalue val; val.b = (int)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT|ARG_TYPE_CHAR: { jclass bclass = env->FindClass("java/lang/Character"); jmethodID ctor = env->GetMethodID(bclass, "<init>", "(C)V"); jvalue val; val.c = (int)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT|ARG_TYPE_SHORT: { jclass bclass = env->FindClass("java/lang/Short"); jmethodID ctor = env->GetMethodID(bclass, "<init>", "(S)V"); jvalue val; val.s = (int)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT|ARG_TYPE_INT: { jclass bclass = env->FindClass("java/lang/Integer"); jmethodID ctor = env->GetMethodID(bclass, "<init>", "(I)V"); jvalue val; val.i = (int)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT|ARG_TYPE_LONG: { jclass bclass = env->FindClass("java/lang/Long"); jmethodID ctor = env->GetMethodID(bclass, "<init>", "(J)V"); jvalue val; val.j = (int64_t)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT|ARG_TYPE_FLOAT: { jclass bclass = env->FindClass("java/lang/Float"); jmethodID ctor = env->GetMethodID(bclass, "<init>", "(F)V"); jvalue val; val.f = (float)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; to_free.push_back(obj); } break; case ARG_NUMBER_CLASS_BIT|ARG_TYPE_DOUBLE: { jclass bclass = env->FindClass("java/lang/Double"); jmethodID ctor = env->GetMethodID(bclass, "<init>", "(D)V"); jvalue val; val.d = (double)(*p_args[i]); jobject obj = env->NewObjectA(bclass, ctor, &val); argv[i].l = obj; to_free.push_back(obj); } break; case ARG_TYPE_STRING: { String s = *p_args[i]; jstring jStr = env->NewStringUTF(s.utf8().get_data()); argv[i].l=jStr; to_free.push_back(jStr); } break; case ARG_TYPE_CLASS: { Ref<JavaObject> jo=*p_args[i]; if (jo.is_valid()) { argv[i].l=jo->instance; } else { argv[i].l=NULL; //I hope this works } } break; case ARG_ARRAY_BIT|ARG_TYPE_BOOLEAN: { Array arr = *p_args[i]; jbooleanArray a = env->NewBooleanArray(arr.size()); for(int j=0; j<arr.size(); j++) { jboolean val = arr[j]; env->SetBooleanArrayRegion(a,j,1,&val); } argv[i].l=a; to_free.push_back(a); } break; case ARG_ARRAY_BIT|ARG_TYPE_BYTE: { Array arr = *p_args[i]; jbyteArray a = env->NewByteArray(arr.size()); for(int j=0; j<arr.size(); j++) { jbyte val = arr[j]; env->SetByteArrayRegion(a,j,1,&val); } argv[i].l=a; to_free.push_back(a); } break; case ARG_ARRAY_BIT|ARG_TYPE_CHAR: { Array arr = *p_args[i]; jcharArray a = env->NewCharArray(arr.size()); for(int j=0; j<arr.size(); j++) { jchar val = arr[j]; env->SetCharArrayRegion(a,j,1,&val); } argv[i].l=a; to_free.push_back(a); } break; case ARG_ARRAY_BIT|ARG_TYPE_SHORT: { Array arr = *p_args[i]; jshortArray a = env->NewShortArray(arr.size()); for(int j=0; j<arr.size(); j++) { jshort val = arr[j]; env->SetShortArrayRegion(a,j,1,&val); } argv[i].l=a; to_free.push_back(a); } break; case ARG_ARRAY_BIT|ARG_TYPE_INT: { Array arr = *p_args[i]; jintArray a = env->NewIntArray(arr.size()); for(int j=0; j<arr.size(); j++) { jint val = arr[j]; env->SetIntArrayRegion(a,j,1,&val); } argv[i].l=a; to_free.push_back(a); } break; case ARG_ARRAY_BIT|ARG_TYPE_LONG: { Array arr = *p_args[i]; jlongArray a = env->NewLongArray(arr.size()); for(int j=0; j<arr.size(); j++) { jlong val = arr[j]; env->SetLongArrayRegion(a,j,1,&val); } argv[i].l=a; to_free.push_back(a); } break; case ARG_ARRAY_BIT|ARG_TYPE_FLOAT: { Array arr = *p_args[i]; jfloatArray a = env->NewFloatArray(arr.size()); for(int j=0; j<arr.size(); j++) { jfloat val = arr[j]; env->SetFloatArrayRegion(a,j,1,&val); } argv[i].l=a; to_free.push_back(a); } break; case ARG_ARRAY_BIT|ARG_TYPE_DOUBLE: { Array arr = *p_args[i]; jdoubleArray a = env->NewDoubleArray(arr.size()); for(int j=0; j<arr.size(); j++) { jdouble val = arr[j]; env->SetDoubleArrayRegion(a,j,1,&val); } argv[i].l=a; to_free.push_back(a); } break; case ARG_ARRAY_BIT|ARG_TYPE_STRING: { Array arr = *p_args[i]; jobjectArray a = env->NewObjectArray(arr.size(),env->FindClass("java/lang/String"),NULL); for(int j=0; j<arr.size(); j++) { String s = arr[j]; jstring jStr = env->NewStringUTF(s.utf8().get_data()); env->SetObjectArrayElement(a,j,jStr); to_free.push_back(jStr); } argv[i].l=a; to_free.push_back(a); } break; case ARG_ARRAY_BIT|ARG_TYPE_CLASS: { argv[i].l=NULL; } break; } } r_error.error=Variant::CallError::CALL_OK; bool success=true; switch(method->return_type) { case ARG_TYPE_VOID: { if (method->_static) { env->CallStaticVoidMethodA(_class,method->method,argv); } else { env->CallVoidMethodA(p_instance->instance,method->method,argv); } ret=Variant(); } break; case ARG_TYPE_BOOLEAN: { if (method->_static) { ret=env->CallStaticBooleanMethodA(_class,method->method,argv); } else { ret=env->CallBooleanMethodA(p_instance->instance,method->method,argv); } } break; case ARG_TYPE_BYTE: { if (method->_static) { ret=env->CallStaticByteMethodA(_class,method->method,argv); } else { ret=env->CallByteMethodA(p_instance->instance,method->method,argv); } } break; case ARG_TYPE_CHAR: { if (method->_static) { ret=env->CallStaticCharMethodA(_class,method->method,argv); } else { ret=env->CallCharMethodA(p_instance->instance,method->method,argv); } } break; case ARG_TYPE_SHORT: { if (method->_static) { ret=env->CallStaticShortMethodA(_class,method->method,argv); } else { ret=env->CallShortMethodA(p_instance->instance,method->method,argv); } } break; case ARG_TYPE_INT: { if (method->_static) { ret=env->CallStaticIntMethodA(_class,method->method,argv); } else { ret=env->CallIntMethodA(p_instance->instance,method->method,argv); } } break; case ARG_TYPE_LONG: { if (method->_static) { ret=env->CallStaticLongMethodA(_class,method->method,argv); } else { ret=env->CallLongMethodA(p_instance->instance,method->method,argv); } } break; case ARG_TYPE_FLOAT: { if (method->_static) { ret=env->CallStaticFloatMethodA(_class,method->method,argv); } else { ret=env->CallFloatMethodA(p_instance->instance,method->method,argv); } } break; case ARG_TYPE_DOUBLE: { if (method->_static) { ret=env->CallStaticDoubleMethodA(_class,method->method,argv); } else { ret=env->CallDoubleMethodA(p_instance->instance,method->method,argv); } } break; default: { jobject obj; if (method->_static) { obj=env->CallStaticObjectMethodA(_class,method->method,argv); } else { obj=env->CallObjectMethodA(p_instance->instance,method->method,argv); } if (!obj) { ret=Variant(); } else { if (!_convert_object_to_variant(env, obj, ret,method->return_type)) { ret=Variant(); r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; success=false; } env->DeleteLocalRef(obj); } } break; } for(List<jobject>::Element *E=to_free.front(); E; E=E->next()) { env->DeleteLocalRef(E->get()); } return success; }
NS_IMETHODIMP nsJavaXPTCStub::CallMethod(PRUint16 aMethodIndex, const XPTMethodDescriptor *aMethodInfo, nsXPTCMiniVariant *aParams) { #ifdef DEBUG_JAVAXPCOM const char* ifaceName; mIInfo->GetNameShared(&ifaceName); LOG(("---> (Java) %s::%s()\n", ifaceName, aMethodInfo->name)); #endif nsresult rv = NS_OK; JNIEnv* env = GetJNIEnv(); jobject javaObject = env->CallObjectMethod(mJavaWeakRef, getReferentMID); nsCAutoString methodSig("("); // Create jvalue array to hold Java params PRUint8 paramCount = aMethodInfo->num_args; jvalue* java_params = nsnull; const nsXPTParamInfo* retvalInfo = nsnull; if (paramCount) { java_params = new jvalue[paramCount]; if (!java_params) return NS_ERROR_OUT_OF_MEMORY; for (PRUint8 i = 0; i < paramCount && NS_SUCCEEDED(rv); i++) { const nsXPTParamInfo ¶mInfo = aMethodInfo->params[i]; if (!paramInfo.IsRetval()) { rv = SetupJavaParams(paramInfo, aMethodInfo, aMethodIndex, aParams, aParams[i], java_params[i], methodSig); } else { retvalInfo = ¶mInfo; } } NS_ASSERTION(NS_SUCCEEDED(rv), "SetupJavaParams failed"); } // Finish method signature if (NS_SUCCEEDED(rv)) { methodSig.Append(')'); if (retvalInfo) { nsCAutoString retvalSig; rv = GetRetvalSig(retvalInfo, aMethodInfo, aMethodIndex, aParams, retvalSig); methodSig.Append(retvalSig); } else { methodSig.Append('V'); } NS_ASSERTION(NS_SUCCEEDED(rv), "GetRetvalSig failed"); } // Get Java method to call jmethodID mid = nsnull; if (NS_SUCCEEDED(rv)) { nsCAutoString methodName; if (XPT_MD_IS_GETTER(aMethodInfo->flags) || XPT_MD_IS_SETTER(aMethodInfo->flags)) { if (XPT_MD_IS_GETTER(aMethodInfo->flags)) methodName.AppendLiteral("get"); else methodName.AppendLiteral("set"); methodName.AppendASCII(aMethodInfo->name); methodName.SetCharAt(toupper(methodName[3]), 3); } else { methodName.AppendASCII(aMethodInfo->name); methodName.SetCharAt(tolower(methodName[0]), 0); } // If it's a Java keyword, then prepend an underscore if (gJavaKeywords->GetEntry(methodName.get())) { methodName.Insert('_', 0); } jclass clazz = env->GetObjectClass(javaObject); if (clazz) mid = env->GetMethodID(clazz, methodName.get(), methodSig.get()); NS_ASSERTION(mid, "Failed to get requested method for Java object"); if (!mid) rv = NS_ERROR_FAILURE; } // Call method jvalue retval; if (NS_SUCCEEDED(rv)) { if (!retvalInfo) { env->CallVoidMethodA(javaObject, mid, java_params); } else { switch (retvalInfo->GetType().TagPart()) { case nsXPTType::T_I8: retval.b = env->CallByteMethodA(javaObject, mid, java_params); break; case nsXPTType::T_I16: case nsXPTType::T_U8: retval.s = env->CallShortMethodA(javaObject, mid, java_params); break; case nsXPTType::T_I32: case nsXPTType::T_U16: retval.i = env->CallIntMethodA(javaObject, mid, java_params); break; case nsXPTType::T_I64: case nsXPTType::T_U32: retval.j = env->CallLongMethodA(javaObject, mid, java_params); break; case nsXPTType::T_FLOAT: retval.f = env->CallFloatMethodA(javaObject, mid, java_params); break; case nsXPTType::T_U64: case nsXPTType::T_DOUBLE: retval.d = env->CallDoubleMethodA(javaObject, mid, java_params); break; case nsXPTType::T_BOOL: retval.z = env->CallBooleanMethodA(javaObject, mid, java_params); break; case nsXPTType::T_CHAR: case nsXPTType::T_WCHAR: retval.c = env->CallCharMethodA(javaObject, mid, java_params); break; case nsXPTType::T_CHAR_STR: case nsXPTType::T_WCHAR_STR: case nsXPTType::T_IID: case nsXPTType::T_ASTRING: case nsXPTType::T_DOMSTRING: case nsXPTType::T_UTF8STRING: case nsXPTType::T_CSTRING: case nsXPTType::T_INTERFACE: case nsXPTType::T_INTERFACE_IS: retval.l = env->CallObjectMethodA(javaObject, mid, java_params); break; case nsXPTType::T_VOID: retval.j = env->CallLongMethodA(javaObject, mid, java_params); break; default: NS_WARNING("Unhandled retval type"); break; } } // Check for exception from called Java function jthrowable exp = env->ExceptionOccurred(); if (exp) { // If the exception is an instance of XPCOMException, then get the // nsresult from the exception instance. Else, default to // NS_ERROR_FAILURE. if (env->IsInstanceOf(exp, xpcomExceptionClass)) { jfieldID fid; fid = env->GetFieldID(xpcomExceptionClass, "errorcode", "J"); if (fid) { rv = env->GetLongField(exp, fid); } else { rv = NS_ERROR_FAILURE; } NS_ASSERTION(fid, "Couldn't get 'errorcode' field of XPCOMException"); } else { rv = NS_ERROR_FAILURE; } } } // Handle any 'inout', 'out' and 'retval' params if (NS_SUCCEEDED(rv)) { for (PRUint8 i = 0; i < paramCount; i++) { const nsXPTParamInfo ¶mInfo = aMethodInfo->params[i]; if (paramInfo.IsIn() && !paramInfo.IsOut() && !paramInfo.IsDipper()) // 'in' continue; // If param is null, then caller is not expecting an output value. if (aParams[i].val.p == nsnull) continue; if (!paramInfo.IsRetval()) { rv = FinalizeJavaParams(paramInfo, aMethodInfo, aMethodIndex, aParams, aParams[i], java_params[i]); } else { rv = FinalizeJavaParams(paramInfo, aMethodInfo, aMethodIndex, aParams, aParams[i], retval); } } NS_ASSERTION(NS_SUCCEEDED(rv), "FinalizeJavaParams/SetXPCOMRetval failed"); } if (java_params) delete [] java_params; #ifdef DEBUG if (env->ExceptionCheck()) env->ExceptionDescribe(); #endif env->ExceptionClear(); LOG(("<--- (Java) %s::%s()\n", ifaceName, aMethodInfo->name)); return rv; }