JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jobject src, jlong ptr, jobject method, jobjectArray args) { threadId myThreadId = my_getThreadId(); bool hasArgsGlobalRef = false; // args needs to be global, you can't send env across thread boundaries DynamicProxyData* dynamicProxyData = (DynamicProxyData*)ptr; dynamicProxyData->args = args; dynamicProxyData->done = false; dynamicProxyData->result = NULL; dynamicProxyData->throwableClass = ""; dynamicProxyData->throwableMessage = ""; jclass methodClazz = env->FindClass("java/lang/reflect/Method"); jmethodID method_getName = env->GetMethodID(methodClazz, "getName", "()Ljava/lang/String;"); dynamicProxyData->methodName = javaObjectToString(env, env->CallObjectMethod(method, method_getName)); assertNoException(env); uv_work_t* req = new uv_work_t(); req->data = dynamicProxyData; if(v8ThreadIdEquals(myThreadId, v8ThreadId)) { #if NODE_MINOR_VERSION >= 10 EIO_AfterCallJs(req, 0); #else EIO_AfterCallJs(req); #endif } else { if (args) { // if args is not null and we have to kick this across the thread boundary, make it a global ref dynamicProxyData->args = (jobjectArray) env->NewGlobalRef(args); hasArgsGlobalRef = true; } uv_queue_work(uv_default_loop(), req, EIO_CallJs, (uv_after_work_cb)EIO_AfterCallJs); while(!dynamicProxyData->done) { my_sleep(100); } } if(!dynamicProxyDataVerify(dynamicProxyData)) { throwNewThrowable(env, "java/lang/IllegalStateException", "dynamicProxyData was corrupted"); } if(hasArgsGlobalRef) { env->DeleteGlobalRef(dynamicProxyData->args); } if (dynamicProxyData->done == DYNAMIC_PROXY_JS_ERROR) { throwNewThrowable(env, dynamicProxyData->throwableClass.c_str(), dynamicProxyData->throwableMessage); } jobject result = NULL; if(dynamicProxyData->result) { // need to retain a local ref so that we can return it, otherwise the returned object gets corrupted result = env->NewLocalRef(dynamicProxyData->result); env->DeleteGlobalRef(dynamicProxyData->result); } return result; }
jobject v8ToJava_javaObject(JNIEnv* env, v8::Local<v8::Object> obj) { JavaObject* javaObject = node::ObjectWrap::Unwrap<JavaObject>(obj); jobject jobj = javaObject->getObject(); jclass nodeDynamicProxyClass = env->FindClass("node/NodeDynamicProxyClass"); if(env->IsInstanceOf(jobj, nodeDynamicProxyClass)) { jfieldID ptrField = env->GetFieldID(nodeDynamicProxyClass, "ptr", "J"); DynamicProxyData* proxyData = (DynamicProxyData*)(long)env->GetLongField(jobj, ptrField); if(!dynamicProxyDataVerify(proxyData)) { return NULL; } jclass dynamicInterface = javaFindClass(env, proxyData->interfaceName); if(dynamicInterface == NULL) { printf("Could not find interface %s\n", proxyData->interfaceName.c_str()); return NULL; } jclass classClazz = env->FindClass("java/lang/Class"); jobjectArray classArray = env->NewObjectArray(1, classClazz, NULL); env->SetObjectArrayElement(classArray, 0, dynamicInterface); jmethodID class_getClassLoader = env->GetMethodID(classClazz, "getClassLoader", "()Ljava/lang/ClassLoader;"); jobject classLoader = env->CallObjectMethod(dynamicInterface, class_getClassLoader); if(classLoader == NULL) { jclass objectClazz = env->FindClass("java/lang/Object"); jmethodID object_getClass = env->GetMethodID(objectClazz, "getClass", "()Ljava/lang/Class;"); jobject jobjClass = env->CallObjectMethod(jobj, object_getClass); classLoader = env->CallObjectMethod(jobjClass, class_getClassLoader); } jclass proxyClass = env->FindClass("java/lang/reflect/Proxy"); jmethodID proxy_newProxyInstance = env->GetStaticMethodID(proxyClass, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;"); if(classLoader == NULL) { printf("Could not get classloader for Proxy\n"); return NULL; } if(classArray == NULL) { printf("Could not create class array for Proxy\n"); return NULL; } if(jobj == NULL) { printf("Not a valid object to wrap\n"); return NULL; } jobj = env->CallStaticObjectMethod(proxyClass, proxy_newProxyInstance, classLoader, classArray, jobj); } return jobj; }
JavaObject::~JavaObject() { JNIEnv *env = m_java->getJavaEnv(); jclass nodeDynamicProxyClass = env->FindClass("node/NodeDynamicProxyClass"); if(env->IsInstanceOf(m_obj, nodeDynamicProxyClass)) { jfieldID ptrField = env->GetFieldID(nodeDynamicProxyClass, "ptr", "J"); DynamicProxyData* proxyData = (DynamicProxyData*)(long)env->GetLongField(m_obj, ptrField); if(dynamicProxyDataVerify(proxyData)) { delete proxyData; } } env->DeleteGlobalRef(m_obj); env->DeleteGlobalRef(m_class); }
void EIO_AfterCallJs(uv_work_t* req, int status) { #else void EIO_AfterCallJs(uv_work_t* req) { #endif DynamicProxyData* dynamicProxyData = static_cast<DynamicProxyData*>(req->data); if(!dynamicProxyDataVerify(dynamicProxyData)) { return; } dynamicProxyData->result = NULL; JNIEnv* env; int ret = dynamicProxyData->java->getJvm()->GetEnv((void**)&env, JNI_BEST_VERSION); if (ret != JNI_OK) { dynamicProxyData->throwableClass = "java/lang/IllegalStateException"; dynamicProxyData->throwableMessage = "Could not retrieve JNIEnv: jvm->GetEnv returned " + to_string<int>(ret); dynamicProxyData->done = DYNAMIC_PROXY_JS_ERROR; return; } Nan::HandleScope scope; v8::Array* v8Args; v8::Function* fn; v8::Handle<v8::Value>* argv; int argc; int i; v8::Local<v8::Value> v8Result; jobject javaResult; v8::Local<v8::Object> dynamicProxyDataFunctions = Nan::New(dynamicProxyData->functions); v8::Local<v8::Value> fnObj = dynamicProxyDataFunctions->Get(Nan::New<v8::String>(dynamicProxyData->methodName.c_str()).ToLocalChecked()); if(fnObj->IsUndefined() || fnObj->IsNull()) { dynamicProxyData->throwableClass = "java/lang/NoSuchMethodError"; dynamicProxyData->throwableMessage = "Could not find js function " + dynamicProxyData->methodName; dynamicProxyData->done = DYNAMIC_PROXY_JS_ERROR; return; } if(!fnObj->IsFunction()) { dynamicProxyData->throwableClass = "java/lang/IllegalStateException"; dynamicProxyData->throwableMessage = dynamicProxyData->methodName + " is not a function"; dynamicProxyData->done = DYNAMIC_PROXY_JS_ERROR; return; } fn = v8::Function::Cast(*fnObj); if(dynamicProxyData->args) { v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, dynamicProxyData->args)); argc = v8Args->Length(); } else { argc = 0; } argv = new v8::Handle<v8::Value>[argc]; for(i=0; i<argc; i++) { argv[i] = v8Args->Get(i); } Nan::TryCatch tryCatch; tryCatch.SetCaptureMessage(true); v8Result = fn->Call(dynamicProxyDataFunctions, argc, argv); delete[] argv; if (tryCatch.HasCaught()) { dynamicProxyData->throwableClass = "node/NodeJsException"; v8::String::Utf8Value message(tryCatch.Message()->Get()); dynamicProxyData->throwableMessage = std::string(*message); tryCatch.Reset(); dynamicProxyData->done = DYNAMIC_PROXY_JS_ERROR; return; } if(!dynamicProxyDataVerify(dynamicProxyData)) { return; } javaResult = v8ToJava(env, v8Result); if(javaResult == NULL) { dynamicProxyData->result = NULL; } else { dynamicProxyData->result = env->NewGlobalRef(javaResult); } dynamicProxyData->done = true; }