void NativeScriptRuntime::CallJavaMethod(const Local<Object>& caller, const string& className, const string& methodName, MetadataEntry *entry, bool isStatic, bool isSuper, const v8::FunctionCallbackInfo<v8::Value>& args) { SET_PROFILER_FRAME(); JEnv env; jclass clazz; jmethodID mid; string *sig = nullptr; string *returnType = nullptr; auto retType = MethodReturnType::Unknown; MethodCache::CacheMethodInfo mi; if ((entry != nullptr) && entry->isResolved) { isStatic = entry->isStatic; if (entry->memberId == nullptr) { clazz = env.FindClass(className); if (clazz == nullptr) { MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); const string callerClassName = callerNode->GetName(); DEBUG_WRITE("Cannot resolve class: %s while calling method: %s callerClassName: %s", className.c_str(), methodName.c_str(), callerClassName.c_str()); clazz = env.FindClass(callerClassName); if (clazz == nullptr) { //todo: plamen5kov: throw exception here DEBUG_WRITE("Cannot resolve caller's class name: %s", callerClassName.c_str()); return; } entry->memberId = isStatic ? env.GetStaticMethodID(clazz, methodName, entry->sig) : env.GetMethodID(clazz, methodName, entry->sig); if (entry->memberId == nullptr) { //todo: plamen5kov: throw exception here DEBUG_WRITE("Cannot resolve a method %s on caller class: %s", methodName.c_str(), callerClassName.c_str()); return; } } else { entry->memberId = isStatic ? env.GetStaticMethodID(clazz, methodName, entry->sig) : env.GetMethodID(clazz, methodName, entry->sig); if (entry->memberId == nullptr) { //todo: plamen5kov: throw exception here DEBUG_WRITE("Cannot resolve a method %s on class: %s", methodName.c_str(), className.c_str()); return; } } entry->clazz = clazz; } mid = reinterpret_cast<jmethodID>(entry->memberId); clazz = entry->clazz; sig = &entry->sig; returnType = &entry->returnType; retType = entry->retType; } else { DEBUG_WRITE("Resolving method: %s on className %s", methodName.c_str(), className.c_str()); clazz = env.FindClass(className); if (clazz != nullptr) { mi = MethodCache::ResolveMethodSignature(className, methodName, args, isStatic); if (mi.mid == nullptr) { DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d", className.c_str(), methodName.c_str(), isStatic, isSuper); return; } } else { MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); const string callerClassName = callerNode->GetName(); DEBUG_WRITE("Resolving method on caller class: %s.%s on className %s", callerClassName.c_str(), methodName.c_str(), className.c_str()); mi = MethodCache::ResolveMethodSignature(callerClassName, methodName, args, isStatic); if (mi.mid == nullptr) { DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d, callerClass=%s", className.c_str(), methodName.c_str(), isStatic, isSuper, callerClassName.c_str()); return; } } clazz = mi.clazz; mid = mi.mid; sig = &mi.signature; returnType = &mi.returnType; retType = mi.retType; } if (!isStatic) { DEBUG_WRITE("CallJavaMethod called %s.%s. Instance id: %d, isSuper=%d", className.c_str(), methodName.c_str(), caller.IsEmpty() ? -42 : caller->GetIdentityHash(), isSuper); } else { DEBUG_WRITE("CallJavaMethod called %s.%s. static method", className.c_str(), methodName.c_str()); } JsArgConverter argConverter(args, false, *sig, entry); if (!argConverter.IsValid()) { JsArgConverter::Error err = argConverter.GetError(); throw NativeScriptException(err.msg); } auto isolate = Isolate::GetCurrent(); jweak callerJavaObject; jvalue* javaArgs = argConverter.ToArgs(); if (!isStatic) { callerJavaObject = objectManager->GetJavaObjectByJsObject(caller); if(callerJavaObject == nullptr) { stringstream ss; ss << "No java object found on which to call \"" << methodName << "\" method. It is possible your Javascript object is not linked with the corresponding Java class. Try passing context(this) to the constructor function."; throw NativeScriptException(ss.str()); } } switch (retType) { case MethodReturnType::Void: { if (isStatic) { env.CallStaticVoidMethodA(clazz, mid, javaArgs); } else if (isSuper) { env.CallNonvirtualVoidMethodA(callerJavaObject, clazz, mid, javaArgs); } else { env.CallVoidMethodA(callerJavaObject, mid, javaArgs); } break; } case MethodReturnType::Boolean: { jboolean result; if (isStatic) { result = env.CallStaticBooleanMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualBooleanMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallBooleanMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result != 0 ? True(isolate) : False(isolate)); break; } case MethodReturnType::Byte: { jbyte result; if (isStatic) { result = env.CallStaticByteMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualByteMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallByteMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case MethodReturnType::Char: { jchar result; if (isStatic) { result = env.CallStaticCharMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualCharMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallCharMethodA(callerJavaObject, mid, javaArgs); } JniLocalRef str(env.NewString(&result, 1)); jboolean bol = true; const char* resP = env.GetStringUTFChars(str, &bol); args.GetReturnValue().Set(ConvertToV8String(resP, 1)); env.ReleaseStringUTFChars(str, resP); break; } case MethodReturnType::Short: { jshort result; if (isStatic) { result = env.CallStaticShortMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualShortMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallShortMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case MethodReturnType::Int: { jint result; if (isStatic) { result = env.CallStaticIntMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualIntMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallIntMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case MethodReturnType::Long: { jlong result; if (isStatic) { result = env.CallStaticLongMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualLongMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallLongMethodA(callerJavaObject, mid, javaArgs); } auto jsLong = ArgConverter::ConvertFromJavaLong(result); args.GetReturnValue().Set(jsLong); break; } case MethodReturnType::Float: { jfloat result; if (isStatic) { result = env.CallStaticFloatMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualFloatMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallFloatMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set((double) result); //TODO: handle float value here correctly. break; } case MethodReturnType::Double: { jdouble result; if (isStatic) { result = env.CallStaticDoubleMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualDoubleMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallDoubleMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case MethodReturnType::String: { jobject result = nullptr; bool exceptionOccurred; if (isStatic) { result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); } if (result != nullptr) { auto objectResult = ArgConverter::jstringToV8String(static_cast<jstring>(result)); args.GetReturnValue().Set(objectResult); env.DeleteLocalRef(result); } else { args.GetReturnValue().Set(Null(isolate)); } break; } case MethodReturnType::Object: { jobject result = nullptr; bool exceptionOccurred; if (isStatic) { result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); } if (result != nullptr) { auto isString = env.IsInstanceOf(result, JAVA_LANG_STRING); Local<Value> objectResult; if (isString) { objectResult = ArgConverter::jstringToV8String((jstring)result); } else { jint javaObjectID = objectManager->GetOrCreateObjectId(result); objectResult = objectManager->GetJsObjectByJavaObject(javaObjectID); if (objectResult.IsEmpty()) { objectResult = objectManager->CreateJSWrapper(javaObjectID, *returnType, result); } } args.GetReturnValue().Set(objectResult); env.DeleteLocalRef(result); } else { args.GetReturnValue().Set(Null(isolate)); } break; } default: { assert(false); break; } } static uint32_t adjustMemCount = 0; if ((++adjustMemCount % 2) == 0) { AdjustAmountOfExternalAllocatedMemory(env, isolate); } }
void NativeScriptRuntime::CallJavaMethod(const Handle<Object>& caller, const string& className, const string& methodName, MetadataEntry *entry, bool isStatic, bool isSuper, const v8::FunctionCallbackInfo<v8::Value>& args) { SET_PROFILER_FRAME(); JEnv env; jclass clazz; jmethodID mid; string sig; if ((entry != nullptr) && entry->isResolved) { isStatic = entry->isStatic; if (entry->memberId == nullptr) { entry->clazz = env.FindClass(className); if (entry->clazz == nullptr) { MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); const string callerClassName = callerNode->GetName(); DEBUG_WRITE("Cannot resolve class: %s while calling method: %s callerClassName: %s", className.c_str(), methodName.c_str(), callerClassName.c_str()); clazz = env.FindClass(callerClassName); if (clazz == nullptr) { DEBUG_WRITE("Cannot resolve caller's class name: %s", callerClassName.c_str()); return; } mid = isStatic ? env.GetStaticMethodID(clazz, methodName, entry->sig) : env.GetMethodID(clazz, methodName, entry->sig); if (mid == nullptr) { DEBUG_WRITE("Cannot resolve a method %s on caller class: %s", methodName.c_str(), callerClassName.c_str()); return; } } else { entry->memberId = isStatic ? env.GetStaticMethodID(entry->clazz, methodName, entry->sig) : env.GetMethodID(entry->clazz, methodName, entry->sig); if (entry->memberId == nullptr) { DEBUG_WRITE("Cannot resolve a method %s on class: %s", methodName.c_str(), className.c_str()); return; } } } if (entry->clazz != nullptr) { clazz = entry->clazz; mid = reinterpret_cast<jmethodID>(entry->memberId); } sig = entry->sig; } else { DEBUG_WRITE("Resolving method: %s on className %s", methodName.c_str(), className.c_str()); MethodCache::CacheMethodInfo mi; clazz = env.FindClass(className); if (clazz != nullptr) { mi = MethodCache::ResolveMethodSignature(className, methodName, args, isStatic); if (mi.mid == nullptr) { DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d", className.c_str(), methodName.c_str(), isStatic, isSuper); return; } } else { MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); const string callerClassName = callerNode->GetName(); DEBUG_WRITE("Resolving method on caller class: %s.%s on className %s", callerClassName.c_str(), methodName.c_str(), className.c_str()); mi = MethodCache::ResolveMethodSignature(callerClassName, methodName, args, isStatic); if (mi.mid == nullptr) { DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d, callerClass=%s", className.c_str(), methodName.c_str(), isStatic, isSuper, callerClassName.c_str()); return; } } clazz = mi.clazz; mid = mi.mid; sig = mi.signature; } if (!isStatic) { DEBUG_WRITE("CallJavaMethod called %s.%s. Instance id: %d, isSuper=%d", className.c_str(), methodName.c_str(), caller.IsEmpty() ? -42 : caller->GetIdentityHash(), isSuper); } else { DEBUG_WRITE("CallJavaMethod called %s.%s. static method", className.c_str(), methodName.c_str()); } JsArgConverter argConverter(args, false, sig); if (!argConverter.IsValid()) { JsArgConverter::Error err = argConverter.GetError(); ExceptionUtil::GetInstance()->ThrowExceptionToJs(err.msg); return; } auto isolate = Isolate::GetCurrent(); jweak callerJavaObject; jvalue* javaArgs = argConverter.ToArgs(); if (!isStatic) { callerJavaObject = objectManager->GetJavaObjectByJsObject(caller); if(callerJavaObject == nullptr) { stringstream ss; ss << "No java object found on which to call \"" << methodName << "\" method. It is possible your Javascript object is not linked with the corresponding Java class. Try passing context(this) to the constructor function."; string exceptionMessage = ss.str(); isolate->ThrowException(v8::Exception::ReferenceError(ConvertToV8String(exceptionMessage))); return; } } auto returnType = GetReturnType(sig); switch (returnType[0]) { case 'V': //void { if (isStatic) { env.CallStaticVoidMethodA(clazz, mid, javaArgs); } else if (isSuper) { env.CallNonvirtualVoidMethodA(callerJavaObject, clazz, mid, javaArgs); } else { env.CallVoidMethodA(callerJavaObject, mid, javaArgs); } break; } case 'Z': //bool { jboolean result; if (isStatic) { result = env.CallStaticBooleanMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualBooleanMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallBooleanMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result != 0 ? True(isolate) : False(isolate)); break; } case 'B': //byte { jbyte result; if (isStatic) { result = env.CallStaticByteMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualByteMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallByteMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case 'C': //char { jchar result; if (isStatic) { result = env.CallStaticCharMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualCharMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallCharMethodA(callerJavaObject, mid, javaArgs); } JniLocalRef str(env.NewString(&result, 1)); jboolean bol = true; const char* resP = env.GetStringUTFChars(str, &bol); args.GetReturnValue().Set(ConvertToV8String(resP, 1)); env.ReleaseStringUTFChars(str, resP); break; } case 'S': //short { jshort result; if (isStatic) { result = env.CallStaticShortMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualShortMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallShortMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case 'I': //int { jint result; if (isStatic) { result = env.CallStaticIntMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualIntMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallIntMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case 'J': //long { jlong result; if (isStatic) { result = env.CallStaticLongMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualLongMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallLongMethodA(callerJavaObject, mid, javaArgs); } auto jsLong = ArgConverter::ConvertFromJavaLong(result); args.GetReturnValue().Set(jsLong); break; } case 'F': //float { jfloat result; if (isStatic) { result = env.CallStaticFloatMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualFloatMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallFloatMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set((double) result); //TODO: handle float value here correctly. break; } case 'D': //double { jdouble result; if (isStatic) { result = env.CallStaticDoubleMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualDoubleMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallDoubleMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case '[': case 'L': { bool isString = returnType == "Ljava/lang/String;"; jobject result = nullptr; bool exceptionOccurred; if (isString) { if (isStatic) { result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); } exceptionOccurred = env.ExceptionCheck() == JNI_TRUE; if (!exceptionOccurred) { auto resultV8String = ArgConverter::jstringToV8String((jstring) result); args.GetReturnValue().Set(resultV8String); } } else { if (isStatic) { result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); } exceptionOccurred = env.ExceptionCheck() == JNI_TRUE; if (!exceptionOccurred) { if (result != nullptr) { isString = env.IsInstanceOf(result, JAVA_LANG_STRING); Handle<Value> objectResult; if (isString) { objectResult = ArgConverter::jstringToV8String((jstring)result); } else { jint javaObjectID = objectManager->GetOrCreateObjectId(result); objectResult = objectManager->GetJsObjectByJavaObject(javaObjectID); if (objectResult.IsEmpty()) { objectResult = objectManager->CreateJSWrapper(javaObjectID, returnType, result); } } args.GetReturnValue().Set(objectResult); } else { args.GetReturnValue().Set(Null(isolate)); } } } if (!exceptionOccurred) { env.DeleteLocalRef(result); } break; } default: { // TODO: ASSERT_FAIL("Unknown return type"); break; } } if (!ExceptionUtil::GetInstance()->CheckForJavaException(env)) { AdjustAmountOfExternalAllocatedMemory(env, isolate); } }