JniLocalRef::~JniLocalRef() { if ((m_obj != nullptr) && !m_isWeak) { JEnv env; env.DeleteLocalRef(m_obj); } }
JsArgToArrayConverter::~JsArgToArrayConverter() { if (m_argsLen > 0) { JEnv env; int length = m_storedIndexes.size(); for (int i = 0; i < length; i++) { int index = m_storedIndexes[i]; env.DeleteLocalRef(m_argsAsObject[index]); } delete[] m_argsAsObject; } }
//delete the returned local reference after use jobjectArray NativeScriptRuntime::GetMethodOverrides(JEnv& env, const Local<Object>& implementationObject) { if (implementationObject.IsEmpty()) { return JavaObjectArrayCache::GetJavaStringArray(0); } vector<jstring> methodNames; auto propNames = implementationObject->GetOwnPropertyNames(); for (int i = 0; i < propNames->Length(); i++) { auto name = propNames->Get(i).As<String>(); auto method = implementationObject->Get(name); bool methodFound = !method.IsEmpty() && method->IsFunction(); if (methodFound) { String::Utf8Value stringValue(name); jstring value = env.NewStringUTF(*stringValue); methodNames.push_back(value); } } int methodCount = methodNames.size(); jobjectArray methodOverrides = JavaObjectArrayCache::GetJavaStringArray(methodCount); for (int i = 0; i < methodCount; i++) { env.SetObjectArrayElement(methodOverrides, i, methodNames[i]); } for (int i = 0; i < methodCount; i++) { env.DeleteLocalRef(methodNames[i]); } return methodOverrides; }
JniLocalRef& JniLocalRef::operator=(const JniLocalRef& rhs) { if(this != &rhs) { m_isWeak = rhs.m_isWeak; if (m_isWeak) { m_obj = rhs.m_obj; } else { JEnv env; if (m_obj != nullptr) { env.DeleteLocalRef(m_obj); } m_obj = (rhs.m_obj != nullptr) ? env.NewLocalRef(rhs.m_obj) : nullptr; } } return *this; }
Handle<Object> NativeScriptRuntime::FindClass(const string& className) { Handle<Object> clazz; JEnv env; jmethodID mid = env.GetStaticMethodID(PlatformClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); assert(mid != nullptr); JniLocalRef name(env.NewStringUTF(className.c_str())); jclass c = (jclass)env.CallStaticObjectMethod(PlatformClass, mid, (jstring)name); if (env.ExceptionCheck() == JNI_FALSE) { jint javaObjectID = objectManager->GetOrCreateObjectId(c); clazz = objectManager->GetJsObjectByJavaObject(javaObjectID); if (clazz.IsEmpty()) { clazz = objectManager->CreateJSWrapper(javaObjectID, "Ljava/lang/Class;", c); } env.DeleteLocalRef(c); } return clazz; }
Local<Value> FieldAccessor::GetJavaField(Isolate *isolate, const Local<Object>& target, FieldCallbackData *fieldData) { JEnv env; EscapableHandleScope handleScope(isolate); auto runtime = Runtime::GetRuntime(isolate); auto objectManager = runtime->GetObjectManager(); Local<Value> fieldResult; JniLocalRef targetJavaObject; const auto& fieldTypeName = fieldData->signature; auto isStatic = fieldData->isStatic; auto isPrimitiveType = fieldTypeName.size() == 1; auto isFieldArray = fieldTypeName[0] == '['; if (fieldData->fid == nullptr) { auto fieldJniSig = isPrimitiveType ? fieldTypeName : (isFieldArray ? fieldTypeName : ("L" + fieldTypeName + ";")); if (isStatic) { fieldData->clazz = env.FindClass(fieldData->declaringType); fieldData->fid = env.GetStaticFieldID(fieldData->clazz, fieldData->name, fieldJniSig); } else { fieldData->clazz = env.FindClass(fieldData->declaringType); fieldData->fid = env.GetFieldID(fieldData->clazz, fieldData->name, fieldJniSig); } } if (!isStatic) { targetJavaObject = objectManager->GetJavaObjectByJsObject(target); if (targetJavaObject.IsNull()) { stringstream ss; ss << "Cannot access property '" << fieldData->name << "' because there is no corresponding Java object"; throw NativeScriptException(ss.str()); } } auto fieldId = fieldData->fid; auto clazz = fieldData->clazz; if (isPrimitiveType) { switch (fieldTypeName[0]) { case 'Z': //bool { jboolean result; if (isStatic) { result = env.GetStaticBooleanField(clazz, fieldId); } else { result = env.GetBooleanField(targetJavaObject, fieldId); } fieldResult = Boolean::New(isolate, (result == JNI_TRUE)); break; } case 'B': //byte { jbyte result; if (isStatic) { result = env.GetStaticByteField(clazz, fieldId); } else { result = env.GetByteField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(Int32::New(isolate, result)); break; } case 'C': //char { jchar result; if (isStatic) { result = env.GetStaticCharField(clazz, fieldId); } else { result = env.GetCharField(targetJavaObject, fieldId); } JniLocalRef str(env.NewString(&result, 1)); jboolean bol = true; const char* resP = env.GetStringUTFChars(str, &bol); fieldResult = handleScope.Escape(ConvertToV8String(resP, 1)); env.ReleaseStringUTFChars(str, resP); break; } case 'S': //short { jshort result; if (isStatic) { result = env.GetStaticShortField(clazz, fieldId); } else { result = env.GetShortField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(Int32::New(isolate, result)); break; } case 'I': //int { jint result; if (isStatic) { result = env.GetStaticIntField(clazz, fieldId); } else { result = env.GetIntField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(Int32::New(isolate, result)); break; } case 'J': //long { jlong result; if (isStatic) { result = env.GetStaticLongField(clazz, fieldId); } else { result = env.GetLongField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(ArgConverter::ConvertFromJavaLong(isolate, result)); break; } case 'F': //float { jfloat result; if (isStatic) { result = env.GetStaticFloatField(clazz, fieldId); } else { result = env.GetFloatField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(Number::New(isolate, (double) result)); break; } case 'D': //double { jdouble result; if (isStatic) { result = env.GetStaticDoubleField(clazz, fieldId); } else { result = env.GetDoubleField(targetJavaObject, fieldId); } fieldResult = handleScope.Escape(Number::New(isolate, result)); break; } default: { stringstream ss; ss << "(InternalError): in FieldAccessor::GetJavaField: Unknown field type: '" << fieldTypeName[0] << "'"; throw NativeScriptException(ss.str()); } } } else { jobject result; if (isStatic) { result = env.GetStaticObjectField(clazz, fieldId); } else { result = env.GetObjectField(targetJavaObject, fieldId); } if (result != nullptr) { bool isString = fieldTypeName == "java/lang/String"; if (isString) { auto resultV8Value = ArgConverter::jstringToV8String((jstring) result); fieldResult = handleScope.Escape(resultV8Value); } else { int javaObjectID = objectManager->GetOrCreateObjectId(result); auto objectResult = objectManager->GetJsObjectByJavaObject(javaObjectID); if (objectResult.IsEmpty()) { objectResult = objectManager->CreateJSWrapper(javaObjectID, fieldTypeName, result); } fieldResult = handleScope.Escape(objectResult); } env.DeleteLocalRef(result); } else { fieldResult = handleScope.Escape(Null(isolate)); } } return fieldResult; }
void FieldAccessor::SetJavaField(const Local<Object>& target, const Local<Value>& value, FieldCallbackData *fieldData) { JEnv env; auto isolate = Isolate::GetCurrent(); HandleScope handleScope(isolate); jweak targetJavaObject; const auto& fieldTypeName = fieldData->signature; auto isStatic = fieldData->isStatic; auto isPrimitiveType = fieldTypeName.size() == 1; auto isFieldArray = fieldTypeName[0] == '['; if (fieldData->fid == nullptr) { auto fieldJniSig = isPrimitiveType ? fieldTypeName : (isFieldArray ? fieldTypeName : ("L" + fieldTypeName + ";")); if (isStatic) { fieldData->clazz = env.FindClass(fieldData->declaringType); assert(fieldData->clazz != nullptr); fieldData->fid = env.GetStaticFieldID(fieldData->clazz, fieldData->name, fieldJniSig); assert(fieldData->fid != nullptr); } else { fieldData->clazz = env.FindClass(fieldData->declaringType); assert(fieldData->clazz != nullptr); fieldData->fid = env.GetFieldID(fieldData->clazz, fieldData->name, fieldJniSig); assert(fieldData->fid != nullptr); } } if (!isStatic) { targetJavaObject = objectManager->GetJavaObjectByJsObject(target); } auto fieldId = fieldData->fid; auto clazz = fieldData->clazz; if (isPrimitiveType) { switch (fieldTypeName[0]) { case 'Z': //bool { //TODO: validate value is a boolean before calling if (isStatic) { env.SetStaticBooleanField(clazz, fieldId, value->BooleanValue()); } else { env.SetBooleanField(targetJavaObject, fieldId, value->BooleanValue()); } break; } case 'B': //byte { //TODO: validate value is a byte before calling if (isStatic) { env.SetStaticByteField(clazz, fieldId, value->Int32Value()); } else { env.SetByteField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'C': //char { //TODO: validate value is a single char String::Utf8Value stringValue(value->ToString()); JniLocalRef value(env.NewStringUTF(*stringValue)); const char* chars = env.GetStringUTFChars(value, 0); if (isStatic) { env.SetStaticCharField(clazz, fieldId, chars[0]); } else { env.SetCharField(targetJavaObject, fieldId, chars[0]); } env.ReleaseStringUTFChars(value, chars); break; } case 'S': //short { //TODO: validate value is a short before calling if (isStatic) { env.SetStaticShortField(clazz, fieldId, value->Int32Value()); } else { env.SetShortField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'I': //int { //TODO: validate value is a int before calling if (isStatic) { env.SetStaticIntField(clazz, fieldId, value->Int32Value()); } else { env.SetIntField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'J': //long { jlong longValue = static_cast<jlong>(ArgConverter::ConvertToJavaLong(value)); if (isStatic) { env.SetStaticLongField(clazz, fieldId, longValue); } else { env.SetLongField(targetJavaObject, fieldId, longValue); } break; } case 'F': //float { if (isStatic) { env.SetStaticFloatField(clazz, fieldId, static_cast<jfloat>(value->NumberValue())); } else { env.SetFloatField(targetJavaObject, fieldId, static_cast<jfloat>(value->NumberValue())); } break; } case 'D': //double { if (isStatic) { env.SetStaticDoubleField(clazz, fieldId, value->NumberValue()); } else { env.SetDoubleField(targetJavaObject, fieldId, value->NumberValue()); } break; } default: { // TODO: ASSERT_FAIL("Unknown field type"); break; } } } else { bool isString = fieldTypeName == "java/lang/String"; jobject result = nullptr; if(!value->IsNull()) { if (isString) { //TODO: validate valie is a string; result = ConvertToJavaString(value); } else { auto objectWithHiddenID = value->ToObject(); result =objectManager->GetJavaObjectByJsObject(objectWithHiddenID); } } if (isStatic) { env.SetStaticObjectField(clazz, fieldId, result); } else { env.SetObjectField(targetJavaObject, fieldId, result); } if (isString) { env.DeleteLocalRef(result); } } }
Local<Value> ArrayElementAccessor::GetArrayElement(const Local<Object>& array, uint32_t index, const string& arraySignature) { JEnv env; Isolate* isolate = Isolate::GetCurrent(); EscapableHandleScope handleScope(isolate); jweak arr = objectManager->GetJavaObjectByJsObject(array); Local<Value> value; jsize startIndex = index; const jsize length = 1; const string elementSignature = arraySignature.substr(1); jboolean isCopy = false; if (elementSignature == "Z") { jbooleanArray boolArr = reinterpret_cast<jbooleanArray>(arr); jboolean boolArrValue; env.GetBooleanArrayRegion(boolArr, startIndex, length, &boolArrValue); value = ConvertToJsValue(env, elementSignature, &boolArrValue); } else if (elementSignature == "B") { jbyteArray byteArr = reinterpret_cast<jbyteArray>(arr); jbyte byteArrValue; env.GetByteArrayRegion(byteArr, startIndex, length, &byteArrValue); value = ConvertToJsValue(env, elementSignature, &byteArrValue); } else if (elementSignature == "C") { jcharArray charArr = reinterpret_cast<jcharArray>(arr); jchar charArrValue; env.GetCharArrayRegion(charArr, startIndex, length, &charArrValue); JniLocalRef s(env.NewString(&charArrValue, 1)); const char* singleChar = env.GetStringUTFChars(s, &isCopy); value = ConvertToJsValue(env, elementSignature, singleChar); env.ReleaseStringUTFChars(s, singleChar); } else if (elementSignature == "S") { jshortArray shortArr = reinterpret_cast<jshortArray>(arr); jshort shortArrValue; env.GetShortArrayRegion(shortArr, startIndex, length, &shortArrValue); value = ConvertToJsValue(env, elementSignature, &shortArrValue); } else if (elementSignature == "I") { jintArray intArr = reinterpret_cast<jintArray>(arr); jint intArrValue; env.GetIntArrayRegion(intArr, startIndex, length, &intArrValue); value = ConvertToJsValue(env, elementSignature, &intArrValue); } else if (elementSignature == "J") { jlongArray longArr = reinterpret_cast<jlongArray>(arr); jlong longArrValue; env.GetLongArrayRegion(longArr, startIndex, length, &longArrValue); value = ConvertToJsValue(env, elementSignature, &longArrValue); } else if (elementSignature == "F") { jfloatArray floatArr = reinterpret_cast<jfloatArray>(arr); jfloat floatArrValue; env.GetFloatArrayRegion(floatArr, startIndex, length, &floatArrValue); value = ConvertToJsValue(env, elementSignature, &floatArrValue); } else if (elementSignature == "D") { jdoubleArray doubleArr = reinterpret_cast<jdoubleArray>(arr); jdouble doubleArrValue; env.GetDoubleArrayRegion(doubleArr, startIndex, length, &doubleArrValue); value = ConvertToJsValue(env, elementSignature, &doubleArrValue); } else { jobject result = env.GetObjectArrayElement(reinterpret_cast<jobjectArray>(arr), index); value = ConvertToJsValue(env, elementSignature, &result); env.DeleteLocalRef(result); } return handleScope.Escape(value); }
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); } }