diff --git a/binding.gyp b/binding.gyp index dd13697b..143cf0b1 100644 --- a/binding.gyp +++ b/binding.gyp @@ -21,9 +21,12 @@ ['OS=="mac"', { 'javaver%' : "=7.0.0" }, diff --git a/src/java.cpp b/src/java.cpp index 13a3433a..fb5cfbd5 100644 --- a/src/java.cpp +++ b/src/java.cpp @@ -9,6 +9,7 @@ #include "methodCallBaton.h" #include "node_NodeDynamicProxyClass.h" #include +#include #include #include @@ -22,6 +23,10 @@ threadId v8ThreadId; +std::queue queue_dynamicProxyJsCallData; +uv_mutex_t uvMutex_dynamicProxyJsCall; +uv_async_t uvAsync_dynamicProxyJsCall; + /*static*/ Nan::Persistent Java::s_ct; /*static*/ std::string Java::s_nativeBindingLocation; @@ -49,11 +54,34 @@ bool v8ThreadIdEquals(threadId a, threadId b) { #endif } +void EIO_CallJs(DynamicProxyJsCallData *callData); + +void uvAsyncCb_dynamicProxyJsCall(uv_async_t *handle) { + DynamicProxyJsCallData *callData; + do { + uv_mutex_lock(&uvMutex_dynamicProxyJsCall); + if(!queue_dynamicProxyJsCallData.empty()) { + callData = queue_dynamicProxyJsCallData.front(); + queue_dynamicProxyJsCallData.pop(); + } else { + callData = NULL; + } + uv_mutex_unlock(&uvMutex_dynamicProxyJsCall); + + if(callData) { + EIO_CallJs(callData); + } + } while(callData); +} + /*static*/ void Java::Init(v8::Local target) { Nan::HandleScope scope; v8ThreadId = my_getThreadId(); + uv_mutex_init(&uvMutex_dynamicProxyJsCall); + uv_async_init(uv_default_loop(), &uvAsync_dynamicProxyJsCall, uvAsyncCb_dynamicProxyJsCall); + v8::Local t = Nan::New(New); s_ct.Reset(t); t->InstanceTemplate()->SetInternalFieldCount(1); @@ -203,7 +231,7 @@ v8::Local Java::createJVM(JavaVM** jvm, JNIEnv** env) { classPath << ":"; #endif } - v8::Local arrayItemValue = classPathArrayTemp->Get(i); + v8::Local arrayItemValue = classPathArrayTemp->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); if(!arrayItemValue->IsString()) { return Nan::TypeError("Classpath must only contain strings"); } @@ -231,7 +259,7 @@ v8::Local Java::createJVM(JavaVM** jvm, JNIEnv** env) { //printf("classPath: %s\n", classPath.str().c_str()); vmOptions[0].optionString = strdup(classPath.str().c_str()); for(uint32_t i=0; iLength(); i++) { - v8::Local arrayItemValue = optionsArrayTemp->Get(i); + v8::Local arrayItemValue = optionsArrayTemp->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); if(!arrayItemValue->IsString()) { delete[] vmOptions; return Nan::TypeError("options must only contain strings"); @@ -769,7 +797,7 @@ NAN_METHOD(Java::newArray) { if(strcmp(className.c_str(), "byte") == 0) { results = env->NewByteArray(arrayObj->Length()); for(uint32_t i=0; iLength(); i++) { - v8::Local item = arrayObj->Get(i); + v8::Local item = arrayObj->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); jobject val = v8ToJava(env, item); jclass byteClazz = env->FindClass("java/lang/Byte"); jmethodID byte_byteValue = env->GetMethodID(byteClazz, "byteValue", "()B"); @@ -783,7 +811,7 @@ NAN_METHOD(Java::newArray) { else if(strcmp(className.c_str(), "char") == 0) { results = env->NewCharArray(arrayObj->Length()); for(uint32_t i=0; iLength(); i++) { - v8::Local item = arrayObj->Get(i); + v8::Local item = arrayObj->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); jobject val = v8ToJava(env, item); jclass stringClazz = env->FindClass("java/lang/String"); jmethodID string_charAt = env->GetMethodID(stringClazz, "charAt", "(I)C"); @@ -797,7 +825,7 @@ NAN_METHOD(Java::newArray) { else if(strcmp(className.c_str(), "short") == 0) { results = env->NewShortArray(arrayObj->Length()); for(uint32_t i=0; iLength(); i++) { - v8::Local item = arrayObj->Get(i); + v8::Local item = arrayObj->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); jobject val = v8ToJava(env, item); jclass shortClazz = env->FindClass("java/lang/Short"); jmethodID short_shortValue = env->GetMethodID(shortClazz, "shortValue", "()S"); @@ -811,7 +839,7 @@ NAN_METHOD(Java::newArray) { else if(strcmp(className.c_str(), "double") == 0) { results = env->NewDoubleArray(arrayObj->Length()); for(uint32_t i=0; iLength(); i++) { - v8::Local item = arrayObj->Get(i); + v8::Local item = arrayObj->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); jobject val = v8ToJava(env, item); jclass doubleClazz = env->FindClass("java/lang/Double"); jmethodID double_doubleValue = env->GetMethodID(doubleClazz, "doubleValue", "()D"); @@ -825,7 +853,7 @@ NAN_METHOD(Java::newArray) { else if(strcmp(className.c_str(), "int") == 0) { results = env->NewIntArray(arrayObj->Length()); for(uint32_t i=0; iLength(); i++) { - v8::Local item = arrayObj->Get(i); + v8::Local item = arrayObj->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); jobject val = v8ToJava(env, item); jclass integerClazz = env->FindClass("java/lang/Integer"); jmethodID integer_intValue = env->GetMethodID(integerClazz, "intValue", "()I"); @@ -839,7 +867,7 @@ NAN_METHOD(Java::newArray) { else if(strcmp(className.c_str(), "float") == 0) { results = env->NewFloatArray(arrayObj->Length()); for(uint32_t i=0; iLength(); i++) { - v8::Local item = arrayObj->Get(i); + v8::Local item = arrayObj->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); jobject val = v8ToJava(env, item); jclass floatClazz = env->FindClass("java/lang/Float"); jmethodID float_floatValue = env->GetMethodID(floatClazz, "floatValue", "()F"); @@ -853,7 +881,7 @@ NAN_METHOD(Java::newArray) { else if(strcmp(className.c_str(), "boolean") == 0) { results = env->NewBooleanArray(arrayObj->Length()); for(uint32_t i=0; iLength(); i++) { - v8::Local item = arrayObj->Get(i); + v8::Local item = arrayObj->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); jobject val = v8ToJava(env, item); jclass booleanClazz = env->FindClass("java/lang/Boolean"); jmethodID boolean_booleanValue = env->GetMethodID(booleanClazz, "booleanValue", "()Z"); @@ -877,7 +905,7 @@ NAN_METHOD(Java::newArray) { results = env->NewObjectArray(arrayObj->Length(), clazz, NULL); for(uint32_t i=0; iLength(); i++) { - v8::Local item = arrayObj->Get(i); + v8::Local item = arrayObj->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); jobject val = v8ToJava(env, item); env->SetObjectArrayElement((jobjectArray)results, i, val); if(env->ExceptionOccurred()) { @@ -1211,9 +1239,6 @@ NAN_METHOD(Java::instanceOf) { info.GetReturnValue().Set(Nan::New(res)); } -void EIO_CallJs(uv_work_t* req) { -} - template std::string to_string(T value) { std::ostringstream os; @@ -1221,23 +1246,22 @@ std::string to_string(T value) { return os.str(); } -#if NODE_MINOR_VERSION >= 10 -void EIO_AfterCallJs(uv_work_t* req, int status) { -#else -void EIO_AfterCallJs(uv_work_t* req) { -#endif - DynamicProxyData* dynamicProxyData = static_cast(req->data); +void EIO_CallJs(DynamicProxyJsCallData *callData) { + DynamicProxyData* dynamicProxyData = callData->dynamicProxyData; + + assert(callData->done == 0); + if(!dynamicProxyDataVerify(dynamicProxyData)) { return; } - dynamicProxyData->result = NULL; + callData->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(ret); - dynamicProxyData->done = DYNAMIC_PROXY_JS_ERROR; + callData->throwableClass = "java/lang/IllegalStateException"; + callData->throwableMessage = "Could not retrieve JNIEnv: jvm->GetEnv returned " + to_string(ret); + callData->done = DYNAMIC_PROXY_JS_ERROR; return; } @@ -1251,31 +1275,31 @@ void EIO_AfterCallJs(uv_work_t* req) { jobject javaResult; v8::Local dynamicProxyDataFunctions = Nan::New(dynamicProxyData->functions); - v8::Local fnObj = dynamicProxyDataFunctions->Get(Nan::New(dynamicProxyData->methodName.c_str()).ToLocalChecked()); + v8::Local fnObj = dynamicProxyDataFunctions->Get(Nan::GetCurrentContext(), Nan::New(callData->methodName.c_str()).ToLocalChecked()).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; + callData->throwableClass = "java/lang/NoSuchMethodError"; + callData->throwableMessage = "Could not find js function " + callData->methodName; + callData->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; + callData->throwableClass = "java/lang/IllegalStateException"; + callData->throwableMessage = callData->methodName + " is not a function"; + callData->done = DYNAMIC_PROXY_JS_ERROR; return; } fn = fnObj.As(); - if(dynamicProxyData->args) { - v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, dynamicProxyData->args)); + if(callData->args) { + v8Args = v8::Array::Cast(*javaArrayToV8(dynamicProxyData->java, env, callData->args)); argc = v8Args->Length(); } else { argc = 0; } argv = new v8::Local[argc]; for(i=0; iGet(i); + argv[i] = v8Args->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); } Nan::TryCatch tryCatch; @@ -1283,11 +1307,11 @@ void EIO_AfterCallJs(uv_work_t* req) { v8Result = Nan::Call(fn, dynamicProxyDataFunctions, argc, argv).FromMaybe(v8::Local()); delete[] argv; if (tryCatch.HasCaught()) { - dynamicProxyData->throwableClass = "node/NodeJsException"; + callData->throwableClass = "node/NodeJsException"; Nan::Utf8String message(tryCatch.Message()->Get()); - dynamicProxyData->throwableMessage = std::string(*message); + callData->throwableMessage = std::string(*message); tryCatch.Reset(); - dynamicProxyData->done = DYNAMIC_PROXY_JS_ERROR; + callData->done = DYNAMIC_PROXY_JS_ERROR; return; } @@ -1297,12 +1321,12 @@ void EIO_AfterCallJs(uv_work_t* req) { javaResult = v8ToJava(env, v8Result); if(javaResult == NULL) { - dynamicProxyData->result = NULL; + callData->result = NULL; } else { - dynamicProxyData->result = env->NewGlobalRef(javaResult); + callData->result = env->NewGlobalRef(javaResult); } - dynamicProxyData->done = true; + callData->done = true; } void throwNewThrowable(JNIEnv* env, const char * excClassName, std::string msg) { @@ -1319,37 +1343,37 @@ JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jo 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 = ""; + + // args needs to be global, you can't send env across thread boundaries + DynamicProxyJsCallData callData; + callData.dynamicProxyData = dynamicProxyData; + callData.args = args; + callData.done = false; + callData.result = NULL; + callData.throwableClass = ""; + callData.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)); + callData.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 + EIO_CallJs(&callData); } 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); + callData.args = (jobjectArray) env->NewGlobalRef(args); hasArgsGlobalRef = true; } - uv_queue_work(uv_default_loop(), req, EIO_CallJs, (uv_after_work_cb)EIO_AfterCallJs); + uv_mutex_lock(&uvMutex_dynamicProxyJsCall); + queue_dynamicProxyJsCallData.push(&callData); // we wait for work to finish, so ok to pass ref to local var + uv_mutex_unlock(&uvMutex_dynamicProxyJsCall); + uv_async_send(&uvAsync_dynamicProxyJsCall); - while(!dynamicProxyData->done) { + while(!callData.done) { my_sleep(100); } } @@ -1358,18 +1382,18 @@ JNIEXPORT jobject JNICALL Java_node_NodeDynamicProxyClass_callJs(JNIEnv *env, jo throwNewThrowable(env, "java/lang/IllegalStateException", "dynamicProxyData was corrupted"); } if(hasArgsGlobalRef) { - env->DeleteGlobalRef(dynamicProxyData->args); + env->DeleteGlobalRef(callData.args); } - if (dynamicProxyData->done == DYNAMIC_PROXY_JS_ERROR) { - throwNewThrowable(env, dynamicProxyData->throwableClass.c_str(), dynamicProxyData->throwableMessage); + if (callData.done == DYNAMIC_PROXY_JS_ERROR) { + throwNewThrowable(env, callData.throwableClass.c_str(), callData.throwableMessage); } jobject result = NULL; - if(dynamicProxyData->result) { + if(callData.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); + result = env->NewLocalRef(callData.result); + env->DeleteGlobalRef(callData.result); } return result; } diff --git a/src/javaObject.cpp b/src/javaObject.cpp index d0afc20d..0d67aa8a 100644 --- a/src/javaObject.cpp +++ b/src/javaObject.cpp @@ -29,8 +29,8 @@ v8::Local promisify; if(java->DoPromise()) { - v8::Local asyncOptions = java->handle()->Get(Nan::New("asyncOptions").ToLocalChecked()).As(); - v8::Local promisifyValue = asyncOptions->Get(Nan::New("promisify").ToLocalChecked()); + v8::Local asyncOptions = java->handle()->Get(Nan::GetCurrentContext(), Nan::New("asyncOptions").ToLocalChecked()).ToLocalChecked().As(); + v8::Local promisifyValue = asyncOptions->Get(Nan::GetCurrentContext(), Nan::New("promisify").ToLocalChecked()).ToLocalChecked(); promisify = promisifyValue.As(); } diff --git a/src/utils.cpp b/src/utils.cpp index 1d5dffc0..1c8aceca 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -275,7 +275,7 @@ static std::string getArrayElementType(v8::Local array, uint32_t arra } for(uint32_t i=0; i arg = array->Get(i); + v8::Local arg = array->Get(Nan::GetCurrentContext(), i).ToLocalChecked(); if (arg->IsArray()) { return kObject; // We can exit as soon as we know java/lang/Object is required. } @@ -332,7 +332,7 @@ jobject v8ToJava(JNIEnv* env, v8::Local arg) { jclass objectClazz = env->FindClass(arrayType.c_str()); jobjectArray result = env->NewObjectArray(arraySize, objectClazz, NULL); for(uint32_t i=0; iGet(i)); + jobject val = v8ToJava(env, array->Get(Nan::GetCurrentContext(), i).ToLocalChecked()); env->SetObjectArrayElement(result, i, val); } return result; @@ -405,7 +405,7 @@ void checkJavaException(JNIEnv* env) { } jobject v8ToJava_javaLong(JNIEnv* env, v8::Local obj) { - jobject longValue = v8ToJava(env, obj->Get(Nan::New("longValue").ToLocalChecked())); + jobject longValue = v8ToJava(env, obj->Get(Nan::GetCurrentContext(), Nan::New("longValue").ToLocalChecked()).ToLocalChecked()); jclass longClazz = env->FindClass("java/lang/Long"); jmethodID long_constructor = env->GetMethodID(longClazz, "", "(Ljava/lang/String;)V"); jobject jobj = env->NewObject(longClazz, long_constructor, longValue); @@ -452,7 +452,7 @@ v8::Local javaExceptionToV8(Java* java, JNIEnv* env, jthrowable ex, c msg << "\n" << javaExceptionToString(env, ex); v8::Local v8ex = v8::Exception::Error(Nan::New(msg.str().c_str()).ToLocalChecked()); - ((v8::Object*)*v8ex)->Set(Nan::New("cause").ToLocalChecked(), javaToV8(java, env, ex)); + ((v8::Object*)*v8ex)->Set(Nan::GetCurrentContext(), Nan::New("cause").ToLocalChecked(), javaToV8(java, env, ex)); return v8ex; } @@ -484,7 +484,11 @@ jvalueType javaGetArrayComponentType(JNIEnv *env, jobjectArray array) { #if (NODE_VERSION_AT_LEAST(4, 0, 0)) v8::Local newArrayBuffer(void* elems, size_t length) { v8::Local ab = v8::ArrayBuffer::New(v8::Isolate::GetCurrent(), length); +#if (V8_MAJOR_VERSION >= 8) + memcpy(ab->GetBackingStore()->Data(), elems, length); +#else memcpy(ab->GetContents().Data(), elems, length); +#endif return ab; } #endif @@ -639,7 +643,7 @@ v8::Local javaArrayToV8(Java* java, JNIEnv* env, jobjectArray objArra jlong* elems = env->GetLongArrayElements((jlongArray)objArray, 0); for(jsize i=0; iSet(i, JavaObject::New(java, obj)); + result->Set(Nan::GetCurrentContext(), i, JavaObject::New(java, obj)); } env->ReleaseLongArrayElements((jlongArray)objArray, elems, 0); } @@ -649,7 +653,7 @@ v8::Local javaArrayToV8(Java* java, JNIEnv* env, jobjectArray objArra for(jsize i=0; iGetObjectArrayElement(objArray, i); v8::Local item = javaToV8(java, env, obj); - result->Set(i, item); + result->Set(Nan::GetCurrentContext(), i, item); } break; } @@ -712,7 +716,7 @@ v8::Local javaToV8(Java* java, JNIEnv* env, jobject obj, DynamicProxy std::string strValue = javaObjectToString(env, obj); v8::Local v8Result = Nan::New((double)result); v8::NumberObject* v8ResultNumberObject = v8::NumberObject::Cast(*v8Result); - v8ResultNumberObject->Set(Nan::New("longValue").ToLocalChecked(), Nan::New(strValue.c_str()).ToLocalChecked()); + v8ResultNumberObject->Set(Nan::GetCurrentContext(), Nan::New("longValue").ToLocalChecked(), Nan::New(strValue.c_str()).ToLocalChecked()); SetHiddenValue(v8ResultNumberObject, Nan::New(V8_HIDDEN_MARKER_JAVA_LONG).ToLocalChecked(), Nan::New(true)); return v8Result; } diff --git a/src/utils.h b/src/utils.h index 9ba87b11..77f4b5b2 100644 --- a/src/utils.h +++ b/src/utils.h @@ -37,13 +37,17 @@ struct DynamicProxyData { std::string interfaceName; Nan::Persistent functions; Nan::Persistent jsObject; + unsigned int markerEnd; +}; + +struct DynamicProxyJsCallData { + DynamicProxyData *dynamicProxyData; std::string methodName; jobjectArray args; jobject result; std::string throwableClass; std::string throwableMessage; int done; - unsigned int markerEnd; }; #define DYNAMIC_PROXY_DATA_MARKER_START 0x12345678